update typescript errors for vercel

This commit is contained in:
Jeff Emmett 2025-11-10 11:19:24 -08:00
parent e727deea19
commit 8d5b41f530
29 changed files with 149 additions and 219 deletions

View File

@ -130,5 +130,6 @@ The Fathom transcript shape includes:

View File

@ -81,5 +81,6 @@ You can also manually edit the environment by:

View File

@ -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) // CRITICAL: Ensure typeName matches ID pattern (fixes misclassification)
// Note: obsidian_vault records are skipped above, so we don't need to handle them here // Note: obsidian_vault records are skipped above, so we don't need to handle them here
if (typeof id === 'string') { if (typeof id === 'string') {
let correctTypeName = record.typeName
if (id.startsWith('shape:') && record.typeName !== 'shape') { if (id.startsWith('shape:') && record.typeName !== 'shape') {
record.typeName = 'shape' correctTypeName = 'shape'
} else if (id.startsWith('page:') && record.typeName !== 'page') { } else if (id.startsWith('page:') && record.typeName !== 'page') {
record.typeName = 'page' correctTypeName = 'page'
} else if (id.startsWith('camera:') && record.typeName !== 'camera') { } else if (id.startsWith('camera:') && record.typeName !== 'camera') {
record.typeName = 'camera' correctTypeName = 'camera'
} else if (id.startsWith('instance:') && record.typeName !== 'instance') { } else if (id.startsWith('instance:') && record.typeName !== 'instance') {
record.typeName = 'instance' correctTypeName = 'instance'
} else if (id.startsWith('pointer:') && record.typeName !== 'pointer') { } else if (id.startsWith('pointer:') && record.typeName !== 'pointer') {
record.typeName = 'pointer' correctTypeName = 'pointer'
} else if (id.startsWith('document:') && record.typeName !== 'document') { } 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 }
} }
} }

View File

