From 8d5b41f53057b594a2b330f4342b833ba6484c09 Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Mon, 10 Nov 2025 11:19:24 -0800 Subject: [PATCH] update typescript errors for vercel --- FATHOM_INTEGRATION.md | 1 + WORKER_ENV_GUIDE.md | 1 + src/automerge/AutomergeToTLStore.ts | 20 ++- src/automerge/CloudflareAdapter.ts | 12 +- src/automerge/useAutomergeStoreV2.ts | 2 +- src/automerge/useAutomergeSyncRepo.ts | 8 +- src/components/StandardizedToolWrapper.tsx | 2 +- src/components/location/LocationDashboard.tsx | 1 + src/components/location/LocationMap.tsx | 1 + .../location/LocationShareDialog.tsx | 2 +- src/components/location/LocationViewer.tsx | 1 + src/components/location/ShareSettings.tsx | 1 + src/css/location.css | 1 + src/hooks/useWebSpeechTranscription.ts | 10 +- src/lib/HoloSphereService.ts | 15 +- src/lib/location/locationStorage.ts | 55 ++++---- src/lib/location/types.ts | 1 + src/routes/Board.tsx | 6 +- src/routes/LocationDashboardRoute.tsx | 1 + src/routes/LocationShareCreate.tsx | 1 + src/routes/LocationShareView.tsx | 1 + src/shapes/ChatBoxShapeUtil.tsx | 4 +- src/shapes/FathomTranscriptShapeUtil.tsx | 9 ++ src/shapes/ObsNoteShapeUtil.tsx | 2 +- src/tools/HolonTool.ts | 128 +----------------- src/tools/ObsNoteTool.ts | 68 +++++----- src/utils/shapeCollisionUtils.ts | 11 +- switch-worker-env.sh | 1 + worker/worker.ts | 2 +- 29 files changed, 149 insertions(+), 219 deletions(-) diff --git a/FATHOM_INTEGRATION.md b/FATHOM_INTEGRATION.md index 9ed4e70..0f64180 100644 --- a/FATHOM_INTEGRATION.md +++ b/FATHOM_INTEGRATION.md @@ -130,5 +130,6 @@ The Fathom transcript shape includes: + diff --git a/WORKER_ENV_GUIDE.md b/WORKER_ENV_GUIDE.md index 0a8100e..a260567 100644 --- a/WORKER_ENV_GUIDE.md +++ b/WORKER_ENV_GUIDE.md @@ -81,5 +81,6 @@ You can also manually edit the environment by: + diff --git a/src/automerge/AutomergeToTLStore.ts b/src/automerge/AutomergeToTLStore.ts index 9b508f0..44e1ec8 100644 --- a/src/automerge/AutomergeToTLStore.ts +++ b/src/automerge/AutomergeToTLStore.ts @@ -97,23 +97,29 @@ export function applyAutomergePatchesToTLStore( } } - const record = updatedObjects[id] || (existingRecord ? JSON.parse(JSON.stringify(existingRecord)) : defaultRecord) + let record = updatedObjects[id] || (existingRecord ? JSON.parse(JSON.stringify(existingRecord)) : defaultRecord) // CRITICAL: Ensure typeName matches ID pattern (fixes misclassification) // Note: obsidian_vault records are skipped above, so we don't need to handle them here if (typeof id === 'string') { + let correctTypeName = record.typeName if (id.startsWith('shape:') && record.typeName !== 'shape') { - record.typeName = 'shape' + correctTypeName = 'shape' } else if (id.startsWith('page:') && record.typeName !== 'page') { - record.typeName = 'page' + correctTypeName = 'page' } else if (id.startsWith('camera:') && record.typeName !== 'camera') { - record.typeName = 'camera' + correctTypeName = 'camera' } else if (id.startsWith('instance:') && record.typeName !== 'instance') { - record.typeName = 'instance' + correctTypeName = 'instance' } else if (id.startsWith('pointer:') && record.typeName !== 'pointer') { - record.typeName = 'pointer' + correctTypeName = 'pointer' } else if (id.startsWith('document:') && record.typeName !== 'document') { - record.typeName = 'document' + correctTypeName = 'document' + } + + // Create new object with correct typeName if it changed + if (correctTypeName !== record.typeName) { + record = { ...record, typeName: correctTypeName } } } diff --git a/src/automerge/CloudflareAdapter.ts b/src/automerge/CloudflareAdapter.ts index 70139db..44f65a0 100644 --- a/src/automerge/CloudflareAdapter.ts +++ b/src/automerge/CloudflareAdapter.ts @@ -219,19 +219,19 @@ export class CloudflareNetworkAdapter extends NetworkAdapter { // We need to handle both binary and text messages if (event.data instanceof ArrayBuffer) { console.log('🔌 CloudflareAdapter: Received binary message (Automerge protocol)') - // Handle binary Automerge sync messages - pass directly to Repo - // Automerge Repo expects binary sync messages as ArrayBuffer + // Handle binary Automerge sync messages - convert ArrayBuffer to Uint8Array + // Automerge Repo expects binary sync messages as Uint8Array this.emit('message', { type: 'sync', - data: event.data + data: new Uint8Array(event.data) }) } else if (event.data instanceof Blob) { - // Handle Blob messages (convert to ArrayBuffer) + // Handle Blob messages (convert to Uint8Array) event.data.arrayBuffer().then((buffer) => { - console.log('🔌 CloudflareAdapter: Received Blob message, converted to ArrayBuffer') + console.log('🔌 CloudflareAdapter: Received Blob message, converted to Uint8Array') this.emit('message', { type: 'sync', - data: buffer + data: new Uint8Array(buffer) }) }) } else { diff --git a/src/automerge/useAutomergeStoreV2.ts b/src/automerge/useAutomergeStoreV2.ts index c4655a5..5a93ee0 100644 --- a/src/automerge/useAutomergeStoreV2.ts +++ b/src/automerge/useAutomergeStoreV2.ts @@ -1488,7 +1488,7 @@ export function useAutomergeStoreV2({ for (const record of failedRecords) { try { // Additional cleanup for failed records - create deep copy - const fixedRecord = JSON.parse(JSON.stringify(record)) + let fixedRecord = JSON.parse(JSON.stringify(record)) // Fix instance records specifically if (fixedRecord.typeName === 'instance') { diff --git a/src/automerge/useAutomergeSyncRepo.ts b/src/automerge/useAutomergeSyncRepo.ts index 3ab03af..8e9220d 100644 --- a/src/automerge/useAutomergeSyncRepo.ts +++ b/src/automerge/useAutomergeSyncRepo.ts @@ -143,13 +143,13 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus return { status: isLoading ? 'loading' : 'not-synced', connectionStatus: 'offline', - store: null + store: undefined } } return { - status: 'synced', - connectionStatus: 'online', + status: 'synced-remote' as const, + connectionStatus: 'online' as const, store } }, [store, isLoading]) @@ -164,5 +164,5 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus return { ...storeWithStatus, presence - } + } as TLStoreWithStatus & { presence: typeof presence } } diff --git a/src/components/StandardizedToolWrapper.tsx b/src/components/StandardizedToolWrapper.tsx index b9a9db0..bcecd50 100644 --- a/src/components/StandardizedToolWrapper.tsx +++ b/src/components/StandardizedToolWrapper.tsx @@ -197,7 +197,7 @@ export const StandardizedToolWrapper: React.FC = ( onPointerDown={handleHeaderPointerDown} onMouseEnter={() => setIsHoveringHeader(true)} onMouseLeave={() => setIsHoveringHeader(false)} - onMouseDown={(e) => { + onMouseDown={(_e) => { // Ensure selection happens on mouse down for immediate visual feedback if (editor && shapeId && !isSelected) { editor.setSelectedShapes([shapeId]) diff --git a/src/components/location/LocationDashboard.tsx b/src/components/location/LocationDashboard.tsx index 162f297..36e986f 100644 --- a/src/components/location/LocationDashboard.tsx +++ b/src/components/location/LocationDashboard.tsx @@ -258,5 +258,6 @@ export const LocationDashboard: React.FC = () => { + diff --git a/src/components/location/LocationMap.tsx b/src/components/location/LocationMap.tsx index d7747c0..85f0d54 100644 --- a/src/components/location/LocationMap.tsx +++ b/src/components/location/LocationMap.tsx @@ -229,5 +229,6 @@ export const LocationMap: React.FC = ({ + diff --git a/src/components/location/LocationShareDialog.tsx b/src/components/location/LocationShareDialog.tsx index 1dfe7ce..76f809a 100644 --- a/src/components/location/LocationShareDialog.tsx +++ b/src/components/location/LocationShareDialog.tsx @@ -8,7 +8,7 @@ import { import React from "react" import { ShareLocation } from "./ShareLocation" -export function LocationShareDialog({ onClose }: TLUiDialogProps) { +export function LocationShareDialog({ onClose: _onClose }: TLUiDialogProps) { return ( <> diff --git a/src/components/location/LocationViewer.tsx b/src/components/location/LocationViewer.tsx index b20d2e1..0e1df4a 100644 --- a/src/components/location/LocationViewer.tsx +++ b/src/components/location/LocationViewer.tsx @@ -171,5 +171,6 @@ export const LocationViewer: React.FC = ({ shareToken }) => + diff --git a/src/components/location/ShareSettings.tsx b/src/components/location/ShareSettings.tsx index aa635dd..4a65cd3 100644 --- a/src/components/location/ShareSettings.tsx +++ b/src/components/location/ShareSettings.tsx @@ -138,5 +138,6 @@ export const ShareSettingsComponent: React.FC = ({ onSetting + diff --git a/src/css/location.css b/src/css/location.css index f624028..42b8d16 100644 --- a/src/css/location.css +++ b/src/css/location.css @@ -413,5 +413,6 @@ + diff --git a/src/hooks/useWebSpeechTranscription.ts b/src/hooks/useWebSpeechTranscription.ts index 6014343..45783dc 100644 --- a/src/hooks/useWebSpeechTranscription.ts +++ b/src/hooks/useWebSpeechTranscription.ts @@ -30,21 +30,21 @@ declare global { } interface SpeechRecognitionResultList { - length: number + readonly length: number item(index: number): SpeechRecognitionResult [index: number]: SpeechRecognitionResult } interface SpeechRecognitionResult { - length: number + readonly length: number item(index: number): SpeechRecognitionAlternative [index: number]: SpeechRecognitionAlternative - isFinal: boolean + readonly isFinal: boolean } interface SpeechRecognitionAlternative { - transcript: string - confidence: number + readonly transcript: string + readonly confidence: number } var SpeechRecognition: { diff --git a/src/lib/HoloSphereService.ts b/src/lib/HoloSphereService.ts index f9e5f86..27a8dc5 100644 --- a/src/lib/HoloSphereService.ts +++ b/src/lib/HoloSphereService.ts @@ -155,7 +155,7 @@ export class HoloSphereService { let unsubscribe: (() => void) | undefined = undefined if (this.sphere.subscribe) { try { - unsubscribe = this.sphere.subscribe(holon, lens, (data: any, key?: string) => { + const subscribeResult = this.sphere.subscribe(holon, lens, (data: any, key?: string) => { subscriptionActive = true console.log(`📥 Subscription callback fired for ${lens}:`, { data, key, dataType: typeof data, isObject: typeof data === 'object', isArray: Array.isArray(data) }) @@ -183,7 +183,18 @@ export class HoloSphereService { console.log(`📥 Current collected data for ${lens}:`, Object.keys(collectedData).length, 'keys') } }) - console.log(`✅ Subscribe called successfully for ${lens}`) + // Handle Promise if subscribe returns one + if (subscribeResult instanceof Promise) { + subscribeResult.then((result) => { + unsubscribe = result?.unsubscribe || undefined + console.log(`✅ Subscribe called successfully for ${lens}`) + }).catch((err) => { + console.error(`❌ Error in subscribe promise for ${lens}:`, err) + }) + } else { + unsubscribe = subscribeResult?.unsubscribe || undefined + console.log(`✅ Subscribe called successfully for ${lens}`) + } } catch (subError) { console.error(`❌ Error calling subscribe for ${lens}:`, subError) } diff --git a/src/lib/location/locationStorage.ts b/src/lib/location/locationStorage.ts index 6fccb1b..02bbc10 100644 --- a/src/lib/location/locationStorage.ts +++ b/src/lib/location/locationStorage.ts @@ -66,9 +66,10 @@ export class LocationStorageService { private async ensureDirectory(path: string[]): Promise { try { const dirPath = odd.path.directory(...path); - const exists = await this.fs.exists(dirPath as any); + const fs = this.fs as any; + const exists = await fs.exists(dirPath); if (!exists) { - await this.fs.mkdir(dirPath as any); + await fs.mkdir(dirPath); } } catch (error) { console.error('Error ensuring directory:', error); @@ -81,10 +82,11 @@ export class LocationStorageService { */ async saveLocation(location: LocationData): Promise { try { - const filePath = odd.path.file(...this.locationsPath, `${location.id}.json`); + const filePath = (odd.path as any).file(...this.locationsPath, `${location.id}.json`); const content = new TextEncoder().encode(JSON.stringify(location, null, 2)); - await this.fs.write(filePath as any, content as any); - await this.fs.publish(); + const fs = this.fs as any; + await fs.write(filePath, content); + await fs.publish(); } catch (error) { console.error('Error saving location:', error); throw error; @@ -96,12 +98,13 @@ export class LocationStorageService { */ async getLocation(locationId: string): Promise { try { - const filePath = odd.path.file(...this.locationsPath, `${locationId}.json`); - const exists = await this.fs.exists(filePath as any); + const filePath = (odd.path as any).file(...this.locationsPath, `${locationId}.json`); + const fs = this.fs as any; + const exists = await fs.exists(filePath); if (!exists) { return null; } - const content = await this.fs.read(filePath as any); + const content = await fs.read(filePath); const text = new TextDecoder().decode(content as Uint8Array); return JSON.parse(text) as LocationData; } catch (error) { @@ -116,12 +119,13 @@ export class LocationStorageService { async createShare(share: LocationShare): Promise { try { // Save share metadata in private directory - const sharePath = odd.path.file(...this.sharesPath, `${share.id}.json`); + const sharePath = (odd.path as any).file(...this.sharesPath, `${share.id}.json`); const shareContent = new TextEncoder().encode(JSON.stringify(share, null, 2)); - await this.fs.write(sharePath as any, shareContent as any); + const fs = this.fs as any; + await fs.write(sharePath, shareContent); // Create public reference file for share validation (only token, not full data) - const publicSharePath = odd.path.file(...this.publicSharesPath, `${share.shareToken}.json`); + const publicSharePath = (odd.path as any).file(...this.publicSharesPath, `${share.shareToken}.json`); const publicShareRef = { shareToken: share.shareToken, shareId: share.id, @@ -129,9 +133,9 @@ export class LocationStorageService { expiresAt: share.expiresAt, }; const publicContent = new TextEncoder().encode(JSON.stringify(publicShareRef, null, 2)); - await this.fs.write(publicSharePath as any, publicContent as any); + await fs.write(publicSharePath, publicContent); - await this.fs.publish(); + await fs.publish(); } catch (error) { console.error('Error creating share:', error); throw error; @@ -144,24 +148,25 @@ export class LocationStorageService { async getShareByToken(shareToken: string): Promise { try { // First check public reference - const publicSharePath = odd.path.file(...this.publicSharesPath, `${shareToken}.json`); - const publicExists = await this.fs.exists(publicSharePath as any); + const publicSharePath = (odd.path as any).file(...this.publicSharesPath, `${shareToken}.json`); + const fs = this.fs as any; + const publicExists = await fs.exists(publicSharePath); if (!publicExists) { return null; } - const publicContent = await this.fs.read(publicSharePath as any); + const publicContent = await fs.read(publicSharePath); const publicText = new TextDecoder().decode(publicContent as Uint8Array); const publicRef = JSON.parse(publicText); // Now get full share from private directory - const sharePath = odd.path.file(...this.sharesPath, `${publicRef.shareId}.json`); - const shareExists = await this.fs.exists(sharePath as any); + const sharePath = (odd.path as any).file(...this.sharesPath, `${publicRef.shareId}.json`); + const shareExists = await fs.exists(sharePath); if (!shareExists) { return null; } - const shareContent = await this.fs.read(sharePath as any); + const shareContent = await fs.read(sharePath); const shareText = new TextDecoder().decode(shareContent as Uint8Array); return JSON.parse(shareText) as LocationShare; } catch (error) { @@ -176,12 +181,13 @@ export class LocationStorageService { async getAllShares(): Promise { try { const dirPath = odd.path.directory(...this.sharesPath); - const exists = await this.fs.exists(dirPath as any); + const fs = this.fs as any; + const exists = await fs.exists(dirPath); if (!exists) { return []; } - const files = await this.fs.ls(dirPath as any); + const files = await fs.ls(dirPath); const shares: LocationShare[] = []; for (const fileName of Object.keys(files)) { @@ -206,12 +212,13 @@ export class LocationStorageService { */ private async getShareById(shareId: string): Promise { try { - const sharePath = odd.path.file(...this.sharesPath, `${shareId}.json`); - const exists = await this.fs.exists(sharePath as any); + const sharePath = (odd.path as any).file(...this.sharesPath, `${shareId}.json`); + const fs = this.fs as any; + const exists = await fs.exists(sharePath); if (!exists) { return null; } - const content = await this.fs.read(sharePath as any); + const content = await fs.read(sharePath); const text = new TextDecoder().decode(content as Uint8Array); return JSON.parse(text) as LocationShare; } catch (error) { diff --git a/src/lib/location/types.ts b/src/lib/location/types.ts index 5be56dd..8e48547 100644 --- a/src/lib/location/types.ts +++ b/src/lib/location/types.ts @@ -43,5 +43,6 @@ export interface GeolocationPosition { + diff --git a/src/routes/Board.tsx b/src/routes/Board.tsx index ca48a38..78c5d02 100644 --- a/src/routes/Board.tsx +++ b/src/routes/Board.tsx @@ -206,7 +206,7 @@ export function Board() { const store = { store: storeWithHandle.store, status: storeWithHandle.status, - connectionStatus: storeWithHandle.connectionStatus, + ...('connectionStatus' in storeWithHandle ? { connectionStatus: storeWithHandle.connectionStatus } : {}), error: storeWithHandle.error } const automergeHandle = storeWithHandle.handle @@ -405,11 +405,11 @@ export function Board() { const isInputFocused = (target && ( target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || - target.isContentEditable + (target instanceof HTMLElement && target.isContentEditable) )) || (activeElement && ( activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA' || - activeElement.isContentEditable + (activeElement instanceof HTMLElement && activeElement.isContentEditable) )); // If an input is focused, let it handle Escape (don't prevent default) diff --git a/src/routes/LocationDashboardRoute.tsx b/src/routes/LocationDashboardRoute.tsx index c8da207..b3a0fd6 100644 --- a/src/routes/LocationDashboardRoute.tsx +++ b/src/routes/LocationDashboardRoute.tsx @@ -25,5 +25,6 @@ export const LocationDashboardRoute: React.FC = () => { + diff --git a/src/routes/LocationShareCreate.tsx b/src/routes/LocationShareCreate.tsx index d13c509..ca1100e 100644 --- a/src/routes/LocationShareCreate.tsx +++ b/src/routes/LocationShareCreate.tsx @@ -25,5 +25,6 @@ export const LocationShareCreate: React.FC = () => { + diff --git a/src/routes/LocationShareView.tsx b/src/routes/LocationShareView.tsx index d8465a3..1548f1e 100644 --- a/src/routes/LocationShareView.tsx +++ b/src/routes/LocationShareView.tsx @@ -39,5 +39,6 @@ export const LocationShareView: React.FC = () => { + diff --git a/src/shapes/ChatBoxShapeUtil.tsx b/src/shapes/ChatBoxShapeUtil.tsx index 36020de..3f5903e 100644 --- a/src/shapes/ChatBoxShapeUtil.tsx +++ b/src/shapes/ChatBoxShapeUtil.tsx @@ -79,8 +79,8 @@ interface Message { // Update the ChatBox component to accept userName export const ChatBox: React.FC = ({ roomId, - w, - h, + w: _w, + h: _h, userName, }) => { const [messages, setMessages] = useState([]) diff --git a/src/shapes/FathomTranscriptShapeUtil.tsx b/src/shapes/FathomTranscriptShapeUtil.tsx index 3171ecf..d6277b2 100644 --- a/src/shapes/FathomTranscriptShapeUtil.tsx +++ b/src/shapes/FathomTranscriptShapeUtil.tsx @@ -113,6 +113,15 @@ export class FathomTranscriptShape extends BaseBoxShapeUtil { return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}` } + const buttonStyle: React.CSSProperties = { + padding: '4px 8px', + fontSize: '10px', + border: '1px solid #ccc', + borderRadius: '4px', + backgroundColor: 'white', + cursor: 'pointer', + } + // Custom header content with meeting info and toggle buttons const headerContent = (
diff --git a/src/shapes/ObsNoteShapeUtil.tsx b/src/shapes/ObsNoteShapeUtil.tsx index b1d55f7..4e25bce 100644 --- a/src/shapes/ObsNoteShapeUtil.tsx +++ b/src/shapes/ObsNoteShapeUtil.tsx @@ -981,7 +981,7 @@ export class ObsNoteShape extends BaseBoxShapeUtil { /** * Sanitize props to ensure all values are JSON serializable */ - private static sanitizeProps(props: Partial): IObsNoteShape['props'] { + public static sanitizeProps(props: Partial): IObsNoteShape['props'] { // Ensure tags is a proper string array const tags = Array.isArray(props.tags) ? props.tags.filter(tag => typeof tag === 'string').map(tag => String(tag)) diff --git a/src/tools/HolonTool.ts b/src/tools/HolonTool.ts index e5edba4..78b5bd7 100644 --- a/src/tools/HolonTool.ts +++ b/src/tools/HolonTool.ts @@ -125,6 +125,10 @@ export class HolonIdle extends StateNode { override onExit = () => { this.cleanupTooltip() + // Clean up event listeners + if ((this as any).cleanup) { + ;(this as any).cleanup() + } } private cleanupTooltip = () => { @@ -278,123 +282,6 @@ export class HolonIdle extends StateNode { } } - private createHolonShape(clickX?: number, clickY?: number) { - try { - // Store current camera position to prevent it from changing - const currentCamera = this.editor.getCamera() - this.editor.stopCameraAnimation() - - // Standardized size: 700x400 (matches default props to fit ID and button) - const shapeWidth = 700 - const shapeHeight = 400 - - // Use click position if available, otherwise fall back to viewport center - let baseX: number - let baseY: number - - if (clickX !== undefined && clickY !== undefined) { - // Position new Holon shape at click location (centered on click) - baseX = clickX - shapeWidth / 2 // Center the shape on click - baseY = clickY - shapeHeight / 2 // Center the shape on click - console.log('📍 HolonTool: Calculated base position from click:', { clickX, clickY, baseX, baseY, shapeWidth, shapeHeight }) - } else { - // Fallback to viewport center if no click coordinates - const viewport = this.editor.getViewportPageBounds() - const centerX = viewport.x + viewport.w / 2 - const centerY = viewport.y + viewport.h / 2 - baseX = centerX - shapeWidth / 2 // Center the shape - baseY = centerY - shapeHeight / 2 // Center the shape - } - - // Find existing Holon shapes for naming - const allShapes = this.editor.getCurrentPageShapes() - const existingHolonShapes = allShapes.filter(s => s.type === 'Holon') - - // ALWAYS use click position directly when provided - user clicked where they want it - // Skip collision detection entirely for user clicks to ensure it appears exactly where clicked - let finalX = baseX - let finalY = baseY - - if (clickX !== undefined && clickY !== undefined) { - // User clicked - ALWAYS use that exact position, no collision detection - // This ensures the shape appears exactly where the user clicked - finalX = baseX - finalY = baseY - console.log('📍 Using click position directly (no collision check):', { - clickPosition: { x: clickX, y: clickY }, - shapePosition: { x: finalX, y: finalY }, - shapeSize: { w: shapeWidth, h: shapeHeight } - }) - } else { - // For fallback (no click), use collision detection - const position = findNonOverlappingPosition( - this.editor, - baseX, - baseY, - shapeWidth, - shapeHeight - ) - finalX = position.x - finalY = position.y - console.log('📍 No click position - using collision detection:', { finalX, finalY }) - } - - // Default coordinates (can be changed by user) - const defaultLat = 40.7128 // NYC - const defaultLng = -74.0060 - const defaultResolution = 7 // City level - - console.log('📍 HolonTool: Final position for shape:', { finalX, finalY, wasOverlap: clickX !== undefined && clickY !== undefined && (finalX !== baseX || finalY !== baseY) }) - - const holonShape = this.editor.createShape({ - type: 'Holon', - x: finalX, - y: finalY, - props: { - w: shapeWidth, - h: shapeHeight, - name: `Holon ${existingHolonShapes.length + 1}`, - description: '', - latitude: defaultLat, - longitude: defaultLng, - resolution: defaultResolution, - holonId: '', - isConnected: false, - isEditing: true, - selectedLens: 'general', - data: {}, - connections: [], - lastUpdated: Date.now() - } - }) - - console.log('✅ Created Holon shape:', holonShape.id) - - // Restore camera position if it changed - const newCamera = this.editor.getCamera() - if (currentCamera.x !== newCamera.x || currentCamera.y !== newCamera.y || currentCamera.z !== newCamera.z) { - this.editor.setCamera(currentCamera, { animation: { duration: 0 } }) - } - - // Select the new shape - setTimeout(() => { - // Preserve camera position when selecting - const cameraBeforeSelect = this.editor.getCamera() - this.editor.stopCameraAnimation() - this.editor.setSelectedShapes([`shape:${holonShape.id}`] as any) - this.editor.setCurrentTool('select') - // Restore camera if it changed during selection - const cameraAfterSelect = this.editor.getCamera() - if (cameraBeforeSelect.x !== cameraAfterSelect.x || cameraBeforeSelect.y !== cameraAfterSelect.y || cameraBeforeSelect.z !== cameraAfterSelect.z) { - this.editor.setCamera(cameraBeforeSelect, { animation: { duration: 0 } }) - } - }, 100) - - } catch (error) { - console.error('❌ Error creating Holon shape:', error) - } - } - private addRefreshAllListener() { // Listen for refresh-all-holons event const handleRefreshAll = async () => { @@ -439,11 +326,4 @@ export class HolonIdle extends StateNode { // Store cleanup function for later use ;(this as any).cleanup = cleanup } - - onExit() { - // Clean up event listeners - if ((this as any).cleanup) { - ;(this as any).cleanup() - } - } } diff --git a/src/tools/ObsNoteTool.ts b/src/tools/ObsNoteTool.ts index 9e3c105..197e550 100644 --- a/src/tools/ObsNoteTool.ts +++ b/src/tools/ObsNoteTool.ts @@ -21,6 +21,38 @@ export class ObsNoteTool extends StateNode { this.addRefreshAllListener() } } + + private addRefreshAllListener() { + // Listen for refresh-all-obsnotes event + const handleRefreshAll = async () => { + const shapeUtil = new ObsNoteShape(this.editor) + shapeUtil.editor = this.editor + + const result = await shapeUtil.refreshAllFromVault() + if (result.success > 0) { + alert(`✅ Refreshed ${result.success} notes from vault!${result.failed > 0 ? ` (${result.failed} failed)` : ''}`) + } else { + alert('❌ Failed to refresh any notes. Check console for details.') + } + } + + window.addEventListener('refresh-all-obsnotes', handleRefreshAll) + + // Clean up listener when tool is deselected + const cleanup = () => { + window.removeEventListener('refresh-all-obsnotes', handleRefreshAll) + } + + // Store cleanup function for later use + ;(this as any).cleanup = cleanup + } + + onExit() { + // Clean up event listeners + if ((this as any).cleanup) { + ;(this as any).cleanup() + } + } } export class ObsNoteIdle extends StateNode { @@ -101,6 +133,10 @@ export class ObsNoteIdle extends StateNode { override onExit = () => { this.cleanupTooltip() + // Clean up event listeners + if ((this as any).cleanup) { + ;(this as any).cleanup() + } } private cleanupTooltip = () => { @@ -182,36 +218,4 @@ export class ObsNoteIdle extends StateNode { console.error('❌ Error creating ObsidianBrowser shape:', error) } } - - private addRefreshAllListener() { - // Listen for refresh-all-obsnotes event - const handleRefreshAll = async () => { - const shapeUtil = new ObsNoteShape(this.editor) - shapeUtil.editor = this.editor - - const result = await shapeUtil.refreshAllFromVault() - if (result.success > 0) { - alert(`✅ Refreshed ${result.success} notes from vault!${result.failed > 0 ? ` (${result.failed} failed)` : ''}`) - } else { - alert('❌ Failed to refresh any notes. Check console for details.') - } - } - - window.addEventListener('refresh-all-obsnotes', handleRefreshAll) - - // Clean up listener when tool is deselected - const cleanup = () => { - window.removeEventListener('refresh-all-obsnotes', handleRefreshAll) - } - - // Store cleanup function for later use - ;(this as any).cleanup = cleanup - } - - onExit() { - // Clean up event listeners - if ((this as any).cleanup) { - ;(this as any).cleanup() - } - } } diff --git a/src/utils/shapeCollisionUtils.ts b/src/utils/shapeCollisionUtils.ts index 6a9abf3..a093600 100644 --- a/src/utils/shapeCollisionUtils.ts +++ b/src/utils/shapeCollisionUtils.ts @@ -1,4 +1,4 @@ -import { Editor, TLShape, Box } from "@tldraw/tldraw" +import { Editor, TLShape, Box, TLShapeId } from "@tldraw/tldraw" /** * Check if two boxes overlap @@ -20,8 +20,9 @@ function boxesOverlap( * Get the bounding box of a shape */ function getShapeBounds(editor: Editor, shape: TLShape | string): Box | null { - const shapeId = typeof shape === 'string' ? shape : shape.id - return editor.getShapePageBounds(shapeId) + const shapeId = typeof shape === 'string' ? (shape as TLShapeId) : shape.id + const bounds = editor.getShapePageBounds(shapeId) + return bounds ?? null } /** @@ -70,7 +71,7 @@ export function resolveOverlaps(editor: Editor, shapeId: string): void { const newY = shapeBox.y // Keep same Y position editor.updateShape({ - id: shapeId, + id: shapeId as TLShapeId, type: shape.type, x: newX, y: newY, @@ -90,7 +91,7 @@ export function resolveOverlaps(editor: Editor, shapeId: string): void { if (boxesOverlap(newShapeBox, otherBox, 20)) { const newY2 = otherBox.y + otherBox.h + 20 editor.updateShape({ - id: shapeId, + id: shapeId as TLShapeId, type: shape.type, x: shapeBox.x, // Keep original X y: newY2, diff --git a/switch-worker-env.sh b/switch-worker-env.sh index bdc975a..93af8e6 100644 --- a/switch-worker-env.sh +++ b/switch-worker-env.sh @@ -60,5 +60,6 @@ echo " npm run dev" + diff --git a/worker/worker.ts b/worker/worker.ts index 7f4b7c6..451ed20 100644 --- a/worker/worker.ts +++ b/worker/worker.ts @@ -562,7 +562,7 @@ const router = AutoRouter({ }) } - const data = await response.json() + const data = await response.json() as { data?: any[] } console.log('Fathom API success, data length:', data?.data?.length || 0) return new Response(JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }