Something went wrong. Try again.
inkfinite (an infinite canvas/whiteboard) monorepo ink.stormlightlabs.org
svelte rust tauri svg canvas perfect-freehand
Something went wrong. Try again.
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156import type { DocumentSnapshot, EditorReconciliationRequest } from '@inkfinite/wasm';import type { EditorDocument } from '@inkfinite/core';import { createEditorReconciliationRequest, fromEditorProjection} from '@inkfinite/core/persistence';import { afterEach, describe, expect, it } from 'vitest';import { getSharedDocumentEngineWorker, resetSharedDocumentEngineWorker} from '../persistence/document-engine';
const snapshot: DocumentSnapshot = { format: 'inkfinite.document', format_version: 2, document_id: 'document:browser-smoke', heads: [], document: { pages: { 'page:one': { id: 'page:one', name: 'Page 1', layer_ids: ['layer:one'], version: 1 } }, page_ids: ['page:one'], layers: { 'layer:one': { id: 'layer:one', page_id: 'page:one', name: 'Default', shape_ids: [], visible: true, locked: false, opacity: 1, version: 1 } }, shapes: {}, bindings: {}, assets: {} }};
const request: EditorReconciliationRequest = { patches: [ { type: 'create_shape', shape: { id: 'shape:rect', kind: 'rect', properties: { w: 20, h: 10, fill: '#ff0000' }, metadata: null, style: { opacity: 1, fill_opacity: null, stroke_opacity: null }, layout: null }, parent: { kind: 'layer', id: 'layer:one' }, transform: { a: 1, b: 0, c: 0, d: 1, e: 4, f: 5 }, anchor: { position: 'last' } } ], actor_id: 'browser', origin: 'human', transaction_id: 'transaction:create', description: 'Create rectangle', timestamp: 1};
afterEach(resetSharedDocumentEngineWorker);
describe('compiled document engine worker', () => { it('runs the canonical browser document lifecycle', async () => { const worker = getSharedDocumentEngineWorker(); let state = await worker.createDocument(snapshot, 'browser'); expect(state.editor_projection.shapes).toEqual({});
state = await worker.applyEditorPatches(request); expect(state.editor_projection.shapes['shape:rect']).toBeDefined(); expect(state.can_undo).toBe(true);
const projected = fromEditorProjection(state.editor_projection, state.snapshot); const beforeEdit: EditorDocument = { pages: projected.pages, layers: projected.layers, shapes: projected.shapes, bindings: projected.bindings, ...(projected.assets ? { assets: projected.assets } : {}) }; const shape = beforeEdit.shapes['shape:rect']; if (!shape) throw new Error('Expected the created rectangle in the editor projection'); const afterEdit: EditorDocument = { ...beforeEdit, shapes: { ...beforeEdit.shapes, [shape.id]: { ...shape, x: 14, y: 15 } } }; const moveRequest = createEditorReconciliationRequest(beforeEdit, afterEdit, { actor_id: 'browser', origin: 'human', transaction_id: 'transaction:move', description: 'Move rectangle', timestamp: 2 }); state = await worker.applyEditorPatches(moveRequest); expect(state.snapshot.document.shapes['shape:rect']?.transform.translation).toEqual({ x: 14, y: 15 });
state = await worker.applyEditorPatches({ ...request, transaction_id: 'transaction:convert', description: 'Convert rectangle', patches: [ { type: 'convert_shape', shape_id: 'shape:rect', kind: 'ellipse', properties: { w: 20, h: 10, fill: '#ff0000' }, style: null } ] }); expect(state.snapshot.document.shapes['shape:rect']?.kind).toBe('ellipse'); expect(state.snapshot.document.shapes['shape:rect']?.style.opacity).toBe(1);
expect((await worker.undoDocument()).snapshot.document.shapes['shape:rect']?.kind).toBe( 'rect' ); state = await worker.redoDocument(); expect(state.snapshot.document.shapes['shape:rect']).toBeDefined();
const reopened = await worker.openDocument(state.bytes, 'browser'); expect(reopened.snapshot.document.shapes['shape:rect']).toBeDefined(); expect(reopened.editor_projection.shapes['shape:rect']).toBeDefined();
const imported = await worker.importDocumentSvg( new TextEncoder().encode('<svg><path d="M0 0L8 8"/></svg>'), 'line.svg', 'page:one', 'layer:one', 2 ); expect(imported.shape_ids.length).toBeGreaterThan(1); const importedChildId = imported.shape_ids.at(-1)!; expect(imported.editor_projection.shapes[importedChildId]).toBeDefined(); const rendered = await worker.render(imported.snapshot, { page_id: 'page:one' }); expect(rendered.status).toBe('success'); if (rendered.status === 'success') { expect(rendered.svg).toContain('<path'); expect(rendered.svg).not.toContain('data:image/svg+xml'); }
const withoutImport = await worker.undoDocument(); expect(withoutImport.snapshot.document.shapes[importedChildId]).toBeUndefined(); const restoredImport = await worker.redoDocument(); expect(restoredImport.snapshot.document.shapes[importedChildId]).toBeDefined(); const reopenedImport = await worker.openDocument(restoredImport.bytes, 'browser'); expect(reopenedImport.editor_projection.shapes[importedChildId]).toBeDefined(); });});