diff --git a/--output b/--output deleted file mode 100644 index bfeb7594..00000000 --- a/--output +++ /dev/null @@ -1,4036 +0,0 @@ -{ - "scriptCompleted": true, - "files": [ - { - "path": ".agents/skills/domain-modeling/ADR-FORMAT.md", - "language": "markdown", - "sizeLines": 47, - "fileCategory": "docs" - }, - { - "path": ".agents/skills/domain-modeling/CONTEXT-FORMAT.md", - "language": "markdown", - "sizeLines": 60, - "fileCategory": "docs" - }, - { - "path": ".agents/skills/domain-modeling/SKILL.md", - "language": "markdown", - "sizeLines": 74, - "fileCategory": "docs" - }, - { - "path": ".agents/skills/teach/GLOSSARY-FORMAT.md", - "language": "markdown", - "sizeLines": 35, - "fileCategory": "docs" - }, - { - "path": ".agents/skills/teach/LEARNING-RECORD-FORMAT.md", - "language": "markdown", - "sizeLines": 46, - "fileCategory": "docs" - }, - { - "path": ".agents/skills/teach/MISSION-FORMAT.md", - "language": "markdown", - "sizeLines": 31, - "fileCategory": "docs" - }, - { - "path": ".agents/skills/teach/MISSION.md", - "language": "markdown", - "sizeLines": 27, - "fileCategory": "docs" - }, - { - "path": ".agents/skills/teach/RESOURCES-FORMAT.md", - "language": "markdown", - "sizeLines": 32, - "fileCategory": "docs" - }, - { - "path": ".agents/skills/teach/SKILL.md", - "language": "markdown", - "sizeLines": 140, - "fileCategory": "docs" - }, - { - "path": ".claude/.deps-index/pending.json", - "language": "json", - "sizeLines": 42, - "fileCategory": "config" - }, - { - "path": ".claude/.deps-index/resolved.json", - "language": "json", - "sizeLines": 137, - "fileCategory": "config" - }, - { - "path": ".claude/.skill-invocation-log", - "language": "unknown", - "sizeLines": 29, - "fileCategory": "code" - }, - { - "path": ".claude/goals/goal.md", - "language": "markdown", - "sizeLines": 74, - "fileCategory": "docs" - }, - { - "path": ".claude/rules/deps-index.md", - "language": "markdown", - "sizeLines": 38, - "fileCategory": "docs" - }, - { - "path": ".claude/settings.json", - "language": "json", - "sizeLines": 5, - "fileCategory": "config" - }, - { - "path": ".claude/skills/librarian/SKILL.md", - "language": "markdown", - "sizeLines": 194, - "fileCategory": "docs" - }, - { - "path": ".claude/skills/product-copy/SKILL.md", - "language": "markdown", - "sizeLines": 105, - "fileCategory": "docs" - }, - { - "path": ".claude/SUGGESTED_SKILLS.md", - "language": "markdown", - "sizeLines": 24, - "fileCategory": "docs" - }, - { - "path": ".gitattributes", - "language": "unknown", - "sizeLines": 4, - "fileCategory": "code" - }, - { - "path": ".github/workflows/deploy.yml", - "language": "yaml", - "sizeLines": 45, - "fileCategory": "infra" - }, - { - "path": ".interface-design/system.md", - "language": "markdown", - "sizeLines": 323, - "fileCategory": "docs" - }, - { - "path": ".mulch/decisions/archie-av.q-counter", - "language": "q-counter", - "sizeLines": 1, - "fileCategory": "code" - }, - { - "path": ".mulch/decisions/archie-linkability.q-counter", - "language": "q-counter", - "sizeLines": 1, - "fileCategory": "code" - }, - { - "path": ".mulch/decisions/archie-narrative.q-counter", - "language": "q-counter", - "sizeLines": 1, - "fileCategory": "code" - }, - { - "path": ".mulch/decisions/archie-persistence.q-counter", - "language": "q-counter", - "sizeLines": 1, - "fileCategory": "code" - }, - { - "path": ".mulch/decisions/archie-ux.q-counter", - "language": "q-counter", - "sizeLines": 1, - "fileCategory": "code" - }, - { - "path": ".mulch/decisions/archie.q-counter", - "language": "q-counter", - "sizeLines": 1, - "fileCategory": "code" - }, - { - "path": ".mulch/expertise/archie.jsonl", - "language": "jsonl", - "sizeLines": 4, - "fileCategory": "code" - }, - { - "path": ".mulch/expertise/architecture.jsonl", - "language": "jsonl", - "sizeLines": 5, - "fileCategory": "code" - }, - { - "path": ".mulch/expertise/decisions.jsonl", - "language": "jsonl", - "sizeLines": 48, - "fileCategory": "code" - }, - { - "path": ".mulch/expertise/infrastructure.jsonl", - "language": "jsonl", - "sizeLines": 14, - "fileCategory": "code" - }, - { - "path": ".mulch/expertise/meta.jsonl", - "language": "jsonl", - "sizeLines": 14, - "fileCategory": "code" - }, - { - "path": ".mulch/expertise/product-research.jsonl", - "language": "jsonl", - "sizeLines": 22, - "fileCategory": "code" - }, - { - "path": ".mulch/mulch.config.yaml", - "language": "yaml", - "sizeLines": 16, - "fileCategory": "config" - }, - { - "path": ".mulch/README.md", - "language": "markdown", - "sizeLines": 21, - "fileCategory": "docs" - }, - { - "path": ".seeds/config.yaml", - "language": "yaml", - "sizeLines": 2, - "fileCategory": "config" - }, - { - "path": ".seeds/issues.jsonl", - "language": "jsonl", - "sizeLines": 76, - "fileCategory": "code" - }, - { - "path": ".seeds/templates.jsonl", - "language": "jsonl", - "sizeLines": 0, - "fileCategory": "code" - }, - { - "path": ".understand-anything/.understandignore", - "language": "unknown", - "sizeLines": 23, - "fileCategory": "code" - }, - { - "path": ".understand-anything/domain-graph.json", - "language": "json", - "sizeLines": 894, - "fileCategory": "config" - }, - { - "path": ".understand-anything/fingerprints.json", - "language": "json", - "sizeLines": 8089, - "fileCategory": "config" - }, - { - "path": ".understand-anything/knowledge-graph.json", - "language": "json", - "sizeLines": 13882, - "fileCategory": "config" - }, - { - "path": ".understand-anything/meta.json", - "language": "json", - "sizeLines": 6, - "fileCategory": "config" - }, - { - "path": ".understand-anything/tmp/changed-files.txt", - "language": "txt", - "sizeLines": 693, - "fileCategory": "docs" - }, - { - "path": "anti-pattern-report.txt", - "language": "txt", - "sizeLines": 267, - "fileCategory": "docs" - }, - { - "path": "apps/studio/.gitkeep", - "language": "unknown", - "sizeLines": 0, - "fileCategory": "code" - }, - { - "path": "apps/studio/index.html", - "language": "html", - "sizeLines": 14, - "fileCategory": "markup" - }, - { - "path": "apps/studio/package.json", - "language": "json", - "sizeLines": 26, - "fileCategory": "config" - }, - { - "path": "apps/studio/public/fonts/fonts.css", - "language": "css", - "sizeLines": 9, - "fileCategory": "markup" - }, - { - "path": "apps/studio/public/fonts/VinqueAntique-Bold.otf", - "language": "otf", - "sizeLines": 481, - "fileCategory": "code" - }, - { - "path": "apps/studio/public/fonts/VinqueAntique-BoldItalic.otf", - "language": "otf", - "sizeLines": 554, - "fileCategory": "code" - }, - { - "path": "apps/studio/README.md", - "language": "markdown", - "sizeLines": 39, - "fileCategory": "docs" - }, - { - "path": "apps/studio/src/AddMapModal.svelte", - "language": "svelte", - "sizeLines": 236, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/App.svelte", - "language": "svelte", - "sizeLines": 2009, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/atmosphere.css", - "language": "css", - "sizeLines": 93, - "fileCategory": "markup" - }, - { - "path": "apps/studio/src/AvEditor.svelte", - "language": "svelte", - "sizeLines": 413, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/bake.ts", - "language": "typescript", - "sizeLines": 103, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/binding-store.svelte.ts", - "language": "typescript", - "sizeLines": 194, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/binding.test.ts", - "language": "typescript", - "sizeLines": 78, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/binding.ts", - "language": "typescript", - "sizeLines": 135, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/CmdK.svelte", - "language": "svelte", - "sizeLines": 167, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/collab.test.ts", - "language": "typescript", - "sizeLines": 42, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/collab.ts", - "language": "typescript", - "sizeLines": 38, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/csv-import.test.ts", - "language": "typescript", - "sizeLines": 177, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/csv-import.ts", - "language": "typescript", - "sizeLines": 187, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/DetailsEditor.svelte", - "language": "svelte", - "sizeLines": 94, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/exhibit-session.svelte.ts", - "language": "typescript", - "sizeLines": 141, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/ExhibitOverview.svelte", - "language": "svelte", - "sizeLines": 466, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/folder-import.test.ts", - "language": "typescript", - "sizeLines": 130, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/folder-import.ts", - "language": "typescript", - "sizeLines": 103, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/geo-notes.test.ts", - "language": "typescript", - "sizeLines": 56, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/geo-notes.ts", - "language": "typescript", - "sizeLines": 45, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/handles-db.ts", - "language": "typescript", - "sizeLines": 74, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/IdentityPrompt.svelte", - "language": "svelte", - "sizeLines": 71, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/iiif-import.test.ts", - "language": "typescript", - "sizeLines": 117, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/iiif-import.ts", - "language": "typescript", - "sizeLines": 125, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/ingest-flows.ts", - "language": "typescript", - "sizeLines": 465, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/library-meta-reducers.test.ts", - "language": "typescript", - "sizeLines": 78, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/library-meta-reducers.ts", - "language": "typescript", - "sizeLines": 52, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/library-meta.svelte.test.ts", - "language": "typescript", - "sizeLines": 77, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/library-meta.svelte.ts", - "language": "typescript", - "sizeLines": 67, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/LibraryHome.svelte", - "language": "svelte", - "sizeLines": 381, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/main.ts", - "language": "typescript", - "sizeLines": 13, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/markers.css", - "language": "css", - "sizeLines": 33, - "fileCategory": "markup" - }, - { - "path": "apps/studio/src/MediaPicker.svelte", - "language": "svelte", - "sizeLines": 110, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/MergeReview.svelte", - "language": "svelte", - "sizeLines": 97, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/narrative-cue-reducer.test.ts", - "language": "typescript", - "sizeLines": 43, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/narrative-cue-reducer.ts", - "language": "typescript", - "sizeLines": 34, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/NarrativeEditor.svelte", - "language": "svelte", - "sizeLines": 276, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/NoteEditor.svelte", - "language": "svelte", - "sizeLines": 138, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/PropsDrawer.svelte", - "language": "svelte", - "sizeLines": 45, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/publish-flows.svelte.ts", - "language": "typescript", - "sizeLines": 172, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/Publish.svelte", - "language": "svelte", - "sizeLines": 213, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/PublishDialog.svelte", - "language": "svelte", - "sizeLines": 260, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/reading-state.svelte.test.ts", - "language": "typescript", - "sizeLines": 96, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/reading-state.svelte.ts", - "language": "typescript", - "sizeLines": 64, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/ReadingsEditor.svelte", - "language": "svelte", - "sizeLines": 105, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/ReadingsModal.svelte", - "language": "svelte", - "sizeLines": 71, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/ReadingsRail.svelte", - "language": "svelte", - "sizeLines": 87, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/RightsEditor.svelte", - "language": "svelte", - "sizeLines": 85, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/save-queue.svelte.test.ts", - "language": "typescript", - "sizeLines": 78, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/save-queue.svelte.ts", - "language": "typescript", - "sizeLines": 71, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/seed-data.ts", - "language": "typescript", - "sizeLines": 145, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/shortcuts.ts", - "language": "typescript", - "sizeLines": 56, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/ShortcutsHelp.svelte", - "language": "svelte", - "sizeLines": 64, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/store.ts", - "language": "typescript", - "sizeLines": 329, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/tokens.css", - "language": "css", - "sizeLines": 122, - "fileCategory": "markup" - }, - { - "path": "apps/studio/src/wadm-import.test.ts", - "language": "typescript", - "sizeLines": 83, - "fileCategory": "code" - }, - { - "path": "apps/studio/src/wadm-import.ts", - "language": "typescript", - "sizeLines": 127, - "fileCategory": "code" - }, - { - "path": "apps/studio/vite.config.ts", - "language": "typescript", - "sizeLines": 38, - "fileCategory": "code" - }, - { - "path": "apps/studio/vitest.config.ts", - "language": "typescript", - "sizeLines": 14, - "fileCategory": "code" - }, - { - "path": "apps/viewer/astro.config.mjs", - "language": "javascript", - "sizeLines": 30, - "fileCategory": "code" - }, - { - "path": "apps/viewer/fixtures/atlas.ts", - "language": "typescript", - "sizeLines": 109, - "fileCategory": "code" - }, - { - "path": "apps/viewer/fixtures/geo.ts", - "language": "typescript", - "sizeLines": 58, - "fileCategory": "code" - }, - { - "path": "apps/viewer/fixtures/sample-data.ts", - "language": "typescript", - "sizeLines": 198, - "fileCategory": "code" - }, - { - "path": "apps/viewer/fixtures/voynich.ts", - "language": "typescript", - "sizeLines": 214, - "fileCategory": "code" - }, - { - "path": "apps/viewer/libraries/README.md", - "language": "markdown", - "sizeLines": 23, - "fileCategory": "docs" - }, - { - "path": "apps/viewer/package.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/fonts/fonts.css", - "language": "css", - "sizeLines": 9, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/public/fonts/VinqueAntique-Bold.otf", - "language": "otf", - "sizeLines": 481, - "fileCategory": "code" - }, - { - "path": "apps/viewer/public/fonts/VinqueAntique-BoldItalic.otf", - "language": "otf", - "sizeLines": 554, - "fileCategory": "code" - }, - { - "path": "apps/viewer/public/published/collection.json", - "language": "json", - "sizeLines": 91, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/exhibits.json", - "language": "json", - "sizeLines": 45, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/geo-map/annotations/history/0000000001Q1E2RFF58H62PFSW.json", - "language": "json", - "sizeLines": 40, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/geo-map/annotations/history/0000000002Q00FVKNSJJ2BGTDE.json", - "language": "json", - "sizeLines": 40, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/geo-map/annotations/history/00000000032HVBH9EHJCR5YMRR.json", - "language": "json", - "sizeLines": 40, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/geo-map/annotations/history/0000000004CK4MH8W1V2DW0N39.json", - "language": "json", - "sizeLines": 40, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/geo-map/annotations/history/index.json", - "language": "json", - "sizeLines": 5, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/geo-map/canvas/m1/annotations.json", - "language": "json", - "sizeLines": 126, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/geo-map/index.html", - "language": "html", - "sizeLines": 34, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/public/published/geo-map/manifest.json", - "language": "json", - "sizeLines": 204, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/index.html", - "language": "html", - "sizeLines": 22, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/0000000001QA9HWMFBS3S6DTJ4.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/00000000021G31YS0Z2ZG6N1RX.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/0000000003EB0YAESVXD6MKPY6.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/0000000004P0QSM9AM36QZM3JY.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/00000000056XKG648T6Z7TAAQX.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000689W361NNJSBV1NNV.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/00000000073MHG0BGX4NK51FTH.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/0000000008DGND2JNBFKR1YW4B.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/00000000093KM2FAXA5WQDQ82Q.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000AN029WSZ324T8AK3J.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000BTCT9T76FKBHTEJK4.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000C36QGH1EA6FN8ZNXN.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000D9Z2PS1FE0YRQNX28.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000EHP928C5GZ7MJ1KGS.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000FV4ZR6350WQCQE3BN.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000GK82XFVPQDZNMX4KC.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000HDQ0N6DX74Q43Q9SG.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000JTC0PHRKF5DWSN6JR.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000KRK6BRR71P6PX20V0.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000MG36QD4YX5V1YJ8HW.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000NXQ7R2V1N9674N2QK.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000PZN8JWP0A1HSVXMJM.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000QRY7P4XVEGX7X1XCA.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/000000000R8Y311HDC71SWDHVT.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/history/index.json", - "language": "json", - "sizeLines": 25, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/readings/community.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/annotations/readings/linguist.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o1/annotations-community.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o1/annotations-linguist.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o1/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o2/annotations-community.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o2/annotations-linguist.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o2/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o3/annotations-community.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o3/annotations-linguist.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o3/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o4/annotations-community.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o4/annotations-linguist.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o4/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o5/annotations-community.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o5/annotations-linguist.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o5/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o6/annotations-community.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o6/annotations-linguist.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o6/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o7/annotations-community.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o7/annotations-linguist.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o7/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o8/annotations-community.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o8/annotations-linguist.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/canvas/o8/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/index.html", - "language": "html", - "sizeLines": 137, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/public/published/language-atlas/manifest.json", - "language": "json", - "sizeLines": 1508, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/language-atlas/readings.json", - "language": "json", - "sizeLines": 13, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/sitemap.txt", - "language": "txt", - "sizeLines": 6, - "fileCategory": "docs" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000001M0GZY8KQDZEF4C74.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000002QERZ7R4AD74QJ481.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000003V9WGXP5NY4Z5C3CA.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000004429NM3GG6S8ZYV05.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000005W9XVMN7C968XY5A2.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000616MM0ADVMBBK0XHJ.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000007GCR82ZZ0V3CKGCP4.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000008MSMNY0VX0THJB32N.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000009P1SYAXVMEKD64YM1.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000AZ5MWJJCKJSVCRP75.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000BQ1719XADN4MSNF3D.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000CGES9SXD5HHBYMGF1.json", - "language": "json", - "sizeLines": 54, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000D5SH6F04BJZCKGCC2.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000EBTVCRTK515MFGEAM.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000FQ49APBGFAPPZ2Z93.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000GD9N71MHZSCFJM2MB.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000HTMR0MJ171Q2W6C1M.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000JV4R2HB536DH4YA1E.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000KN9BECJFQPP1EDDVP.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000M8K248PTFFZR2Y36H.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000NSFASYX2TFC4DE7GZ.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000PHK5Q5G86C31C1M59.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000Q47KB1X12V54QJQ7N.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000RPZP2N8FCZGDY2CSY.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000SPZ8YDM9SEQKNS8A9.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000T5N0B9CP2EE6P6Z5W.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000VJPMVWXVNE66V47J0.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000W8BQMN0WT7K65A25J.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000XAGKZA0P14VWSNZ0Q.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000YTTF352G3HGS6SAGC.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000000ZKTQFJWBE5DHG6CEX.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/00000000103N8G97G375T4RR47.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000011YTBN4W131SCZKA7M.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000012RSQD9VZQ7601ZAA6.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000013E2ZHQMTVKKYX844Z.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000014RM3WBR0S55GK9QKA.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000015XBMQ294BD4FB74XX.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000016PE2QB9MW3RR9FE5K.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/00000000174012M3JVECQ4CWZJ.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000001876WEDG0CY9Q9PFNG.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/0000000019PGCFHGFQM3XPC4YF.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000001AYSHEDSMARM8NK98D.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000001BASS5X8QB9YS79JAD.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000001C9W0J2BKMJA125KR2.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000001DKSXRNKR02TMX11VZ.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000001EM5QNPFCQ5A2K3W6H.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000001FHXMQBYHST49K7EYG.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/000000001G39GVAR3H0FA56RD5.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/history/index.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/narrative.json", - "language": "json", - "sizeLines": 182, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/readings/abjad.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/readings/cipher.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/annotations/readings/hoax.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o1/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o1/annotations-cipher.json", - "language": "json", - "sizeLines": 56, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o1/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o1/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o10/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o10/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o10/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o10/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o11/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o11/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o11/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o11/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o12/annotations-abjad.json", - "language": "json", - "sizeLines": 11, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o12/annotations-cipher.json", - "language": "json", - "sizeLines": 11, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o12/annotations-hoax.json", - "language": "json", - "sizeLines": 11, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o12/annotations.json", - "language": "json", - "sizeLines": 98, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o2/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o2/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o2/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o2/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o3/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o3/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o3/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o3/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o4/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o4/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o4/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o4/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o5/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o5/annotations-cipher.json", - "language": "json", - "sizeLines": 51, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o5/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o5/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o6/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o6/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o6/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o6/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o7/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o7/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o7/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o7/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o8/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o8/annotations-cipher.json", - "language": "json", - "sizeLines": 51, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o8/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o8/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o9/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o9/annotations-cipher.json", - "language": "json", - "sizeLines": 51, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o9/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/canvas/o9/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/index.html", - "language": "html", - "sizeLines": 265, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/public/published/voynich-reading/manifest.json", - "language": "json", - "sizeLines": 3085, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-reading/readings.json", - "language": "json", - "sizeLines": 19, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/annotations/history/0000000001M0GZY8KQDZEF4C74.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/annotations/history/0000000002QERZ7R4AD74QJ481.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/annotations/history/0000000003V9WGXP5NY4Z5C3CA.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/annotations/history/0000000004429NM3GG6S8ZYV05.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/annotations/history/index.json", - "language": "json", - "sizeLines": 5, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/annotations/readings/abjad.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/annotations/readings/cipher.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/annotations/readings/hoax.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/canvas/o9/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/canvas/o9/annotations-cipher.json", - "language": "json", - "sizeLines": 51, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/canvas/o9/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/canvas/o9/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/index.html", - "language": "html", - "sizeLines": 38, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/manifest.json", - "language": "json", - "sizeLines": 274, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich-rosettes/readings.json", - "language": "json", - "sizeLines": 19, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000001M0GZY8KQDZEF4C74.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000002QERZ7R4AD74QJ481.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000003V9WGXP5NY4Z5C3CA.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000004429NM3GG6S8ZYV05.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000005W9XVMN7C968XY5A2.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000616MM0ADVMBBK0XHJ.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000007GCR82ZZ0V3CKGCP4.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000008MSMNY0VX0THJB32N.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000009P1SYAXVMEKD64YM1.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000AZ5MWJJCKJSVCRP75.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000BQ1719XADN4MSNF3D.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000CGES9SXD5HHBYMGF1.json", - "language": "json", - "sizeLines": 54, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000D5SH6F04BJZCKGCC2.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000EBTVCRTK515MFGEAM.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000FQ49APBGFAPPZ2Z93.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000GD9N71MHZSCFJM2MB.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000HTMR0MJ171Q2W6C1M.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000JV4R2HB536DH4YA1E.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000KN9BECJFQPP1EDDVP.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000M8K248PTFFZR2Y36H.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000NSFASYX2TFC4DE7GZ.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000PHK5Q5G86C31C1M59.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000Q47KB1X12V54QJQ7N.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000RPZP2N8FCZGDY2CSY.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000SPZ8YDM9SEQKNS8A9.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000T5N0B9CP2EE6P6Z5W.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000VJPMVWXVNE66V47J0.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000W8BQMN0WT7K65A25J.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000XAGKZA0P14VWSNZ0Q.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000YTTF352G3HGS6SAGC.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000000ZKTQFJWBE5DHG6CEX.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/00000000103N8G97G375T4RR47.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000011YTBN4W131SCZKA7M.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000012RSQD9VZQ7601ZAA6.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000013E2ZHQMTVKKYX844Z.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000014RM3WBR0S55GK9QKA.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000015XBMQ294BD4FB74XX.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000016PE2QB9MW3RR9FE5K.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/00000000174012M3JVECQ4CWZJ.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000001876WEDG0CY9Q9PFNG.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/0000000019PGCFHGFQM3XPC4YF.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000001AYSHEDSMARM8NK98D.json", - "language": "json", - "sizeLines": 44, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000001BASS5X8QB9YS79JAD.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000001C9W0J2BKMJA125KR2.json", - "language": "json", - "sizeLines": 34, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000001DKSXRNKR02TMX11VZ.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000001EM5QNPFCQ5A2K3W6H.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000001FHXMQBYHST49K7EYG.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/000000001G39GVAR3H0FA56RD5.json", - "language": "json", - "sizeLines": 33, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/history/index.json", - "language": "json", - "sizeLines": 49, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/readings/abjad.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/readings/cipher.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/annotations/readings/hoax.json", - "language": "json", - "sizeLines": 14, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o1/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o1/annotations-cipher.json", - "language": "json", - "sizeLines": 56, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o1/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o1/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o10/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o10/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o10/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o10/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o11/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o11/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o11/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o11/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o12/annotations-abjad.json", - "language": "json", - "sizeLines": 11, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o12/annotations-cipher.json", - "language": "json", - "sizeLines": 11, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o12/annotations-hoax.json", - "language": "json", - "sizeLines": 11, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o12/annotations.json", - "language": "json", - "sizeLines": 98, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o2/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o2/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o2/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o2/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o3/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o3/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o3/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o3/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o4/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o4/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o4/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o4/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o5/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o5/annotations-cipher.json", - "language": "json", - "sizeLines": 51, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o5/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o5/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o6/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o6/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o6/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o6/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o7/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o7/annotations-cipher.json", - "language": "json", - "sizeLines": 46, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o7/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o7/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o8/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o8/annotations-cipher.json", - "language": "json", - "sizeLines": 51, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o8/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o8/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o9/annotations-abjad.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o9/annotations-cipher.json", - "language": "json", - "sizeLines": 51, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o9/annotations-hoax.json", - "language": "json", - "sizeLines": 36, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/canvas/o9/annotations.json", - "language": "json", - "sizeLines": 29, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/index.html", - "language": "html", - "sizeLines": 265, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/public/published/voynich/manifest.json", - "language": "json", - "sizeLines": 2915, - "fileCategory": "config" - }, - { - "path": "apps/viewer/public/published/voynich/readings.json", - "language": "json", - "sizeLines": 19, - "fileCategory": "config" - }, - { - "path": "apps/viewer/README.md", - "language": "markdown", - "sizeLines": 43, - "fileCategory": "docs" - }, - { - "path": "apps/viewer/scripts/gen-published.mts", - "language": "mts", - "sizeLines": 134, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/aside-persistence.ts", - "language": "typescript", - "sizeLines": 39, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/atmosphere.css", - "language": "css", - "sizeLines": 93, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/src/cite-cards.test.ts", - "language": "typescript", - "sizeLines": 41, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/cite-cards.ts", - "language": "typescript", - "sizeLines": 34, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/cite-context.ts", - "language": "typescript", - "sizeLines": 12, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/Credit.svelte", - "language": "svelte", - "sizeLines": 61, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/EmptyHall.svelte", - "language": "svelte", - "sizeLines": 163, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/ExhibitCiteCard.svelte", - "language": "svelte", - "sizeLines": 47, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/ExhibitView.svelte", - "language": "svelte", - "sizeLines": 409, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/Gallery.svelte", - "language": "svelte", - "sizeLines": 76, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/MediaPlayer.svelte", - "language": "svelte", - "sizeLines": 263, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/MediaThumbnail.svelte", - "language": "svelte", - "sizeLines": 93, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/narrative-escape.test.ts", - "language": "typescript", - "sizeLines": 100, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/NarrativeReader.svelte", - "language": "svelte", - "sizeLines": 316, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/NoteLightbox.svelte", - "language": "svelte", - "sizeLines": 117, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/NoteMedia.svelte", - "language": "svelte", - "sizeLines": 66, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/ObjectGrid.svelte", - "language": "svelte", - "sizeLines": 96, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/ProseCites.svelte", - "language": "svelte", - "sizeLines": 20, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/Reader.svelte", - "language": "svelte", - "sizeLines": 345, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/ReadingLegend.svelte", - "language": "svelte", - "sizeLines": 105, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/ReadingSheet.svelte", - "language": "svelte", - "sizeLines": 51, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/SidebarObjectNav.svelte", - "language": "svelte", - "sizeLines": 87, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/components/ViewerShell.svelte", - "language": "svelte", - "sizeLines": 383, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/markers.css", - "language": "css", - "sizeLines": 33, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/src/og-image.ts", - "language": "typescript", - "sizeLines": 16, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/pages/[slug].astro", - "language": "astro", - "sizeLines": 122, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/pages/index.astro", - "language": "astro", - "sizeLines": 44, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/pages/robots.txt.ts", - "language": "typescript", - "sizeLines": 7, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/pages/sitemap.xml.ts", - "language": "typescript", - "sizeLines": 12, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/published-base.ts", - "language": "typescript", - "sizeLines": 32, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/published.test.ts", - "language": "typescript", - "sizeLines": 169, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/published.ts", - "language": "typescript", - "sizeLines": 290, - "fileCategory": "code" - }, - { - "path": "apps/viewer/src/tokens.css", - "language": "css", - "sizeLines": 143, - "fileCategory": "markup" - }, - { - "path": "apps/viewer/tsconfig.json", - "language": "json", - "sizeLines": 9, - "fileCategory": "config" - }, - { - "path": "archie.config.json", - "language": "json", - "sizeLines": 6, - "fileCategory": "config" - }, - { - "path": "CLAUDE.md", - "language": "markdown", - "sizeLines": 62, - "fileCategory": "docs" - }, - { - "path": "design/fonts/licenses/FOPVHS - font by lirkiv.txt", - "language": "txt", - "sizeLines": 11, - "fileCategory": "docs" - }, - { - "path": "design/fonts/licenses/License LARAZ Font.txt", - "language": "txt", - "sizeLines": 2, - "fileCategory": "docs" - }, - { - "path": "design/fonts/VinqueAntique-Bold.otf", - "language": "otf", - "sizeLines": 481, - "fileCategory": "code" - }, - { - "path": "design/fonts/VinqueAntique-BoldItalic.otf", - "language": "otf", - "sizeLines": 554, - "fileCategory": "code" - }, - { - "path": "docs/adr/0001-objects-are-exhibit-nested.md", - "language": "markdown", - "sizeLines": 23, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0002-rendering-and-framework.md", - "language": "markdown", - "sizeLines": 25, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0003-annotation-spine-append-only-version-dag.md", - "language": "markdown", - "sizeLines": 41, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0004-no-wasm-vips-tiling.md", - "language": "markdown", - "sizeLines": 10, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0005-narrative-section-model.md", - "language": "markdown", - "sizeLines": 17, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0006-edit-at-locus-spatiotemporal-selectors.md", - "language": "markdown", - "sizeLines": 79, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0007-readings-as-annotationpages.md", - "language": "markdown", - "sizeLines": 31, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0008-viewer-one-shell-dual-mode.md", - "language": "markdown", - "sizeLines": 15, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0009-canonical-hosted-instance-for-src.md", - "language": "markdown", - "sizeLines": 19, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0010-portable-viewer-read-seam.md", - "language": "markdown", - "sizeLines": 22, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0011-gesture-initiated-creation.md", - "language": "markdown", - "sizeLines": 71, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0012-self-hosted-fonts.md", - "language": "markdown", - "sizeLines": 31, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0013-canonical-origin-github-pages.md", - "language": "markdown", - "sizeLines": 35, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0014-self-describing-published-artifact.md", - "language": "markdown", - "sizeLines": 48, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0015-map-medium-bounded-extent.md", - "language": "markdown", - "sizeLines": 16, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0016-narrative-as-emergent-reading-mode.md", - "language": "markdown", - "sizeLines": 49, - "fileCategory": "docs" - }, - { - "path": "docs/adr/0017-section-as-supplementing-annotation.md", - "language": "markdown", - "sizeLines": 42, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/_meta.json", - "language": "json", - "sizeLines": 45, - "fileCategory": "config" - }, - { - "path": "docs/architecture/cross-cutting/patterns.md", - "language": "markdown", - "sizeLines": 88, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/domain.md", - "language": "markdown", - "sizeLines": 63, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/ecosystem.md", - "language": "markdown", - "sizeLines": 71, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/evolution.md", - "language": "markdown", - "sizeLines": 74, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/features.json", - "language": "json", - "sizeLines": 40, - "fileCategory": "config" - }, - { - "path": "docs/architecture/infrastructure.md", - "language": "markdown", - "sizeLines": 86, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/overview.md", - "language": "markdown", - "sizeLines": 101, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/risk-map.md", - "language": "markdown", - "sizeLines": 55, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems.md", - "language": "markdown", - "sizeLines": 99, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/annotation-spine/components.md", - "language": "markdown", - "sizeLines": 91, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/annotation-spine/contracts.md", - "language": "markdown", - "sizeLines": 81, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/publish/AUDIT-gh-publish.md", - "language": "markdown", - "sizeLines": 78, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/publish/components.md", - "language": "markdown", - "sizeLines": 61, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/publish/contracts.md", - "language": "markdown", - "sizeLines": 76, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/rendering/components.md", - "language": "markdown", - "sizeLines": 104, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/rendering/contracts.md", - "language": "markdown", - "sizeLines": 56, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/storage/components.md", - "language": "markdown", - "sizeLines": 53, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/storage/contracts.md", - "language": "markdown", - "sizeLines": 83, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/studio/components.md", - "language": "markdown", - "sizeLines": 76, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/studio/contracts.md", - "language": "markdown", - "sizeLines": 82, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/viewer/components.md", - "language": "markdown", - "sizeLines": 82, - "fileCategory": "docs" - }, - { - "path": "docs/architecture/subsystems/viewer/contracts.md", - "language": "markdown", - "sizeLines": 86, - "fileCategory": "docs" - }, - { - "path": "docs/bundle-size.json", - "language": "json", - "sizeLines": 31, - "fileCategory": "config" - }, - { - "path": "docs/decisions/archie-av.md", - "language": "markdown", - "sizeLines": 15, - "fileCategory": "docs" - }, - { - "path": "docs/decisions/archie-linkability.md", - "language": "markdown", - "sizeLines": 15, - "fileCategory": "docs" - }, - { - "path": "docs/decisions/archie-narrative.md", - "language": "markdown", - "sizeLines": 15, - "fileCategory": "docs" - }, - { - "path": "docs/decisions/archie-persistence.md", - "language": "markdown", - "sizeLines": 16, - "fileCategory": "docs" - }, - { - "path": "docs/decisions/archie-ux.md", - "language": "markdown", - "sizeLines": 15, - "fileCategory": "docs" - }, - { - "path": "docs/decisions/archie.md", - "language": "markdown", - "sizeLines": 20, - "fileCategory": "docs" - }, - { - "path": "docs/decisions/PROPOSALS.md", - "language": "markdown", - "sizeLines": 107, - "fileCategory": "docs" - }, - { - "path": "docs/GOAL.md", - "language": "markdown", - "sizeLines": 248, - "fileCategory": "docs" - }, - { - "path": "docs/IMPLEMENTATION-STRATEGY.md", - "language": "markdown", - "sizeLines": 307, - "fileCategory": "docs" - }, - { - "path": "docs/screenshots/auto/manifest.json", - "language": "json", - "sizeLines": 57, - "fileCategory": "config" - }, - { - "path": "package.json", - "language": "json", - "sizeLines": 23, - "fileCategory": "config" - }, - { - "path": "packages/render-core/package.json", - "language": "json", - "sizeLines": 23, - "fileCategory": "config" - }, - { - "path": "packages/render-core/src/av/time.test.ts", - "language": "typescript", - "sizeLines": 69, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/av/time.ts", - "language": "typescript", - "sizeLines": 71, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/av/transcript.test.ts", - "language": "typescript", - "sizeLines": 73, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/av/transcript.ts", - "language": "typescript", - "sizeLines": 103, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/exif/orientation.test.ts", - "language": "typescript", - "sizeLines": 51, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/exif/orientation.ts", - "language": "typescript", - "sizeLines": 46, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/exif/read.test.ts", - "language": "typescript", - "sizeLines": 142, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/exif/read.ts", - "language": "typescript", - "sizeLines": 148, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/binding.test.ts", - "language": "typescript", - "sizeLines": 98, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/binding.ts", - "language": "typescript", - "sizeLines": 109, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/conformance.test.ts", - "language": "typescript", - "sizeLines": 8, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/conformance.ts", - "language": "typescript", - "sizeLines": 96, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/fsa.ts", - "language": "typescript", - "sizeLines": 50, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/memory.ts", - "language": "typescript", - "sizeLines": 71, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/seam.test.ts", - "language": "typescript", - "sizeLines": 97, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/seam.ts", - "language": "typescript", - "sizeLines": 40, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/zip.streaming.test.ts", - "language": "typescript", - "sizeLines": 159, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/zip.test.ts", - "language": "typescript", - "sizeLines": 67, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/fs/zip.ts", - "language": "typescript", - "sizeLines": 156, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/coverage.test.ts", - "language": "typescript", - "sizeLines": 101, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/coverage.ts", - "language": "typescript", - "sizeLines": 59, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/downscale.test.ts", - "language": "typescript", - "sizeLines": 75, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/downscale.ts", - "language": "typescript", - "sizeLines": 23, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/geo.test.ts", - "language": "typescript", - "sizeLines": 133, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/geo.ts", - "language": "typescript", - "sizeLines": 127, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/marginalia.test.ts", - "language": "typescript", - "sizeLines": 137, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/marginalia.ts", - "language": "typescript", - "sizeLines": 134, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/mediafragment.test.ts", - "language": "typescript", - "sizeLines": 88, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/mediafragment.ts", - "language": "typescript", - "sizeLines": 77, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/panel-resize.test.ts", - "language": "typescript", - "sizeLines": 65, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/panel-resize.ts", - "language": "typescript", - "sizeLines": 41, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/selector.test.ts", - "language": "typescript", - "sizeLines": 109, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/geometry/selector.ts", - "language": "typescript", - "sizeLines": 128, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/canvasid.test.ts", - "language": "typescript", - "sizeLines": 41, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/canvasid.ts", - "language": "typescript", - "sizeLines": 16, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/collection.ts", - "language": "typescript", - "sizeLines": 29, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/exhibits.ts", - "language": "typescript", - "sizeLines": 89, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/gallery.test.ts", - "language": "typescript", - "sizeLines": 91, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/manifest-geo.test.ts", - "language": "typescript", - "sizeLines": 43, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/manifest.test.ts", - "language": "typescript", - "sizeLines": 187, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/manifest.ts", - "language": "typescript", - "sizeLines": 369, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/presentation.ts", - "language": "typescript", - "sizeLines": 148, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/resolve.test.ts", - "language": "typescript", - "sizeLines": 74, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/resolve.ts", - "language": "typescript", - "sizeLines": 102, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/rights.test.ts", - "language": "typescript", - "sizeLines": 191, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/iiif/rights.ts", - "language": "typescript", - "sizeLines": 85, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/index.test.ts", - "language": "typescript", - "sizeLines": 8, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/index.ts", - "language": "typescript", - "sizeLines": 87, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/link/link.test.ts", - "language": "typescript", - "sizeLines": 155, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/link/link.ts", - "language": "typescript", - "sizeLines": 202, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/migrate/migrate.test.ts", - "language": "typescript", - "sizeLines": 51, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/migrate/migrate.ts", - "language": "typescript", - "sizeLines": 89, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/model/layout.test.ts", - "language": "typescript", - "sizeLines": 43, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/model/layout.ts", - "language": "typescript", - "sizeLines": 36, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/model/library.test.ts", - "language": "typescript", - "sizeLines": 31, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/model/model.test.ts", - "language": "typescript", - "sizeLines": 42, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/model/model.ts", - "language": "typescript", - "sizeLines": 195, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/note/media.test.ts", - "language": "typescript", - "sizeLines": 52, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/note/media.ts", - "language": "typescript", - "sizeLines": 73, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/__snapshots__/voynich-readings.test.ts.snap", - "language": "snap", - "sizeLines": 234, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/ghpages.test.ts", - "language": "typescript", - "sizeLines": 159, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/ghpages.ts", - "language": "typescript", - "sizeLines": 209, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/interop.test.ts", - "language": "typescript", - "sizeLines": 102, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/merge.test.ts", - "language": "typescript", - "sizeLines": 53, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/merge.ts", - "language": "typescript", - "sizeLines": 64, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/portable.test.ts", - "language": "typescript", - "sizeLines": 200, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/portable.ts", - "language": "typescript", - "sizeLines": 188, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/preview.test.ts", - "language": "typescript", - "sizeLines": 86, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/read.test.ts", - "language": "typescript", - "sizeLines": 110, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/read.ts", - "language": "typescript", - "sizeLines": 103, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/site-geo.test.ts", - "language": "typescript", - "sizeLines": 45, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/site.test.ts", - "language": "typescript", - "sizeLines": 286, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/site.ts", - "language": "typescript", - "sizeLines": 420, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/static-pages.test.ts", - "language": "typescript", - "sizeLines": 155, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/static-pages.ts", - "language": "typescript", - "sizeLines": 125, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/voynich-readings.test.ts", - "language": "typescript", - "sizeLines": 125, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/working.test.ts", - "language": "typescript", - "sizeLines": 159, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/publish/working.ts", - "language": "typescript", - "sizeLines": 275, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/query/body.ts", - "language": "typescript", - "sizeLines": 10, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/query/filter.test.ts", - "language": "typescript", - "sizeLines": 44, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/query/filter.ts", - "language": "typescript", - "sizeLines": 54, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/query/marker-style.test.ts", - "language": "typescript", - "sizeLines": 64, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/query/marker-style.ts", - "language": "typescript", - "sizeLines": 45, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/query/published.test.ts", - "language": "typescript", - "sizeLines": 76, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/query/published.ts", - "language": "typescript", - "sizeLines": 105, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/query/reading.test.ts", - "language": "typescript", - "sizeLines": 187, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/session/session.test.ts", - "language": "typescript", - "sizeLines": 188, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/session/session.ts", - "language": "typescript", - "sizeLines": 237, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/deserialize.test.ts", - "language": "typescript", - "sizeLines": 130, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/deserialize.ts", - "language": "typescript", - "sizeLines": 110, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/geo-persist.test.ts", - "language": "typescript", - "sizeLines": 62, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/heads.test.ts", - "language": "typescript", - "sizeLines": 72, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/heads.ts", - "language": "typescript", - "sizeLines": 33, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/index.ts", - "language": "typescript", - "sizeLines": 8, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/interop.test.ts", - "language": "typescript", - "sizeLines": 115, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/log.test.ts", - "language": "typescript", - "sizeLines": 121, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/log.ts", - "language": "typescript", - "sizeLines": 172, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/merge.test.ts", - "language": "typescript", - "sizeLines": 141, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/merge.ts", - "language": "typescript", - "sizeLines": 218, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/persist.test.ts", - "language": "typescript", - "sizeLines": 110, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/persist.ts", - "language": "typescript", - "sizeLines": 72, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/resolve.test.ts", - "language": "typescript", - "sizeLines": 62, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/serialize.test.ts", - "language": "typescript", - "sizeLines": 140, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/spine/serialize.ts", - "language": "typescript", - "sizeLines": 236, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/text/sanitize.test.ts", - "language": "typescript", - "sizeLines": 74, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/text/sanitize.ts", - "language": "typescript", - "sizeLines": 45, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/url/breadcrumb.test.ts", - "language": "typescript", - "sizeLines": 37, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/url/breadcrumb.ts", - "language": "typescript", - "sizeLines": 43, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/url/deeplink.test.ts", - "language": "typescript", - "sizeLines": 52, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/url/deeplink.ts", - "language": "typescript", - "sizeLines": 90, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/url/route.test.ts", - "language": "typescript", - "sizeLines": 99, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/url/route.ts", - "language": "typescript", - "sizeLines": 64, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/wadm/brand.test.ts", - "language": "typescript", - "sizeLines": 81, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/wadm/brand.ts", - "language": "typescript", - "sizeLines": 144, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/wadm/index.ts", - "language": "typescript", - "sizeLines": 3, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/wadm/types.test.ts", - "language": "typescript", - "sizeLines": 116, - "fileCategory": "code" - }, - { - "path": "packages/render-core/src/wadm/types.ts", - "language": "typescript", - "sizeLines": 248, - "fileCategory": "code" - }, - { - "path": "packages/render-core/test/fixtures/exif/manifest.json", - "language": "json", - "sizeLines": 18, - "fileCategory": "config" - }, - { - "path": "packages/render-core/test/fixtures/exif/README.md", - "language": "markdown", - "sizeLines": 25, - "fileCategory": "docs" - }, - { - "path": "packages/render-core/tsconfig.json", - "language": "json", - "sizeLines": 8, - "fileCategory": "config" - }, - { - "path": "packages/render-mount/package.json", - "language": "json", - "sizeLines": 25, - "fileCategory": "config" - }, - { - "path": "packages/render-mount/src/fitbounds.test.ts", - "language": "typescript", - "sizeLines": 127, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/fitbounds.ts", - "language": "typescript", - "sizeLines": 110, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/frame-overlay.ts", - "language": "typescript", - "sizeLines": 95, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/gate.test.ts", - "language": "typescript", - "sizeLines": 61, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/gesture-guard.test.ts", - "language": "typescript", - "sizeLines": 51, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/gesture-guard.ts", - "language": "typescript", - "sizeLines": 44, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/index.test.ts", - "language": "typescript", - "sizeLines": 17, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/index.ts", - "language": "typescript", - "sizeLines": 17, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/mount-guard.test.ts", - "language": "typescript", - "sizeLines": 60, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/mount.ts", - "language": "typescript", - "sizeLines": 352, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/surface.ts", - "language": "typescript", - "sizeLines": 77, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/xyz.test.ts", - "language": "typescript", - "sizeLines": 27, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/xyz.ts", - "language": "typescript", - "sizeLines": 34, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/zoom-band.test.ts", - "language": "typescript", - "sizeLines": 22, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/src/zoom-band.ts", - "language": "typescript", - "sizeLines": 13, - "fileCategory": "code" - }, - { - "path": "packages/render-mount/tsconfig.json", - "language": "json", - "sizeLines": 8, - "fileCategory": "config" - }, - { - "path": "packages/render-mount/vitest.config.ts", - "language": "typescript", - "sizeLines": 11, - "fileCategory": "code" - }, - { - "path": "packages/render-svelte/package.json", - "language": "json", - "sizeLines": 30, - "fileCategory": "config" - }, - { - "path": "packages/render-svelte/src/Canvas.svelte", - "language": "svelte", - "sizeLines": 184, - "fileCategory": "code" - }, - { - "path": "packages/render-svelte/src/controller.test.ts", - "language": "typescript", - "sizeLines": 108, - "fileCategory": "code" - }, - { - "path": "packages/render-svelte/src/controller.ts", - "language": "typescript", - "sizeLines": 64, - "fileCategory": "code" - }, - { - "path": "packages/render-svelte/src/index.ts", - "language": "typescript", - "sizeLines": 13, - "fileCategory": "code" - }, - { - "path": "packages/render-svelte/src/MarginColumn.svelte", - "language": "svelte", - "sizeLines": 138, - "fileCategory": "code" - }, - { - "path": "packages/render-svelte/src/ResizeDivider.svelte", - "language": "svelte", - "sizeLines": 172, - "fileCategory": "code" - }, - { - "path": "packages/render-svelte/tsconfig.json", - "language": "json", - "sizeLines": 8, - "fileCategory": "config" - }, - { - "path": "pnpm-workspace.yaml", - "language": "yaml", - "sizeLines": 18, - "fileCategory": "config" - }, - { - "path": "README.md", - "language": "markdown", - "sizeLines": 341, - "fileCategory": "docs" - }, - { - "path": "scripts/build-gh-pages.sh", - "language": "shell", - "sizeLines": 79, - "fileCategory": "script" - }, - { - "path": "scripts/bundle-size.mjs", - "language": "javascript", - "sizeLines": 84, - "fileCategory": "code" - }, - { - "path": "scripts/capture-screenshots.mjs", - "language": "javascript", - "sizeLines": 170, - "fileCategory": "code" - }, - { - "path": "scripts/dev-proxy.mjs", - "language": "javascript", - "sizeLines": 39, - "fileCategory": "code" - }, - { - "path": "scripts/dev.sh", - "language": "shell", - "sizeLines": 31, - "fileCategory": "script" - }, - { - "path": "scripts/import-voynich.mjs", - "language": "javascript", - "sizeLines": 104, - "fileCategory": "code" - }, - { - "path": "scripts/start.mjs", - "language": "javascript", - "sizeLines": 284, - "fileCategory": "code" - }, - { - "path": "scripts/verify-marginalia.mjs", - "language": "javascript", - "sizeLines": 132, - "fileCategory": "code" - }, - { - "path": "scripts/verify-readings-rail.mjs", - "language": "javascript", - "sizeLines": 171, - "fileCategory": "code" - }, - { - "path": "skills-lock.json", - "language": "json", - "sizeLines": 17, - "fileCategory": "config" - }, - { - "path": "start.cmd", - "language": "batch", - "sizeLines": 18, - "fileCategory": "script" - }, - { - "path": "start.command", - "language": "command", - "sizeLines": 4, - "fileCategory": "code" - }, - { - "path": "start.sh", - "language": "shell", - "sizeLines": 17, - "fileCategory": "script" - }, - { - "path": "tsconfig.base.json", - "language": "json", - "sizeLines": 19, - "fileCategory": "config" - } - ], - "totalFiles": 666, - "filteredByIgnore": 0, - "estimatedComplexity": "very-large", - "stats": { - "filesScanned": 666, - "byCategory": { - "docs": 74, - "config": 309, - "code": 263, - "infra": 1, - "markup": 15, - "script": 4 - }, - "byLanguage": { - "markdown": 69, - "json": 306, - "unknown": 4, - "yaml": 4, - "q-counter": 6, - "jsonl": 8, - "txt": 5, - "html": 7, - "css": 8, - "otf": 6, - "svelte": 40, - "typescript": 186, - "javascript": 8, - "mts": 1, - "astro": 2, - "snap": 1, - "shell": 3, - "batch": 1, - "command": 1 - } - } -} \ No newline at end of file diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 00000000..fb7fc844 --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,104 @@ +name: Checks + +on: + push: + pull_request: + branches: [main] + +concurrency: + group: checks-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + lfs: true + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck every package + run: pnpm typecheck + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + lfs: true + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run every package's test suite + # Per-package vitest (root vitest binary fails rune tests — never invoke it directly). + run: pnpm test + + astro-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + lfs: true + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: astro check (apps/viewer) + run: pnpm --filter @archie/viewer run check + + gh-pages-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + lfs: true + + - uses: pnpm/action-setup@v4 + with: + version: 10 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build Studio + Viewer (verification only — deploy.yml owns the real deploy build) + run: bash scripts/build-gh-pages.sh diff --git a/.gitignore b/.gitignore index 56a00f30..fea7cff5 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,17 @@ pnpm-debug.log* .env.* !.env.example +# ───────────────────────────────────────────────────────────── +# Shell-redirect residue (accidental `cmd > --output`-style typos) +# ───────────────────────────────────────────────────────────── +/--output + +# ───────────────────────────────────────────────────────────── +# One-shot analysis dumps — no generator script lives in this repo; +# regenerate with whatever external tool produced it, never commit the output. +# ───────────────────────────────────────────────────────────── +/anti-pattern-report.txt + # ───────────────────────────────────────────────────────────── # Editor / OS # ───────────────────────────────────────────────────────────── diff --git a/ISSUES.md b/ISSUES.md new file mode 100644 index 00000000..a6240686 --- /dev/null +++ b/ISSUES.md @@ -0,0 +1,668 @@ +# ISSUES — Archie tend backlog + +Generated 2026-07-05 by a tend diagnosis. Commit examined: `2091557` (main). +Evidence gathered by two codebase walks (core packages + apps/ops surface); every +symptom below was verified against files on disk, not inferred. Issues are ordered +by leverage; directions by strength. Each **Run it** block is self-contained — +paste it into a fresh session with no skill loaded. + +--- + +## Issue 1 — CI is deploy-only and typecheck is red on main + +**Status:** queued + +**Symptom.** `.github/workflows/deploy.yml` is the only workflow; it builds +(`scripts/build-gh-pages.sh`) and deploys Pages — no vitest, no tsc, no +`astro check`. Meanwhile `pnpm typecheck` from root **fails (exit 2)**: +`packages/render-core/src/link/link-object.test.ts:30-31` (TS2339: `objectId`/`noteId` +missing on `ViewerRoute`), `packages/render-core/src/spine/wholeobject.test.ts:48` +(TS2352), and `packages/render-mount/src/mount.ts:251` (TS2345 — HANDOFF said :238, +"pre-existing"), which cascades so `render-mount`, `render-svelte`, and +`archie-viewer` all fail typecheck too. `apps/studio/package.json` has no +`typecheck` script at all. The ~1163-test suite runs only on developer machines. + +**Rungs.** L2↔L4: the README promises "both must pass" for PRs while nothing +automated has ever enforced it, and the check is currently failing. + +**Why it's high-leverage.** Every other issue's fix wants a green gate to land +behind; today a red typecheck deploys without complaint. *Lesson: continuous +integration — a check that doesn't run automatically will stop running, and a +gate never seen to fail is decoration, not protection.* + +**Loop.** Tripwire installation. Ledger `ledgers/GATE.md`. Phase 1 inventory +checks (test, typecheck per package, astro check, build); phase 2 repair each to +green locally (the 4 typecheck failures above, plus adding a studio typecheck +script); phase 3 wire into GitHub Actions on push; phase 4 trip each wire once +with a planted defect on a branch. Done: every check green in CI on main and +each witnessed red once. + +**Run it:** + +``` +Set up CI for this repo (GitHub Actions; a deploy workflow already exists at +.github/workflows/deploy.yml — add checks, don't disturb deploy). Ledger +ledgers/GATE.md: check | command | local result | CI wired | tripped red | commit. +Inventory every check the repo can run: pnpm typecheck (per package — note +apps/studio has no typecheck script; add a minimal one), per-app pnpm exec vitest +(root vitest binary fails rune tests — run per package/app), astro check for +apps/viewer, and the gh-pages build. Known-red right now: render-core +link-object.test.ts:30-31 (ViewerRoute objectId/noteId), spine/wholeobject.test.ts:48 +(TS2352), render-mount mount.ts:251 (TS2345, cascades to render-svelte and +archie-viewer). Make each check pass locally first — one fix per commit — then +wire them into a CI job on every push (Node 22+, pnpm 9, LFS checkout), then +prove each wire trips: one deliberate defect per check on a branch, watch it go +red, revert. Done only when every check is green on main AND each has been seen +to fail once. +``` + +**Strength:** Strong. + +--- + +## Issue 2 — The operational docs actively misdirect + +**Status:** queued + +**Symptom.** Three documents contradict the code they describe: +- `HANDOFF.md` (2026-06-21) says the embed feature is UNCOMMITTED on + `feat/archie-viewer-embed`, `packages/archie-viewer/` untracked, ADRs 0019-0021 + untracked, "commit required" — all of it shipped to main (`c471b93`, v1.1; + 26 tracked files in `packages/archie-viewer/`; ADRs 0019-0022 tracked). +- `docs/IMPLEMENTATION-STRATEGY.md` §"Deferred-work registry" (dated 2026-05-25) + lists as pending: IIIF Content-State wiring (shipped — ADR-0022, `content-state.ts`), + viewer empty/error states (shipped — `EmptyHall.svelte` mounted in `ViewerShell`), + search "out of v1" (shipped — `SearchOverlay.svelte` mounted in `ExhibitView`), + narrative section-authoring "next phase" (shipped — `NarrativeEditor.svelte` + lazy-mounted at `App.svelte:88-89`). README calls this file "the canonical + remaining-work list." +- `README.md:226` advertises "conflict-card resolution" as a shipped Collaboration + feature; `MergeReview.svelte` is imported by nothing (see Direction 1). + +**Rungs.** L1↔L2: the purpose-level documents describe a different product than +the code implements — in both directions. + +**Why it's high-leverage.** These are exactly the files a fresh session (or +contributor) reads first; following HANDOFF today risks re-doing or reverting +shipped work. *Lesson: documentation drift — a README that lies is worse than +none; every reader, including the AI next session, builds on the lie.* + +**Loop.** Claim-vs-reality diff over `HANDOFF.md`, `README.md`, +`docs/IMPLEMENTATION-STRATEGY.md`. Ledger `ledgers/CLAIMS.md`. Done: re-running +the full diff finds nothing. + +**Run it:** + +``` +Extract every claim HANDOFF.md, README.md ("Features", "Status & roadmap", +"Known limitations"), and docs/IMPLEMENTATION-STRATEGY.md (the deferred-work +registry) make about what this app does or what work remains, and check each +against the code; also list what the code does that these docs never mention. +Known drift to seed the diff: HANDOFF claims the archie-viewer embed is +uncommitted (it shipped in c471b93); the deferred registry lists Content-State +wiring, viewer empty states, search, and narrative section-authoring as pending +(all shipped); README:226 claims conflict-card resolution ships (MergeReview.svelte +is mounted nowhere — log this row as blocked-on-verdict, see ISSUES.md Direction 1, +don't resolve it unilaterally). Ledger ledgers/CLAIMS.md: claim | where claimed | +what the code does | type (claimed-not-implemented / implemented-not-documented / +implemented-differently) | resolution | commit | recheck. Finish the whole diff +before resolving anything; then per row fix the code, fix the docs, or deprecate +(HANDOFF.md is superseded — archiving/rewriting it is a valid resolution) — one +commit each, rechecking the row after its fix. Done when re-running the full +diff finds nothing. +``` + +**Strength:** Strong. + +--- + +## Issue 3 — Tracked artifacts that lie: a NUL byte, a `--output` file, twin bundles, stale reports + +**Status:** done 2026-07-05 — ledger: ledgers/ARTIFACTS.md + +**Symptom.** +- `apps/studio/src/App.svelte` (2147 lines, the studio's central component) + contains exactly **one NUL byte** — present in the committed blob. grep, + git grep, ripgrep, and the fff tools all treat the file as binary and silently + return zero matches; every "grep is unreliable in this repo" experience traces + to this byte. Verified: `tr -d -c '\000' < apps/studio/src/App.svelte | wc -c` → 1. +- A file literally named `--output` (113 KB, shell-redirect residue) is tracked + at repo root (committed in `5d5bf50`). +- `dist/` at root duplicates `packages/archie-viewer/dist/` (same bundle files, + hand-synced for the jsDelivr `@v1/dist` URL per `a656cda`) — two copies that + can silently diverge. +- `anti-pattern-report.txt` (40 KB, 2026-06-22) is a committed one-shot lint dump + that drifts the moment code changes; `node-compile-cache/` and + `v8-compile-cache-1000/` sit at repo root. + +**Rungs.** L3↔L4: repository structure that misleads both tools and readers about +what is source. + +**Why it's high-leverage.** The NUL byte alone silently defeats every grep-based +tool — reviews, audits, and agents have been reporting false "0 matches" on the +most important studio file. *Lesson: dead-code elimination extends to artifacts — +tracked junk isn't free; it's text your tools reread, misparse, or silently skip.* + +**Loop.** Deletion sweep adapted to tracked artifacts. Ledger +`ledgers/ARTIFACTS.md`. Done: zero accidental rows remain; every deliberate +artifact (root `dist/`) carries its reason and a sync-check. + +**Run it:** + +``` +Audit every tracked file/directory at the Archie repo root that is not source or +docs: --output (shell-redirect residue, tracked), anti-pattern-report.txt +(committed one-shot lint dump), dist/ (hand-synced twin of +packages/archie-viewer/dist/ for the jsDelivr @v1/dist URL — deliberate, verify +the two copies are byte-identical), node-compile-cache/, v8-compile-cache-1000/, +gh-pages-dist/, plus the single NUL byte inside apps/studio/src/App.svelte +(verify with: tr -d -c '\000' < apps/studio/src/App.svelte | wc -c; it makes +grep/ripgrep/git-grep treat the file as binary). Ledger ledgers/ARTIFACTS.md: +artifact | tracked? | why it exists | class (accident / generated / deliberate) | +action | commit | verified. Classify everything before deleting anything; check +.gitignore covers the generated classes. For the NUL byte: locate it (grep -abo +$'\0' apps/studio/src/App.svelte), remove it with a byte-exact edit, then prove +the fix by grepping App.svelte for a known string (e.g. MergeReview should now +be findable-or-absent honestly) and run the studio tests (pnpm --filter +@archie/studio exec vitest run). One action per commit. For root dist/: keep or +replace with a build step, but record the sync rule either way. Done at zero +accident rows and every deliberate row carrying its reason. +``` + +**Strength:** Strong. + +--- + +## Issue 4 — Silent failure on the persistence path + +**Status:** queued + +**Symptom.** Bare or degrading catches sit exactly where a local-first tool can +least afford them: `apps/studio/src/binding.ts:95`, `save-queue.svelte.ts:47`, +`store.ts:298` (bare catch-alls, no rethrow/log/surface — the "did my work +save?" path); `packages/archie-viewer/src/element.ts:254-261` renders an +**empty exhibit** on any open failure with only a console.error; +`apps/studio/src/ingest-flows.ts:446` catches every zip-open failure into one +generic alert, discarding the real reason (zip-bomb cap hit? marker mismatch?). +`apps/viewer/src/published.ts` probes degrade to null on any throw. Repo-wide +the 2026-06-22 anti-pattern report counted 24 catch-alls and 301 fire-and-forget +promises; the ones above were re-verified live. + +**Rungs.** L2↔L4: the product promises "your work autosaves"; the implementation +can fail that promise without anyone finding out. + +**Why it's high-leverage.** A swallowed save error in a no-server tool is data +loss with no support channel. *Lesson: observability — a swallowed failure still +happens; you've just agreed to learn about it from your users.* + +**Loop.** Silence audit, blast-radius-ordered (save/binding first, ingest second, +viewer degrades last). Ledger `ledgers/SILENCE.md`. Done: every row surfaced or +reported, every forced failure observed landing. + +**Run it:** + +``` +Find every error-handling site in the Archie studio and the archie-viewer element +— catch/except blocks, rejection callbacks, external calls with no failure branch. +Start from these verified sites but inventory exhaustively: apps/studio/src/ +binding.ts:95, save-queue.svelte.ts:47, store.ts:298 (bare catch-alls on the +persistence path), ingest-flows.ts:446 (generic alert discards the real zip-open +reason — zip-bomb cap vs marker mismatch should read differently), +packages/archie-viewer/src/element.ts:254-261 (open failure renders an empty +exhibit, console-only), apps/viewer/src/published.ts fetchJsonOptional/ +initLiveSource (degrade-to-null probes — some are by design; record the design +intent in-row rather than assuming). Ledger ledgers/SILENCE.md: site | trigger | +disposal today | should be | fix commit | forced check. Classify each — unhandled / +swallowed / logged-and-lost / surfaced / reported — fixing nothing; then fix so +every failure reaches a message the user sees or a log a human reads, persistence +(save-queue/binding/store) first. There is a toast layer in the studio (shipped +in e45f38b) — surface through it. Then force each fixed site's failure in a dev +run — a simulated fault (injected exception, corrupt zip fixture, unreachable +URL) counts; never fire real failures at live services — and confirm it lands +where the row says. Run per-app tests (pnpm --filter exec vitest run) after +each fix. Done when every row reads surfaced or reported (or by-design, with the +design cited) and every forced failure was observed. +``` + +**Strength:** Strong. + +--- + +## Issue 5 — The untrusted-zip open path exists twice + +**Status:** queued + +**Symptom.** `packages/archie-viewer/src/load.ts` (251 lines) and +`apps/viewer/src/published.ts` (318 lines) both define near-verbatim: +`openError()`, `openZipBytes()`, `openLibraryFromFile()`, +`SRC_MAX_BYTES = 256*1024*1024`, `openLibraryFromSrc()` (same cap logic, same +user-facing strings), and an HTTP-JSON source with the identical degrade comment. +Each file's header names the other as "donor." HANDOFF logged it as "tech debt — +not blocking." The divergence (instance-scoped vs module-global) is real but +bounded. + +**Rungs.** L3↔L4: one security-relevant concern (opening untrusted zips, with its +size caps and marker validation) implemented twice — a fix to one copy can +silently skip the other. + +**Why it's high-leverage.** This is the trust boundary for hostile input; a +future hardening (cap change, marker rule) has to be remembered twice or it's a +vulnerability in one consumer. *Lesson: canonicalization — every second way to do +the same job doubles the ways the next change can go wrong; for security seams +it doubles the attack surface too.* + +**Loop.** Canonicalization: pick the winner (likely a shared module in +`@render/core` or a new small package both consumers import, instance-scoped as +load.ts already is), enumerate every losing call site first, migrate under +tests, lock the rule. Ledger `ledgers/CANON.md`. Done: zero losing call sites +and the lock recorded. + +**Run it:** + +``` +This codebase opens untrusted .archie.zip / published-tree sources two ways: +packages/archie-viewer/src/load.ts (instance-scoped) and apps/viewer/src/ +published.ts (module-global) duplicate openError, openZipBytes, +openLibraryFromFile, SRC_MAX_BYTES, openLibraryFromSrc, and the HTTP-JSON source +near-verbatim (each header names the other as donor). Pick the winner with +reasons — note apps/viewer and packages/archie-viewer must not depend on each +other (README architecture contract: apps share only @render/* packages), so the +canonical copy likely lives in render-core or a shared package; the +instance-scoped shape (load.ts) is the more general of the two. Ledger +ledgers/CANON.md: losing call site | file | migrated commit | tests green. +Enumerate every losing call site BEFORE migrating any (beware: grep undercounts +until ISSUES.md Issue 3's NUL-byte fix lands); then migrate them all under tests +(pnpm --filter @archie/viewer exec vitest run and pnpm --filter archie-viewer +exec vitest run), committing as you go. Keep behavior identical — same caps, +same user-facing strings, marker validation still asserted before open. At zero +losing call sites, add the rule to CLAUDE.md: the untrusted-source open path +lives in one module; never copy it. Done at zero call sites and the lock recorded. +``` + +**Strength:** Strong. + +--- + +## Issue 6 — The IIIF projection core is untested + +**Status:** queued + +**Symptom.** In `render-core`, the files with no test sibling include +`iiif/presentation.ts` (148 lines — the Presentation-3 Manifest/Collection +projection, the product's interop claim), `iiif/exhibits.ts` (89), +`iiif/collection.ts` (29), `query/body.ts` (10). render-core has 67 test files +overall — coverage is strong elsewhere (spine, fs, publish) which makes this +hole specific, not systemic. + +**Rungs.** L2↔L4: "third-party IIIF tools can read your work directly" is a +headline behavior resting on the largest untested core file. + +**Why it's high-leverage.** IIIF output is consumed by external viewers (Mirador, +UV) that fail silently on malformed manifests; regressions here are invisible to +Archie's own UI. *Lesson: test coverage — tests are executable memory; they let +you change code you no longer remember without re-understanding it first.* + +**Loop.** Story-driven coverage climb scoped to `packages/render-core/src/iiif/`. +Ledger `ledgers/COVERAGE.md`. Done: every iiif/ file has behavior-asserting +tests and every exposed bug's row reads pass. + +**Run it:** + +``` +In packages/render-core, the iiif/ projection files lack test siblings: +iiif/presentation.ts (148 lines — IIIF Presentation 3 Manifest/Collection +projection), iiif/exhibits.ts (89), iiif/collection.ts (29), query/body.ts (10). +Rank by how much the app depends on them (presentation.ts first — the published +manifests at apps/viewer/public/published/*/manifest.json are its output and +serve as reference fixtures for what correct output looks like). Ledger +ledgers/COVERAGE.md: file | behavior under test | before | after | bug exposed | +fix commit | retest. Write tests that assert what the code should do — valid P3 +structure (type, items, annotation pages, Ranges for sections, requiredStatement/ +rights passthrough, the archie:geo anchor) — never tests that merely touch lines; +follow the existing test idiom (*.test.ts alongside source, run via pnpm +--filter @render/core exec vitest run). When a test exposes a real bug, give it +its own row and its own fix commit. Done when every iiif/ file has a +behavior-asserting test sibling and every bug row reads pass. +``` + +**Strength:** Worth exploring. + +--- + +## Issue 7 — The ingest boundary's negative space + +**Status:** queued + +**Symptom.** The zip path is capped (`ZIP_LIMITS` 512 MB / 50k entries / 100× +ratio, `fs/zip.ts:29`) and `?src=` is capped (`SRC_MAX_BYTES`), but the remote +IIIF manifest fetch (`fetchManifest`, caller of `apps/studio/src/iiif-import.ts`) +has **no byte cap** — the one remote-input vector without a guard. The other +ingest flows (folder import, CSV import at `App.svelte:1585`, WADM import at +`:1591`) have alert-on-error but their invalid/empty/huge cases are unprobed. + +**Rungs.** L2↔L4: user-facing import promises vs unguarded inputs at the trust +boundary. + +**Why it's high-leverage.** Ingest is where hostile or merely broken input meets +the app; each unhandled case is a hang, a flood, or a silent wrong result. +*Lesson: negative testing — the happy path is the demo, the failure paths are +the product.* + +**Loop.** Negative-space matrix over the studio's ingest flows. Ledger +`ledgers/NEGSPACE.md`. Done: every row passes — a clear error message passes, a +stack trace or hang doesn't. + +**Run it:** + +``` +For every ingest flow in the Archie studio — IIIF manifest URL import +(apps/studio/src/iiif-import.ts + its fetchManifest caller), image-folder +import, .archie.zip open (ingest-flows.ts), CSV notes import (csv-import.ts), +WADM import, VTT/SRT transcript import — probe the cases it does NOT handle: +invalid input (malformed JSON/CSV, wrong file type), empty data, huge input +(note: fetchManifest has NO byte cap today, unlike the zip path's ZIP_LIMITS — +probe with a simulated oversized response, never a real one), remote service +down/404/non-JSON 200, double-submit, mid-flow interruption. Probe a local dev +run (node scripts/start.mjs both) with test fixtures — never the live deployment; +simulate dependency failures rather than causing them. Ledger +ledgers/NEGSPACE.md: item | case | actual | verdict | fix commit | retest. Fill +every actual before fixing anything — a clear error message passes; a stack +trace, hang, or silent wrong result fails. Then fix row by row, one commit each, +running the studio tests per fix. Done when every row reads pass. +``` + +**Strength:** Worth exploring. + +--- + +## Issue 8 — Fresh-clone setup silently loses LFS + +**Status:** queued + +**Symptom.** `core.hooksPath` is set to `qa/hooks` locally. The four untracked +files there (`post-checkout`, `post-commit`, `post-merge`, `pre-push`) are the +stock Git-LFS hooks that `git lfs install` dropped into the active hooksPath — +untracked, so they vanish on a fresh clone even after `qa/hooks/install.sh` +runs, while `deploy.yml` and the `libraries/*.archie.zip` assets depend on LFS +(a broken deploy from exactly this — missing LFS in checkout — was fixed in +`a0b6dc0`). Separately, the `qa/` gate (`gate.mjs`, `features.jsonl`) is wired +to nothing in CI. + +**Rungs.** L2↔L4: the repo runs on this machine because of state the repo +doesn't contain. + +**Why it's high-leverage.** The next clone (or contributor) gets a repo whose +pushes silently skip LFS objects — the class of failure that already broke a +deploy once. *Lesson: reproducible environments — if the app only runs on your +machine, you own a machine, not software; every setup step lives in the repo, +as automation before prose.* + +**Loop.** Cold-start rehearsal scoped to clone→hooks→LFS→dev-servers. Ledger +`ledgers/COLDSTART.md`. Done: a fresh clone in a clean directory reaches working +dev servers and LFS-correct pushes with zero undocumented steps. + +**Run it:** + +``` +In a clean directory, get Archie running from a fresh git clone — never a copy +of the working folder. Ledger ledgers/COLDSTART.md: prerequisite | where +consumed | documented? | stumble | fix commit | clean rerun. First inventory +every prerequisite the repo consumes: Node 22+ (README), pnpm 9+, git-lfs +(libraries/*.archie.zip are LFS-tracked; deploy.yml checks out with lfs:true), +core.hooksPath=qa/hooks (set by qa/hooks/install.sh — but the stock LFS hooks +post-checkout/post-commit/post-merge/pre-push in qa/hooks are UNTRACKED in the +original repo, so a fresh clone loses LFS hook wiring even after install.sh; +this is the known bug to fix — either track LFS-invoking hooks in qa/hooks or +have install.sh run git lfs install --hookspath-aware), and any seed/gen steps +(vite-node apps/viewer/scripts/gen-published.mts). Then rehearse: clone, follow +only committed files + README, reach node scripts/start.mjs both serving studio +and viewer, and verify git lfs status is functional — logging every stumble +verbatim and fixing nothing. Then fix per row — automation over prose — commit +per row (in the real repo, not the rehearsal clone), and re-rehearse from +scratch until a full run needs zero undocumented steps. +``` + +**Strength:** Worth exploring. + +--- + +## Issue 9 — The showroom exhibit is stranded at ~80% + +**Status:** queued + +**Symptom.** `docs/showroom/` holds the full prep — `exhibit.md` (4 readings, +21-section tour), 21 coordinate-free CSVs (87 rows, verified by `verify.mjs`), +`ASSEMBLE.md`, `SHOWROOM-NOTES.md` — and 21/21 screenshots exist in +`docs/screenshots/auto/`. But the ASSEMBLE step never ran: no `showroom/` in +`apps/viewer/public/published/`, no showroom fixture, and +`docs/plans/SHOWROOM-EXHIBIT-PLAN.md:104-110` still ends on "Open decisions +(gating)" that HANDOFF claims were decided. + +**Rungs.** L1↔L2: a documented deliverable ("Archie annotates Archie" — feature +list, tutorial, and showroom in one) exists as prep but not as product. + +**Why it's high-leverage.** It's the highest-value marketing/onboarding artifact +the project has designed, and it dogfoods CSV import + region placement — the +assembly is itself a product test. *Lesson: end-to-end journeys — the assembly +was deliberately designed as a human-in-Studio session (coordinate-free CSVs so +the curator draws boxes); an 80%-done deliverable with no tracking row quietly +becomes 0%.* + +**Loop.** Journey walk riding `docs/showroom/ASSEMBLE.md`, with you as the hands +for the draw-boxes steps. Ledger `ledgers/SHOWROOM.md`. Done: the showroom +exhibit is published in the viewer tree (or the deliverable is explicitly parked +with its reason in the plan doc). + +**Run it:** + +``` +Assemble the Archie showroom exhibit per docs/showroom/ASSEMBLE.md: build the +exhibit from the 21 PNGs in docs/screenshots/auto/ (21 objects), create the 4 +readings (studio/viewer/embed/power — colours in docs/showroom/exhibit.md), +import the 21 CSVs from docs/showroom/csv/ via the Studio CSV import (each row +becomes a pending note; verify counts against node --experimental-strip-types +docs/showroom/verify.mjs → 87 rows), then the region-drawing pass ("Set area") +— this step needs a human curator: run the studio locally (node scripts/start.mjs +both), drive everything scriptable, and recruit me as the hands for drawing, +telling me exactly which object/note/region is next and logging what I report. +Wire the 21-section narrative tour, set metadata, publish into the viewer tree. +Ledger ledgers/SHOWROOM.md: step | expected | actual | friction 0-3 | fix commit +| re-walk. Every stumble in the CSV-import/pending-notes/Set-area flow is +product feedback — log it with a friction score; out-of-scope bugs go to +ISSUES.md as queued candidates, not fixed mid-walk. First, resolve the plan's +open decision: does the showroom live as a permanent seed fixture or a +hand-built published exhibit — ask me before building. Done when the showroom +exhibit is published and the walk log is complete, or the deliverable is +explicitly parked with its reason recorded in docs/plans/SHOWROOM-EXHIBIT-PLAN.md. +``` + +**Strength:** Worth exploring (needs the user in the loop by design). + +--- + +## Directions + +### Direction 1 — The collaboration machinery is built, tested, and unreachable + +**Status:** queued + +**Surplus.** `apps/studio/src/MergeReview.svelte` (conflict cards; its own header +calls it the "#1 validation-priority invention") and `IdentityPrompt.svelte` are +imported by no component — `App.svelte` (2147 lines) mounts neither. +`AnnotationSession.importChanges()` (`session.ts:147`) has no app caller. The +spine's DAG-classification layer — `lineage`, `ancestors`, `commonAncestor`, +`classifyMerge`, `classifyLogical`, `conflictTiebreak` in `spine/merge.ts` — has +zero callers outside its own file and tests. The only live "collaboration" path +(`ingest-flows.ts:454`) **replaces** the project on zip open; it never merges. +Meanwhile `README.md:226` claims "Silent DAG merge; conflict-card resolution; +identity prompt" as shipped. + +**Rungs.** L3/L4 → L2: a whole subsystem out-provides a product surface that +doesn't exist. + +**Who feels it.** The educator/collaborator personas the README courts: a +teacher receiving 30 student zips today opens each one and watches it *overwrite* +their library — the workaround is manual, one-browser-profile-per-student. +*Lesson: product discovery — a capability no user can reach doesn't exist for +them; the cheapest item on any roadmap is one the backend already does.* + +**Intent.** Forgotten-latent, most likely: the README and +`IMPLEMENTATION-STRATEGY.md:266` both claim it shipped — this reads as work that +fell out of the mount tree, not deliberate deferral. `unknown` on the deeper +DAG-classification functions. + +**Loop.** Capability-reach diff scoped to collaboration/merge. Ledger +`ledgers/CAPABILITY.md`. Done: every gated/orphaned operation verdicted with +reason — nothing built. + +**Run it:** + +``` +List every operation the Archie collaboration/merge subsystem exposes — start +from packages/render-core/src/spine/merge.ts (lineage, ancestors, commonAncestor, +classifyMerge, classifyLogical, conflictTiebreak, resolveConflict, headsOf), +session.ts (importChanges:147, resolveConflict path), and the studio components +MergeReview.svelte and IdentityPrompt.svelte — and record each one's user path +and gate. Read paths from code only — note apps/studio/src/App.svelte contains +a NUL byte that makes grep report false zero-matches; use grep -a or Read +(see ISSUES.md Issue 3). Known evidence: MergeReview/IdentityPrompt are mounted +nowhere; importChanges has no app caller; opening a colleague's zip +(ingest-flows.ts openZip → replaceProjectFrom) REPLACES rather than merges; yet +README.md:226 claims conflict-card resolution shipped. Ledger +ledgers/CAPABILITY.md: operation | defined at | user path | gate | intent | +class (reachable / gated / orphaned) | verdict | commissioned as. Inventory +everything before judging anything — a second full pass must add no rows; read +each orphaned row's intent from comments and git history (when did MergeReview +last change, was a mount ever removed?). Then bring me the orphaned rows for +verdicts in clusters (merge-UI, import-merge path, DAG-classification functions) +— pursue / park / reject, my reason logged in-row. Build nothing, open no gate: +a pursue only commissions a spec interview, pre-mortem, or prototype brief, its +prompt written into the row; a reject on the DAG functions queues a deletion +sweep, and either verdict resolves the README claim row from ledgers/CLAIMS.md +(Issue 2). Done when every orphaned row holds a verdict and its reason. +``` + +**Strength:** Strong. + +### Direction 2 — Version history is written forever and shown never + +**Status:** queued + +**Surplus.** The annotation spine persists full provenance: `mergeParents` +serialized as `archie:mergeParents` (`spine/serialize.ts:112`, read back at +`deserialize.ts:62`) with zero display consumers; `lastEditor` written on every +append (`session.ts:98/116/191`) whose only display site is the unmounted +`MergeReview.svelte:51`. The append-only version-parent DAG — the README's +"core innovation" — has no version-history view in studio or viewer. + +**Rungs.** L4 → L2: the data model's most distinctive stored value never reaches +a user's eyes. + +**Who feels it.** The DH-scholar persona: "notes are versioned" is a citability +promise, but a scholar wanting to see or cite an earlier version of a note has +no surface — the workaround is keeping dated zip exports by hand. +*Lesson: dark data — every value stored but never shown is a feature you already +paid for and forgot to ship; surfacing beats greenfield.* + +**Intent.** Designed-latent at the spine level (ADR-0003 chose append-only +deliberately, non-destructive edits are load-bearing for merge); `unknown` +whether a history *view* was ever scoped — no ADR or deferred-registry row +mentions one. + +**Loop.** Dark-data census scoped to the spine's persisted values. Ledger +`ledgers/DARKDATA.md`. Done: every dark row verdicted — nothing built. + +**Run it:** + +``` +Inventory every value the Archie annotation spine stores or computes and keeps — +walk packages/render-core/src/spine/ (log, heads, serialize: note +archie:mergeParents at serialize.ts:112) and session.ts (lastEditor written at +:98/116/191), plus what publish/site.ts projects into the published tree — and +trace which ever reach a user through a studio screen, viewer surface, published +IIIF output, or export. Evidence from code and git only; name fields, not data. +No grep hits is not yet dark — check wildcard serialization paths (the IIIF +projection may carry values into manifest.json that no viewer displays: that is +surfaced-to-IIIF-but-dark-in-app, record it as its own class note). Known +candidates: mergeParents (zero display consumers), lastEditor (only display +site is the unmounted MergeReview.svelte:51), full version-parent lineage +(append-only log per ADR-0003 — read that ADR for intent before classifying). +Ledger ledgers/DARKDATA.md: value | written at | surfaced at | class (surfaced / +internal / dark) | intent | verdict | commissioned as. Classify everything +before judging anything — a second full pass must add no rows. Then bring me +the dark rows for verdicts in clusters sharing an intent (provenance/history +values together) — pursue / park / reject, my reason logged in-row. Build and +delete nothing: a pursue only commissions a spec interview, pre-mortem, or +prototype brief (e.g. "a note's History panel" spec interview), its prompt +written into the row. Done when every dark row holds a verdict and its reason. +``` + +**Strength:** Strong. + +### Direction 3 — The embed element outgrew its snippet generator + +**Status:** queued + +**Surplus.** `packages/archie-viewer/src/element.ts:74` observes four attributes +— `src`, `target`, `iiif-content`, `offline` — all shipped, ADR-blessed +(0021/0022), and documented in `recipes/`. The one UI that hands users their +embed code, `apps/studio/src/PublishDialog.svelte:73-74`, emits only +``. Deep-linking, offline/kiosk mode, and IIIF +Content-State are reachable only by hand-editing HTML against the recipes. + +**Rungs.** L2 (element capability) → L2 (studio surface): one feature's own +halves out of step. + +**Who feels it.** The educator or curator embedding an exhibit: to deep-link a +specific note into a course page they must find `recipes/EMBED.md` and hand-write +the cite-ladder hash. The Cmd+K citation UI already computes these routes +in-app. *Lesson: product discovery — the cheapest roadmap item is one the +backend already does; here even the UI for composing the value (Cmd+K) already +exists, just not connected to the snippet.* + +**Intent.** Designed-latent for the attributes themselves (recipes document them +deliberately); `unknown` for the dialog gap — likely just sequencing +(PublishDialog's snippet shipped in the same wave the attributes were landing). + +**Loop.** Capability-reach diff, small scope. Ledger `ledgers/CAPABILITY.md` +(same ledger as Direction 1, separate cluster). Done: each unexposed attribute +verdicted. + +**Run it:** + +``` +List every capability the element exposes (packages/ +archie-viewer/src/element.ts:74 — observedAttributes src, target, iiif-content, +offline; plus behaviors each unlocks per docs/adr/0021 and 0022 and recipes/) +and record which the Studio's embed-snippet generator +(apps/studio/src/PublishDialog.svelte:73-74 — currently emits src only, plus an +iframe fallback) lets an author reach. Also record adjacent in-app sources of +the values (the Cmd+K citation flow already computes cite-ladder routes usable +as target). Ledger ledgers/CAPABILITY.md, new section "embed-snippet": operation +| defined at | user path | gate | intent | class | verdict | commissioned as. +Inventory before judging; read intent from the ADRs and recipes (documented = +deliberate capability; absent from the dialog may be sequencing, check git +history of PublishDialog.svelte). Then bring me the rows for verdicts — +pursue / park / reject with my reason in-row. Build nothing: a pursue +commissions a spec interview or prototype brief (e.g. "snippet builder with +target picker and offline toggle"), its prompt written into the row. Done when +every non-reachable capability holds a verdict and its reason. +``` + +**Strength:** Worth exploring. + +--- + +## Top recommendation + +**Issue 1 — tripwire installation.** The typecheck is red on main and nothing +automated runs the ~1163 tests: that's a burning platform, and every other fix +in this file wants a green gate to land behind. Run Issue 3 (the NUL byte) +immediately after — or fold its App.svelte fix into Issue 1's repair phase — +because until that byte is gone, every grep-based verification in the other +loops silently lies about the studio's largest file. + +**Top direction:** Direction 1 — the collaboration machinery. Strong surplus, +already claimed shipped in the README, and its verdict unblocks a row in Issue +2's claims diff. It converges to decisions, not builds — cheap to run whenever +you have verdict energy. diff --git a/README.md b/README.md index 43f2bab7..c8b2fb6a 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,11 @@ Load the runtime from jsDelivr serving the pinned `@v1` git tag, then place one ``` +> [!TIP] +> Maintainer note: that URL resolves at the **repo root** `dist/`, a hand-synced copy of +> `packages/archie-viewer/dist/` (ADR-0019 amendment). Before tagging a new `@vN`, run +> `pnpm --filter archie-viewer build && pnpm sync-dist && pnpm sync-dist:check`. + Pin `@v1` so an upstream change can't silently alter your embed. The public surface is three attributes (frozen — [ADR-0021](docs/adr/0021-archie-viewer-target-contract.md)): | Attribute | What it does | diff --git a/anti-pattern-report.txt b/anti-pattern-report.txt deleted file mode 100644 index b638d47a..00000000 --- a/anti-pattern-report.txt +++ /dev/null @@ -1,351 +0,0 @@ -=== FINDINGS === -[catch-all] .agents/skills/teach/.scout/shoot.mjs:17 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] apps/studio/src/binding.ts:95 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] apps/studio/src/save-queue.svelte.ts:47 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] apps/studio/src/store.ts:298 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] docs/learn/assets/models.js:115 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] docs/learn/assets/models.js:121 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:113 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:127 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:178 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:201 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:246 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:278 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:304 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:342 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:386 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:399 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:400 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:420 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:515 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/capture-screenshots.mjs:70 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/verify-marginalia.mjs:27 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/verify-readings-rail.mjs:148 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/verify-readings-rail.mjs:154 -- bare catch-all with no rethrow/reject/error-return/logging in body -[catch-all] scripts/verify-readings-rail.mjs:28 -- bare catch-all with no rethrow/reject/error-return/logging in body -[console-only-error] packages/archie-viewer/src/element.ts:257 -- console.error with no UI feedback in surrounding context -[fire-and-forget] .agents/skills/teach/.scout/diag.mjs:2 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] .agents/skills/teach/.scout/diag.mjs:3 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] .agents/skills/teach/.scout/shoot.mjs:12 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] .agents/skills/teach/.scout/shoot.mjs:13 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/AvEditor.svelte:230 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/AvEditor.svelte:316 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/bake.ts:35 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/binding-store.svelte.ts:52 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/binding.ts:74 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/dzi-slicer.ts:89 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/exhibit-session.svelte.ts:105 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/exhibit-session.svelte.ts:107 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/exhibit-session.svelte.ts:120 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/exhibit-session.svelte.ts:122 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/exhibit-session.svelte.ts:129 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/exhibit-session.svelte.ts:137 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/exhibit-session.svelte.ts:61 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/folder-backend.ts:40 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/folder-backend.ts:42 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/folder-backend.ts:44 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/folder-backend.ts:47 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/folder-backend.ts:55 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/folder-backend.ts:57 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/folder-backend.ts:58 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/folder-backend.ts:64 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/handles-db.ts:24 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:100 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:104 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:118 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:119 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:127 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:151 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:152 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:161 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:171 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:177 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:183 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:189 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:190 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:436 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/ingest-flows.ts:99 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/library-meta.svelte.ts:30 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/library-meta.svelte.ts:54 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/library-meta.svelte.ts:55 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/library-meta.svelte.ts:56 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/library-meta.svelte.ts:60 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/library-meta.svelte.ts:61 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:144 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:146 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:153 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:154 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:158 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:164 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:165 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:169 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:170 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:171 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:191 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/publish-flows.svelte.ts:192 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:177 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:178 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:179 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:180 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:185 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:191 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:198 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:218 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:28 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:29 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:40 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:43 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:44 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/store.ts:97 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/tauri-fs.ts:67 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/tauri-fs.ts:68 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/tauri-fs.ts:70 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/tauri-fs.ts:76 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/tauri-fs.ts:77 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/tauri-fs.ts:82 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/studio/src/tauri-fs.ts:83 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/viewer/src/components/ViewerShell.svelte:276 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/viewer/src/published.ts:26 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/viewer/src/published.ts:306 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] apps/viewer/src/published.ts:315 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/archie-viewer/src/reader.ts:5 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/archie-viewer/src/reader.ts:65 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:43 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:50 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:51 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:52 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:53 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:54 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:55 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:56 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:60 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:61 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:64 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:65 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:67 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:73 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:74 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:75 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:76 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:77 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:78 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:79 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:80 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:84 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:85 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:86 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:90 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:91 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:92 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/conformance.ts:93 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/fsa.ts:12 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/fsa.ts:15 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/fsa.ts:29 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/fsa.ts:32 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/memory.ts:22 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:102 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:106 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:112 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:116 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:130 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:63 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:73 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:79 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:93 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/tauri.ts:94 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/zip.ts:161 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/zip.ts:168 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/zip.ts:172 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/zip.ts:173 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/fs/zip.ts:67 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:164 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:166 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:173 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:174 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:175 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:177 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:179 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:184 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:191 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/ghpages.ts:201 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/marker.ts:53 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/portable.ts:141 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/portable.ts:88 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/read.ts:37 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/read.ts:38 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/read.ts:39 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/read.ts:40 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/read.ts:87 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/read.ts:90 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/read.ts:91 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:143 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:144 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:145 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:146 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:150 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:151 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:152 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:153 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:166 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:169 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:170 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:171 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:172 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:196 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:199 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:200 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:202 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:221 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:228 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:232 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:233 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:237 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:239 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:240 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:241 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:242 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:248 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:251 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:259 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:261 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:262 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:263 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:264 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:279 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:282 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:285 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:321 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:323 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:325 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:326 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:327 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:328 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:340 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:341 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:342 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:347 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:354 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:355 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:357 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:368 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:369 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:376 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:378 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/publish/site.ts:380 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/session/session.ts:235 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/session/session.ts:243 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/session/session.ts:79 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:18 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:19 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:20 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:21 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:25 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:26 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:35 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:38 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:39 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-core/src/spine/persist.ts:51 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-mount/src/mount.ts:111 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] packages/render-mount/src/read-mount.ts:206 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:158 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:159 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:160 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:162 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:164 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:165 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:166 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:172 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:173 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:175 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:176 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:340 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:373 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:374 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:58 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/capture-screenshots.mjs:59 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:101 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:102 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:103 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:104 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:107 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:109 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:111 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:112 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:115 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:116 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:117 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:119 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:121 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:124 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:64 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:66 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:68 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:69 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:70 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:71 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:72 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:74 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:79 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:80 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:81 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:82 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:86 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:87 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:88 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:89 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:90 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:93 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:94 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-marginalia.mjs:96 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:100 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:102 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:103 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:104 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:106 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:108 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:113 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:114 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:115 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:116 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:117 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:118 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:119 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:120 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:121 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:125 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:126 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:127 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:128 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:129 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:130 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:131 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:137 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:139 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:140 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:141 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:146 -- await with no try/catch or .catch() in nearby context -[fire-and-forget] scripts/verify-readings-rail.mjs:147 -- await with no try/catch or .catch() in nearby context -[impact-scope] apps/studio/src/seed-data.ts:0 -- 3/5 importers of seed-data have no tests (Tier 0: 5 direct callers, Tier 1: depth 2 transitive=79) -[impact-scope] apps/viewer/fixtures/sample-data.ts:0 -- 4/9 importers of sample-data have no tests (Tier 0: 9 direct callers, Tier 1: depth 2 transitive=159) -[impact-scope] packages/archie-viewer/src/element.ts:0 -- 14/38 importers of element have no tests (Tier 0: 38 direct callers, Tier 1: depth 2 transitive=306) -[impact-scope] packages/render-core/src/publish/marker.ts:0 -- 15/45 importers of marker have no tests (Tier 0: 45 direct callers, Tier 1: depth 2 transitive=339) -[impact-scope] packages/render-mount/src/read-mount.test.ts:0 -- 1/2 importers of read-mount.test have no tests (Tier 0: 2 direct callers, Tier 1: depth 2 transitive=6) -[impact-scope] packages/render-mount/src/read-mount.ts:0 -- 2/7 importers of read-mount have no tests (Tier 0: 7 direct callers, Tier 1: depth 2 transitive=130) -[silent-catch] apps/studio/src/ingest-flows.ts:201 -- catch body logs error but never surfaces to user -[silent-catch] packages/archie-viewer/src/element.ts:254 -- catch body logs error but never surfaces to user -[unpaired-resource] apps/studio/src/App.svelte:48001 -- 1 acquire calls but no release in file (lock/unlock) -[untested-churn] apps/studio/src/App.svelte:0 -- 44 commits (6mo), no test file -[untested-churn] apps/studio/src/ExhibitOverview.svelte:0 -- 12 commits (6mo), no test file -[untested-churn] apps/studio/src/LibraryHome.svelte:0 -- 11 commits (6mo), no test file -[untested-churn] apps/studio/src/NarrativeEditor.svelte:0 -- 9 commits (6mo), no test file -[untested-churn] apps/studio/src/PublishDialog.svelte:0 -- 10 commits (6mo), no test file -[untested-churn] apps/studio/src/store.ts:0 -- 10 commits (6mo), no test file -[untested-churn] apps/viewer/src/components/ExhibitView.svelte:0 -- 18 commits (6mo), no test file -[untested-churn] apps/viewer/src/components/MediaPlayer.svelte:0 -- 12 commits (6mo), no test file -[untested-churn] apps/viewer/src/components/NarrativeReader.svelte:0 -- 18 commits (6mo), no test file -[untested-churn] apps/viewer/src/components/Reader.svelte:0 -- 18 commits (6mo), no test file -[untested-churn] apps/viewer/src/components/ReadingLegend.svelte:0 -- 9 commits (6mo), no test file -[untested-churn] apps/viewer/src/components/ViewerShell.svelte:0 -- 13 commits (6mo), no test file -[untested-churn] packages/render-svelte/src/Canvas.svelte:0 -- 12 commits (6mo), no test file - -=== RISK SCORES === diff --git a/apps/studio/package.json b/apps/studio/package.json index c03626f4..2de54140 100644 --- a/apps/studio/package.json +++ b/apps/studio/package.json @@ -10,7 +10,8 @@ "dev": "node scripts/sync-learn.mjs && vite", "build": "node scripts/sync-learn.mjs && vite build", "preview": "vite preview", - "test": "vitest run" + "test": "vitest run", + "typecheck": "tsc --noEmit" }, "dependencies": { "@annotorious/openseadragon": "^3.8.2", diff --git a/apps/studio/src/AddMapModal.svelte b/apps/studio/src/AddMapModal.svelte index 381bc65d..67736585 100644 --- a/apps/studio/src/AddMapModal.svelte +++ b/apps/studio/src/AddMapModal.svelte @@ -3,10 +3,10 @@ // permit static-site embedding, attribution baked in → resolves D6), set the bounded extent on a // pan/zoom world locator (drag the box / drag handles to clamp · drag the map to pan · wheel/± to zoom), // name it; emits a tileSource descriptor + label. Bounds fields + presets remain for precision. - import { lngLatToPixel, pixelToLngLat, type TileSourceDescriptor } from "@render/core"; + import { lngLatToPixel, pixelToLngLat, type XyzTileSource } from "@render/core"; let { onadd, onclose }: { - onadd: (m: { label: string; tileSource: TileSourceDescriptor }) => void; + onadd: (m: { label: string; tileSource: XyzTileSource }) => void; onclose: () => void; } = $props(); @@ -130,7 +130,7 @@ function submit() { if (!valid) return; const base = { kind: "xyz" as const, tileSize: 256, minZoom: 0, maxZoom, bounds: [west, south, east, north] as [number, number, number, number] }; - const tileSource: TileSourceDescriptor = useCustom + const tileSource: XyzTileSource = useCustom ? { ...base, template: customTemplate.trim(), ...(customAttribution.trim() ? { attribution: customAttribution.trim() } : {}) } : { ...base, template: provider.template, attribution: provider.attribution }; onadd({ label: label.trim() || `${provider.name} map`, tileSource }); diff --git a/apps/studio/src/App.svelte b/apps/studio/src/App.svelte index 19868de7..0261f5c0 100644 --- a/apps/studio/src/App.svelte +++ b/apps/studio/src/App.svelte @@ -651,7 +651,7 @@ } // IngestContext hook: stage coordinate-free CSV rows, deduped by (object, comment). Returns the NEW count. function addPendingNotes(incoming: CsvPendingNote[]): number { - const key = (p: { objectId: string; comment: string }) => `${p.objectId}${p.comment}`; + const key = (p: { objectId: string; comment: string }) => `${p.objectId}\0${p.comment}`; const seen = new Set(pendingNotes.map(key)); let added = 0; for (const n of incoming) { diff --git a/apps/studio/src/binding.ts b/apps/studio/src/binding.ts index 0170b386..52900a5c 100644 --- a/apps/studio/src/binding.ts +++ b/apps/studio/src/binding.ts @@ -88,7 +88,10 @@ export async function saveZipToDisk(fs: ZipFilesystem, filename: string): Promis const writable = await handle.createWritable(); try { await fs.streamZip({ - write: (chunk) => writable.write(chunk as unknown as ArrayBufferView), + // fflate's zip stream always hands back a freshly-allocated (never SharedArrayBuffer-backed) + // Uint8Array; FileSystemWritableFileStream.write's type is narrower than Uint8Array's default + // generic (which allows ArrayBufferLike, i.e. SharedArrayBuffer too). + write: (chunk) => writable.write(chunk as Uint8Array), close: () => writable.close(), }); } catch (e) { diff --git a/apps/studio/src/collab.test.ts b/apps/studio/src/collab.test.ts index 4b7dc6ae..da6a98a2 100644 --- a/apps/studio/src/collab.test.ts +++ b/apps/studio/src/collab.test.ts @@ -16,7 +16,7 @@ describe("collabBreakdown — who wrote what, on live notes", () => { note(priya, 1); note(priya, 2); const me = new AnnotationSession(asClientId("me"), priya.entries); const mine = note(me, 3); - const b = collabBreakdown({ ex: me.log }, asClientId("me")); + const b = collabBreakdown({ ex: me.entries }, asClientId("me")); expect(b).toEqual({ others: [{ editor: "priya", count: 2 }], yours: 1 }); const afterDelete = new AnnotationSession(asClientId("me"), me.entries); diff --git a/apps/studio/src/geo-notes.ts b/apps/studio/src/geo-notes.ts index 54efd939..fdce0d87 100644 --- a/apps/studio/src/geo-notes.ts +++ b/apps/studio/src/geo-notes.ts @@ -1,11 +1,12 @@ // Geo-note selector math (the DOMINO cut out of App.svelte): the two pure helpers that turn a note's // pixel selector / stored geo-truth into a lng/lat readout, and a drawn region's WORLD-pixel selector // into its lng/lat anchor. Both took the reactive `currentTileSource` via closure in App.svelte; here -// they take the `TileSourceDescriptor` explicitly, so they are pure and unit-testable. The underlying +// they take the map's `XyzTileSource` explicitly (geo readout only makes sense for a slippy-map +// basemap, never a DZI pyramid), so they are pure and unit-testable. The underlying // projection (pixel↔lng/lat) is core's geometry/geo — this module only composes it over a note's shape. import { formatLngLat, parseFragmentXYWH, parsePolygonPoints, pixelToLngLat, - type AnnotationRecord, type W3CTarget, type GeoAnchor, type TileSourceDescriptor, + type AnnotationRecord, type W3CTarget, type GeoAnchor, type XyzTileSource, } from "@render/core"; /** The selector `value` string off a record's target (xywh / polygon fragment), or "" if absent. */ @@ -15,7 +16,7 @@ export const selectorValue = (r: AnnotationRecord): string => // Geo readout (geo-annotation, Q5): the region's CENTRE lng/lat. Prefer the stored geo-truth (archie:geo, // ADR-0015 — record.geo); fall back to deriving from the pixel selector for any pre-geo record. Returns // null off a Map (no tileSource) or when the shape can't be located. -export function geoLabelOf(r: AnnotationRecord, ts: TileSourceDescriptor | undefined): string | null { +export function geoLabelOf(r: AnnotationRecord, ts: XyzTileSource | undefined): string | null { if (!ts) return null; if (r.geo?.type === "bbox") return formatLngLat({ lng: (r.geo.west + r.geo.east) / 2, lat: (r.geo.south + r.geo.north) / 2 }); if (r.geo?.type === "polygon" && r.geo.coordinates.length) { @@ -29,7 +30,7 @@ export function geoLabelOf(r: AnnotationRecord, ts: TileSourceDescriptor | undef // Geo-truth capture (Q4 / ADR-0015): turn a drawn region's WORLD-pixel selector into its lng/lat anchor // (the source of truth). Box → bbox (NW/SE corners); Outline → polygon (each vertex). undefined off-map. -export function geoForTarget(target: W3CTarget, ts: TileSourceDescriptor | undefined): GeoAnchor | undefined { +export function geoForTarget(target: W3CTarget, ts: XyzTileSource | undefined): GeoAnchor | undefined { if (!ts) return undefined; const v = (target as { selector?: { value?: string } } | undefined)?.selector?.value; if (!v) return undefined; diff --git a/apps/studio/src/ingest-flows.ts b/apps/studio/src/ingest-flows.ts index d5c191c3..805d089f 100644 --- a/apps/studio/src/ingest-flows.ts +++ b/apps/studio/src/ingest-flows.ts @@ -10,7 +10,7 @@ import { AnnotationSession, loadLibrary, ZipFilesystem, libraryToWorking, mediaTypeFromSource, readExifOrientation, isOrientationNoop, orientationTransform, MAX_MASTER_DIM, readExifCaptureDate, - type Library, type ClientId, type TileSourceDescriptor, type W3CTextualBody, + type Library, type ClientId, type XyzTileSource, type W3CTextualBody, type WorkingObjectMeta as ObjectMeta, } from "@render/core"; import { bakeDisplayMaster, downscaleIfNeeded, bakeThumbnail } from "./bake.js"; @@ -120,7 +120,7 @@ export function createIngestFlows(ctx: IngestContext) { } // Add-map modal (Phase 3 / Q3 — invented UX, human-gated): a Map is an Object whose source is its tile // template and which carries the tileSource descriptor (medium = Map). The modal supplies template + bounds. - async function addMapObject(m: { label: string; tileSource: TileSourceDescriptor }) { + async function addMapObject(m: { label: string; tileSource: XyzTileSource }) { const ex = exhibit(); if (!ex) return; const id = nextObjectId(ex); diff --git a/apps/studio/src/publish-flows.svelte.ts b/apps/studio/src/publish-flows.svelte.ts index 034114f0..2edf10a3 100644 --- a/apps/studio/src/publish-flows.svelte.ts +++ b/apps/studio/src/publish-flows.svelte.ts @@ -7,7 +7,7 @@ // library-meta.svelte.ts): the $state container is never reassigned, getters stay live. import { MemoryFilesystem, publishLibrary, libraryToZipFs, collectFiles, publishToGitHub, renderMarkdown, - type Filesystem, type Library, type AnnotationLog, type BrokenLink, type GitHubTarget, type PublishProgress, type LogicalId, + type Filesystem, type Library, type AnnotationLog, type BrokenLink, type GitHubTarget, type PublishProgress, } from "@render/core"; import { supportsFileStreamSave, saveZipToDisk } from "./binding.js"; import { pickFolderBinding } from "./folder-backend.js"; @@ -143,7 +143,7 @@ export function createPublishFlows(deps: PublishDeps) { async function projectSite(withOriginals: boolean): Promise<{ fs: MemoryFilesystem; brokenLinks: BrokenLink[] }> { const logs = await deps.loadAllLogs(); const fs = new MemoryFilesystem(); - const { brokenLinks } = await publishLibrary(fs, deps.buildFullLibrary(), (id: LogicalId) => logs[id] ?? [], { baseUrl: deps.baseUrl, getAsset, getThumbnail, tileObject, tileRemote, ...STATIC_PAGE_OPTS, ...(withOriginals ? { getOriginal: (slug: string, name: string) => readOriginalBytes(slug, name) } : {}) }); + const { brokenLinks } = await publishLibrary(fs, deps.buildFullLibrary(), (id: string) => logs[id] ?? [], { baseUrl: deps.baseUrl, getAsset, getThumbnail, tileObject, tileRemote, ...STATIC_PAGE_OPTS, ...(withOriginals ? { getOriginal: (slug: string, name: string) => readOriginalBytes(slug, name) } : {}) }); if (brokenLinks.length > 0) console.warn(`Publish: ${brokenLinks.length} broken intra-Library link(s) degraded to plain text`, brokenLinks); return { fs, brokenLinks }; } @@ -156,13 +156,13 @@ export function createPublishFlows(deps: PublishDeps) { // ONE zip builder for the three zip paths (project Save / dialog download / local publish). async function buildZipFs() { const logs = await deps.loadAllLogs(); - return libraryToZipFs(deps.buildFullLibrary(), (id: LogicalId) => logs[id] ?? [], { baseUrl: deps.baseUrl, getAsset, getThumbnail, tileObject, tileRemote, ...STATIC_PAGE_OPTS }); + return libraryToZipFs(deps.buildFullLibrary(), (id: string) => logs[id] ?? [], { baseUrl: deps.baseUrl, getAsset, getThumbnail, tileObject, tileRemote, ...STATIC_PAGE_OPTS }); } // ONE folder writer for the two folder sinks (binding autosave/Save + local publish). Takes the // Filesystem seam directly (FSA or Tauri), so the caller owns capability selection (folder-backend). async function writeTree(fs: Filesystem) { const logs = await deps.loadAllLogs(); - await publishLibrary(fs, deps.buildFullLibrary(), (id: LogicalId) => logs[id] ?? [], { baseUrl: deps.baseUrl, getAsset, getThumbnail, tileObject, tileRemote, ...STATIC_PAGE_OPTS }); + await publishLibrary(fs, deps.buildFullLibrary(), (id: string) => logs[id] ?? [], { baseUrl: deps.baseUrl, getAsset, getThumbnail, tileObject, tileRemote, ...STATIC_PAGE_OPTS }); } /** Download the library as .archie.zip (size-guarded). False = the user declined/cancelled. */ async function downloadProjectZip(): Promise { diff --git a/apps/studio/src/save-queue.svelte.ts b/apps/studio/src/save-queue.svelte.ts index 47202c25..d6b9ad72 100644 --- a/apps/studio/src/save-queue.svelte.ts +++ b/apps/studio/src/save-queue.svelte.ts @@ -26,7 +26,7 @@ export const saveStatus = { }, get error(): string | null { const msgs = Object.values(s.errors); - return msgs.length > 0 ? msgs[msgs.length - 1] : null; + return msgs.length > 0 ? msgs[msgs.length - 1]! : null; }, get pending(): number { return s.pending; diff --git a/apps/studio/src/store.ts b/apps/studio/src/store.ts index f70a18fc..c67fb363 100644 --- a/apps/studio/src/store.ts +++ b/apps/studio/src/store.ts @@ -7,17 +7,18 @@ // {PROJECT}/annotations/ — the "sample" exhibit's annotations (LEGACY path, // kept so pre-multi-exhibit work isn't orphaned) // {PROJECT}/exhibits/{slug}/annotations/ — every OTHER exhibit's annotations -import { FsaFilesystem, type FsDirectory, type WorkingLibraryMeta, type LayoutType } from "@render/core"; - // The persisted working-store SHAPES live in core now (Q-3 archie-persistence: the Viewer's live // source reads the same format via loadWorkingLibrary). Re-exported under their original Studio // names so import sites stay stable; this module remains the WRITER of the layout. -export type { - WorkingObjectProvenance as ObjectProvenance, - WorkingObjectMeta as ObjectMeta, - WorkingExhibitMeta as ExhibitMeta, - WorkingLibraryMeta as LibraryMeta, +import { + FsaFilesystem, + type FsDirectory, + type WorkingObjectProvenance as ObjectProvenance, + type WorkingObjectMeta as ObjectMeta, + type WorkingExhibitMeta as ExhibitMeta, + type WorkingLibraryMeta as LibraryMeta, } from "@render/core"; +export type { ObjectProvenance, ObjectMeta, ExhibitMeta, LibraryMeta }; const PROJECT = "archie-demo-project"; const SAMPLE_SLUG = "sample"; @@ -45,53 +46,6 @@ export async function openExhibitAnnotationsDir(slug: string): Promise1 object → grid, else single). Kept OPTIONAL for back-compat read-tolerance — - * legacy stored data is harmless and IGNORED; the Studio NEVER writes this field anymore. `LayoutType` - * imported from render-core so this stays the single source of truth (no duplicated string union). */ - layout?: LayoutType; - /** RESERVED (§43 reading-MODE axis) — v1.1 pacing variant (slideshow/scrollytelling). Unused in v1. */ - mode?: string; - objects: ObjectMeta[]; - /** Ordered narrative sections (the authored spine; IIIF Ranges at publish). Present for narrative exhibits. */ - sections?: Section[]; - /** The exhibit's curated Readings (interpretive passes; ADR-0007). A note references one by id (`record.reading`). */ - readings?: Reading[]; - /** Bundled defaults only: bump when the seeded notes change so the reconcile reseeds (P0 fixture iteration). */ - seedVersion?: number; -} -/** The authored library structure persisted at `{PROJECT}/library.json`. Carries the library-level - * identity (`title`/`summary`) + `RightsFields` (collection credit/license). `title`/`summary` were - * previously hardcoded in buildFullLibrary; now authorable so the Library has a home (rights grill Q6). */ -export interface LibraryMeta extends RightsFields { - title?: string; - summary?: string; - exhibits: ExhibitMeta[]; -} - /** Read the authored library structure. Null if OPFS unsupported or nothing authored yet. */ export async function loadLibraryMeta(): Promise { const project = await openProjectDir(); diff --git a/apps/studio/tsconfig.json b/apps/studio/tsconfig.json new file mode 100644 index 00000000..564a5990 --- /dev/null +++ b/apps/studio/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src"] +} diff --git a/apps/viewer/fixtures/geo.ts b/apps/viewer/fixtures/geo.ts index 46e00da5..d2e3562f 100644 --- a/apps/viewer/fixtures/geo.ts +++ b/apps/viewer/fixtures/geo.ts @@ -8,14 +8,14 @@ // ONLY here. Both the Studio (seed-data.ts → seededGeo / DEFAULT_EXHIBITS) and the Viewer's published library // (sample-data.ts → buildGeoLog) import this file; neither redefines the basemap or recomputes the pin // geometry, so the authored Studio seed and the published bake cannot drift. Mirrors voynich.ts / atlas.ts. -import { asObjectId, lngLatToPixel, pixelToLngLat, thumbnailUrl, type AObject, type RightsFields, type TileSourceDescriptor } from "@render/core"; +import { asObjectId, lngLatToPixel, pixelToLngLat, thumbnailUrl, type AObject, type RightsFields, type XyzTileSource } from "@render/core"; /** OSM raster XYZ template — `{z}/{x}/{y}` slippy tiles, fetched live. */ export const GEO_TEMPLATE = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"; /** The basemap descriptor: whole-world Web-Mercator, maxZoom 6 (world→continent, light to demo). The full * pixel extent is tileSize·2^maxZoom = 256·2^6 = 16384px square — the coordinate frame every pin lives in. */ -export const geoBasemap: TileSourceDescriptor = { kind: "xyz", template: GEO_TEMPLATE, tileSize: 256, minZoom: 0, maxZoom: 6, attribution: "© OpenStreetMap contributors" }; +export const geoBasemap: XyzTileSource = { kind: "xyz", template: GEO_TEMPLATE, tileSize: 256, minZoom: 0, maxZoom: 6, attribution: "© OpenStreetMap contributors" }; /** The OSM tile usage policy REQUIRES attribution — surfaced as a credit on the Map canvas. */ export const geoRights: RightsFields = { rights: "https://opendatacommons.org/licenses/odbl/", requiredStatement: { label: "Basemap", value: "© OpenStreetMap contributors, ODbL." } }; diff --git a/apps/viewer/package.json b/apps/viewer/package.json index 18bd34cc..96288408 100644 --- a/apps/viewer/package.json +++ b/apps/viewer/package.json @@ -14,6 +14,7 @@ "preview": "astro preview", "test": "vitest run", "check": "astro check", + "pretypecheck": "astro sync", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/docs/adr/0019-embeddable-read-only-archie-viewer.md b/docs/adr/0019-embeddable-read-only-archie-viewer.md index 0cfb5d95..221ddb2b 100644 --- a/docs/adr/0019-embeddable-read-only-archie-viewer.md +++ b/docs/adr/0019-embeddable-read-only-archie-viewer.md @@ -55,6 +55,13 @@ already-pure `render-core` selectors (`geometry/selector.ts`), reusing the in-re read-only path WITHOUT regressing the editor mount (shared by both apps via `@render/svelte`). - The element name + attribute schema become a frozen public API (annomea shipped this inconsistently — `` vs ``, its `EMBED-AUDIT.md`); locked in a follow-on grill before code. +- **Amendment (2026-07-05, tend Issue 3):** jsDelivr's `/gh/` serving resolves paths at the **repo + root**, not inside a package subpath, so `packages/archie-viewer/dist/` — the actual build output — + is hand-mirrored to a root-level `dist/` (`a656cda`). This is a second copy that can silently diverge + from the package build; there is no repo-root build step to replace it with. Rule: after + `pnpm --filter archie-viewer build` and before tagging a new `@vN`, run `pnpm sync-dist` + (`scripts/sync-dist.mjs`) to resync the root copy, and `pnpm sync-dist:check` to verify it before + release — it diffs both trees and exits 1 on drift. ## Alternatives rejected diff --git a/ledgers/ARTIFACTS.md b/ledgers/ARTIFACTS.md new file mode 100644 index 00000000..a11733d3 --- /dev/null +++ b/ledgers/ARTIFACTS.md @@ -0,0 +1,25 @@ +# ARTIFACTS — tracked-artifact audit (ISSUES.md Issue 3) + +Inventory taken 2026-07-05 against `main` @ `8de4a8f`. Deletion sweep adapted to tracked +artifacts, plus the byte-exact NUL fix. Classify everything before touching anything. + +| artifact | tracked? | why it exists | class | action | commit | verified | +|---|---|---|---|---|---|---| +| `--output` (repo root, 113 KB) | yes, committed `5d5bf50` | shell-redirect residue — a command's stdout was accidentally redirected into a literal file named `--output` instead of a flag being consumed | accident | `git rm`; add `/--output` to `.gitignore` so the same typo can't recommit it | `d8017be` | verified: `git ls-files -- --output` empty | +| `anti-pattern-report.txt` (repo root, 40 KB, 351 lines) | yes, committed `0c3717e`, last touched `e45f38b` | one-shot dump from an external lint/anti-pattern scan tool; no generator script exists anywhere in this repo (checked `.agents/`, `qa/`, `scripts/` — none produce this format), so it can only drift the moment code changes and can never be regenerated in-repo | accident | `git rm`; add `anti-pattern-report.txt` to `.gitignore` | `df866c8` | verified: `git ls-files anti-pattern-report.txt` empty | +| `dist/` (repo root, 6 files) | yes, committed `c471b93` + sync commit `a656cda` ("publish dist/ at repo root to match the jsDelivr @v1/dist embed URL") | deliberate hand-sync twin of `packages/archie-viewer/dist/`, required because the README's jsDelivr recipe (`README.md:260`) pins `cdn.jsdelivr.net/gh/.../dist/archie-viewer.js` at the **repo root**, not the package subpath | deliberate | keep (no repo-root build step exists to replace it); confirmed byte-identical to `packages/archie-viewer/dist/` today (`diff -rq` → no output); added `scripts/sync-dist.mjs` (+ `pnpm sync-dist` / `pnpm sync-dist:check`) and documented the release rule in ADR-0019 + README so a future `archie-viewer` rebuild can't silently diverge the root copy | `9242d3f` | verified byte-identical pre-fix (`diff -rq`); `pnpm sync-dist:check` passes post-fix | +| `node-compile-cache/` (repo root, untracked) | no | Node/tsc compile cache, disk-only | generated | none — `.gitignore:27` already covers `node-compile-cache/` | n/a | verified: `git ls-files` empty, `.gitignore` line present | +| `v8-compile-cache-1000/` (repo root, untracked) | no | V8 compile cache, PID-scoped, disk-only | generated | none — `.gitignore:28` already covers `v8-compile-cache-*/` | n/a | verified: `git ls-files` empty, `.gitignore` line present | +| `gh-pages-dist/` (repo root, untracked) | no | local output of `scripts/build-gh-pages.sh`, disk-only | generated | none — `.gitignore:15` already covers `gh-pages-dist/` | n/a | verified: `git ls-files` empty, `.gitignore` line present | +| NUL byte in `apps/studio/src/App.svelte` (line 654, byte offset 48001) | yes (part of a tracked source file, not a standalone artifact) | a dedup-key builder in `addPendingNotes` — `` `${p.objectId}\x00${p.comment}` `` — needed a separator character guaranteed not to appear in real data; the author (or a paste) put a **literal raw NUL byte** in the source text instead of writing the JS/TS escape sequence `\0`. Runtime behavior is identical either way, but the raw byte makes grep, ripgrep, `git grep`, and the fff tools treat the entire 2147-line file as binary and silently report zero matches — confirmed as the root cause of every "grep is unreliable in this repo" experience, and independently confirmed by `anti-pattern-report.txt`'s own `[unpaired-resource]` false-positive at the identical byte offset 48001 | accident | byte-exact edit: replace the single raw `0x00` byte with the two ASCII characters `\` `0` (the `\0` escape) — zero behavior change, same runtime string | `c96b787` | verified: `file(1)` now reports text, `git grep`/plain `grep` match inside the file, studio suite green (148/148) | + +## Notes + +- `node-compile-cache/`, `v8-compile-cache-1000/`, `gh-pages-dist/` are disk clutter but not + a tracked-artifact problem — left in place; deleting rebuildable local caches is out of this + loop's scope (nothing to commit, nothing a fresh clone would inherit). +- Zero accident rows remaining is the done-when for this ledger; the deliberate `dist/` row + now carries its reason and a verifiable sync-check (`pnpm sync-dist:check`). + +**Done 2026-07-05.** Zero accident rows remain; the one deliberate row (`dist/`) carries its +reason and a sync-check; the three generated rows are confirmed already `.gitignore`d. diff --git a/ledgers/GATE.md b/ledgers/GATE.md new file mode 100644 index 00000000..5e3e5296 --- /dev/null +++ b/ledgers/GATE.md @@ -0,0 +1,94 @@ +# GATE — CI tripwire ledger (ISSUES.md Issue 1) + +Inventory taken 2026-07-05 against `main` @ `2091557`. Node v24.14.0, pnpm 11.0.9 locally; +`deploy.yml` pins Node 24 / pnpm 10 — new checks job will match `deploy.yml`, not this machine. + +| check | command | local result | CI job | commit | +|---|---|---|---|---| +| render-core test | `pnpm --filter @render/core exec vitest run` | pass (688/688) | `test` | — | +| render-core typecheck | `pnpm --filter @render/core exec tsc --noEmit` | pass | `typecheck` | `ee244d8` | +| render-mount test | `pnpm --filter @render/mount exec vitest run` | pass (116/116) | `test` | — | +| render-mount typecheck | `pnpm --filter @render/mount exec tsc --noEmit` | pass | `typecheck` | `7415fe2`, `8693795` | +| render-svelte test | `pnpm --filter @render/svelte exec vitest run` | pass (7/7) | `test` | — | +| render-svelte typecheck | `pnpm --filter @render/svelte exec tsc --noEmit` | pass (cleared once render-mount's mount.ts:251 was fixed, as predicted) | `typecheck` | `7415fe2` | +| archie-viewer test | `pnpm --filter @render/archie-viewer exec vitest run` | pass (98/98) | `test` | — | +| archie-viewer typecheck | `pnpm --filter @render/archie-viewer exec tsc --noEmit` | pass (same cascade, same fix) | `typecheck` | `7415fe2` | +| viewer test | `pnpm --filter @archie/viewer exec vitest run` | pass (63/63) | `test` | — | +| viewer typecheck | `pnpm --filter @archie/viewer exec tsc --noEmit` | pass (needed `pretypecheck: astro sync` — see below) | `typecheck` | `cd421bc` | +| viewer astro check | `pnpm --filter @archie/viewer run check` | pass (0 errors/warnings/hints, 33 files) | `astro-check` | — | +| studio test | `pnpm --filter @archie/studio exec vitest run` | pass (148/148) | `test` | — | +| studio typecheck | `pnpm --filter @archie/studio exec tsc --noEmit` | pass — added `tsconfig.json` (extends root, `include: ["src"]`, no `rootDir`/`outDir` since `seed-data.ts` imports `apps/viewer/fixtures/*`) + `"typecheck": "tsc --noEmit"` script | `typecheck` | `8de4a8f`, `9a1aa33`, `7c05375`, `fb42311`, `2dfa0e1` | +| gh-pages build | `bash scripts/build-gh-pages.sh` | pass | `gh-pages-build` | — | + +CI wiring: `.github/workflows/checks.yml` (commit `aa02d0b`), 4 independent jobs, on every push + +PRs targeting main. `deploy.yml` untouched. Verified end-to-end via PR #1 (`ci/wire-checks`). + +## Tripped red — each job proven to catch a real defect (PR #1, `ci/wire-checks`) + +One deliberate defect planted per job, pushed, watched fail in both the push- and PR-triggered +Actions runs, then reverted — confirming each job actually gates on its class of failure and that +the four jobs are correctly isolated (a defect in one job's domain never fails the other three). + +| job | defect | trip commit | red confirmed | revert commit | +|---|---|---|---|---| +| `typecheck` | `const x: string = 12345` in a throwaway `render-core` file | `c0d9663` | yes (both runs) — `test`/`astro-check`/`gh-pages-build` stayed green | `0cc2383` | +| `test` | `expect(1).toBe(2)` in a throwaway `render-core` test | `531b60d` | yes (both runs) — other 3 jobs stayed green | `68835ea` | +| `astro-check` | undefined-variable reference in a throwaway `.astro` page (a diagnostic bare `tsc` never sees — confirmed locally: `astro check` exit 1, `tsc --noEmit` exit 0) | `237a10d` | yes (both runs) — other 3 jobs stayed green | `91f58c1` | +| `gh-pages-build` | corrupted `archie.config.json` `canonicalOrigin` to omit `/Archie/`, tripping the script's own explicit guard (confirmed locally first) | `a319599` | yes (both runs) — other 3 jobs stayed green | `e30c736` | + +Done: every check green on `ci/wire-checks` AND each witnessed failing exactly once. + +## Fresh-clone verification (not just the dirty working copy) + +Before trusting any of the above, cloned into `/tmp` and re-ran `pnpm install --frozen-lockfile` + +all 4 checks there. This caught a real bug the local working copy was masking: `apps/viewer`'s +typecheck depends on `.astro/types.d.ts` (the `astro/client` triple-slash reference that types +`import.meta.env`) — gitignored, astro-generated, and only present locally because `astro dev`/ +`build`/`check` had already been run here. A genuine fresh checkout (any CI runner) had nothing to +generate it, so `tsc --noEmit` failed on `published.ts`'s `import.meta.env` access. Fixed with a +`pretypecheck: astro sync` script (`cd421bc`, matching the existing predev/prebuild convention). +Re-verified clean on a second fresh clone after the fix. + +## Concurrent-session branch mishap (resolved) + +A second session was working ISSUES.md Issue 3/4 on the same checked-out working directory at the +same time. Both sessions' git commands share one `.git` — at some point the other session checked +out `main` mid-flow, so one of my commits (the deliberate typecheck-tripwire test, see below) landed +on local `main` instead of `ci/wire-checks`, and one of their commits landed on `ci/wire-checks` +instead of `main`. Resolved once the other session paused: reverted the tripwire commit off `main` +(non-destructive `git revert`, safe since `main` was never pushed with it) and reset the local+remote +`ci/wire-checks` branch back to its correct tip (`cd421bc`) — both stray commits were independently +verified safe to drop (one already an ancestor of `main`, the other byte-identical in content to a +commit already on `main`), and the reset/force-push was only done after explicit user authorization +naming the exact commits being overwritten. + +## Scope correction vs. ISSUES.md + +ISSUES.md's "known-red" list named only 3 sites (render-core x2, render-mount mount.ts:251). +Actual inventory found more: + +- **render-mount** also fails on ~20 pre-existing `noUncheckedIndexedAccess` / + `exactOptionalPropertyTypes` errors in `frame-overlay.test.ts`, `read-overlay-security.test.ts`, + `read-overlay.test.ts` — untouched by the mount.ts:251 fix, never mentioned in the issue. +- **render-svelte** and **archie-viewer** typecheck failures are *not* independent — both show the + identical single `mount.ts(251,43)` error, reached because `tsc` follows the imported source + graph even though neither package's own `include` covers `render-mount/src`. Fixing mount.ts:251 + once should clear both, pending re-verification. +- **studio** typechecking has never run before; a bare `tsc --noEmit` (matching every other + package's pattern) surfaces 22 errors on the first try, concentrated in `store.ts` (9 — looks + like a broken/incomplete type re-export, `RightsFields`/`MediaType`/etc. reported as + "cannot find name"), plus one real branded-type misuse in `publish-flows.svelte.ts` (×3, same + `LogicalId` cause), a `TileSourceDescriptor`/`Extentish` mismatch in `geo-notes.ts` (×4) + + its test (×1), and five one-off sites (`binding.ts`, `collab.test.ts`, `ingest-flows.ts`, + `save-queue.svelte.ts`, plus the `TileSourceDescriptor` overlap in `ingest-flows.ts`). + +Net: ~26 individual TypeScript error sites to clear across 3 packages before `pnpm -r typecheck` +is green — more than the 4 the issue named, none look architectural (no test asserts wrong +behavior), all look like mechanical strict-mode / branded-type fixes. + +## Tests are already green everywhere + +Every package's own `vitest run` passes today when invoked per-package (as above). The +"root vitest binary fails rune tests" problem noted elsewhere only bites the root `vitest` binary +run directly — `pnpm -r --no-bail test` (each package running its own local `vitest`) already +works and needs no fix. Issue 1's test-inventory step is done; nothing to repair there. diff --git a/package.json b/package.json index f9400693..c56025a6 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "build": "pnpm -r build", "build:gh-pages": "bash scripts/build-gh-pages.sh", "bundle:baseline": "node scripts/bundle-size.mjs", - "bundle:check": "node scripts/bundle-size.mjs --check" + "bundle:check": "node scripts/bundle-size.mjs --check", + "sync-dist": "node scripts/sync-dist.mjs", + "sync-dist:check": "node scripts/sync-dist.mjs --check" }, "devDependencies": { "@tauri-apps/cli": "^2.11.3", diff --git a/packages/render-core/src/link/link-object.test.ts b/packages/render-core/src/link/link-object.test.ts index c90b5255..365860b9 100644 --- a/packages/render-core/src/link/link-object.test.ts +++ b/packages/render-core/src/link/link-object.test.ts @@ -27,8 +27,10 @@ describe("Object cite route grammar (route.ts)", () => { expect(parseRoute(routeToHash(r))).toEqual(r); }); it("does not confuse `/o/` with the `/a/` note tail", () => { - expect(parseRoute("#/voynich/a/n1").objectId).toBeUndefined(); - expect(parseRoute("#/voynich/o/f1r").noteId).toBeUndefined(); + const noteRoute = parseRoute("#/voynich/a/n1"); + expect(noteRoute.view === "exhibit" ? noteRoute.objectId : undefined).toBeUndefined(); + const objectRoute = parseRoute("#/voynich/o/f1r"); + expect(objectRoute.view === "exhibit" ? objectRoute.noteId : undefined).toBeUndefined(); }); }); diff --git a/packages/render-core/src/spine/wholeobject.test.ts b/packages/render-core/src/spine/wholeobject.test.ts index 5933353f..01969f49 100644 --- a/packages/render-core/src/spine/wholeobject.test.ts +++ b/packages/render-core/src/spine/wholeobject.test.ts @@ -45,7 +45,7 @@ describe("archie:wholeObject = region-override (the closed seam)", () => { const { log } = appendNew([], { target: region("xywh=pixel:0,0,10,10"), wholeObject: true, lastEditor: editor }); const head = toHeadsPage(log, "p").items[0]!; - expect((head as Record)["archie:wholeObject"]).toBe(true); + expect((head as unknown as Record)["archie:wholeObject"]).toBe(true); expect(wholeObjectFlagOf(head)).toBe(true); // the reader the viewer already calls now sees a real value const back = fromHistory(Object.values(toHistory(log).pages)); diff --git a/packages/render-mount/src/frame-overlay.test.ts b/packages/render-mount/src/frame-overlay.test.ts index 24b82e65..11b7a8a4 100644 --- a/packages/render-mount/src/frame-overlay.test.ts +++ b/packages/render-mount/src/frame-overlay.test.ts @@ -34,7 +34,7 @@ describe("createFrameOverlay.draw", () => { const v = fakeViewer(); createFrameOverlay(v).draw(frame()); expect(v.overlays).toHaveLength(1); - const svg = v.overlays[0].element as SVGSVGElement; + const svg = v.overlays[0]!.element as SVGSVGElement; expect(svg.tagName.toLowerCase()).toBe("svg"); expect(svg.getAttribute("viewBox")).toBe("0 0 100 100"); expect(svg.style.pointerEvents).toBe("none"); // centre stays pan/zoom-free @@ -43,9 +43,9 @@ describe("createFrameOverlay.draw", () => { it("draws a halo rect + a clickable colour rect carrying the frame colour", () => { const v = fakeViewer(); createFrameOverlay(v).draw(frame()); - const rects = (v.overlays[0].element as SVGSVGElement).querySelectorAll("rect"); + const rects = (v.overlays[0]!.element as SVGSVGElement).querySelectorAll("rect"); expect(rects).toHaveLength(2); - const colour = rects[1]; + const colour = rects[1]!; expect(colour.getAttribute("stroke")).toBe("#c83"); expect((colour as SVGElement).style.pointerEvents).toBe("stroke"); // only the line is the hit target expect(colour.getAttribute("vector-effect")).toBe("non-scaling-stroke"); @@ -55,7 +55,7 @@ describe("createFrameOverlay.draw", () => { const v = fakeViewer(); const onActivate = vi.fn(); createFrameOverlay(v).draw(frame(onActivate)); - const colour = (v.overlays[0].element as SVGSVGElement).querySelectorAll("rect")[1]; + const colour = (v.overlays[0]!.element as SVGSVGElement).querySelectorAll("rect")[1]!; colour.dispatchEvent(new Event("click")); expect(onActivate).toHaveBeenCalledTimes(1); }); @@ -68,7 +68,7 @@ describe("createFrameOverlay.draw", () => { // simulate the image opening — but it still has no item in this fake, so re-queues; flip to ready: const ready = fakeViewer(); Object.assign(v.world, ready.world); - v.openHandlers[0](); + v.openHandlers[0]!(); expect(v.overlays).toHaveLength(1); }); diff --git a/packages/render-mount/src/frame-overlay.ts b/packages/render-mount/src/frame-overlay.ts index cd9cf05d..956e9abf 100644 --- a/packages/render-mount/src/frame-overlay.ts +++ b/packages/render-mount/src/frame-overlay.ts @@ -11,7 +11,9 @@ import type { FrameOverlay } from "./surface.js"; /** The minimal OSD viewer surface this overlay needs — keeps the module decoupled from the full OSD type. */ export interface FrameViewerLike { - addOverlay(options: { element: HTMLElement | SVGElement; location: unknown }): void; + // OSD's own OverlayOptions.element type is HTMLElement-only (@types/openseadragon), even though + // an SVGElement works fine at runtime — the SVG frame below is cast at its one call site. + addOverlay(options: { element: HTMLElement; location: unknown }): void; removeOverlay(element: HTMLElement | SVGElement): void; world: { getItemAt(i: number): { getBounds(immediately?: boolean): unknown } | undefined }; addOnceHandler?(name: string, handler: () => void): void; @@ -86,7 +88,7 @@ export function createFrameOverlay(viewer: FrameViewerLike): FrameOverlayControl svg.append(rect(frame.colour, "1.5", true)); // the quiet colour border — the click target // Anchor to the OBJECT: OSD positions + sizes the SVG to the image's viewport Rect every render frame, // so the border tracks the object through pan/zoom instead of sticking to the viewport edges. - viewer.addOverlay({ element: svg, location: item.getBounds() }); + viewer.addOverlay({ element: svg as unknown as HTMLElement, location: item.getBounds() }); frameEl = svg; }; diff --git a/packages/render-mount/src/mount.ts b/packages/render-mount/src/mount.ts index 6d4cf66f..875819dd 100644 --- a/packages/render-mount/src/mount.ts +++ b/packages/render-mount/src/mount.ts @@ -16,7 +16,7 @@ import type { ImageAnnotation, W3CImageAnnotation, DrawingStyle, DrawingStyleExp import { mountPlugin } from "@annotorious/plugin-tools"; import { resolveTileSource, isDegenerateSelectorValue, selectorOf, selectorBBox, regionPixelRect } from "@render/core"; import { dispatchFitBounds, applyFitBounds, clampedFitRect, type FitOptions, type ViewportLike } from "./fitbounds.js"; -import { createFrameOverlay } from "./frame-overlay.js"; +import { createFrameOverlay, type FrameViewerLike } from "./frame-overlay.js"; import { GestureGuard } from "./gesture-guard.js"; import { zoomBand } from "./zoom-band.js"; import { xyzTileSource } from "./xyz.js"; @@ -248,7 +248,11 @@ export async function createMount(container: HTMLElement, opts: MountOptions): P // Coverage-border overlay (7e1f) — a standalone rendering concern (createFrameOverlay). It frames the // WHOLE OBJECT: the SVG is added as an OSD overlay at the image's bounds, so it tracks the object through // pan/zoom (not a fixed viewport border). setFrame re-draws (replacing any current frame); null clears it. - const frameOverlay = createFrameOverlay(viewer); + // FrameViewerLike is deliberately a minimal duck-typed capability (frame-overlay.ts stays + // decoupled from OSD's concrete Point/Rect/OverlayOptions types); OSD's real Viewer satisfies it + // at runtime (addOverlay takes {element, location}) but its own types are narrower/wider than + // the duck type in ways TS can't verify structurally — asserted once here, at the wiring point. + const frameOverlay = createFrameOverlay(viewer as unknown as FrameViewerLike); // Shared rect math for markerScreenRect(s): selector bbox in image px → viewer-element coords + // the container's page offset, so a position:fixed anchor works regardless of layout (ADR-0006). diff --git a/packages/render-mount/src/read-overlay-security.test.ts b/packages/render-mount/src/read-overlay-security.test.ts index 99e142ff..0c70ec91 100644 --- a/packages/render-mount/src/read-overlay-security.test.ts +++ b/packages/render-mount/src/read-overlay-security.test.ts @@ -37,7 +37,7 @@ describe("STANDING: hostile SvgSelector renders as geometry only — never inner it("(b) the drawn points attribute = ONLY the parsed numeric coords", () => { const v = fakeViewer(); createReadOnlyOverlay(v).setAnnotations([hostileAnn]); - const svg = v.overlays[0].element as SVGSVGElement; + const svg = v.overlays[0]!.element as SVGSVGElement; const poly = svg.querySelector("polygon")!; expect(poly.getAttribute("points")).toBe("0,0 100,0 50,80"); // bbox-local of the 3 valid points }); @@ -45,7 +45,7 @@ describe("STANDING: hostile SvgSelector renders as geometry only — never inner it("(a) no overlay node's innerHTML contains ")]); - const label = (v.overlays[0].element as SVGSVGElement).getAttribute("aria-label")!; + const label = (v.overlays[0]!.element as SVGSVGElement).getAttribute("aria-label")!; expect(label).toBe("annotation p"); expect(label).not.toContain("<"); }); diff --git a/scripts/sync-dist.mjs b/scripts/sync-dist.mjs new file mode 100644 index 00000000..9dace3e7 --- /dev/null +++ b/scripts/sync-dist.mjs @@ -0,0 +1,52 @@ +// Sync packages/archie-viewer/dist/ to the repo root dist/. +// Canonical build output is packages/archie-viewer/dist/ (built by +// `pnpm --filter archie-viewer build`); this copy exists ONLY so the README's +// jsDelivr recipe (cdn.jsdelivr.net/gh/.../@v1/dist/archie-viewer.js) resolves +// at the repo root, which is where jsDelivr's /gh/ raw-file serving looks. +// Run before tagging a new @vN release (see docs/adr/0019, README "Embed an exhibit"). +// `--check` compares without writing and exits 1 on any drift — no baseline file, +// the source of truth is the package's own dist/. +import { cpSync, existsSync, readdirSync, readFileSync, rmSync, mkdirSync } from "node:fs"; +import { join } from "node:path"; + +const root = process.cwd(); +const src = join(root, "packages", "archie-viewer", "dist"); +const dest = join(root, "dist"); + +if (!existsSync(src)) { + console.error(`[sync-dist] source missing: ${src} — run \`pnpm --filter archie-viewer build\` first`); + process.exit(1); +} + +function listFiles(dir) { + return readdirSync(dir).sort(); +} + +if (process.argv.includes("--check")) { + if (!existsSync(dest)) { + console.error(`[sync-dist] ${dest} is missing — run \`node scripts/sync-dist.mjs\` first`); + process.exit(1); + } + const srcFiles = listFiles(src); + const destFiles = listFiles(dest); + let drifted = srcFiles.length !== destFiles.length; + if (!drifted) { + for (const name of srcFiles) { + if (!readFileSync(join(src, name)).equals(readFileSync(join(dest, name)))) { + drifted = true; + break; + } + } + } + if (drifted) { + console.error(`[sync-dist] ${dest} has drifted from ${src} — run \`node scripts/sync-dist.mjs\` to resync`); + process.exit(1); + } + console.log("[sync-dist] root dist/ matches packages/archie-viewer/dist/"); + process.exit(0); +} + +rmSync(dest, { recursive: true, force: true }); +mkdirSync(dest, { recursive: true }); +cpSync(src, dest, { recursive: true }); +console.log(`[sync-dist] copied ${src} -> ${dest}`);