@ -219,19 +219,19 @@ export class CloudflareNetworkAdapter extends NetworkAdapter {
// We need to handle both binary and text messages // We need to handle both binary and text messages
if (event.data instanceof ArrayBuffer) { if (event.data instanceof ArrayBuffer) {
console.log('🔌 CloudflareAdapter: Received binary message (Automerge protocol)') console.log('🔌 CloudflareAdapter: Received binary message (Automerge protocol)')
// Handle binary Automerge sync messages - pass directly to Repo // Handle binary Automerge sync messages - convert ArrayBuffer to Uint8Array
// Automerge Repo expects binary sync messages as ArrayBuffer // Automerge Repo expects binary sync messages as Uint8Array
this.emit('message', { this.emit('message', {
type: 'sync', type: 'sync',
data: event.data data: new Uint8Array(event.data)
}) })
} else if (event.data instanceof Blob) { } else if (event.data instanceof Blob) {
// Handle Blob messages (convert to ArrayBuffer) // Handle Blob messages (convert to Uint8Array)
event.data.arrayBuffer().then((buffer) => { 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', { this.emit('message', {
type: 'sync', type: 'sync',
data: buffer data: new Uint8Array(buffer)
}) })
}) })
} else { } else {

View File

@ -1488,7 +1488,7 @@ export function useAutomergeStoreV2({
for (const record of failedRecords) { for (const record of failedRecords) {
try { try {
// Additional cleanup for failed records - create deep copy // 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 // Fix instance records specifically
if (fixedRecord.typeName === 'instance') { if (fixedRecord.typeName === 'instance') {

View File

@ -143,13 +143,13 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
return { return {
status: isLoading ? 'loading' : 'not-synced', status: isLoading ? 'loading' : 'not-synced',
connectionStatus: 'offline', connectionStatus: 'offline',
store: null store: undefined
} }
} }
return { return {
status: 'synced', status: 'synced-remote' as const,
connectionStatus: 'online', connectionStatus: 'online' as const,
store store
} }
}, [store, isLoading]) }, [store, isLoading])
@ -164,5 +164,5 @@ export function useAutomergeSync(config: AutomergeSyncConfig): TLStoreWithStatus
return { return {
...storeWithStatus, ...storeWithStatus,
presence presence
} } as TLStoreWithStatus & { presence: typeof presence }
} }

View File

@ -197,7 +197,7 @@ export const StandardizedToolWrapper: React.FC<StandardizedToolWrapperProps> = (
onPointerDown={handleHeaderPointerDown} onPointerDown={handleHeaderPointerDown}
onMouseEnter={() => setIsHoveringHeader(true)} onMouseEnter={() => setIsHoveringHeader(true)}
onMouseLeave={() => setIsHoveringHeader(false)} onMouseLeave={() => setIsHoveringHeader(false)}
onMouseDown={(e) => { onMouseDown={(_e) => {
// Ensure selection happens on mouse down for immediate visual feedback // Ensure selection happens on mouse down for immediate visual feedback
if (editor && shapeId && !isSelected) { if (editor && shapeId && !isSelected) {
editor.setSelectedShapes([shapeId]) editor.setSelectedShapes([shapeId])

View File

@ -258,5 +258,6 @@ export const LocationDashboard: React.FC = () => {

View File

@ -229,5 +229,6 @@ export const LocationMap: React.FC<LocationMapProps> = ({

View File

@ -8,7 +8,7 @@ import {
import React from "react" import React from "react"
import { ShareLocation } from "./ShareLocation" import { ShareLocation } from "./ShareLocation"
export function LocationShareDialog({ onClose }: TLUiDialogProps) { export function LocationShareDialog({ onClose: _onClose }: TLUiDialogProps) {
return ( return (
<> <>
<TldrawUiDialogHeader> <TldrawUiDialogHeader>

View File

@ -171,5 +171,6 @@ export const LocationViewer: React.FC<LocationViewerProps> = ({ shareToken }) =>

View File

@ -138,5 +138,6 @@ export const ShareSettingsComponent: React.FC<ShareSettingsProps> = ({ onSetting

View File

@ -413,5 +413,6 @@

View File

@ -30,21 +30,21 @@ declare global {
} }
interface SpeechRecognitionResultList { interface SpeechRecognitionResultList {
length: number readonly length: number
item(index: number): SpeechRecognitionResult item(index: number): SpeechRecognitionResult
[index: number]: SpeechRecognitionResult [index: number]: SpeechRecognitionResult
} }
interface SpeechRecognitionResult { interface SpeechRecognitionResult {
length: number readonly length: number
item(index: number): SpeechRecognitionAlternative item(index: number): SpeechRecognitionAlternative
[index: number]: SpeechRecognitionAlternative [index: number]: SpeechRecognitionAlternative
isFinal: boolean readonly isFinal: boolean
} }
interface SpeechRecognitionAlternative { interface SpeechRecognitionAlternative {
transcript: string readonly transcript: string
confidence: number readonly confidence: number
} }
var SpeechRecognition: { var SpeechRecognition: {

View File

@ -155,7 +155,7 @@ export class HoloSphereService {
let unsubscribe: (() => void) | undefined = undefined let unsubscribe: (() => void) | undefined = undefined
if (this.sphere.subscribe) { if (this.sphere.subscribe) {
try { try {
unsubscribe = this.sphere.subscribe(holon, lens, (data: any, key?: string) => { const subscribeResult = this.sphere.subscribe(holon, lens, (data: any, key?: string) => {
subscriptionActive = true subscriptionActive = true
console.log(`📥 Subscription callback fired for ${lens}:`, { data, key, dataType: typeof data, isObject: typeof data === 'object', isArray: Array.isArray(data) }) 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(`📥 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) { } catch (subError) {
console.error(`❌ Error calling subscribe for ${lens}:`, subError) console.error(`❌ Error calling subscribe for ${lens}:`, subError)
} }

View File

@ -66,9 +66,10 @@ export class LocationStorageService {
private async ensureDirectory(path: string[]): Promise<void> { private async ensureDirectory(path: string[]): Promise<void> {
try { try {
const dirPath = odd.path.directory(...path); 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) { if (!exists) {
await this.fs.mkdir(dirPath as any); await fs.mkdir(dirPath);
} }
} catch (error) { } catch (error) {
console.error('Error ensuring directory:', error); console.error('Error ensuring directory:', error);
@ -81,10 +82,11 @@ export class LocationStorageService {
*/ */
async saveLocation(location: LocationData): Promise<void> { async saveLocation(location: LocationData): Promise<void> {
try { 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)); const content = new TextEncoder().encode(JSON.stringify(location, null, 2));
await this.fs.write(filePath as any, content as any); const fs = this.fs as any;
await this.fs.publish(); await fs.write(filePath, content);
await fs.publish();
} catch (error) { } catch (error) {
console.error('Error saving location:', error); console.error('Error saving location:', error);
throw error; throw error;
@ -96,12 +98,13 @@ export class LocationStorageService {
*/ */
async getLocation(locationId: string): Promise<LocationData | null> { async getLocation(locationId: string): Promise<LocationData | null> {
try { try {
const filePath = odd.path.file(...this.locationsPath, `${locationId}.json`); const filePath = (odd.path as any).file(...this.locationsPath, `${locationId}.json`);
const exists = await this.fs.exists(filePath as any); const fs = this.fs as any;
const exists = await fs.exists(filePath);
if (!exists) { if (!exists) {
return null; 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); const text = new TextDecoder().decode(content as Uint8Array);
return JSON.parse(text) as LocationData; return JSON.parse(text) as LocationData;
} catch (error) { } catch (error) {
@ -116,12 +119,13 @@ export class LocationStorageService {
async createShare(share: LocationShare): Promise<void> { async createShare(share: LocationShare): Promise<void> {
try { try {
// Save share metadata in private directory // 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)); 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) // 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 = { const publicShareRef = {
shareToken: share.shareToken, shareToken: share.shareToken,
shareId: share.id, shareId: share.id,
@ -129,9 +133,9 @@ export class LocationStorageService {
expiresAt: share.expiresAt, expiresAt: share.expiresAt,
}; };
const publicContent = new TextEncoder().encode(JSON.stringify(publicShareRef, null, 2)); 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) { } catch (error) {
console.error('Error creating share:', error); console.error('Error creating share:', error);
throw error; throw error;
@ -144,24 +148,25 @@ export class LocationStorageService {
async getShareByToken(shareToken: string): Promise<LocationShare | null> { async getShareByToken(shareToken: string): Promise<LocationShare | null> {
try { try {
// First check public reference // First check public reference
const publicSharePath = odd.path.file(...this.publicSharesPath, `${shareToken}.json`); const publicSharePath = (odd.path as any).file(...this.publicSharesPath, `${shareToken}.json`);
const publicExists = await this.fs.exists(publicSharePath as any); const fs = this.fs as any;
const publicExists = await fs.exists(publicSharePath);
if (!publicExists) { if (!publicExists) {
return null; 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 publicText = new TextDecoder().decode(publicContent as Uint8Array);
const publicRef = JSON.parse(publicText); const publicRef = JSON.parse(publicText);
// Now get full share from private directory // Now get full share from private directory
const sharePath = odd.path.file(...this.sharesPath, `${publicRef.shareId}.json`); const sharePath = (odd.path as any).file(...this.sharesPath, `${publicRef.shareId}.json`);
const shareExists = await this.fs.exists(sharePath as any); const shareExists = await fs.exists(sharePath);
if (!shareExists) { if (!shareExists) {
return null; 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); const shareText = new TextDecoder().decode(shareContent as Uint8Array);
return JSON.parse(shareText) as LocationShare; return JSON.parse(shareText) as LocationShare;
} catch (error) { } catch (error) {
@ -176,12 +181,13 @@ export class LocationStorageService {
async getAllShares(): Promise<LocationShare[]> { async getAllShares(): Promise<LocationShare[]> {
try { try {
const dirPath = odd.path.directory(...this.sharesPath); 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) { if (!exists) {
return []; return [];
} }
const files = await this.fs.ls(dirPath as any); const files = await fs.ls(dirPath);
const shares: LocationShare[] = []; const shares: LocationShare[] = [];
for (const fileName of Object.keys(files)) { for (const fileName of Object.keys(files)) {
@ -206,12 +212,13 @@ export class LocationStorageService {
*/ */
private async getShareById(shareId: string): Promise<LocationShare | null> { private async getShareById(shareId: string): Promise<LocationShare | null> {
try { try {
const sharePath = odd.path.file(...this.sharesPath, `${shareId}.json`); const sharePath = (odd.path as any).file(...this.sharesPath, `${shareId}.json`);
const exists = await this.fs.exists(sharePath as any); const fs = this.fs as any;
const exists = await fs.exists(sharePath);
if (!exists) { if (!exists) {
return null; 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); const text = new TextDecoder().decode(content as Uint8Array);
return JSON.parse(text) as LocationShare; return JSON.parse(text) as LocationShare;
} catch (error) { } catch (error) {

View File

@ -43,5 +43,6 @@ export interface GeolocationPosition {

View File

@ -206,7 +206,7 @@ export function Board() {
const store = { const store = {
store: storeWithHandle.store, store: storeWithHandle.store,
status: storeWithHandle.status, status: storeWithHandle.status,
connectionStatus: storeWithHandle.connectionStatus, ...('connectionStatus' in storeWithHandle ? { connectionStatus: storeWithHandle.connectionStatus } : {}),
error: storeWithHandle.error error: storeWithHandle.error
} }
const automergeHandle = storeWithHandle.handle const automergeHandle = storeWithHandle.handle
@ -405,11 +405,11 @@ export function Board() {
const isInputFocused = (target && ( const isInputFocused = (target && (
target.tagName === 'INPUT' || target.tagName === 'INPUT' ||
target.tagName === 'TEXTAREA' || target.tagName === 'TEXTAREA' ||
target.isContentEditable (target instanceof HTMLElement && target.isContentEditable)
)) || (activeElement && ( )) || (activeElement && (
activeElement.tagName === 'INPUT' || activeElement.tagName === 'INPUT' ||
activeElement.tagName === 'TEXTAREA' || activeElement.tagName === 'TEXTAREA' ||
activeElement.isContentEditable (activeElement instanceof HTMLElement && activeElement.isContentEditable)
)); ));
// If an input is focused, let it handle Escape (don't prevent default) // If an input is focused, let it handle Escape (don't prevent default)

View File

@ -25,5 +25,6 @@ export const LocationDashboardRoute: React.FC = () => {

View File

@ -25,5 +25,6 @@ export const LocationShareCreate: React.FC = () => {

View File

@ -39,5 +39,6 @@ export const LocationShareView: React.FC = () => {

View File

@ -79,8 +79,8 @@ interface Message {
// Update the ChatBox component to accept userName // Update the ChatBox component to accept userName
export const ChatBox: React.FC<IChatBoxShape["props"]> = ({ export const ChatBox: React.FC<IChatBoxShape["props"]> = ({
roomId, roomId,
w, w: _w,
h, h: _h,
userName, userName,
}) => { }) => {
const [messages, setMessages] = useState<Message[]>([]) const [messages, setMessages] = useState<Message[]>([])

View File

@ -113,6 +113,15 @@ export class FathomTranscriptShape extends BaseBoxShapeUtil<IFathomTranscript> {
return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}` 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 // Custom header content with meeting info and toggle buttons
const headerContent = ( const headerContent = (
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%', gap: '8px' }}> <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%', gap: '8px' }}>

View File

@ -981,7 +981,7 @@ export class ObsNoteShape extends BaseBoxShapeUtil<IObsNoteShape> {
/** /**
* Sanitize props to ensure all values are JSON serializable * Sanitize props to ensure all values are JSON serializable
*/ */
private static sanitizeProps(props: Partial<IObsNoteShape['props']>): IObsNoteShape['props'] { public static sanitizeProps(props: Partial<IObsNoteShape['props']>): IObsNoteShape['props'] {
// Ensure tags is a proper string array // Ensure tags is a proper string array
const tags = Array.isArray(props.tags) const tags = Array.isArray(props.tags)
? props.tags.filter(tag => typeof tag === 'string').map(tag => String(tag)) ? props.tags.filter(tag => typeof tag === 'string').map(tag => String(tag))

View File

@ -125,6 +125,10 @@ export class HolonIdle extends StateNode {
override onExit = () => { override onExit = () => {
this.cleanupTooltip() this.cleanupTooltip()
// Clean up event listeners
if ((this as any).cleanup) {
;(this as any).cleanup()
}
} }
private cleanupTooltip = () => { 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() { private addRefreshAllListener() {
// Listen for refresh-all-holons event // Listen for refresh-all-holons event
const handleRefreshAll = async () => { const handleRefreshAll = async () => {
@ -439,11 +326,4 @@ export class HolonIdle extends StateNode {
// Store cleanup function for later use // Store cleanup function for later use
;(this as any).cleanup = cleanup ;(this as any).cleanup = cleanup
} }
onExit() {
// Clean up event listeners
if ((this as any).cleanup) {
;(this as any).cleanup()
}
}
} }

View File

@ -21,6 +21,38 @@ export class ObsNoteTool extends StateNode {
this.addRefreshAllListener() 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 { export class ObsNoteIdle extends StateNode {
@ -101,6 +133,10 @@ export class ObsNoteIdle extends StateNode {
override onExit = () => { override onExit = () => {
this.cleanupTooltip() this.cleanupTooltip()
// Clean up event listeners
if ((this as any).cleanup) {
;(this as any).cleanup()
}
} }
private cleanupTooltip = () => { private cleanupTooltip = () => {
@ -182,36 +218,4 @@ export class ObsNoteIdle extends StateNode {
console.error('❌ Error creating ObsidianBrowser shape:', error) 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()
}
}
} }

View File

@ -1,4 +1,4 @@
import { Editor, TLShape, Box } from "@tldraw/tldraw" import { Editor, TLShape, Box, TLShapeId } from "@tldraw/tldraw"
/** /**
* Check if two boxes overlap * Check if two boxes overlap
@ -20,8 +20,9 @@ function boxesOverlap(
* Get the bounding box of a shape * Get the bounding box of a shape
*/ */
function getShapeBounds(editor: Editor, shape: TLShape | string): Box | null { function getShapeBounds(editor: Editor, shape: TLShape | string): Box | null {
const shapeId = typeof shape === 'string' ? shape : shape.id const shapeId = typeof shape === 'string' ? (shape as TLShapeId) : shape.id
return editor.getShapePageBounds(shapeId) 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 const newY = shapeBox.y // Keep same Y position
editor.updateShape({ editor.updateShape({
id: shapeId, id: shapeId as TLShapeId,
type: shape.type, type: shape.type,
x: newX, x: newX,
y: newY, y: newY,
@ -90,7 +91,7 @@ export function resolveOverlaps(editor: Editor, shapeId: string): void {
if (boxesOverlap(newShapeBox, otherBox, 20)) { if (boxesOverlap(newShapeBox, otherBox, 20)) {
const newY2 = otherBox.y + otherBox.h + 20 const newY2 = otherBox.y + otherBox.h + 20
editor.updateShape({ editor.updateShape({
id: shapeId, id: shapeId as TLShapeId,
type: shape.type, type: shape.type,
x: shapeBox.x, // Keep original X x: shapeBox.x, // Keep original X
y: newY2, y: newY2,

View File

@ -60,5 +60,6 @@ echo " npm run dev"

View File

@ -562,7 +562,7 @@ const router = AutoRouter<IRequest, [env: Environment, ctx: ExecutionContext]>({
}) })
} }
const data = await response.json() const data = await response.json() as { data?: any[] }
console.log('Fathom API success, data length:', data?.data?.length || 0) console.log('Fathom API success, data length:', data?.data?.length || 0)
return new Response(JSON.stringify(data), { return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' } headers: { 'Content-Type': 'application/json' }