@vibefield/rosetta-room 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +247 -2
- package/dist/HistoryRoom.d.ts +98 -0
- package/dist/HistoryRoom.js +440 -0
- package/dist/TranscriptReader.d.ts +22 -0
- package/dist/TranscriptReader.js +72 -0
- package/dist/components/ColdStart.d.ts +4 -0
- package/dist/components/ColdStart.js +5 -0
- package/dist/components/FilterChips.d.ts +4 -0
- package/dist/components/FilterChips.js +5 -0
- package/dist/components/HostAct.d.ts +9 -0
- package/dist/components/HostAct.js +16 -0
- package/dist/components/HostMenu.d.ts +41 -0
- package/dist/components/HostMenu.js +216 -0
- package/dist/components/IngestStatus.d.ts +4 -0
- package/dist/components/IngestStatus.js +20 -0
- package/dist/components/ListActivityNotice.d.ts +5 -0
- package/dist/components/ListActivityNotice.js +4 -0
- package/dist/components/ListSections.d.ts +20 -0
- package/dist/components/ListSections.js +39 -0
- package/dist/components/RenameHeading.d.ts +9 -0
- package/dist/components/RenameHeading.js +75 -0
- package/dist/components/SearchStrip.d.ts +5 -0
- package/dist/components/SearchStrip.js +8 -0
- package/dist/components/SummaryMetrics.d.ts +13 -0
- package/dist/components/SummaryMetrics.js +9 -0
- package/dist/components/SummaryPanels.d.ts +35 -0
- package/dist/components/SummaryPanels.js +117 -0
- package/dist/components/Transcript.d.ts +46 -0
- package/dist/components/Transcript.js +358 -0
- package/dist/components/charts/ActivityChartDetail.d.ts +13 -0
- package/dist/components/charts/ActivityChartDetail.js +78 -0
- package/dist/components/charts/CalendarHeatmap.d.ts +16 -0
- package/dist/components/charts/CalendarHeatmap.js +55 -0
- package/dist/components/charts/DayActivityChart.d.ts +8 -0
- package/dist/components/charts/DayActivityChart.js +71 -0
- package/dist/components/charts/ModelsChartDetail.d.ts +7 -0
- package/dist/components/charts/ModelsChartDetail.js +19 -0
- package/dist/components/charts/ProjectChartDialog.d.ts +19 -0
- package/dist/components/charts/ProjectChartDialog.js +39 -0
- package/dist/components/charts/Streamgraph.d.ts +16 -0
- package/dist/components/charts/Streamgraph.js +94 -0
- package/dist/components/charts/Treemap.d.ts +26 -0
- package/dist/components/charts/Treemap.js +81 -0
- package/dist/components/charts/index.d.ts +9 -0
- package/dist/components/charts/index.js +4 -0
- package/dist/components/charts/streamgraph-layout.d.ts +23 -0
- package/dist/components/charts/streamgraph-layout.js +114 -0
- package/dist/components/charts/treemap-layout.d.ts +32 -0
- package/dist/components/charts/treemap-layout.js +140 -0
- package/dist/components/charts/types.d.ts +65 -0
- package/dist/components/charts/types.js +8 -0
- package/dist/components/file-viewer/FileViewerBrowsers.d.ts +10 -0
- package/dist/components/file-viewer/FileViewerBrowsers.js +65 -0
- package/dist/components/file-viewer/FileViewerDialog.d.ts +33 -0
- package/dist/components/file-viewer/FileViewerDialog.js +82 -0
- package/dist/components/file-viewer/FileViewerHeader.d.ts +26 -0
- package/dist/components/file-viewer/FileViewerHeader.js +17 -0
- package/dist/components/file-viewer/ImageGalleryDialog.d.ts +14 -0
- package/dist/components/file-viewer/ImageGalleryDialog.js +98 -0
- package/dist/components/file-viewer/diff/index.d.ts +2 -0
- package/dist/components/file-viewer/diff/index.js +2 -0
- package/dist/components/file-viewer/diff/parse.d.ts +2 -0
- package/dist/components/file-viewer/diff/parse.js +164 -0
- package/dist/components/file-viewer/diff/types.d.ts +44 -0
- package/dist/components/file-viewer/diff/types.js +7 -0
- package/dist/components/file-viewer/gallery-source.d.ts +11 -0
- package/dist/components/file-viewer/gallery-source.js +75 -0
- package/dist/components/file-viewer/image-resource.d.ts +19 -0
- package/dist/components/file-viewer/image-resource.js +60 -0
- package/dist/components/file-viewer/index.d.ts +5 -0
- package/dist/components/file-viewer/index.js +8 -0
- package/dist/components/file-viewer/markdown/blocks.d.ts +2 -0
- package/dist/components/file-viewer/markdown/blocks.js +277 -0
- package/dist/components/file-viewer/markdown/index.d.ts +4 -0
- package/dist/components/file-viewer/markdown/index.js +35 -0
- package/dist/components/file-viewer/markdown/inline.d.ts +10 -0
- package/dist/components/file-viewer/markdown/inline.js +178 -0
- package/dist/components/file-viewer/markdown/render.d.ts +9 -0
- package/dist/components/file-viewer/markdown/render.js +78 -0
- package/dist/components/file-viewer/markdown/types.d.ts +57 -0
- package/dist/components/file-viewer/markdown/types.js +11 -0
- package/dist/components/file-viewer/minimap/CodeMinimap.d.ts +10 -0
- package/dist/components/file-viewer/minimap/CodeMinimap.js +296 -0
- package/dist/components/file-viewer/minimap/layout.d.ts +60 -0
- package/dist/components/file-viewer/minimap/layout.js +117 -0
- package/dist/components/file-viewer/minimap/palette.d.ts +2 -0
- package/dist/components/file-viewer/minimap/palette.js +73 -0
- package/dist/components/file-viewer/minimap/raster.d.ts +19 -0
- package/dist/components/file-viewer/minimap/raster.js +122 -0
- package/dist/components/file-viewer/registry.d.ts +25 -0
- package/dist/components/file-viewer/registry.js +182 -0
- package/dist/components/file-viewer/syntax/grammars.d.ts +16 -0
- package/dist/components/file-viewer/syntax/grammars.js +221 -0
- package/dist/components/file-viewer/syntax/index.d.ts +8 -0
- package/dist/components/file-viewer/syntax/index.js +9 -0
- package/dist/components/file-viewer/syntax/languages.d.ts +14 -0
- package/dist/components/file-viewer/syntax/languages.js +115 -0
- package/dist/components/file-viewer/syntax/tokenize-config.d.ts +2 -0
- package/dist/components/file-viewer/syntax/tokenize-config.js +132 -0
- package/dist/components/file-viewer/syntax/tokenize-css.d.ts +2 -0
- package/dist/components/file-viewer/syntax/tokenize-css.js +169 -0
- package/dist/components/file-viewer/syntax/tokenize-markup.d.ts +2 -0
- package/dist/components/file-viewer/syntax/tokenize-markup.js +124 -0
- package/dist/components/file-viewer/syntax/tokenize.d.ts +9 -0
- package/dist/components/file-viewer/syntax/tokenize.js +289 -0
- package/dist/components/file-viewer/syntax/types.d.ts +74 -0
- package/dist/components/file-viewer/syntax/types.js +13 -0
- package/dist/components/file-viewer/types.d.ts +111 -0
- package/dist/components/file-viewer/types.js +8 -0
- package/dist/components/file-viewer/use-file-content.d.ts +26 -0
- package/dist/components/file-viewer/use-file-content.js +144 -0
- package/dist/components/file-viewer/views/BinaryView.d.ts +2 -0
- package/dist/components/file-viewer/views/BinaryView.js +34 -0
- package/dist/components/file-viewer/views/CodeView.d.ts +2 -0
- package/dist/components/file-viewer/views/CodeView.js +46 -0
- package/dist/components/file-viewer/views/DiffBody.d.ts +7 -0
- package/dist/components/file-viewer/views/DiffBody.js +77 -0
- package/dist/components/file-viewer/views/DiffView.d.ts +2 -0
- package/dist/components/file-viewer/views/DiffView.js +11 -0
- package/dist/components/file-viewer/views/ImageView.d.ts +9 -0
- package/dist/components/file-viewer/views/ImageView.js +28 -0
- package/dist/components/file-viewer/views/MarkdownView.d.ts +9 -0
- package/dist/components/file-viewer/views/MarkdownView.js +20 -0
- package/dist/components/file-viewer/views/SvgView.d.ts +9 -0
- package/dist/components/file-viewer/views/SvgView.js +10 -0
- package/dist/components/git/ChangeTree.d.ts +20 -0
- package/dist/components/git/ChangeTree.js +35 -0
- package/dist/components/git/CommitViewer.d.ts +12 -0
- package/dist/components/git/CommitViewer.js +85 -0
- package/dist/components/git/GitChanges.d.ts +17 -0
- package/dist/components/git/GitChanges.js +70 -0
- package/dist/components/git/GitHistory.d.ts +9 -0
- package/dist/components/git/GitHistory.js +58 -0
- package/dist/components/git/co-author.d.ts +14 -0
- package/dist/components/git/co-author.js +55 -0
- package/dist/components/git/git-file-source.d.ts +18 -0
- package/dist/components/git/git-file-source.js +53 -0
- package/dist/components/git/git-host.d.ts +24 -0
- package/dist/components/git/git-host.js +130 -0
- package/dist/components/git/index.d.ts +10 -0
- package/dist/components/git/index.js +12 -0
- package/dist/components/git/path-tree.d.ts +23 -0
- package/dist/components/git/path-tree.js +73 -0
- package/dist/components/git/session-files.d.ts +9 -0
- package/dist/components/git/session-files.js +50 -0
- package/dist/components/git/types.d.ts +92 -0
- package/dist/components/git/types.js +9 -0
- package/dist/components/projects/ProjectActivitySparkline.d.ts +6 -0
- package/dist/components/projects/ProjectActivitySparkline.js +12 -0
- package/dist/components/projects/ProjectList.d.ts +25 -0
- package/dist/components/projects/ProjectList.js +43 -0
- package/dist/components/projects/ProjectListItem.d.ts +19 -0
- package/dist/components/projects/ProjectListItem.js +78 -0
- package/dist/components/projects/ProjectsColumn.d.ts +19 -0
- package/dist/components/projects/ProjectsColumn.js +36 -0
- package/dist/components/projects/index.d.ts +8 -0
- package/dist/components/projects/index.js +4 -0
- package/dist/components/search/MatchStepper.d.ts +9 -0
- package/dist/components/search/MatchStepper.js +14 -0
- package/dist/components/search/SearchLauncher.d.ts +12 -0
- package/dist/components/search/SearchLauncher.js +13 -0
- package/dist/components/search/SearchSheet.d.ts +29 -0
- package/dist/components/search/SearchSheet.js +230 -0
- package/dist/components/search/index.d.ts +8 -0
- package/dist/components/search/index.js +4 -0
- package/dist/components/search/shortcut.d.ts +25 -0
- package/dist/components/search/shortcut.js +32 -0
- package/dist/components/sessions/SessionList.d.ts +24 -0
- package/dist/components/sessions/SessionList.js +76 -0
- package/dist/components/sessions/SessionListItem.d.ts +13 -0
- package/dist/components/sessions/SessionListItem.js +58 -0
- package/dist/components/sessions/SessionsColumn.d.ts +26 -0
- package/dist/components/sessions/SessionsColumn.js +32 -0
- package/dist/components/sessions/index.d.ts +9 -0
- package/dist/components/sessions/index.js +4 -0
- package/dist/components/sessions/session-display.d.ts +8 -0
- package/dist/components/sessions/session-display.js +18 -0
- package/dist/components/sessions/types.d.ts +7 -0
- package/dist/components/sessions/types.js +1 -0
- package/dist/components/transcript/Activity.d.ts +45 -0
- package/dist/components/transcript/Activity.js +75 -0
- package/dist/components/transcript/Anchor.d.ts +8 -0
- package/dist/components/transcript/Anchor.js +23 -0
- package/dist/components/transcript/Attachments.d.ts +21 -0
- package/dist/components/transcript/Attachments.js +138 -0
- package/dist/components/transcript/AuthoredMessage.d.ts +19 -0
- package/dist/components/transcript/AuthoredMessage.js +17 -0
- package/dist/components/transcript/Compaction.d.ts +9 -0
- package/dist/components/transcript/Compaction.js +50 -0
- package/dist/components/transcript/DayPill.d.ts +38 -0
- package/dist/components/transcript/DayPill.js +68 -0
- package/dist/components/transcript/Document.d.ts +32 -0
- package/dist/components/transcript/Document.js +503 -0
- package/dist/components/transcript/Favicon.d.ts +32 -0
- package/dist/components/transcript/Favicon.js +71 -0
- package/dist/components/transcript/HeadOfDocument.d.ts +19 -0
- package/dist/components/transcript/HeadOfDocument.js +44 -0
- package/dist/components/transcript/Ledger.d.ts +53 -0
- package/dist/components/transcript/Ledger.js +182 -0
- package/dist/components/transcript/LineDetail.d.ts +24 -0
- package/dist/components/transcript/LineDetail.js +81 -0
- package/dist/components/transcript/Marker.d.ts +33 -0
- package/dist/components/transcript/Marker.js +21 -0
- package/dist/components/transcript/MessagePreview.d.ts +12 -0
- package/dist/components/transcript/MessagePreview.js +58 -0
- package/dist/components/transcript/Prompt.d.ts +20 -0
- package/dist/components/transcript/Prompt.js +55 -0
- package/dist/components/transcript/Prose.d.ts +19 -0
- package/dist/components/transcript/Prose.js +109 -0
- package/dist/components/transcript/ReceiptPill.d.ts +10 -0
- package/dist/components/transcript/ReceiptPill.js +10 -0
- package/dist/components/transcript/ResponseHead.d.ts +9 -0
- package/dist/components/transcript/ResponseHead.js +36 -0
- package/dist/components/transcript/Subthread.d.ts +19 -0
- package/dist/components/transcript/Subthread.js +96 -0
- package/dist/components/transcript/Team.d.ts +53 -0
- package/dist/components/transcript/Team.js +185 -0
- package/dist/components/transcript/Thinking.d.ts +24 -0
- package/dist/components/transcript/Thinking.js +50 -0
- package/dist/components/transcript/TimelineMeta.d.ts +9 -0
- package/dist/components/transcript/TimelineMeta.js +161 -0
- package/dist/components/transcript/TranscriptLoading.d.ts +10 -0
- package/dist/components/transcript/TranscriptLoading.js +10 -0
- package/dist/components/transcript/WebDetail.d.ts +8 -0
- package/dist/components/transcript/WebDetail.js +29 -0
- package/dist/components/transcript/Workflow.d.ts +12 -0
- package/dist/components/transcript/Workflow.js +208 -0
- package/dist/components/transcript/activity-plan.d.ts +84 -0
- package/dist/components/transcript/activity-plan.js +209 -0
- package/dist/components/transcript/attachment-file-source.d.ts +23 -0
- package/dist/components/transcript/attachment-file-source.js +103 -0
- package/dist/components/transcript/attachment-gallery.d.ts +12 -0
- package/dist/components/transcript/attachment-gallery.js +33 -0
- package/dist/components/transcript/detail-parts.d.ts +50 -0
- package/dist/components/transcript/detail-parts.js +295 -0
- package/dist/components/transcript/disclosure-state.d.ts +6 -0
- package/dist/components/transcript/disclosure-state.js +32 -0
- package/dist/components/transcript/disclosure-store.d.ts +8 -0
- package/dist/components/transcript/disclosure-store.js +31 -0
- package/dist/components/transcript/document-model.d.ts +15 -0
- package/dist/components/transcript/document-model.js +104 -0
- package/dist/components/transcript/document-plan.d.ts +28 -0
- package/dist/components/transcript/document-plan.js +145 -0
- package/dist/components/transcript/fold-preference.d.ts +17 -0
- package/dist/components/transcript/fold-preference.js +63 -0
- package/dist/components/transcript/glyphs.d.ts +15 -0
- package/dist/components/transcript/glyphs.js +47 -0
- package/dist/components/transcript/index.d.ts +23 -0
- package/dist/components/transcript/index.js +22 -0
- package/dist/components/transcript/message-file-source.d.ts +15 -0
- package/dist/components/transcript/message-file-source.js +31 -0
- package/dist/components/transcript/message-preview.d.ts +9 -0
- package/dist/components/transcript/message-preview.js +46 -0
- package/dist/components/transcript/read-file-source.d.ts +7 -0
- package/dist/components/transcript/read-file-source.js +34 -0
- package/dist/components/transcript/reader-context.d.ts +13 -0
- package/dist/components/transcript/reader-context.js +55 -0
- package/dist/components/transcript/reading-identity.d.ts +2 -0
- package/dist/components/transcript/reading-identity.js +52 -0
- package/dist/components/transcript/scroll-to-row.d.ts +2 -0
- package/dist/components/transcript/scroll-to-row.js +13 -0
- package/dist/components/transcript/thread-index.d.ts +14 -0
- package/dist/components/transcript/thread-index.js +40 -0
- package/dist/components/transcript/thread-navigation.d.ts +5 -0
- package/dist/components/transcript/thread-navigation.js +3 -0
- package/dist/components/transcript/timeline-meta-file-source.d.ts +6 -0
- package/dist/components/transcript/timeline-meta-file-source.js +55 -0
- package/dist/components/transcript/timeline-meta-state.d.ts +12 -0
- package/dist/components/transcript/timeline-meta-state.js +24 -0
- package/dist/components/ui/ColumnHeading.d.ts +27 -0
- package/dist/components/ui/ColumnHeading.js +7 -0
- package/dist/components/ui/ConfirmDialog.d.ts +14 -0
- package/dist/components/ui/ConfirmDialog.js +22 -0
- package/dist/components/ui/DialogShell.d.ts +18 -0
- package/dist/components/ui/DialogShell.js +40 -0
- package/dist/components/ui/EmptyState.d.ts +10 -0
- package/dist/components/ui/EmptyState.js +6 -0
- package/dist/components/ui/ListSummaryHeader.d.ts +9 -0
- package/dist/components/ui/ListSummaryHeader.js +6 -0
- package/dist/components/ui/ListToolbar.d.ts +6 -0
- package/dist/components/ui/ListToolbar.js +5 -0
- package/dist/components/ui/ResultGroupHeader.d.ts +9 -0
- package/dist/components/ui/ResultGroupHeader.js +5 -0
- package/dist/components/ui/SegmentedControl.d.ts +16 -0
- package/dist/components/ui/SegmentedControl.js +9 -0
- package/dist/components/ui/SummaryMetric.d.ts +13 -0
- package/dist/components/ui/SummaryMetric.js +10 -0
- package/dist/components/ui/Veil.d.ts +7 -0
- package/dist/components/ui/Veil.js +5 -0
- package/dist/components/ui/index.d.ts +20 -0
- package/dist/components/ui/index.js +10 -0
- package/dist/components/worktree/WorktreeColumn.d.ts +22 -0
- package/dist/components/worktree/WorktreeColumn.js +170 -0
- package/dist/components/worktree/WorktreeFileTree.d.ts +19 -0
- package/dist/components/worktree/WorktreeFileTree.js +60 -0
- package/dist/components/worktree/WorktreeHeader.d.ts +32 -0
- package/dist/components/worktree/WorktreeHeader.js +81 -0
- package/dist/components/worktree/WorktreeList.d.ts +8 -0
- package/dist/components/worktree/WorktreeList.js +12 -0
- package/dist/components/worktree/index.d.ts +12 -0
- package/dist/components/worktree/index.js +7 -0
- package/dist/components/worktree/types.d.ts +17 -0
- package/dist/components/worktree/types.js +3 -0
- package/dist/components/worktree/use-worktree-guest.d.ts +21 -0
- package/dist/components/worktree/use-worktree-guest.js +92 -0
- package/dist/components/worktree/worktree-file-commands.d.ts +19 -0
- package/dist/components/worktree/worktree-file-commands.js +112 -0
- package/dist/components/worktree/worktree-file-source.d.ts +15 -0
- package/dist/components/worktree/worktree-file-source.js +75 -0
- package/dist/components/worktree/worktree-host.d.ts +31 -0
- package/dist/components/worktree/worktree-host.js +242 -0
- package/dist/fixtures/index.d.ts +24 -0
- package/dist/fixtures/index.js +292 -0
- package/dist/host-inputs.d.ts +231 -0
- package/dist/host-inputs.js +35 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +14 -0
- package/dist/lib/activity-details.d.ts +23 -0
- package/dist/lib/activity-details.js +78 -0
- package/dist/lib/activity-order.d.ts +32 -0
- package/dist/lib/activity-order.js +86 -0
- package/dist/lib/agent-paths.d.ts +6 -0
- package/dist/lib/agent-paths.js +19 -0
- package/dist/lib/agents.d.ts +15 -0
- package/dist/lib/agents.js +60 -0
- package/dist/lib/catalog-status.d.ts +3 -0
- package/dist/lib/catalog-status.js +10 -0
- package/dist/lib/chart-data.d.ts +14 -0
- package/dist/lib/chart-data.js +151 -0
- package/dist/lib/day-activity.d.ts +21 -0
- package/dist/lib/day-activity.js +56 -0
- package/dist/lib/format.d.ts +24 -0
- package/dist/lib/format.js +116 -0
- package/dist/lib/hooks.d.ts +118 -0
- package/dist/lib/hooks.js +608 -0
- package/dist/lib/icons.d.ts +21 -0
- package/dist/lib/icons.js +49 -0
- package/dist/lib/image-media.d.ts +6 -0
- package/dist/lib/image-media.js +30 -0
- package/dist/lib/ingest-activity.d.ts +50 -0
- package/dist/lib/ingest-activity.js +151 -0
- package/dist/lib/ingest.d.ts +25 -0
- package/dist/lib/ingest.js +70 -0
- package/dist/lib/model-labels.d.ts +5 -0
- package/dist/lib/model-labels.js +28 -0
- package/dist/lib/model.d.ts +73 -0
- package/dist/lib/model.js +141 -0
- package/dist/lib/motion.d.ts +13 -0
- package/dist/lib/motion.js +80 -0
- package/dist/lib/room-storage.d.ts +13 -0
- package/dist/lib/room-storage.js +40 -0
- package/dist/lib/search-results.d.ts +27 -0
- package/dist/lib/search-results.js +83 -0
- package/dist/lib/search-sheet.d.ts +119 -0
- package/dist/lib/search-sheet.js +282 -0
- package/dist/lib/sections.d.ts +19 -0
- package/dist/lib/sections.js +36 -0
- package/dist/lib/session-activity.d.ts +5 -0
- package/dist/lib/session-activity.js +15 -0
- package/dist/lib/session-leases.d.ts +15 -0
- package/dist/lib/session-leases.js +61 -0
- package/dist/lib/spend.d.ts +42 -0
- package/dist/lib/spend.js +112 -0
- package/dist/lib/tint.d.ts +9 -0
- package/dist/lib/tint.js +53 -0
- package/dist/lib/transcript/collaboration.d.ts +97 -0
- package/dist/lib/transcript/collaboration.js +423 -0
- package/dist/lib/transcript/families.d.ts +212 -0
- package/dist/lib/transcript/families.js +906 -0
- package/dist/lib/transcript/favicons.d.ts +54 -0
- package/dist/lib/transcript/favicons.js +183 -0
- package/dist/lib/transcript/head-of-document.d.ts +27 -0
- package/dist/lib/transcript/head-of-document.js +274 -0
- package/dist/lib/transcript/head-state.d.ts +182 -0
- package/dist/lib/transcript/head-state.js +305 -0
- package/dist/lib/transcript/history-window.d.ts +13 -0
- package/dist/lib/transcript/history-window.js +52 -0
- package/dist/lib/transcript/images.d.ts +8 -0
- package/dist/lib/transcript/images.js +60 -0
- package/dist/lib/transcript/index.d.ts +15 -0
- package/dist/lib/transcript/index.js +8 -0
- package/dist/lib/transcript/ledger.d.ts +143 -0
- package/dist/lib/transcript/ledger.js +513 -0
- package/dist/lib/transcript/protocol.d.ts +23 -0
- package/dist/lib/transcript/protocol.js +1 -0
- package/dist/lib/transcript/sittings.d.ts +75 -0
- package/dist/lib/transcript/sittings.js +147 -0
- package/dist/lib/transcript/tail-scroll.d.ts +36 -0
- package/dist/lib/transcript/tail-scroll.js +167 -0
- package/dist/lib/transcript/tail-state.d.ts +17 -0
- package/dist/lib/transcript/tail-state.js +30 -0
- package/dist/lib/transcript/turns.d.ts +192 -0
- package/dist/lib/transcript/turns.js +576 -0
- package/dist/lib/transcript/use-transcript-opening.d.ts +12 -0
- package/dist/lib/transcript/use-transcript-opening.js +31 -0
- package/dist/lib/transcript/use-transcript-tail.d.ts +25 -0
- package/dist/lib/transcript/use-transcript-tail.js +158 -0
- package/dist/lib/transcript/vocabulary.d.ts +119 -0
- package/dist/lib/transcript/vocabulary.js +293 -0
- package/dist/lib/transcript/web-address.d.ts +6 -0
- package/dist/lib/transcript/web-address.js +18 -0
- package/dist/lib/transcript/workflow.d.ts +31 -0
- package/dist/lib/transcript/workflow.js +133 -0
- package/dist/lib/use-activity-order.d.ts +2 -0
- package/dist/lib/use-activity-order.js +23 -0
- package/dist/lib/use-day-usage.d.ts +8 -0
- package/dist/lib/use-day-usage.js +31 -0
- package/dist/lib/use-deferred-list-order.d.ts +26 -0
- package/dist/lib/use-deferred-list-order.js +142 -0
- package/dist/lib/use-ingest-activity.d.ts +2 -0
- package/dist/lib/use-ingest-activity.js +49 -0
- package/dist/lib/use-list-motion.d.ts +4 -0
- package/dist/lib/use-list-motion.js +67 -0
- package/dist/ports.d.ts +222 -0
- package/dist/ports.js +3 -0
- package/dist/room-context.d.ts +60 -0
- package/dist/room-context.js +87 -0
- package/dist/styles/activity-chart.css +127 -0
- package/dist/styles/attachments.css +29 -0
- package/dist/styles/favicon.css +73 -0
- package/dist/styles/history-room.css +18 -0
- package/dist/styles/host-inputs.css +250 -0
- package/dist/styles/image-gallery.css +30 -0
- package/dist/styles/ingest.css +51 -0
- package/dist/styles/message-preview.css +34 -0
- package/dist/styles/models-chart.css +66 -0
- package/dist/styles/project-chart.css +19 -0
- package/dist/styles/reader.css +33 -0
- package/dist/styles/room.css +1411 -0
- package/dist/styles/search.css +252 -0
- package/dist/styles/transcript-activity.css +70 -0
- package/dist/styles/transcript-head.css +220 -0
- package/dist/styles/transcript-loading.css +57 -0
- package/dist/styles/transcript.css +1226 -0
- package/package.json +61 -4
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// One tokenizer, many languages. Everything a language differs by lives in
|
|
2
|
+
// its `Grammar` table; this file is the single pass that reads one.
|
|
3
|
+
//
|
|
4
|
+
// It is a lexer and nothing more: it never looks further than the next
|
|
5
|
+
// character, so it can be wrong about anything that needs a parser to know
|
|
6
|
+
// (a regex literal that looks like division, a generic that looks like a
|
|
7
|
+
// comparison). It is wrong quietly — the worst case is a span in the wrong
|
|
8
|
+
// colour, never a span that disappears.
|
|
9
|
+
//
|
|
10
|
+
// Every pattern here is sticky (`y`) and matched against the whole text at an
|
|
11
|
+
// offset. The obvious spelling — `pattern.exec(text.slice(index))` — copies
|
|
12
|
+
// the rest of the file once per token, which is quadratic and turns a 2 MB
|
|
13
|
+
// source file into a frozen window.
|
|
14
|
+
/** `1_000`, `0xFF`, `1.5e-9`, `42u32` — with the suffix the systems
|
|
15
|
+
* languages hang off the end. */
|
|
16
|
+
const NUMBER = /(?:0[xX][0-9a-fA-F_]+|0[bB][01_]+|0[oO][0-7_]+|(?:\d[\d_]*(?:\.\d[\d_]*)?|\.\d[\d_]*)(?:[eE][+-]?\d+)?)(?:[a-zA-Z_][a-zA-Z0-9_]*)?/y;
|
|
17
|
+
const IDENTIFIER = /[A-Za-z_$][A-Za-z0-9_$]*/y;
|
|
18
|
+
const WHITESPACE = /[ \t\r\n]+/y;
|
|
19
|
+
/** Structure the eye is meant to skip over. */
|
|
20
|
+
const OPERATOR_CHARS = new Set("+-*/%=<>!&|^~?:;,.()[]{}@#\\");
|
|
21
|
+
class Cursor {
|
|
22
|
+
index = 0;
|
|
23
|
+
/** The last token that was not pure whitespace, which is the only context a
|
|
24
|
+
* one-character lookbehind ever needs. */
|
|
25
|
+
lastKind = null;
|
|
26
|
+
lastText = "";
|
|
27
|
+
out = [];
|
|
28
|
+
/** Text emitted as `plain` but not yet flushed, so a run of plain
|
|
29
|
+
* characters lands as one token instead of one token per character. */
|
|
30
|
+
pending = "";
|
|
31
|
+
text;
|
|
32
|
+
// Spelled out rather than as a parameter property: the latter is TypeScript
|
|
33
|
+
// that EMITS code, so a plain `node --strip-types` probe of this module
|
|
34
|
+
// refuses the file. The tokenizer is worth being able to run that way.
|
|
35
|
+
constructor(text) {
|
|
36
|
+
this.text = text;
|
|
37
|
+
}
|
|
38
|
+
get done() {
|
|
39
|
+
return this.index >= this.text.length;
|
|
40
|
+
}
|
|
41
|
+
peek(offset = 0) {
|
|
42
|
+
return this.text[this.index + offset] ?? "";
|
|
43
|
+
}
|
|
44
|
+
/** Does the source read `literal` right here? */
|
|
45
|
+
looking(literal) {
|
|
46
|
+
return literal.length > 0 && this.text.startsWith(literal, this.index);
|
|
47
|
+
}
|
|
48
|
+
/** Match a STICKY pattern at the cursor. */
|
|
49
|
+
match(pattern) {
|
|
50
|
+
pattern.lastIndex = this.index;
|
|
51
|
+
const found = pattern.exec(this.text);
|
|
52
|
+
return found === null ? null : found[0];
|
|
53
|
+
}
|
|
54
|
+
/** The next character that is not a space or a tab, without moving. */
|
|
55
|
+
peekNonSpace(from) {
|
|
56
|
+
let i = from;
|
|
57
|
+
while (i < this.text.length && (this.text[i] === " " || this.text[i] === "\t"))
|
|
58
|
+
i += 1;
|
|
59
|
+
return this.text[i] ?? "";
|
|
60
|
+
}
|
|
61
|
+
/** Emit `text` as `kind` and advance past it. */
|
|
62
|
+
take(kind, text) {
|
|
63
|
+
if (text.length === 0)
|
|
64
|
+
return;
|
|
65
|
+
const significant = text.trim();
|
|
66
|
+
if (significant !== "") {
|
|
67
|
+
this.lastKind = kind;
|
|
68
|
+
this.lastText = significant;
|
|
69
|
+
}
|
|
70
|
+
if (kind === "plain") {
|
|
71
|
+
this.pending += text;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.flush();
|
|
75
|
+
this.out.push({ kind, text });
|
|
76
|
+
}
|
|
77
|
+
this.index += text.length;
|
|
78
|
+
}
|
|
79
|
+
/** Retag the last token, for the lookahead a lexer can only do afterwards
|
|
80
|
+
* (a JSON key is a string until the `:` proves otherwise). */
|
|
81
|
+
retagLast(kind) {
|
|
82
|
+
const last = this.out[this.out.length - 1];
|
|
83
|
+
if (last !== undefined && this.pending === "") {
|
|
84
|
+
this.out[this.out.length - 1] = { kind, text: last.text };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Consume through `closer`, or to the end of the text if it never closes —
|
|
88
|
+
* an unterminated comment is a fact about the file, not a crash. */
|
|
89
|
+
consumeUntil(opener, closer, escape) {
|
|
90
|
+
const from = this.index + opener.length;
|
|
91
|
+
if (escape === undefined) {
|
|
92
|
+
const end = this.text.indexOf(closer, from);
|
|
93
|
+
return end === -1 ? this.text.slice(this.index) : this.text.slice(this.index, end + closer.length);
|
|
94
|
+
}
|
|
95
|
+
let i = from;
|
|
96
|
+
while (i < this.text.length) {
|
|
97
|
+
if (this.text.startsWith(escape, i)) {
|
|
98
|
+
i += escape.length + 1;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (this.text.startsWith(closer, i)) {
|
|
102
|
+
return this.text.slice(this.index, i + closer.length);
|
|
103
|
+
}
|
|
104
|
+
i += 1;
|
|
105
|
+
}
|
|
106
|
+
return this.text.slice(this.index);
|
|
107
|
+
}
|
|
108
|
+
finish() {
|
|
109
|
+
this.flush();
|
|
110
|
+
return this.out;
|
|
111
|
+
}
|
|
112
|
+
flush() {
|
|
113
|
+
if (this.pending === "")
|
|
114
|
+
return;
|
|
115
|
+
this.out.push({ kind: "plain", text: this.pending });
|
|
116
|
+
this.pending = "";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function classifyIdentifier(word, grammar, next) {
|
|
120
|
+
if (grammar.keywords?.has(word))
|
|
121
|
+
return "keyword";
|
|
122
|
+
if (grammar.constants?.has(word))
|
|
123
|
+
return "constant";
|
|
124
|
+
if (grammar.types?.has(word))
|
|
125
|
+
return "type";
|
|
126
|
+
// A capital outranks the call position: `new Uint8Array(...)`,
|
|
127
|
+
// `String::from(...)` and `<Button …>` are types being used, and painting
|
|
128
|
+
// them as functions teaches the reader that the two are the same thing.
|
|
129
|
+
// `HTML` and `X` are shouted constants or one letter, and a rule without
|
|
130
|
+
// the lowercase test paints most of a C header amber.
|
|
131
|
+
if (grammar.capitalIsType === true && /^[A-Z]/.test(word) && /[a-z]/.test(word))
|
|
132
|
+
return "type";
|
|
133
|
+
if (next === "(")
|
|
134
|
+
return "function";
|
|
135
|
+
if (grammar.propertyBeforeColon === true && next === ":")
|
|
136
|
+
return "property";
|
|
137
|
+
return "plain";
|
|
138
|
+
}
|
|
139
|
+
/** `'a` is a lifetime and `'a'` is a character, and only the character after
|
|
140
|
+
* the identifier tells them apart. */
|
|
141
|
+
function readsAsLifetime(cursor) {
|
|
142
|
+
if (!/[A-Za-z_]/.test(cursor.peek(1)))
|
|
143
|
+
return false;
|
|
144
|
+
let offset = 1;
|
|
145
|
+
while (/[A-Za-z0-9_]/.test(cursor.peek(offset)))
|
|
146
|
+
offset += 1;
|
|
147
|
+
return cursor.peek(offset) !== "'";
|
|
148
|
+
}
|
|
149
|
+
const LIFETIME = /'[A-Za-z0-9_]+/y;
|
|
150
|
+
/** A literal, with character classes read as units so a `/` inside `[^/]`
|
|
151
|
+
* does not end it, and the flags taken along. */
|
|
152
|
+
const REGEX_LITERAL = /\/(?![*/])(?:\[(?:[^\]\\\n]|\\.)*\]|[^/\\\n[]|\\.)+\/[dgimsuvy]*/y;
|
|
153
|
+
/** Keywords a regex may follow. After anything else that is a VALUE — an
|
|
154
|
+
* identifier, a number, a closing bracket — the slash is division. */
|
|
155
|
+
const REGEX_KEYWORDS = new Set([
|
|
156
|
+
"return", "typeof", "instanceof", "in", "of", "new", "delete", "void", "case",
|
|
157
|
+
"do", "else", "yield", "await", "throw",
|
|
158
|
+
]);
|
|
159
|
+
/**
|
|
160
|
+
* Is the `/` at the cursor opening a literal, or dividing?
|
|
161
|
+
*
|
|
162
|
+
* No lexer can always be right about this — `a++ /b/ g` is legal division —
|
|
163
|
+
* but the previous significant token answers it for every line anyone
|
|
164
|
+
* actually writes, and being wrong costs a span its colour and nothing else.
|
|
165
|
+
*/
|
|
166
|
+
function startsRegex(cursor) {
|
|
167
|
+
if (cursor.lastKind === null)
|
|
168
|
+
return true;
|
|
169
|
+
if (cursor.lastKind === "operator")
|
|
170
|
+
return cursor.lastText !== ")" && cursor.lastText !== "]";
|
|
171
|
+
if (cursor.lastKind === "keyword")
|
|
172
|
+
return REGEX_KEYWORDS.has(cursor.lastText);
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
/** Sigil-led variable patterns, built once per grammar rather than once per
|
|
176
|
+
* character — the same quadratic trap the sticky patterns avoid. */
|
|
177
|
+
const sigilPatterns = new WeakMap();
|
|
178
|
+
function sigilsOf(grammar) {
|
|
179
|
+
const cached = sigilPatterns.get(grammar);
|
|
180
|
+
if (cached !== undefined)
|
|
181
|
+
return cached;
|
|
182
|
+
const built = (grammar.variableSigils ?? []).map((sigil) => [sigil, new RegExp(`${sigil.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}[A-Za-z_{][A-Za-z0-9_}]*`, "y")]);
|
|
183
|
+
sigilPatterns.set(grammar, built);
|
|
184
|
+
return built;
|
|
185
|
+
}
|
|
186
|
+
export function tokenize(text, grammar) {
|
|
187
|
+
const cursor = new Cursor(text);
|
|
188
|
+
const sigils = sigilsOf(grammar);
|
|
189
|
+
while (!cursor.done) {
|
|
190
|
+
const char = cursor.peek();
|
|
191
|
+
// Whitespace, in one run. Newlines included — lines are cut afterwards,
|
|
192
|
+
// so a block comment stays one token across all of them.
|
|
193
|
+
if (char === " " || char === "\t" || char === "\n" || char === "\r") {
|
|
194
|
+
cursor.take("plain", cursor.match(WHITESPACE) ?? char);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const block = grammar.blockComments?.find(([open]) => cursor.looking(open));
|
|
198
|
+
if (block !== undefined) {
|
|
199
|
+
cursor.take("comment", cursor.consumeUntil(block[0], block[1]));
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
const lineComment = grammar.lineComments?.find((open) => cursor.looking(open));
|
|
203
|
+
if (lineComment !== undefined) {
|
|
204
|
+
const end = text.indexOf("\n", cursor.index);
|
|
205
|
+
cursor.take("comment", text.slice(cursor.index, end === -1 ? text.length : end));
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (grammar.annotation !== undefined) {
|
|
209
|
+
const annotation = cursor.match(grammar.annotation);
|
|
210
|
+
if (annotation !== null && annotation.length > 0) {
|
|
211
|
+
cursor.take("type", annotation);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (char === "'" && grammar.lifetimeQuote === true && readsAsLifetime(cursor)) {
|
|
216
|
+
cursor.take("type", cursor.match(LIFETIME) ?? "'");
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (char === "/" && grammar.regexLiterals === true && startsRegex(cursor)) {
|
|
220
|
+
const literal = cursor.match(REGEX_LITERAL);
|
|
221
|
+
if (literal !== null) {
|
|
222
|
+
// A literal is text quoted from another language, which is what a
|
|
223
|
+
// string is; it takes the same accent for the same reason.
|
|
224
|
+
cursor.take("string", literal);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const string = grammar.strings?.find((rule) => cursor.looking(rule.open));
|
|
229
|
+
if (string !== undefined) {
|
|
230
|
+
const raw = cursor.consumeUntil(string.open, string.close ?? string.open, string.escape);
|
|
231
|
+
// A single-line literal that ran past a newline never closed: stop at
|
|
232
|
+
// the line, so one stray quote does not paint the rest of the file.
|
|
233
|
+
const newline = string.multiline === true ? -1 : raw.indexOf("\n");
|
|
234
|
+
cursor.take("string", newline === -1 ? raw : raw.slice(0, newline));
|
|
235
|
+
if (grammar.propertyBeforeColon === true && cursor.peekNonSpace(cursor.index) === ":") {
|
|
236
|
+
cursor.retagLast("property");
|
|
237
|
+
}
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const sigil = sigils.find(([lead]) => cursor.looking(lead));
|
|
241
|
+
if (sigil !== undefined) {
|
|
242
|
+
const variable = cursor.match(sigil[1]);
|
|
243
|
+
if (variable !== null) {
|
|
244
|
+
cursor.take("constant", variable);
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (/[0-9]/.test(char) || (char === "." && /[0-9]/.test(cursor.peek(1)))) {
|
|
249
|
+
const number = cursor.match(NUMBER);
|
|
250
|
+
if (number !== null) {
|
|
251
|
+
cursor.take("number", number);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const word = cursor.match(IDENTIFIER);
|
|
256
|
+
if (word !== null) {
|
|
257
|
+
const next = cursor.peekNonSpace(cursor.index + word.length);
|
|
258
|
+
cursor.take(classifyIdentifier(word, grammar, next), word);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
cursor.take(OPERATOR_CHARS.has(char) ? "operator" : "plain", char);
|
|
262
|
+
}
|
|
263
|
+
return cursor.finish();
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Cut a token stream into lines. Tokens that span newlines — block comments,
|
|
267
|
+
* template literals, docstrings — are split, and each piece keeps its kind,
|
|
268
|
+
* which is the whole reason tokenizing runs over the file rather than over
|
|
269
|
+
* each line in turn.
|
|
270
|
+
*/
|
|
271
|
+
export function splitLines(tokens) {
|
|
272
|
+
const lines = [[]];
|
|
273
|
+
for (const token of tokens) {
|
|
274
|
+
if (!token.text.includes("\n")) {
|
|
275
|
+
if (token.text.length > 0)
|
|
276
|
+
lines[lines.length - 1].push(token);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
const pieces = token.text.split("\n");
|
|
280
|
+
for (const [index, piece] of pieces.entries()) {
|
|
281
|
+
if (index > 0)
|
|
282
|
+
lines.push([]);
|
|
283
|
+
const trimmed = piece.endsWith("\r") ? piece.slice(0, -1) : piece;
|
|
284
|
+
if (trimmed.length > 0)
|
|
285
|
+
lines[lines.length - 1].push({ kind: token.kind, text: trimmed });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return lines;
|
|
289
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export type TokenKind =
|
|
2
|
+
/** `// like this` — the quietest step of the ramp, never a hue. */
|
|
3
|
+
"comment"
|
|
4
|
+
/** A literal run of text: quoted, templated, or heredoc'd. */
|
|
5
|
+
| "string"
|
|
6
|
+
/** A numeric literal. Shares its accent with `constant`: both are values. */
|
|
7
|
+
| "number"
|
|
8
|
+
/** `fn`, `const`, `impl`, `async` — the language's own words. */
|
|
9
|
+
| "keyword"
|
|
10
|
+
/** A named type. Also markup's attribute names, which play the same part. */
|
|
11
|
+
| "type"
|
|
12
|
+
/** A name in call position. */
|
|
13
|
+
| "function"
|
|
14
|
+
/** `true`, `null`, `None`, `self` — a value the language names for you. */
|
|
15
|
+
| "constant"
|
|
16
|
+
/** An object key, a config key, a markup attribute. */
|
|
17
|
+
| "property"
|
|
18
|
+
/** A markup element name. Shares its accent with `keyword`. */
|
|
19
|
+
| "tag"
|
|
20
|
+
/** Brackets, commas, arrows — structure the eye skips. */
|
|
21
|
+
| "operator"
|
|
22
|
+
/** Everything else: identifiers, prose, whitespace. */
|
|
23
|
+
| "plain";
|
|
24
|
+
export interface Token {
|
|
25
|
+
readonly kind: TokenKind;
|
|
26
|
+
readonly text: string;
|
|
27
|
+
}
|
|
28
|
+
/** One source line's worth of tokens. Never contains a newline. */
|
|
29
|
+
export type TokenLine = readonly Token[];
|
|
30
|
+
/** How one flavour of string literal opens, closes and escapes. */
|
|
31
|
+
export interface StringRule {
|
|
32
|
+
readonly open: string;
|
|
33
|
+
/** Defaults to `open`. */
|
|
34
|
+
readonly close?: string;
|
|
35
|
+
/** May a newline appear inside it? */
|
|
36
|
+
readonly multiline?: boolean;
|
|
37
|
+
/** The escape character, if the literal has one. */
|
|
38
|
+
readonly escape?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* One language's shape, as data. The tokenizer is one function; a language is
|
|
42
|
+
* a table — which is what makes adding one a five-line change rather than a
|
|
43
|
+
* new code path.
|
|
44
|
+
*/
|
|
45
|
+
export interface Grammar {
|
|
46
|
+
readonly lineComments?: readonly string[];
|
|
47
|
+
readonly blockComments?: readonly (readonly [string, string])[];
|
|
48
|
+
/** Tried in order, so `"""` must be listed before `"`. */
|
|
49
|
+
readonly strings?: readonly StringRule[];
|
|
50
|
+
readonly keywords?: ReadonlySet<string>;
|
|
51
|
+
/** Values the language names: `true`, `nil`, `self`. */
|
|
52
|
+
readonly constants?: ReadonlySet<string>;
|
|
53
|
+
/** Types the language ships with: `u32`, `String`, `boolean`. */
|
|
54
|
+
readonly types?: ReadonlySet<string>;
|
|
55
|
+
/** Whether an identifier that starts with a capital reads as a type. */
|
|
56
|
+
readonly capitalIsType?: boolean;
|
|
57
|
+
/** `#[derive]`, `@override`, `#pragma` — a sigil-led annotation. */
|
|
58
|
+
readonly annotation?: RegExp;
|
|
59
|
+
/** An identifier before `:` is a key. True for JSON and object literals. */
|
|
60
|
+
readonly propertyBeforeColon?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* `'a` is a lifetime, `'a'` is a character. Rust needs this told to it;
|
|
63
|
+
* every other language with `'` strings does not.
|
|
64
|
+
*/
|
|
65
|
+
readonly lifetimeQuote?: boolean;
|
|
66
|
+
/** Sigil-led variables — shell's `$PATH`, Ruby's `@ivar`. */
|
|
67
|
+
readonly variableSigils?: readonly string[];
|
|
68
|
+
/**
|
|
69
|
+
* `/` opens a literal as well as dividing. Only the JS family has the
|
|
70
|
+
* ambiguity, and only what came BEFORE the slash can settle it — see
|
|
71
|
+
* `startsRegex`.
|
|
72
|
+
*/
|
|
73
|
+
readonly regexLiterals?: boolean;
|
|
74
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// What a highlighter is allowed to say about a piece of code.
|
|
2
|
+
//
|
|
3
|
+
// The kind set is CLOSED, and deliberately small. DESIGN.md §2.6 gives the
|
|
4
|
+
// room eight curated accents for closed sets — "these group and label; they
|
|
5
|
+
// never signal state" — and a token kind is exactly that: a label on a span
|
|
6
|
+
// of someone else's document. Six of the eight are spent here; red and pink
|
|
7
|
+
// are left alone, because red means failure everywhere else in this room and
|
|
8
|
+
// a reader must not learn a second meaning for it.
|
|
9
|
+
//
|
|
10
|
+
// This is a HIGHLIGHTER, not a parser. It knows a string from a comment from
|
|
11
|
+
// a keyword. It does not resolve types, scopes or imports, and nothing built
|
|
12
|
+
// on it may claim it does.
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { ComponentType, ReactNode } from "react";
|
|
2
|
+
import type { Language } from "./syntax/index.js";
|
|
3
|
+
/** A file the viewer has been asked to show. */
|
|
4
|
+
export interface FileHandle {
|
|
5
|
+
/** Stable identity for this open — remounts the body and cancels the last
|
|
6
|
+
* read when it changes. */
|
|
7
|
+
readonly key: string;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
/** Absolute path, when the caller can derive one. `null` costs the reader
|
|
10
|
+
* the path line and path-only acts; it never costs them the
|
|
11
|
+
* file, because reading goes through the source, not the path. */
|
|
12
|
+
readonly path: string | null;
|
|
13
|
+
/** Known byte length; a negative value means the source has not supplied
|
|
14
|
+
* a size yet. Reads also check their actual length before rendering. */
|
|
15
|
+
readonly size: number;
|
|
16
|
+
/**
|
|
17
|
+
* Whatever the source needs to find the file again — an entry id, a
|
|
18
|
+
* handle, a URL. OPAQUE here on purpose: the only code allowed to read it
|
|
19
|
+
* is the source that wrote it, which is what keeps the viewer free of any
|
|
20
|
+
* knowledge of where files come from.
|
|
21
|
+
*/
|
|
22
|
+
readonly ref?: unknown;
|
|
23
|
+
}
|
|
24
|
+
/** Where bytes come from. A caller can be a mille engine, a fixture, or a
|
|
25
|
+
* test. */
|
|
26
|
+
export interface FileSource {
|
|
27
|
+
read(handle: FileHandle, signal: AbortSignal): Promise<Uint8Array>;
|
|
28
|
+
/** Sized, browser-cacheable resources. The source owns address validation;
|
|
29
|
+
* a viewer never needs to copy full-image bytes to show a small thumbnail. */
|
|
30
|
+
image?: ((handle: FileHandle) => ImageResource | null) | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* A unified diff of this handle, for a view whose payload is `"compare"`.
|
|
33
|
+
*
|
|
34
|
+
* Optional, because most sources have one version of a file and nothing to
|
|
35
|
+
* compare it to. A source that omits it must never resolve a kind that
|
|
36
|
+
* asks for one — the registry decides that from the name, and a diff is
|
|
37
|
+
* only ever asked for by a caller that built the handle.
|
|
38
|
+
*/
|
|
39
|
+
compare?(handle: FileHandle, signal: AbortSignal): Promise<{
|
|
40
|
+
readonly unified: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
/** The sized renditions a source may address. */
|
|
44
|
+
export type ImageRendition = "full" | "preview" | "rail" | "tile" | "tile2x";
|
|
45
|
+
/** An image address: now, once the source has it, or not at all. */
|
|
46
|
+
export type ImageUrl = string | null | PromiseLike<string | null>;
|
|
47
|
+
/** Sized, browser-loadable renditions of one image, asked for one at a time:
|
|
48
|
+
* a source that must fetch before it can address an image fetches only the
|
|
49
|
+
* rendition a view shows. */
|
|
50
|
+
export interface ImageResource {
|
|
51
|
+
/** Stable for this image: the same key asks for the same bytes. */
|
|
52
|
+
readonly key: string;
|
|
53
|
+
url(rendition: ImageRendition): ImageUrl;
|
|
54
|
+
readonly width?: number | undefined;
|
|
55
|
+
readonly height?: number | undefined;
|
|
56
|
+
}
|
|
57
|
+
/** A view's own switch, rendered as the room's one switch shape. */
|
|
58
|
+
export interface ViewMode {
|
|
59
|
+
readonly id: string;
|
|
60
|
+
readonly label: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* An act the dialog can offer for a file. Named rather than supplied as a
|
|
64
|
+
* closure: a view is a data table, and the side effects live in the one place
|
|
65
|
+
* that is allowed to have them.
|
|
66
|
+
*/
|
|
67
|
+
export type ViewerActionId = "copy-path" | "open-in-browser";
|
|
68
|
+
export interface ViewerDefinition {
|
|
69
|
+
readonly id: string;
|
|
70
|
+
/** How the content must arrive. `"text"` also gets `OpenFile.text`
|
|
71
|
+
* decoded; `"compare"` asks the source for a unified diff instead of
|
|
72
|
+
* bytes, and the file's own size limit does not apply — git bounds its
|
|
73
|
+
* own output, and a one-line change to a huge file is a small diff. */
|
|
74
|
+
readonly payload: "text" | "bytes" | "compare";
|
|
75
|
+
/** The most this view will read. Over it, the dialog says so and reads
|
|
76
|
+
* nothing — a limit the reader is told is a fact; a hang is not. */
|
|
77
|
+
readonly maxBytes: number;
|
|
78
|
+
readonly modes?: readonly ViewMode[];
|
|
79
|
+
readonly actions?: readonly ViewerActionId[];
|
|
80
|
+
readonly Body: ComponentType<ViewBodyProps>;
|
|
81
|
+
}
|
|
82
|
+
/** What the registry decided a file is. */
|
|
83
|
+
export interface FileKind {
|
|
84
|
+
readonly viewer: ViewerDefinition;
|
|
85
|
+
/** The chip beside the name: "TypeScript", "Markdown", "PNG". */
|
|
86
|
+
readonly label: string;
|
|
87
|
+
/** Set when the body highlights source. */
|
|
88
|
+
readonly language: Language | null;
|
|
89
|
+
/** Set when the body needs a blob: `image/png`, `image/svg+xml`. */
|
|
90
|
+
readonly mime: string | null;
|
|
91
|
+
}
|
|
92
|
+
/** A file that has been read. */
|
|
93
|
+
export interface OpenFile {
|
|
94
|
+
readonly handle: FileHandle;
|
|
95
|
+
readonly kind: FileKind;
|
|
96
|
+
readonly bytes: Uint8Array;
|
|
97
|
+
/** Decoded UTF-8, or `null` for a view whose payload is `"bytes"`. */
|
|
98
|
+
readonly text: string | null;
|
|
99
|
+
/** The unified diff, for a `"compare"` view. */
|
|
100
|
+
readonly unified: string | null;
|
|
101
|
+
readonly image?: ImageResource;
|
|
102
|
+
readonly imageAttempt?: number;
|
|
103
|
+
}
|
|
104
|
+
export interface ViewBodyProps {
|
|
105
|
+
readonly onRetry?: (() => void) | undefined;
|
|
106
|
+
/** Optional rich Markdown presentation supplied by the content owner. */
|
|
107
|
+
readonly renderMarkdown?: ((text: string) => ReactNode) | undefined;
|
|
108
|
+
readonly file: OpenFile;
|
|
109
|
+
/** The current mode's id, or `""` for a view with no modes. */
|
|
110
|
+
readonly mode: string;
|
|
111
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The file viewer's contract, and the whole reason it is a peer of the
|
|
2
|
+
// worktree column rather than a part of it: nothing here knows what a mille
|
|
3
|
+
// engine is, what a project is, or where the bytes came from. Hand it a name,
|
|
4
|
+
// a size and something that can produce bytes, and it shows the file.
|
|
5
|
+
//
|
|
6
|
+
// That seam is what lets the same dialog serve a search result, a session's
|
|
7
|
+
// footprint or a dropped path later without being rewritten.
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { FileHandle, FileSource, OpenFile } from "./types.js";
|
|
2
|
+
export type FileContent = {
|
|
3
|
+
readonly status: "reading";
|
|
4
|
+
} | {
|
|
5
|
+
readonly status: "empty";
|
|
6
|
+
} | {
|
|
7
|
+
readonly status: "too-large";
|
|
8
|
+
readonly limit: number;
|
|
9
|
+
} | {
|
|
10
|
+
readonly status: "failed";
|
|
11
|
+
readonly reason: string;
|
|
12
|
+
} | {
|
|
13
|
+
readonly status: "ready";
|
|
14
|
+
readonly file: OpenFile;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Read `handle` through `source`.
|
|
18
|
+
*
|
|
19
|
+
* `source` must be stable across renders — it identifies the engine the read
|
|
20
|
+
* goes to, and a fresh object every render would re-read the file on every
|
|
21
|
+
* render. The worktree column memoizes one per engine.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useFileContent(handle: FileHandle | null, source: FileSource,
|
|
24
|
+
/** Set when the caller is opening a CHANGE rather than a file — the one
|
|
25
|
+
* decision the registry cannot make from a name. */
|
|
26
|
+
asDiff?: boolean, attempt?: number, asImage?: boolean): FileContent;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// Reading one file, and every honest thing that can happen instead.
|
|
2
|
+
//
|
|
3
|
+
// Two of the five states are answered WITHOUT reading anything, from the size
|
|
4
|
+
// the tree already knows: an empty file, and a file past the view's limit.
|
|
5
|
+
// That ordering is the point — a viewer that discovers a 400 MB file by
|
|
6
|
+
// loading it has already done the damage by the time it can report it.
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
import { BINARY_KIND, diffKind, imageKind, looksBinary, resolveKind } from "./registry.js";
|
|
9
|
+
const READING = { status: "reading" };
|
|
10
|
+
/** What a failed read can be told about itself. mille raises a
|
|
11
|
+
* `FileSystemError` carrying a POSIX code, which is the most useful half. */
|
|
12
|
+
function describe(error) {
|
|
13
|
+
if (error === null || typeof error !== "object")
|
|
14
|
+
return "the read failed";
|
|
15
|
+
const { code, message } = error;
|
|
16
|
+
if (typeof code === "string" && code !== "") {
|
|
17
|
+
return typeof message === "string" && message !== "" ? `${code} · ${message}` : code;
|
|
18
|
+
}
|
|
19
|
+
return typeof message === "string" && message !== "" ? message : "the read failed";
|
|
20
|
+
}
|
|
21
|
+
function decode(bytes) {
|
|
22
|
+
const text = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
23
|
+
// A byte-order mark is an encoding artefact, not the file's first
|
|
24
|
+
// character, and left in place it shows up as a blank glyph on line one.
|
|
25
|
+
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read `handle` through `source`.
|
|
29
|
+
*
|
|
30
|
+
* `source` must be stable across renders — it identifies the engine the read
|
|
31
|
+
* goes to, and a fresh object every render would re-read the file on every
|
|
32
|
+
* render. The worktree column memoizes one per engine.
|
|
33
|
+
*/
|
|
34
|
+
export function useFileContent(handle, source,
|
|
35
|
+
/** Set when the caller is opening a CHANGE rather than a file — the one
|
|
36
|
+
* decision the registry cannot make from a name. */
|
|
37
|
+
asDiff = false, attempt = 0, asImage = false) {
|
|
38
|
+
const [state, setState] = useState({
|
|
39
|
+
handleKey: null,
|
|
40
|
+
source: null,
|
|
41
|
+
asDiff: false,
|
|
42
|
+
asImage: false,
|
|
43
|
+
value: READING,
|
|
44
|
+
});
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (handle === null) {
|
|
47
|
+
setState({ handleKey: null, source, asDiff, asImage, value: READING });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const publish = (value) => {
|
|
51
|
+
setState({ handleKey: handle.key, source, asDiff, asImage, value });
|
|
52
|
+
};
|
|
53
|
+
const kind = asDiff ? diffKind(handle.name) : asImage ? imageKind(handle.name) : resolveKind(handle.name);
|
|
54
|
+
if (!asDiff && (asImage || kind.viewer.id === "image")) {
|
|
55
|
+
try {
|
|
56
|
+
const image = source.image?.(handle);
|
|
57
|
+
if (image) {
|
|
58
|
+
publish({ status: "ready", file: { handle, kind: imageKind("Image"),
|
|
59
|
+
bytes: new Uint8Array(0), text: null, unified: null, image, imageAttempt: attempt } });
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
publish({ status: "failed", reason: describe(error) });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (kind.viewer.payload === "compare") {
|
|
69
|
+
if (source.compare === undefined) {
|
|
70
|
+
publish({ status: "failed", reason: "this source has nothing to compare against" });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
publish(READING);
|
|
74
|
+
const comparing = new AbortController();
|
|
75
|
+
void source
|
|
76
|
+
.compare(handle, comparing.signal)
|
|
77
|
+
.then(({ unified }) => {
|
|
78
|
+
if (comparing.signal.aborted)
|
|
79
|
+
return;
|
|
80
|
+
publish({
|
|
81
|
+
status: "ready",
|
|
82
|
+
file: { handle, kind, bytes: new Uint8Array(0), text: null, unified },
|
|
83
|
+
});
|
|
84
|
+
})
|
|
85
|
+
.catch((error) => {
|
|
86
|
+
if (comparing.signal.aborted)
|
|
87
|
+
return;
|
|
88
|
+
publish({ status: "failed", reason: describe(error) });
|
|
89
|
+
});
|
|
90
|
+
return () => comparing.abort();
|
|
91
|
+
}
|
|
92
|
+
if (handle.size === 0) {
|
|
93
|
+
publish({ status: "empty" });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (handle.size > kind.viewer.maxBytes) {
|
|
97
|
+
publish({ status: "too-large", limit: kind.viewer.maxBytes });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
publish(READING);
|
|
101
|
+
const controller = new AbortController();
|
|
102
|
+
void (async () => {
|
|
103
|
+
try {
|
|
104
|
+
const bytes = await source.read(handle, controller.signal);
|
|
105
|
+
if (controller.signal.aborted)
|
|
106
|
+
return;
|
|
107
|
+
// The name said text and the bytes disagree. The bytes win: this is
|
|
108
|
+
// the one place a file's kind is decided by what is in it, and it can
|
|
109
|
+
// only ever downgrade — a hex dump claims less, never more.
|
|
110
|
+
const binary = kind.viewer.payload === "text" && looksBinary(bytes);
|
|
111
|
+
const resolved = asImage ? imageKind(handle.name, bytes) : binary ? BINARY_KIND : kind;
|
|
112
|
+
if (bytes.byteLength > resolved.viewer.maxBytes) {
|
|
113
|
+
publish({ status: "too-large", limit: resolved.viewer.maxBytes });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
publish({
|
|
117
|
+
status: "ready",
|
|
118
|
+
file: {
|
|
119
|
+
handle,
|
|
120
|
+
kind: resolved,
|
|
121
|
+
bytes,
|
|
122
|
+
text: resolved.viewer.payload === "text" ? decode(bytes) : null,
|
|
123
|
+
unified: null,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (controller.signal.aborted)
|
|
129
|
+
return;
|
|
130
|
+
publish({ status: "failed", reason: describe(error) });
|
|
131
|
+
}
|
|
132
|
+
})();
|
|
133
|
+
return () => controller.abort();
|
|
134
|
+
}, [handle, source, asDiff, attempt, asImage]);
|
|
135
|
+
// Effects change after render. Without an identity check, the first frame
|
|
136
|
+
// after switching files carries the previous file's state — harmlessly
|
|
137
|
+
// visible before, but unacceptable once an action can export those bytes.
|
|
138
|
+
return state.handleKey === (handle?.key ?? null) &&
|
|
139
|
+
state.source === source &&
|
|
140
|
+
state.asImage === asImage &&
|
|
141
|
+
state.asDiff === asDiff
|
|
142
|
+
? state.value
|
|
143
|
+
: READING;
|
|
144
|
+
}
|