@vibefield/rosetta-room 0.0.0-stage → 0.1.1
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,140 @@
|
|
|
1
|
+
const DEFAULT_MINIMUM_VISIBLE_SHARE = 0.009;
|
|
2
|
+
const DEFAULT_MINIMUM_LABEL_WIDTH = 46;
|
|
3
|
+
// Two 11px lines plus the tile's top/bottom insets and a safe interline gap.
|
|
4
|
+
const DEFAULT_MINIMUM_LABEL_HEIGHT = 38;
|
|
5
|
+
function sum(values) {
|
|
6
|
+
return values.reduce((total, value) => total + value, 0);
|
|
7
|
+
}
|
|
8
|
+
function worstAspectRatio(areas, rowLength) {
|
|
9
|
+
if (areas.length === 0 || rowLength <= 0)
|
|
10
|
+
return Number.POSITIVE_INFINITY;
|
|
11
|
+
const area = sum(areas);
|
|
12
|
+
let largest = Number.NEGATIVE_INFINITY;
|
|
13
|
+
let smallest = Number.POSITIVE_INFINITY;
|
|
14
|
+
for (const itemArea of areas) {
|
|
15
|
+
largest = Math.max(largest, itemArea);
|
|
16
|
+
smallest = Math.min(smallest, itemArea);
|
|
17
|
+
}
|
|
18
|
+
if (area <= 0 || smallest <= 0)
|
|
19
|
+
return Number.POSITIVE_INFINITY;
|
|
20
|
+
return Math.max((rowLength * rowLength * largest) / (area * area), (area * area) / (rowLength * rowLength * smallest));
|
|
21
|
+
}
|
|
22
|
+
/** Squarified treemap layout by Bruls, Huizing, and van Wijk. */
|
|
23
|
+
function squarify(areas, containerWidth, containerHeight) {
|
|
24
|
+
const rectangles = [];
|
|
25
|
+
let remainingX = 0;
|
|
26
|
+
let remainingY = 0;
|
|
27
|
+
let remainingWidth = containerWidth;
|
|
28
|
+
let remainingHeight = containerHeight;
|
|
29
|
+
let areaIndex = 0;
|
|
30
|
+
let currentRow = [];
|
|
31
|
+
let rowRunsHorizontally = false;
|
|
32
|
+
let rowLength = 0;
|
|
33
|
+
const placeCurrentRow = () => {
|
|
34
|
+
const rowArea = sum(currentRow);
|
|
35
|
+
const rowThickness = rowLength > 0 ? rowArea / rowLength : 0;
|
|
36
|
+
let rowOffset = 0;
|
|
37
|
+
for (const area of currentRow) {
|
|
38
|
+
const itemLength = rowThickness > 0 ? area / rowThickness : 0;
|
|
39
|
+
rectangles.push(rowRunsHorizontally
|
|
40
|
+
? {
|
|
41
|
+
x: remainingX + rowOffset,
|
|
42
|
+
y: remainingY,
|
|
43
|
+
width: itemLength,
|
|
44
|
+
height: rowThickness,
|
|
45
|
+
}
|
|
46
|
+
: {
|
|
47
|
+
x: remainingX,
|
|
48
|
+
y: remainingY + rowOffset,
|
|
49
|
+
width: rowThickness,
|
|
50
|
+
height: itemLength,
|
|
51
|
+
});
|
|
52
|
+
rowOffset += itemLength;
|
|
53
|
+
}
|
|
54
|
+
if (rowRunsHorizontally) {
|
|
55
|
+
remainingY += rowThickness;
|
|
56
|
+
remainingHeight = Math.max(0, remainingHeight - rowThickness);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
remainingX += rowThickness;
|
|
60
|
+
remainingWidth = Math.max(0, remainingWidth - rowThickness);
|
|
61
|
+
}
|
|
62
|
+
currentRow = [];
|
|
63
|
+
};
|
|
64
|
+
while (areaIndex < areas.length) {
|
|
65
|
+
if (currentRow.length === 0) {
|
|
66
|
+
rowRunsHorizontally = remainingHeight > remainingWidth;
|
|
67
|
+
rowLength = rowRunsHorizontally ? remainingWidth : remainingHeight;
|
|
68
|
+
}
|
|
69
|
+
const candidate = areas[areaIndex];
|
|
70
|
+
if (currentRow.length === 0 ||
|
|
71
|
+
worstAspectRatio([...currentRow, candidate], rowLength) <=
|
|
72
|
+
worstAspectRatio(currentRow, rowLength)) {
|
|
73
|
+
currentRow.push(candidate);
|
|
74
|
+
areaIndex += 1;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
placeCurrentRow();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (currentRow.length > 0)
|
|
81
|
+
placeCurrentRow();
|
|
82
|
+
return rectangles;
|
|
83
|
+
}
|
|
84
|
+
function isPositiveFinite(value) {
|
|
85
|
+
return Number.isFinite(value) && value > 0;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Sorts, normalises, and lays out treemap items. Zero and invalid values are
|
|
89
|
+
* omitted because they have no meaningful area.
|
|
90
|
+
*/
|
|
91
|
+
export function createTreemapLayout(items, { width, height, minimumVisibleShare = DEFAULT_MINIMUM_VISIBLE_SHARE, minimumLabelWidth = DEFAULT_MINIMUM_LABEL_WIDTH, minimumLabelHeight = DEFAULT_MINIMUM_LABEL_HEIGHT, }) {
|
|
92
|
+
if (!Number.isFinite(width) ||
|
|
93
|
+
!Number.isFinite(height) ||
|
|
94
|
+
width <= 0 ||
|
|
95
|
+
height <= 0) {
|
|
96
|
+
return { tiles: [], hiddenLabelCount: 0, minimumSizedItemCount: 0 };
|
|
97
|
+
}
|
|
98
|
+
const sortedItems = items
|
|
99
|
+
.filter((item) => isPositiveFinite(item.value))
|
|
100
|
+
.map((item, originalIndex) => ({ item, originalIndex }))
|
|
101
|
+
.sort((left, right) => right.item.value - left.item.value || left.originalIndex - right.originalIndex)
|
|
102
|
+
.map(({ item }) => item);
|
|
103
|
+
const total = sum(sortedItems.map((item) => item.value));
|
|
104
|
+
if (total <= 0) {
|
|
105
|
+
return { tiles: [], hiddenLabelCount: 0, minimumSizedItemCount: 0 };
|
|
106
|
+
}
|
|
107
|
+
const safeMinimumShare = Number.isFinite(minimumVisibleShare)
|
|
108
|
+
? Math.max(0, minimumVisibleShare)
|
|
109
|
+
: DEFAULT_MINIMUM_VISIBLE_SHARE;
|
|
110
|
+
const safeMinimumLabelWidth = Number.isFinite(minimumLabelWidth)
|
|
111
|
+
? Math.max(0, minimumLabelWidth)
|
|
112
|
+
: DEFAULT_MINIMUM_LABEL_WIDTH;
|
|
113
|
+
const safeMinimumLabelHeight = Number.isFinite(minimumLabelHeight)
|
|
114
|
+
? Math.max(0, minimumLabelHeight)
|
|
115
|
+
: DEFAULT_MINIMUM_LABEL_HEIGHT;
|
|
116
|
+
const exactShares = sortedItems.map((item) => item.value / total);
|
|
117
|
+
const visualShares = exactShares.map((share) => Math.max(share, safeMinimumShare));
|
|
118
|
+
const areaScale = (width * height) / sum(visualShares);
|
|
119
|
+
const rectangles = squarify(visualShares.map((share) => share * areaScale), width, height);
|
|
120
|
+
let hiddenLabelCount = 0;
|
|
121
|
+
let minimumSizedItemCount = 0;
|
|
122
|
+
const tiles = rectangles.map((rectangle, index) => {
|
|
123
|
+
const share = exactShares[index];
|
|
124
|
+
const isMinimumSized = share < safeMinimumShare;
|
|
125
|
+
const canShowLabel = rectangle.width >= safeMinimumLabelWidth &&
|
|
126
|
+
rectangle.height >= safeMinimumLabelHeight;
|
|
127
|
+
if (isMinimumSized)
|
|
128
|
+
minimumSizedItemCount += 1;
|
|
129
|
+
if (!canShowLabel)
|
|
130
|
+
hiddenLabelCount += 1;
|
|
131
|
+
return {
|
|
132
|
+
item: sortedItems[index],
|
|
133
|
+
rectangle,
|
|
134
|
+
share,
|
|
135
|
+
isMinimumSized,
|
|
136
|
+
canShowLabel,
|
|
137
|
+
};
|
|
138
|
+
});
|
|
139
|
+
return { tiles, hiddenLabelCount, minimumSizedItemCount };
|
|
140
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presentation-ready chart data schemas.
|
|
3
|
+
*
|
|
4
|
+
* These types intentionally contain no protocol rows or application view
|
|
5
|
+
* models. Callers translate domain data once, then the chart components only
|
|
6
|
+
* decide how to render it.
|
|
7
|
+
*/
|
|
8
|
+
export interface CalendarHeatmapCellData {
|
|
9
|
+
/** Stable key, normally an ISO calendar date. */
|
|
10
|
+
readonly id: string;
|
|
11
|
+
/** Non-negative magnitude. `null` means the period is not available yet. */
|
|
12
|
+
readonly value: number | null;
|
|
13
|
+
/** Human-readable description used for the native tooltip. */
|
|
14
|
+
readonly label: string;
|
|
15
|
+
}
|
|
16
|
+
export interface CalendarHeatmapWeekData {
|
|
17
|
+
/** Stable key for the column, normally the week's opening date. */
|
|
18
|
+
readonly id: string;
|
|
19
|
+
/** Optional axis label displayed above this column. */
|
|
20
|
+
readonly label?: string | undefined;
|
|
21
|
+
/** Ordered cells within the week. The app supplies Sunday through Saturday. */
|
|
22
|
+
readonly days: readonly CalendarHeatmapCellData[];
|
|
23
|
+
}
|
|
24
|
+
export interface CalendarHeatmapData {
|
|
25
|
+
readonly weeks: readonly CalendarHeatmapWeekData[];
|
|
26
|
+
}
|
|
27
|
+
export interface StreamgraphDomainPointData {
|
|
28
|
+
/** Stable x-axis key, such as an ISO date. */
|
|
29
|
+
readonly id: string;
|
|
30
|
+
/** Optional visible tick label for this point. */
|
|
31
|
+
readonly label?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface StreamgraphSeriesData {
|
|
34
|
+
/** Stable identifier, unique within the chart. */
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly label: string;
|
|
37
|
+
/** Any valid CSS color value. */
|
|
38
|
+
readonly color: string;
|
|
39
|
+
/**
|
|
40
|
+
* Magnitudes aligned by index with `StreamgraphData.domain`.
|
|
41
|
+
* Missing, non-finite, or negative values are rendered as zero.
|
|
42
|
+
*/
|
|
43
|
+
readonly values: readonly number[];
|
|
44
|
+
}
|
|
45
|
+
export interface StreamgraphData {
|
|
46
|
+
/** Ordered x-axis shared by every series. Point ids must be unique. */
|
|
47
|
+
readonly domain: readonly StreamgraphDomainPointData[];
|
|
48
|
+
/** Series values must use one consistent unit within a chart. */
|
|
49
|
+
readonly series: readonly StreamgraphSeriesData[];
|
|
50
|
+
}
|
|
51
|
+
export interface TreemapItemData {
|
|
52
|
+
/** Stable identifier, unique within the chart. */
|
|
53
|
+
readonly id: string;
|
|
54
|
+
readonly label: string;
|
|
55
|
+
/** Non-negative magnitude used to calculate the item's area. */
|
|
56
|
+
readonly value: number;
|
|
57
|
+
/** Any valid CSS color value. */
|
|
58
|
+
readonly color: string;
|
|
59
|
+
/** The value is a lower bound (part of it could not be measured); the
|
|
60
|
+
* chart marks it rather than presenting it as exact. */
|
|
61
|
+
readonly partial?: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface TreemapData {
|
|
64
|
+
readonly items: readonly TreemapItemData[];
|
|
65
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { HostPort } from "../../ports.js";
|
|
2
|
+
/** Browser discovery and launches stay with the host. This component mounts
|
|
3
|
+
* only for a browser-readable file, so other viewers never scan the machine. */
|
|
4
|
+
export declare function FileViewerBrowsers({ name, bytes, listBrowsers, openInBrowser, onReceipt }: {
|
|
5
|
+
name: string;
|
|
6
|
+
bytes: Uint8Array | null;
|
|
7
|
+
listBrowsers: HostPort["browsers"] | undefined;
|
|
8
|
+
openInBrowser: HostPort["openInBrowser"];
|
|
9
|
+
onReceipt: ((text: string) => void) | undefined;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { ExternalGlyph } from "../../lib/icons.js";
|
|
4
|
+
/** Browser discovery and launches stay with the host. This component mounts
|
|
5
|
+
* only for a browser-readable file, so other viewers never scan the machine. */
|
|
6
|
+
export function FileViewerBrowsers({ name, bytes, listBrowsers, openInBrowser, onReceipt }) {
|
|
7
|
+
const [list, setList] = useState({ status: "loading" });
|
|
8
|
+
const [attempt, setAttempt] = useState(0);
|
|
9
|
+
const [pending, setPending] = useState(null);
|
|
10
|
+
const launching = useRef(false);
|
|
11
|
+
const mounted = useRef(true);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
mounted.current = true;
|
|
14
|
+
return () => { mounted.current = false; };
|
|
15
|
+
}, []);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
if (!listBrowsers)
|
|
18
|
+
return;
|
|
19
|
+
let alive = true;
|
|
20
|
+
let generation = 0;
|
|
21
|
+
const refresh = async () => {
|
|
22
|
+
const current = ++generation;
|
|
23
|
+
try {
|
|
24
|
+
const browsers = await listBrowsers();
|
|
25
|
+
if (alive && current === generation)
|
|
26
|
+
setList({ status: "ready", browsers });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
if (alive && current === generation)
|
|
30
|
+
setList({ status: "failed" });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
setList({ status: "loading" });
|
|
34
|
+
void refresh();
|
|
35
|
+
const focus = () => { void refresh(); };
|
|
36
|
+
window.addEventListener("focus", focus);
|
|
37
|
+
return () => { alive = false; window.removeEventListener("focus", focus); };
|
|
38
|
+
}, [listBrowsers, attempt]);
|
|
39
|
+
const open = async (browser) => {
|
|
40
|
+
if (bytes === null || launching.current)
|
|
41
|
+
return;
|
|
42
|
+
launching.current = true;
|
|
43
|
+
setPending(browser?.id ?? "default");
|
|
44
|
+
const label = browser?.name ?? "your browser";
|
|
45
|
+
let opened = false;
|
|
46
|
+
try {
|
|
47
|
+
opened = await openInBrowser(name, bytes, browser?.id);
|
|
48
|
+
}
|
|
49
|
+
catch { /* reported below */ }
|
|
50
|
+
if (mounted.current) {
|
|
51
|
+
launching.current = false;
|
|
52
|
+
setPending(null);
|
|
53
|
+
onReceipt?.(opened ? `opened in ${label}` : `could not open in ${label}`);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
// Keep the existing capability for embedded hosts that do not enumerate
|
|
57
|
+
// browsers. A host returning an empty list never invents a default button.
|
|
58
|
+
if (!listBrowsers)
|
|
59
|
+
return _jsx("button", { className: "ah-iconbtn", type: "button", title: "Open in browser", "aria-label": "Open in browser", disabled: bytes === null || pending !== null, onClick: () => { void open(); }, children: _jsx(ExternalGlyph, {}) });
|
|
60
|
+
return _jsx("div", { className: "ah-fileview__browsers", role: "group", "aria-label": "Open in browser", children: list.status === "loading" ? _jsx("span", { className: "ah-fileview__browser-status", role: "status", children: "Finding browsers\u2026" })
|
|
61
|
+
: list.status === "failed" ? _jsx("button", { className: "ah-iconbtn", type: "button", title: "Could not find browsers. Retry", "aria-label": "Retry browser detection", onClick: () => setAttempt(value => value + 1), children: _jsx(ExternalGlyph, {}) })
|
|
62
|
+
: list.browsers.length === 0 ? _jsx("span", { className: "ah-fileview__browser-status", role: "status", children: "No supported browsers found" })
|
|
63
|
+
: list.browsers.map(browser => _jsx("button", { className: "ah-iconbtn ah-fileview__browser", type: "button", "data-browser-id": browser.id, title: `Open in ${browser.name}`, "aria-label": `Open in ${browser.name}`, "aria-busy": pending === browser.id, disabled: bytes === null || pending !== null, onClick: () => { void open(browser); }, children: browser.icon ? _jsx("img", { src: browser.icon, alt: "", draggable: false })
|
|
64
|
+
: _jsx("span", { className: "ah-fileview__browser-fallback", "aria-hidden": "true", children: browser.name.charAt(0) }) }, browser.id)) });
|
|
65
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { KeyboardEventHandler, ReactNode } from "react";
|
|
2
|
+
import type { HostPort } from "../../ports.js";
|
|
3
|
+
import type { FileHandle, FileSource, ViewBodyProps } from "./types.js";
|
|
4
|
+
export interface FileViewerDialogProps {
|
|
5
|
+
readonly renderMarkdown?: ViewBodyProps["renderMarkdown"];
|
|
6
|
+
/** The file being read, or `null` for a closed dialog. */
|
|
7
|
+
readonly handle: FileHandle | null;
|
|
8
|
+
/** Where bytes come from. Must be stable across renders. */
|
|
9
|
+
readonly source: FileSource;
|
|
10
|
+
/** The project's §2.6 accent, so the file wears the colour of the thing it
|
|
11
|
+
* belongs to — the same marker its project and session already carry. */
|
|
12
|
+
readonly tint: string | null;
|
|
13
|
+
readonly homeDirectory: string | null;
|
|
14
|
+
readonly onClose: () => void;
|
|
15
|
+
/** The room's receipt line, so an act is never silent. */
|
|
16
|
+
readonly onReceipt?: (text: string) => void;
|
|
17
|
+
/** Desktop browser capabilities, absent in a plain browser tab. */
|
|
18
|
+
readonly openInBrowser: HostPort["openInBrowser"] | null;
|
|
19
|
+
readonly listBrowsers?: HostPort["browsers"] | undefined;
|
|
20
|
+
/** Open this handle as a CHANGE rather than as a file. The registry reads
|
|
21
|
+
* kinds off a name, and a name cannot say which of the two acts is being
|
|
22
|
+
* served. */
|
|
23
|
+
readonly asDiff?: boolean;
|
|
24
|
+
readonly gallery?: {
|
|
25
|
+
readonly key: string;
|
|
26
|
+
readonly footer: ReactNode;
|
|
27
|
+
readonly previous: (() => void) | null;
|
|
28
|
+
readonly next: (() => void) | null;
|
|
29
|
+
readonly onKeyDown: KeyboardEventHandler<HTMLDivElement>;
|
|
30
|
+
readonly onRetry: () => void;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare function FileViewerDialog({ handle, source, tint, homeDirectory, onClose, onReceipt, openInBrowser, listBrowsers, asDiff, renderMarkdown, gallery, }: FileViewerDialogProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
// The popup. A modal, on purpose: reading a file is a DETOUR from reading
|
|
3
|
+
// history, and a surface you must dismiss says that, where a fifth column
|
|
4
|
+
// would quietly become a second reading surface competing with the measure.
|
|
5
|
+
// It also keeps H-2 — the room's columns change content, never geometry, and
|
|
6
|
+
// a layer above them changes neither.
|
|
7
|
+
//
|
|
8
|
+
// The material is the one the design system already provisioned and nothing
|
|
9
|
+
// had yet spent: `--vf-shadow-dialog`, `--vf-backdrop-dim`, and the stage
|
|
10
|
+
// receding to 0.98 behind it (DESIGN.md §5 and motion law M5). No new tokens.
|
|
11
|
+
//
|
|
12
|
+
// Radix owns the mechanics a modal must not get wrong — focus trap, focus
|
|
13
|
+
// restore, Escape, scroll lock, `aria-modal`, the portal. It was already a
|
|
14
|
+
// dependency of this app and had no user; this is its user.
|
|
15
|
+
import { useEffect, useState } from "react";
|
|
16
|
+
import { DialogShell } from "../ui/index.js";
|
|
17
|
+
import { diffKind, imageKind, resolveKind } from "./registry.js";
|
|
18
|
+
import { useFileContent } from "./use-file-content.js";
|
|
19
|
+
import { FileViewerBrowsers } from "./FileViewerBrowsers.js";
|
|
20
|
+
import { FileViewerHeader } from "./FileViewerHeader.js";
|
|
21
|
+
import { fmtBytes } from "../../lib/format.js";
|
|
22
|
+
export function FileViewerDialog({ handle, source, tint, homeDirectory, onClose, onReceipt, openInBrowser, listBrowsers, asDiff = false, renderMarkdown, gallery, }) {
|
|
23
|
+
const [attempt, setAttempt] = useState(0);
|
|
24
|
+
const content = useFileContent(handle, source, asDiff, attempt, Boolean(gallery));
|
|
25
|
+
const retry = () => { gallery?.onRetry(); setAttempt(value => value + 1); };
|
|
26
|
+
// The reader's choice of mode, kept only while it names a mode the current
|
|
27
|
+
// view actually has — so opening a `.md` after an image does not land on a
|
|
28
|
+
// switch position that no longer exists.
|
|
29
|
+
const [chosen, setChosen] = useState(null);
|
|
30
|
+
const [fullscreen, setFullscreen] = useState(false);
|
|
31
|
+
const fileKey = gallery?.key ?? handle?.key ?? null;
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
setChosen(null);
|
|
34
|
+
setFullscreen(false);
|
|
35
|
+
}, [fileKey]);
|
|
36
|
+
if (handle === null)
|
|
37
|
+
return null;
|
|
38
|
+
// Before the bytes land, the name is the only evidence there is — and it is
|
|
39
|
+
// what the reader clicked, so the band can be right about it immediately.
|
|
40
|
+
// A read that disagrees (a `.png` full of text, a `.json` full of NULs)
|
|
41
|
+
// corrects the chip when it resolves.
|
|
42
|
+
const kind = content.status === "ready"
|
|
43
|
+
? content.file.kind
|
|
44
|
+
: asDiff
|
|
45
|
+
? diffKind(handle.name)
|
|
46
|
+
: gallery ? imageKind(handle.name) : resolveKind(handle.name);
|
|
47
|
+
const modes = kind.viewer.modes ?? [];
|
|
48
|
+
const mode = chosen !== null && modes.some((m) => m.id === chosen) ? chosen : (modes[0]?.id ?? "");
|
|
49
|
+
const perform = (action) => {
|
|
50
|
+
if (action === "copy-path") {
|
|
51
|
+
const path = handle.path;
|
|
52
|
+
if (path === null)
|
|
53
|
+
return null;
|
|
54
|
+
return {
|
|
55
|
+
id: action,
|
|
56
|
+
label: "Copy path",
|
|
57
|
+
glyph: "copy",
|
|
58
|
+
run: () => {
|
|
59
|
+
void navigator.clipboard
|
|
60
|
+
.writeText(path)
|
|
61
|
+
.then(() => onReceipt?.("path copied"))
|
|
62
|
+
.catch(() => onReceipt?.("the clipboard refused"));
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
};
|
|
68
|
+
const actions = (kind.viewer.actions ?? [])
|
|
69
|
+
.map(perform)
|
|
70
|
+
.filter((action) => action !== null);
|
|
71
|
+
const browserBytes = content.status === "ready" && content.file.handle.key === handle.key
|
|
72
|
+
? content.file.bytes : content.status === "empty" ? new Uint8Array(0) : null;
|
|
73
|
+
const browserActions = openInBrowser && kind.viewer.actions?.includes("open-in-browser")
|
|
74
|
+
? _jsx(FileViewerBrowsers, { name: handle.name, bytes: browserBytes, listBrowsers: listBrowsers, openInBrowser: openInBrowser, onReceipt: onReceipt }, handle.key)
|
|
75
|
+
: null;
|
|
76
|
+
const Body = kind.viewer.Body;
|
|
77
|
+
return (_jsxs(DialogShell, { title: `${handle.name}${handle.path !== null ? ` — ${handle.path}` : ""}`, onClose: onClose, fullscreen: fullscreen, onExitFullscreen: () => setFullscreen(false), onKeyDown: gallery?.onKeyDown, children: [_jsx(FileViewerHeader, { handle: handle.size < 0 && content.status === "ready" && !content.file.image ? { ...handle, size: content.file.bytes.byteLength } : handle, kind: kind, tint: tint, homeDirectory: homeDirectory, modes: modes, mode: mode, onModeChange: setChosen, actions: actions, browserActions: browserActions, fullscreen: fullscreen, onToggleFullscreen: () => setFullscreen((current) => !current), onClose: onClose }), _jsxs("div", { className: "ah-fileview__body", "data-gallery": gallery?.footer ? "" : undefined, children: [content.status === "ready" ? (_jsx(Body, { file: content.file, mode: mode, renderMarkdown: renderMarkdown, onRetry: gallery ? retry : undefined }, `${handle.key}:${attempt}`)) : content.status === "reading" ? (
|
|
78
|
+
// Styled to appear only if the read is slow enough to be worth
|
|
79
|
+
// mentioning; a local file that lands in three milliseconds
|
|
80
|
+
// never flashes this.
|
|
81
|
+
_jsxs("div", { className: "ah-fileview__state", "data-waiting": true, children: [_jsx("b", { children: gallery ? "Loading image…" : "Reading" }), !gallery && _jsxs("span", { children: [fmtBytes(handle.size), " from the worktree engine."] })] })) : content.status === "empty" ? (_jsxs("div", { className: "ah-fileview__state", children: [_jsx("b", { children: "This file is empty" }), _jsx("span", { children: "Zero bytes on disk. There is nothing to show, and nothing is wrong." })] })) : content.status === "too-large" ? (_jsxs("div", { className: "ah-fileview__state", children: [_jsx("b", { children: "Too large to open here" }), _jsxs("span", { children: [handle.size < 0 ? "The image exceeds" : `${fmtBytes(handle.size)}, against`, " a ", fmtBytes(content.limit), " limit for a", " ", kind.label, " file. The limit is here so the window stays answerable."] })] })) : (_jsxs("div", { className: "ah-fileview__state", children: [_jsx("b", { children: "Could not read this file" }), _jsx("span", { children: content.reason }), gallery && _jsx("button", { className: "ah-fileview__retry", type: "button", onClick: retry, children: "Retry" })] })), gallery?.footer && _jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "ah-image-gallery__step ah-image-gallery__step--previous", "aria-label": "Previous image", title: "Previous image (\u2190)", "aria-disabled": gallery.previous === null, onClick: () => gallery.previous?.(), children: "\u2039" }), _jsx("button", { type: "button", className: "ah-image-gallery__step ah-image-gallery__step--next", "aria-label": "Next image", title: "Next image (\u2192)", "aria-disabled": gallery.next === null, onClick: () => gallery.next?.(), children: "\u203A" })] })] }), gallery?.footer] }));
|
|
82
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { FileHandle, FileKind, ViewMode } from "./types.js";
|
|
3
|
+
export interface HeaderAction {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly glyph: "copy" | "external";
|
|
7
|
+
readonly disabled?: boolean;
|
|
8
|
+
readonly run: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface FileViewerHeaderProps {
|
|
11
|
+
readonly handle: FileHandle;
|
|
12
|
+
/** What the registry decided, or `null` before the read resolves it — the
|
|
13
|
+
* band names the file either way. */
|
|
14
|
+
readonly kind: FileKind | null;
|
|
15
|
+
readonly tint: string | null;
|
|
16
|
+
readonly homeDirectory: string | null;
|
|
17
|
+
readonly modes: readonly ViewMode[];
|
|
18
|
+
readonly mode: string;
|
|
19
|
+
readonly onModeChange: (mode: string) => void;
|
|
20
|
+
readonly actions: readonly HeaderAction[];
|
|
21
|
+
readonly browserActions?: ReactNode;
|
|
22
|
+
readonly fullscreen: boolean;
|
|
23
|
+
readonly onToggleFullscreen: () => void;
|
|
24
|
+
readonly onClose: () => void;
|
|
25
|
+
}
|
|
26
|
+
export declare function FileViewerHeader({ handle, kind, tint, homeDirectory, modes, mode, onModeChange, actions, browserActions, fullscreen, onToggleFullscreen, onClose, }: FileViewerHeaderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ColumnHeading } from "../ui/index.js";
|
|
3
|
+
import { SegmentedControl } from "../ui/index.js";
|
|
4
|
+
import { CloseGlyph, CopyGlyph, ExitFullscreenGlyph, ExternalGlyph, FullscreenGlyph, } from "../../lib/icons.js";
|
|
5
|
+
import { fmtBytes, tildePath } from "../../lib/format.js";
|
|
6
|
+
/** The folder the file is in, tilde-abbreviated. `null` when the caller could
|
|
7
|
+
* not derive a path, which is a missing line rather than a wrong one. */
|
|
8
|
+
function directoryOf(path, home) {
|
|
9
|
+
if (path === null)
|
|
10
|
+
return null;
|
|
11
|
+
const cut = path.lastIndexOf("/");
|
|
12
|
+
return cut <= 0 ? path : tildePath(path.slice(0, cut), home);
|
|
13
|
+
}
|
|
14
|
+
export function FileViewerHeader({ handle, kind, tint, homeDirectory, modes, mode, onModeChange, actions, browserActions, fullscreen, onToggleFullscreen, onClose, }) {
|
|
15
|
+
const directory = directoryOf(handle.path, homeDirectory);
|
|
16
|
+
return (_jsxs("header", { className: "ah-fileview__head", children: [_jsxs("div", { className: "ah-fileview__title", children: [_jsx(ColumnHeading, { name: handle.name, tint: tint, mono: true, title: handle.path ?? undefined }), kind !== null && _jsx("span", { className: "ah-fileview__kind", children: kind.label }), handle.size > 0 && _jsx("span", { className: "ah-caption tnum", children: fmtBytes(handle.size) })] }), _jsxs("div", { className: "ah-fileview__acts", children: [modes.length > 1 && (_jsx(SegmentedControl, { className: "ah-tabseg", accessibleLabel: "How to read this file", value: mode, options: modes.map((option) => ({ value: option.id, label: option.label })), onValueChange: onModeChange })), browserActions, actions.map((action) => (_jsx("button", { className: "ah-iconbtn", type: "button", title: action.label, "aria-label": action.label, disabled: action.disabled, onClick: action.run, children: action.glyph === "copy" ? _jsx(CopyGlyph, {}) : _jsx(ExternalGlyph, {}) }, action.id))), _jsx("button", { className: "ah-iconbtn", type: "button", title: fullscreen ? "Exit full screen (Esc)" : "View full screen", "aria-label": fullscreen ? "Exit full screen" : "View full screen", "aria-pressed": fullscreen, onClick: onToggleFullscreen, children: fullscreen ? _jsx(ExitFullscreenGlyph, {}) : _jsx(FullscreenGlyph, {}) }), _jsx("button", { className: "ah-iconbtn", type: "button", title: "Close (Esc)", "aria-label": "Close", onClick: onClose, children: _jsx(CloseGlyph, {}) })] }), directory !== null && (_jsx("p", { className: "ah-fileview__path ah-idname", title: handle.path ?? undefined, children: _jsx("bdi", { children: directory }) }))] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FileViewerDialogProps } from "./FileViewerDialog.js";
|
|
2
|
+
import type { FileHandle } from "./types.js";
|
|
3
|
+
import "../../styles/image-gallery.css";
|
|
4
|
+
export interface GalleryImage {
|
|
5
|
+
readonly key: string;
|
|
6
|
+
readonly label: string;
|
|
7
|
+
/** Unrecorded/reference-only images keep their place without an invented read. */
|
|
8
|
+
readonly handle: FileHandle | null;
|
|
9
|
+
}
|
|
10
|
+
export interface ImageGalleryDialogProps extends Omit<FileViewerDialogProps, "handle" | "gallery" | "asDiff" | "renderMarkdown"> {
|
|
11
|
+
readonly images: readonly GalleryImage[];
|
|
12
|
+
readonly initialKey: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function ImageGalleryDialog({ images, initialKey, source, ...props }: ImageGalleryDialogProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { FileViewerDialog } from "./FileViewerDialog.js";
|
|
4
|
+
import { createGallerySource } from "./gallery-source.js";
|
|
5
|
+
import { useFileContent } from "./use-file-content.js";
|
|
6
|
+
import { useImageUrl, useImageVisibility } from "./image-resource.js";
|
|
7
|
+
import "../../styles/image-gallery.css";
|
|
8
|
+
function Thumbnail({ image, source, selected, index, revision, onSelect }) {
|
|
9
|
+
const ref = useRef(null);
|
|
10
|
+
const visible = useImageVisibility(ref, true);
|
|
11
|
+
const [bytesUrl, setBytesUrl] = useState(null);
|
|
12
|
+
const [failed, setFailed] = useState(false);
|
|
13
|
+
const content = useFileContent(visible || selected ? image.handle : null, source, false, revision, true);
|
|
14
|
+
const addressed = useImageUrl(content.status === "ready" ? content.file.image : null, "rail", revision);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (content.status !== "ready") {
|
|
17
|
+
setBytesUrl(null);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
setFailed(false);
|
|
21
|
+
if (content.file.image) {
|
|
22
|
+
setBytesUrl(null);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const objectUrl = URL.createObjectURL(new Blob([content.file.bytes], { type: content.file.kind.mime ?? "application/octet-stream" }));
|
|
26
|
+
setBytesUrl(objectUrl);
|
|
27
|
+
return () => URL.revokeObjectURL(objectUrl);
|
|
28
|
+
}, [content, revision]);
|
|
29
|
+
const url = content.status === "ready" && content.file.image ? addressed.url : bytesUrl;
|
|
30
|
+
const label = `${image.label}${image.handle ? ` — ${image.handle.name}` : " — Image unavailable"}`;
|
|
31
|
+
return _jsx("button", { ref: ref, className: "ah-image-gallery__thumb", type: "button", "data-image-key": image.key, "aria-label": `View ${label}`, title: label, disabled: image.handle === null, "aria-current": selected ? "true" : undefined, tabIndex: selected ? 0 : -1, onClick: onSelect, children: (visible || selected) && url && !failed ? _jsx("img", { src: url, alt: "", decoding: "async", onError: () => setFailed(true) })
|
|
32
|
+
: _jsx("span", { "aria-hidden": "true", children: image.handle === null ? "▧" : index + 1 }) });
|
|
33
|
+
}
|
|
34
|
+
export function ImageGalleryDialog({ images, initialKey, source, ...props }) {
|
|
35
|
+
const id = useId();
|
|
36
|
+
const [selectedKey, setSelectedKey] = useState(initialKey);
|
|
37
|
+
const [revision, setRevision] = useState(0);
|
|
38
|
+
const shared = useMemo(() => createGallerySource(source), [source]);
|
|
39
|
+
useEffect(() => () => shared.dispose(), [shared]);
|
|
40
|
+
const rail = useRef(null);
|
|
41
|
+
const selected = images.findIndex(image => image.key === selectedKey);
|
|
42
|
+
const current = images[selected];
|
|
43
|
+
const available = images.flatMap((image, index) => image.handle === null ? [] : [index]);
|
|
44
|
+
const position = available.indexOf(selected);
|
|
45
|
+
const previous = available[position - 1] ?? -1;
|
|
46
|
+
const next = available[position + 1] ?? -1;
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
const node = rail.current;
|
|
49
|
+
const thumb = node?.querySelector('[aria-current="true"]');
|
|
50
|
+
if (!node || !thumb)
|
|
51
|
+
return;
|
|
52
|
+
const reveal = () => {
|
|
53
|
+
const bounds = node.getBoundingClientRect(), target = thumb.getBoundingClientRect();
|
|
54
|
+
if (target.left < bounds.left + 4)
|
|
55
|
+
node.scrollLeft += target.left - bounds.left - 4;
|
|
56
|
+
else if (target.right > bounds.right - 4)
|
|
57
|
+
node.scrollLeft += target.right - bounds.right + 4;
|
|
58
|
+
};
|
|
59
|
+
reveal();
|
|
60
|
+
const observer = new ResizeObserver(reveal);
|
|
61
|
+
observer.observe(node);
|
|
62
|
+
return () => observer.disconnect();
|
|
63
|
+
}, [selectedKey]);
|
|
64
|
+
if (!current?.handle)
|
|
65
|
+
return null;
|
|
66
|
+
const select = (index, focus = false) => {
|
|
67
|
+
const image = images[index];
|
|
68
|
+
if (!image?.handle)
|
|
69
|
+
return;
|
|
70
|
+
setSelectedKey(image.key);
|
|
71
|
+
if (focus)
|
|
72
|
+
rail.current?.querySelectorAll("button")[index]?.focus({ preventScroll: true });
|
|
73
|
+
};
|
|
74
|
+
const navigate = (event) => {
|
|
75
|
+
if (images.length <= 1)
|
|
76
|
+
return;
|
|
77
|
+
if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)
|
|
78
|
+
return;
|
|
79
|
+
if (!(event.target instanceof Element) || event.target.closest('input, textarea, select, [contenteditable="true"], .ah-code-shell'))
|
|
80
|
+
return;
|
|
81
|
+
const index = event.key === "ArrowLeft" ? previous : event.key === "ArrowRight" ? next
|
|
82
|
+
: event.key === "Home" ? available[0] ?? -1
|
|
83
|
+
: event.key === "End" ? available.at(-1) ?? -1 : null;
|
|
84
|
+
if (index === null)
|
|
85
|
+
return;
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
event.stopPropagation();
|
|
88
|
+
select(index, Boolean(event.target.closest(".ah-image-gallery__rail")));
|
|
89
|
+
};
|
|
90
|
+
return _jsx(FileViewerDialog, { ...props, handle: current.handle, source: shared.source, gallery: { key: id, onKeyDown: navigate, onRetry: () => {
|
|
91
|
+
shared.invalidate(current.handle.key);
|
|
92
|
+
setRevision(value => value + 1);
|
|
93
|
+
},
|
|
94
|
+
previous: previous >= 0 ? () => select(previous) : null,
|
|
95
|
+
next: next >= 0 ? () => select(next) : null,
|
|
96
|
+
footer: images.length > 1 ? _jsxs("footer", { className: "ah-image-gallery", children: [_jsxs("div", { className: "ah-image-gallery__caption", children: [_jsx("span", { children: "Images in this row" }), _jsxs("span", { role: "status", "aria-live": "polite", "aria-atomic": "true", children: [selected + 1, " / ", images.length] })] }), _jsx("div", { className: "ah-image-gallery__rail", ref: rail, role: "group", "aria-label": "Images in this row", children: images.map((image, index) => _jsx(Thumbnail, { image: image, source: shared.source, index: index, revision: revision, selected: index === selected, onSelect: () => select(index) }, image.key)) })] }) : null,
|
|
97
|
+
} });
|
|
98
|
+
}
|