diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js index ee6e686..477ed48 100644 --- a/apps/web/eslint.config.js +++ b/apps/web/eslint.config.js @@ -5,9 +5,11 @@ import svelte from "eslint-plugin-svelte"; import { defineConfig } from "eslint/config"; import globals from "globals"; import { fileURLToPath } from "node:url"; +import { dirname } from "node:path"; import ts from "typescript-eslint"; import svelteConfig from "./svelte.config.js"; +const __dirname = dirname(fileURLToPath(import.meta.url)); const gitignorePath = fileURLToPath(new URL("./.gitignore", import.meta.url)); export default defineConfig( @@ -18,18 +20,38 @@ export default defineConfig( prettier, ...svelte.configs.prettier, { - languageOptions: { globals: { ...globals.browser, ...globals.node } }, + languageOptions: { + globals: { ...globals.browser, ...globals.node }, + parserOptions: { + tsconfigRootDir: __dirname, + }, + }, rules: { // typescript-eslint strongly recommend that you do not use the no-undef lint rule on TypeScript projects. // see: https://typescript-eslint.io/troubleshooting/faqs/eslint/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors "no-undef": "off", + // Allow unused vars that start with _ + "@typescript-eslint/no-unused-vars": [ + "error", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + caughtErrorsIgnorePattern: "^_", + }, + ], }, }, { files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"], languageOptions: { - parserOptions: { projectService: true, extraFileExtensions: [".svelte"], parser: ts.parser, svelteConfig }, + parserOptions: { + projectService: true, + extraFileExtensions: [".svelte"], + parser: ts.parser, + svelteConfig, + tsconfigRootDir: __dirname, + }, }, }, ); diff --git a/apps/web/src/lib/canvas/Canvas.svelte b/apps/web/src/lib/canvas/Canvas.svelte index 29349e9..c298d90 100644 --- a/apps/web/src/lib/canvas/Canvas.svelte +++ b/apps/web/src/lib/canvas/Canvas.svelte @@ -4,6 +4,7 @@ import TitleBar from '$lib/components/TitleBar.svelte'; import Toolbar from '$lib/components/Toolbar.svelte'; import { createInputAdapter, type InputAdapter } from '$lib/input'; + import { createPlatformRepo, detectPlatform } from '$lib/platform'; import { createPersistenceManager, createSnapStore, @@ -17,7 +18,6 @@ CursorStore, EditorState, EllipseTool, - InkfiniteDB, LineTool, RectTool, SelectTool, @@ -26,7 +26,6 @@ Store, TextTool, createToolMap, - createWebDocRepo, diffDoc, getShapesOnCurrentPage, routeAction, @@ -34,6 +33,7 @@ switchTool, type Action, type CommandKind, + type DocRepo, type LoadedDoc, type PersistenceSink, type ToolId, @@ -42,11 +42,12 @@ import { createRenderer, type Renderer } from 'inkfinite-renderer'; import { onDestroy, onMount } from 'svelte'; - let repo: ReturnType | null = null; + let repo: DocRepo | null = null; let sink: PersistenceSink | null = null; let persistenceManager: ReturnType | null = null; + const platform = detectPlatform(); const fallbackStatusStore = createStatusStore({ - backend: 'indexeddb', + backend: platform === 'desktop' ? 'filesystem' : 'indexeddb', state: 'saved', pendingWrites: 0 }); @@ -752,66 +753,88 @@ } onMount(() => { - const db = new InkfiniteDB(); - repo = createWebDocRepo(db); - persistenceManager = createPersistenceManager(db, repo, { sink: { debounceMs: 200 } }); - sink = persistenceManager.sink; - persistenceStatusStore = persistenceManager.status; let disposed = false; - const hydrate = async () => { - const repoInstance = repo; - if (!repoInstance) { - return; + const initialize = async () => { + const { repo: platformRepo, platform: detectedPlatform, db } = await createPlatformRepo(); + repo = platformRepo; + + if (detectedPlatform === 'web' && db) { + persistenceManager = createPersistenceManager(db, repo, { sink: { debounceMs: 200 } }); + sink = persistenceManager.sink; + persistenceStatusStore = persistenceManager.status; + } else { + const { createPersistenceSink } = await import('inkfinite-core'); + sink = createPersistenceSink(repo, { debounceMs: 500 }); } - try { - const boards = await repoInstance.listBoards(); - const id = boards[0]?.id ?? (await repoInstance.createBoard('My board')); - if (disposed) { + + const hydrate = async () => { + const repoInstance = repo; + if (!repoInstance) { return; } - activeBoardId = id; - const loaded = await repoInstance.loadDoc(id); - if (!disposed) { - persistenceManager?.setActiveBoard(id); - applyLoadedDoc(loaded); + try { + if (detectedPlatform === 'web') { + const boards = await repoInstance.listBoards(); + const id = boards[0]?.id ?? (await repoInstance.createBoard('My board')); + if (disposed) { + return; + } + activeBoardId = id; + const loaded = await repoInstance.loadDoc(id); + if (!disposed) { + persistenceManager?.setActiveBoard(id); + applyLoadedDoc(loaded); + } + } else { + const id = await repoInstance.createBoard('Untitled'); + if (disposed) { + return; + } + activeBoardId = id; + const loaded = await repoInstance.loadDoc(id); + if (!disposed) { + applyLoadedDoc(loaded); + } + } + } catch (error) { + console.error('Failed to load board', error); } - } catch (error) { - console.error('Failed to load board', error); - } - }; - - hydrate(); + }; - function getCamera() { - return store.getState().camera; - } + await hydrate(); - renderer = createRenderer(canvas!, store, { - snapProvider, - cursorProvider, - pointerStateProvider, - handleProvider - }); - inputAdapter = createInputAdapter({ - canvas: canvas!, - getCamera, - getViewport, - onAction: handleAction, - onCursorUpdate: (world, screen) => cursorStore.updateCursor(world, screen) - }); + function getCamera() { + return store.getState().camera; + } - function handleBeforeUnload() { - if (sink) { - void sink.flush(); + renderer = createRenderer(canvas!, store, { + snapProvider, + cursorProvider, + pointerStateProvider, + handleProvider + }); + inputAdapter = createInputAdapter({ + canvas: canvas!, + getCamera, + getViewport, + onAction: handleAction, + onCursorUpdate: (world, screen) => cursorStore.updateCursor(world, screen) + }); + + function handleBeforeUnload() { + if (sink) { + void sink.flush(); + } } - } - window.addEventListener('beforeunload', handleBeforeUnload); + window.addEventListener('beforeunload', handleBeforeUnload); + }; + + initialize(); return () => { disposed = true; - window.removeEventListener('beforeunload', handleBeforeUnload); }; }); diff --git a/apps/web/src/lib/components/HistoryViewer.svelte b/apps/web/src/lib/components/HistoryViewer.svelte index 114700d..7396211 100644 --- a/apps/web/src/lib/components/HistoryViewer.svelte +++ b/apps/web/src/lib/components/HistoryViewer.svelte @@ -53,7 +53,7 @@

No actions to undo

{:else}