@joewinke/jatui 0.1.25 → 0.1.26

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.
Files changed (242) hide show
  1. package/dist/actions/collapse.d.ts +80 -0
  2. package/dist/actions/collapse.js +134 -0
  3. package/dist/actions/railNav.d.ts +167 -0
  4. package/dist/actions/railNav.js +351 -0
  5. package/dist/components/AnnotationLayer.svelte +108 -0
  6. package/dist/components/AnnotationLayer.svelte.d.ts +12 -0
  7. package/dist/components/AnnotationPanel.svelte +319 -0
  8. package/dist/components/AnnotationPanel.svelte.d.ts +13 -0
  9. package/dist/components/AudioWaveform.svelte +701 -0
  10. package/dist/components/AudioWaveform.svelte.d.ts +19 -0
  11. package/dist/components/AvailabilityModal.svelte +177 -0
  12. package/dist/components/AvailabilityModal.svelte.d.ts +14 -0
  13. package/dist/components/AvatarUpload.svelte +234 -0
  14. package/dist/components/AvatarUpload.svelte.d.ts +23 -0
  15. package/dist/components/Badge.svelte +38 -0
  16. package/dist/components/Badge.svelte.d.ts +14 -0
  17. package/dist/components/BookingForm.svelte +278 -0
  18. package/dist/components/BookingForm.svelte.d.ts +37 -0
  19. package/dist/components/BurndownChart.svelte +778 -0
  20. package/dist/components/BurndownChart.svelte.d.ts +17 -0
  21. package/dist/components/Button.svelte +81 -0
  22. package/dist/components/Button.svelte.d.ts +21 -0
  23. package/dist/components/CalendarPicker.svelte +284 -0
  24. package/dist/components/CalendarPicker.svelte.d.ts +22 -0
  25. package/dist/components/Card.svelte +67 -0
  26. package/dist/components/Card.svelte.d.ts +17 -0
  27. package/dist/components/CharacterCounter.svelte +82 -0
  28. package/dist/components/CharacterCounter.svelte.d.ts +10 -0
  29. package/dist/components/ChipInput.svelte +609 -0
  30. package/dist/components/ChipInput.svelte.d.ts +55 -0
  31. package/dist/components/Collapse.svelte +91 -0
  32. package/dist/components/Collapse.svelte.d.ts +18 -0
  33. package/dist/components/ColorSelector.svelte +167 -0
  34. package/dist/components/ColorSelector.svelte.d.ts +9 -0
  35. package/dist/components/CommentThread.svelte +773 -0
  36. package/dist/components/CommentThread.svelte.d.ts +32 -0
  37. package/dist/components/ConfirmDialog.svelte +348 -0
  38. package/dist/components/ConfirmDialog.svelte.d.ts +3 -0
  39. package/dist/components/ConfirmModal.svelte +142 -0
  40. package/dist/components/ConfirmModal.svelte.d.ts +15 -0
  41. package/dist/components/ContextMenu.svelte +188 -0
  42. package/dist/components/ContextMenu.svelte.d.ts +31 -0
  43. package/dist/components/CountdownTimer.svelte +94 -0
  44. package/dist/components/CountdownTimer.svelte.d.ts +10 -0
  45. package/dist/components/DateRangePicker.svelte +194 -0
  46. package/dist/components/DateRangePicker.svelte.d.ts +10 -0
  47. package/dist/components/Drawer.svelte +143 -0
  48. package/dist/components/Drawer.svelte.d.ts +33 -0
  49. package/dist/components/EmojiPicker.svelte +159 -0
  50. package/dist/components/EmojiPicker.svelte.d.ts +8 -0
  51. package/dist/components/EntityPreviewCard.svelte +104 -0
  52. package/dist/components/EntityPreviewCard.svelte.d.ts +9 -0
  53. package/dist/components/FileDropzone.svelte +493 -0
  54. package/dist/components/FileDropzone.svelte.d.ts +40 -0
  55. package/dist/components/FilePicker.svelte +488 -0
  56. package/dist/components/FilePicker.svelte.d.ts +20 -0
  57. package/dist/components/FileThumbnail.svelte +80 -0
  58. package/dist/components/FileThumbnail.svelte.d.ts +12 -0
  59. package/dist/components/FilterDropdown.svelte +203 -0
  60. package/dist/components/FilterDropdown.svelte.d.ts +48 -0
  61. package/dist/components/FloatingActionBar.svelte +354 -0
  62. package/dist/components/FloatingActionBar.svelte.d.ts +51 -0
  63. package/dist/components/GPSTracker.svelte +202 -0
  64. package/dist/components/GPSTracker.svelte.d.ts +34 -0
  65. package/dist/components/HunkDiffView.svelte +348 -0
  66. package/dist/components/HunkDiffView.svelte.d.ts +9 -0
  67. package/dist/components/ImageLightbox.svelte +274 -0
  68. package/dist/components/ImageLightbox.svelte.d.ts +25 -0
  69. package/dist/components/ImageUpload.svelte +147 -0
  70. package/dist/components/ImageUpload.svelte.d.ts +10 -0
  71. package/dist/components/InlineEdit.svelte +291 -0
  72. package/dist/components/InlineEdit.svelte.d.ts +45 -0
  73. package/dist/components/InputDialog.svelte +327 -0
  74. package/dist/components/InputDialog.svelte.d.ts +3 -0
  75. package/dist/components/KeyboardShortcutsOverlay.svelte +296 -0
  76. package/dist/components/KeyboardShortcutsOverlay.svelte.d.ts +42 -0
  77. package/dist/components/LazyImage.svelte +123 -0
  78. package/dist/components/LazyImage.svelte.d.ts +14 -0
  79. package/dist/components/LinkShortener.svelte +274 -0
  80. package/dist/components/LinkShortener.svelte.d.ts +27 -0
  81. package/dist/components/LoadingSpinner.svelte +106 -0
  82. package/dist/components/LoadingSpinner.svelte.d.ts +21 -0
  83. package/dist/components/LocationMap.svelte +186 -0
  84. package/dist/components/LocationMap.svelte.d.ts +11 -0
  85. package/dist/components/MapView.svelte +341 -0
  86. package/dist/components/MapView.svelte.d.ts +20 -0
  87. package/dist/components/MarkupEditor.svelte +485 -0
  88. package/dist/components/MarkupEditor.svelte.d.ts +10 -0
  89. package/dist/components/MarkupOverlay.svelte +55 -0
  90. package/dist/components/MarkupOverlay.svelte.d.ts +8 -0
  91. package/dist/components/MediaWorkbench.svelte +871 -0
  92. package/dist/components/MediaWorkbench.svelte.d.ts +9 -0
  93. package/dist/components/MilestoneCard.svelte +98 -0
  94. package/dist/components/MilestoneCard.svelte.d.ts +16 -0
  95. package/dist/components/MilestoneTimeline.svelte +91 -0
  96. package/dist/components/MilestoneTimeline.svelte.d.ts +15 -0
  97. package/dist/components/Modal.svelte +244 -0
  98. package/dist/components/Modal.svelte.d.ts +35 -0
  99. package/dist/components/PDFViewer.svelte +105 -0
  100. package/dist/components/PDFViewer.svelte.d.ts +9 -0
  101. package/dist/components/PdfThumbnail.svelte +63 -0
  102. package/dist/components/PdfThumbnail.svelte.d.ts +9 -0
  103. package/dist/components/PhoneInput.svelte +212 -0
  104. package/dist/components/PhoneInput.svelte.d.ts +25 -0
  105. package/dist/components/ResizableDivider.svelte +307 -0
  106. package/dist/components/ResizableDivider.svelte.d.ts +26 -0
  107. package/dist/components/ResizablePanel.svelte +305 -0
  108. package/dist/components/ResizablePanel.svelte.d.ts +18 -0
  109. package/dist/components/SearchDropdown.svelte +421 -0
  110. package/dist/components/SearchDropdown.svelte.d.ts +24 -0
  111. package/dist/components/SelectInput.svelte +237 -0
  112. package/dist/components/SelectInput.svelte.d.ts +26 -0
  113. package/dist/components/SidebarUserFooter.svelte +34 -0
  114. package/dist/components/SidebarUserFooter.svelte.d.ts +16 -0
  115. package/dist/components/SignaturePad.svelte +175 -0
  116. package/dist/components/SignaturePad.svelte.d.ts +17 -0
  117. package/dist/components/SmartImageEditor.svelte +720 -0
  118. package/dist/components/SmartImageEditor.svelte.d.ts +24 -0
  119. package/dist/components/SortDropdown.svelte +156 -0
  120. package/dist/components/SortDropdown.svelte.d.ts +40 -0
  121. package/dist/components/Sparkline.svelte +116 -0
  122. package/dist/components/Sparkline.svelte.d.ts +12 -0
  123. package/dist/components/SpeechForm.svelte +114 -0
  124. package/dist/components/SpeechForm.svelte.d.ts +17 -0
  125. package/dist/components/StatusBadge.svelte +157 -0
  126. package/dist/components/StatusBadge.svelte.d.ts +24 -0
  127. package/dist/components/TaskTypeIcon.svelte +54 -0
  128. package/dist/components/TaskTypeIcon.svelte.d.ts +17 -0
  129. package/dist/components/TextArea.svelte +162 -0
  130. package/dist/components/TextArea.svelte.d.ts +30 -0
  131. package/dist/components/TextInput.svelte +131 -0
  132. package/dist/components/TextInput.svelte.d.ts +29 -0
  133. package/dist/components/ThemeSelector.svelte +206 -0
  134. package/dist/components/ThemeSelector.svelte.d.ts +14 -0
  135. package/dist/components/TimeSlotPicker.svelte +162 -0
  136. package/dist/components/TimeSlotPicker.svelte.d.ts +18 -0
  137. package/dist/components/UserAvatar.svelte +72 -0
  138. package/dist/components/UserAvatar.svelte.d.ts +11 -0
  139. package/dist/components/VariablePicker.svelte +170 -0
  140. package/dist/components/VariablePicker.svelte.d.ts +40 -0
  141. package/dist/components/VoicePlayer.svelte +422 -0
  142. package/dist/components/VoicePlayer.svelte.d.ts +18 -0
  143. package/dist/components/linked-columns/LinkedColumns.svelte +526 -0
  144. package/dist/components/linked-columns/LinkedColumns.svelte.d.ts +130 -0
  145. package/dist/components/markup.d.ts +47 -0
  146. package/dist/components/markup.js +201 -0
  147. package/dist/components/messaging/Avatar.svelte +81 -0
  148. package/dist/components/messaging/Avatar.svelte.d.ts +11 -0
  149. package/dist/components/messaging/ChannelInfoModal.svelte +163 -0
  150. package/dist/components/messaging/ChannelInfoModal.svelte.d.ts +18 -0
  151. package/dist/components/messaging/ChannelList.svelte +107 -0
  152. package/dist/components/messaging/ChannelList.svelte.d.ts +11 -0
  153. package/dist/components/messaging/ChannelMemberAvatarStack.svelte +69 -0
  154. package/dist/components/messaging/ChannelMemberAvatarStack.svelte.d.ts +11 -0
  155. package/dist/components/messaging/ChannelMembersModal.svelte +182 -0
  156. package/dist/components/messaging/ChannelMembersModal.svelte.d.ts +16 -0
  157. package/dist/components/messaging/CreateChannelModal.svelte +190 -0
  158. package/dist/components/messaging/CreateChannelModal.svelte.d.ts +14 -0
  159. package/dist/components/messaging/DirectMessageList.svelte +145 -0
  160. package/dist/components/messaging/DirectMessageList.svelte.d.ts +12 -0
  161. package/dist/components/messaging/EmojiSelector.svelte +261 -0
  162. package/dist/components/messaging/EmojiSelector.svelte.d.ts +19 -0
  163. package/dist/components/messaging/MentionAutocomplete.svelte +193 -0
  164. package/dist/components/messaging/MentionAutocomplete.svelte.d.ts +17 -0
  165. package/dist/components/messaging/MessageAttachment.svelte +273 -0
  166. package/dist/components/messaging/MessageAttachment.svelte.d.ts +13 -0
  167. package/dist/components/messaging/MessageAttachmentUpload.svelte +243 -0
  168. package/dist/components/messaging/MessageAttachmentUpload.svelte.d.ts +19 -0
  169. package/dist/components/messaging/MessageInput.svelte +452 -0
  170. package/dist/components/messaging/MessageInput.svelte.d.ts +28 -0
  171. package/dist/components/messaging/MessageItem.svelte +341 -0
  172. package/dist/components/messaging/MessageItem.svelte.d.ts +41 -0
  173. package/dist/components/messaging/MessageThread.svelte +306 -0
  174. package/dist/components/messaging/MessageThread.svelte.d.ts +30 -0
  175. package/dist/components/messaging/NotificationSettingsModal.svelte +234 -0
  176. package/dist/components/messaging/NotificationSettingsModal.svelte.d.ts +13 -0
  177. package/dist/components/messaging/QuotedMessageDisplay.svelte +123 -0
  178. package/dist/components/messaging/QuotedMessageDisplay.svelte.d.ts +10 -0
  179. package/dist/components/messaging/StartDMModal.svelte +100 -0
  180. package/dist/components/messaging/StartDMModal.svelte.d.ts +14 -0
  181. package/dist/components/messaging/ThreadPanel.svelte +153 -0
  182. package/dist/components/messaging/ThreadPanel.svelte.d.ts +16 -0
  183. package/dist/components/pipeline/DESIGN.md +361 -0
  184. package/dist/components/pipeline/Pipeline.svelte +391 -0
  185. package/dist/components/pipeline/Pipeline.svelte.d.ts +28 -0
  186. package/dist/components/pipeline/PipelineCard.svelte +94 -0
  187. package/dist/components/pipeline/PipelineCard.svelte.d.ts +13 -0
  188. package/dist/components/pipeline/PipelineColumn.svelte +163 -0
  189. package/dist/components/pipeline/PipelineColumn.svelte.d.ts +30 -0
  190. package/dist/components/replay/ChapterTimeline.svelte +326 -0
  191. package/dist/components/replay/ChapterTimeline.svelte.d.ts +39 -0
  192. package/dist/components/session-nav/transcriptModel.d.ts +132 -0
  193. package/dist/components/session-nav/transcriptModel.js +285 -0
  194. package/dist/data/emojis.d.ts +18 -0
  195. package/dist/data/emojis.js +1196 -0
  196. package/dist/index.d.ts +142 -0
  197. package/dist/index.js +142 -0
  198. package/dist/stores/confirmDialog.svelte.d.ts +21 -0
  199. package/dist/stores/confirmDialog.svelte.js +31 -0
  200. package/dist/stores/inputDialog.svelte.d.ts +24 -0
  201. package/dist/stores/inputDialog.svelte.js +32 -0
  202. package/dist/styles/rail.css +63 -0
  203. package/dist/types/annotation.d.ts +32 -0
  204. package/dist/types/annotation.js +3 -0
  205. package/dist/types/booking.d.ts +115 -0
  206. package/dist/types/booking.js +24 -0
  207. package/dist/types/comments.d.ts +65 -0
  208. package/dist/types/comments.js +22 -0
  209. package/dist/types/entityPreview.d.ts +42 -0
  210. package/dist/types/entityPreview.js +10 -0
  211. package/dist/types/filePicker.d.ts +15 -0
  212. package/dist/types/filePicker.js +1 -0
  213. package/dist/types/googleMaps.d.ts +51 -0
  214. package/dist/types/maps.d.ts +43 -0
  215. package/dist/types/maps.js +4 -0
  216. package/dist/types/messaging.d.ts +282 -0
  217. package/dist/types/messaging.js +100 -0
  218. package/dist/types/milestone.d.ts +20 -0
  219. package/dist/types/milestone.js +7 -0
  220. package/dist/types/pipeline.d.ts +115 -0
  221. package/dist/types/pipeline.js +15 -0
  222. package/dist/types/smartImageEditor.d.ts +42 -0
  223. package/dist/types/smartImageEditor.js +1 -0
  224. package/dist/types/templateVars.d.ts +34 -0
  225. package/dist/types/templateVars.js +12 -0
  226. package/dist/utils/currency.d.ts +8 -0
  227. package/dist/utils/currency.js +17 -0
  228. package/dist/utils/daisyuiColors.d.ts +37 -0
  229. package/dist/utils/daisyuiColors.js +152 -0
  230. package/dist/utils/dateFormatters.d.ts +69 -0
  231. package/dist/utils/dateFormatters.js +199 -0
  232. package/dist/utils/googleMapsLoader.d.ts +19 -0
  233. package/dist/utils/googleMapsLoader.js +121 -0
  234. package/dist/utils/mentionParser.d.ts +67 -0
  235. package/dist/utils/mentionParser.js +132 -0
  236. package/dist/utils/phone.d.ts +13 -0
  237. package/dist/utils/phone.js +67 -0
  238. package/dist/utils/phoneFormat.d.ts +26 -0
  239. package/dist/utils/phoneFormat.js +69 -0
  240. package/dist/utils/taskUtils.d.ts +37 -0
  241. package/dist/utils/taskUtils.js +71 -0
  242. package/package.json +3 -2
@@ -0,0 +1,30 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { PipelineStage, PipelineItem, StageColors, PipelineDragConfig } from '../../types/pipeline';
3
+ interface Props {
4
+ stage: PipelineStage;
5
+ items: PipelineItem[];
6
+ colors: StageColors;
7
+ drag?: PipelineDragConfig;
8
+ collapsed?: boolean;
9
+ minHeight?: number;
10
+ onToggleCollapse?: () => void;
11
+ onDndConsider?: (e: CustomEvent) => void;
12
+ onDndFinalize?: (e: CustomEvent) => void;
13
+ onItemClick?: (item: PipelineItem) => void;
14
+ header?: Snippet<[{
15
+ stage: PipelineStage;
16
+ count: number;
17
+ totalValue: number;
18
+ }]>;
19
+ card?: Snippet<[{
20
+ item: PipelineItem;
21
+ stage: PipelineStage;
22
+ }]>;
23
+ emptyState?: Snippet<[{
24
+ stage: PipelineStage;
25
+ }]>;
26
+ class?: string;
27
+ }
28
+ declare const PipelineColumn: import("svelte").Component<Props, {}, "">;
29
+ type PipelineColumn = ReturnType<typeof PipelineColumn>;
30
+ export default PipelineColumn;
@@ -0,0 +1,326 @@
1
+ <script lang="ts">
2
+ /**
3
+ * ChapterTimeline — horizontal chapter-marker scrubber for WorkBlocks.
4
+ *
5
+ * Renders one chapter marker per WorkBlock, colored by the block's signal
6
+ * type. Clicking a marker seeks to that block. The active block is
7
+ * highlighted. Keyboard left/right arrows step through chapters.
8
+ *
9
+ * Extracted from the JAT IDE (jat-jj79f.5 C1) and promoted into jatui
10
+ * so JST projects can embed session replay without taking a dependency
11
+ * on the full IDE codebase (jat-jj79f.16 G1).
12
+ *
13
+ * The IDE-specific `SESSION_STATE_VISUALS` import has been replaced with
14
+ * an inline `SIGNAL_COLORS` map that covers the same canonical signal types.
15
+ */
16
+
17
+ export interface ChapterBlock {
18
+ id: string;
19
+ signal: { type: string; label: string };
20
+ itemCount: number;
21
+ lineCount: number;
22
+ filesTouched: { path: string; how: string }[];
23
+ checkpoint: { timestamp: string | null };
24
+ summary: string | null;
25
+ }
26
+
27
+ /** Minimal color descriptors for each canonical signal state. */
28
+ interface SignalColor {
29
+ accent: string;
30
+ bgTint: string;
31
+ glow: string;
32
+ shortLabel: string;
33
+ }
34
+
35
+ const SIGNAL_COLORS: Record<string, SignalColor> = {
36
+ starting: { accent: 'oklch(0.70 0.16 250)', bgTint: 'oklch(0.70 0.16 250 / 0.10)', glow: 'oklch(0.70 0.16 250 / 0.45)', shortLabel: '🚀 Starting' },
37
+ working: { accent: 'oklch(0.75 0.16 85)', bgTint: 'oklch(0.75 0.16 85 / 0.08)', glow: 'oklch(0.75 0.16 85 / 0.45)', shortLabel: '🔧 Working' },
38
+ 'needs-input': { accent: 'oklch(0.68 0.19 25)', bgTint: 'oklch(0.68 0.19 25 / 0.08)', glow: 'oklch(0.68 0.19 25 / 0.4)', shortLabel: '❓ Needs Input' },
39
+ 'ready-for-review': { accent: 'oklch(0.72 0.16 200)', bgTint: 'oklch(0.72 0.16 200 / 0.08)', glow: 'oklch(0.72 0.16 200 / 0.4)', shortLabel: '🔍 Review' },
40
+ completing: { accent: 'oklch(0.72 0.16 170)', bgTint: 'oklch(0.72 0.16 170 / 0.08)', glow: 'oklch(0.72 0.16 170 / 0.4)', shortLabel: '⚙️ Completing' },
41
+ completed: { accent: 'oklch(0.66 0.18 145)', bgTint: 'oklch(0.66 0.18 145 / 0.08)', glow: 'oklch(0.66 0.18 145 / 0.4)', shortLabel: '✅ Done' },
42
+ paused: { accent: 'oklch(0.65 0.08 250)', bgTint: 'oklch(0.65 0.08 250 / 0.06)', glow: 'oklch(0.65 0.08 250 / 0.25)', shortLabel: '⏸ Paused' },
43
+ idle: { accent: 'oklch(0.55 0.03 250)', bgTint: 'oklch(0.55 0.03 250 / 0.04)', glow: 'oklch(0.55 0.03 250 / 0.2)', shortLabel: 'Idle' },
44
+ };
45
+
46
+ const DEFAULT_COLOR: SignalColor = SIGNAL_COLORS.idle;
47
+
48
+ let {
49
+ blocks = [],
50
+ activeBlockId = null,
51
+ onSeek = (_blockId: string) => {},
52
+ }: {
53
+ blocks: ChapterBlock[];
54
+ activeBlockId: string | null;
55
+ onSeek?: (blockId: string) => void;
56
+ } = $props();
57
+
58
+ function rawTypeToState(raw: string): string {
59
+ switch (raw) {
60
+ case 'needs_input': return 'needs-input';
61
+ case 'review': return 'ready-for-review';
62
+ case 'complete':
63
+ case 'completed': return 'completed';
64
+ default: return raw;
65
+ }
66
+ }
67
+
68
+ function visual(signalType: string): SignalColor {
69
+ return SIGNAL_COLORS[rawTypeToState(signalType)] ?? DEFAULT_COLOR;
70
+ }
71
+
72
+ function glyph(signalType: string): string {
73
+ const v = visual(signalType);
74
+ const first = v.shortLabel.trim().split(/\s+/)[0];
75
+ return /\p{Emoji}/u.test(first) ? first : '●';
76
+ }
77
+
78
+ function formatTimestamp(ts: string | null): string {
79
+ if (!ts) return '';
80
+ try {
81
+ return new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
82
+ } catch {
83
+ return ts;
84
+ }
85
+ }
86
+
87
+ function handleKey(e: KeyboardEvent) {
88
+ if (!blocks.length) return;
89
+ const cur = blocks.findIndex((b) => b.id === activeBlockId);
90
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
91
+ e.preventDefault();
92
+ const prev = Math.max(0, cur - 1);
93
+ onSeek(blocks[prev].id);
94
+ } else if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
95
+ e.preventDefault();
96
+ const next = Math.min(blocks.length - 1, cur + 1);
97
+ onSeek(blocks[next].id);
98
+ }
99
+ }
100
+
101
+ // Proportional widths: each chapter segment is proportional to its lineCount.
102
+ const totalLines = $derived(blocks.reduce((s, b) => s + Math.max(1, b.lineCount), 0));
103
+ function widthPct(b: ChapterBlock): number {
104
+ return (Math.max(1, b.lineCount) / (totalLines || 1)) * 100;
105
+ }
106
+ </script>
107
+
108
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
109
+ <div
110
+ class="chapter-timeline"
111
+ role="slider"
112
+ tabindex="0"
113
+ aria-label="Session chapter timeline"
114
+ aria-valuenow={blocks.findIndex((b) => b.id === activeBlockId) + 1}
115
+ aria-valuemin={1}
116
+ aria-valuemax={blocks.length}
117
+ onkeydown={handleKey}
118
+ >
119
+ {#if blocks.length === 0}
120
+ <div class="empty">No chapters — session has no jat-signal boundaries yet.</div>
121
+ {:else}
122
+ <div class="track">
123
+ {#each blocks as block, i (block.id)}
124
+ {@const v = visual(block.signal.type)}
125
+ {@const isActive = block.id === activeBlockId}
126
+ {@const w = widthPct(block)}
127
+ <button
128
+ class="chapter"
129
+ class:active={isActive}
130
+ style="
131
+ width: {w}%;
132
+ min-width: 2rem;
133
+ --chapter-accent: {v.accent};
134
+ --chapter-tint: {v.bgTint};
135
+ --chapter-glow: {v.glow};
136
+ "
137
+ title="Block {i + 1}: {block.signal.label || block.signal.type}{block.summary ? '\n' + block.summary : ''}{block.checkpoint.timestamp ? '\n' + formatTimestamp(block.checkpoint.timestamp) : ''}"
138
+ onclick={() => onSeek(block.id)}
139
+ aria-pressed={isActive}
140
+ >
141
+ <span class="ch-glyph">{glyph(block.signal.type)}</span>
142
+ <span class="ch-label">{block.signal.label || block.signal.type}</span>
143
+ <span class="ch-meta">{block.itemCount}i</span>
144
+ </button>
145
+ {/each}
146
+ </div>
147
+
148
+ <!-- Chapter detail strip: show summary / files / timestamp for active block -->
149
+ {#each blocks as block (block.id)}
150
+ {#if block.id === activeBlockId}
151
+ <div class="ch-detail">
152
+ <span class="ch-detail-idx">
153
+ Block {blocks.indexOf(block) + 1}/{blocks.length}
154
+ </span>
155
+ {#if block.summary}
156
+ <span class="ch-detail-summary">{block.summary}</span>
157
+ {:else}
158
+ <span class="ch-detail-summary muted">{block.signal.label || block.signal.type}</span>
159
+ {/if}
160
+ {#if block.filesTouched.length > 0}
161
+ <span class="ch-detail-files">
162
+ {#each block.filesTouched as f (f.path)}
163
+ <span class="ch-file">{f.path.split('/').pop()}</span>
164
+ {/each}
165
+ </span>
166
+ {/if}
167
+ {#if block.checkpoint.timestamp}
168
+ <span class="ch-detail-ts">{formatTimestamp(block.checkpoint.timestamp)}</span>
169
+ {/if}
170
+ </div>
171
+ {/if}
172
+ {/each}
173
+ {/if}
174
+ </div>
175
+
176
+ <style>
177
+ .chapter-timeline {
178
+ display: flex;
179
+ flex-direction: column;
180
+ gap: 0.4rem;
181
+ outline: none;
182
+ }
183
+
184
+ .empty {
185
+ font-size: 0.75rem;
186
+ color: oklch(0.55 0.02 250);
187
+ padding: 0.5rem;
188
+ text-align: center;
189
+ }
190
+
191
+ .track {
192
+ display: flex;
193
+ height: 2.25rem;
194
+ border-radius: 0.5rem;
195
+ overflow: hidden;
196
+ background: oklch(0.14 0.01 250);
197
+ border: 1px solid oklch(0.22 0.02 250);
198
+ gap: 1px;
199
+ }
200
+
201
+ .chapter {
202
+ display: flex;
203
+ align-items: center;
204
+ gap: 0.3rem;
205
+ padding: 0 0.5rem;
206
+ background: var(--chapter-tint, oklch(0.18 0.01 250));
207
+ border: none;
208
+ border-right: 1px solid oklch(0.20 0.02 250);
209
+ cursor: pointer;
210
+ transition: background 0.12s ease, box-shadow 0.12s ease;
211
+ overflow: hidden;
212
+ white-space: nowrap;
213
+ position: relative;
214
+ }
215
+
216
+ .chapter:last-child {
217
+ border-right: none;
218
+ }
219
+
220
+ .chapter:hover {
221
+ background: color-mix(in oklch, var(--chapter-accent) 18%, oklch(0.16 0.01 250));
222
+ box-shadow: inset 0 0 0 1px color-mix(in oklch, var(--chapter-accent) 40%, transparent);
223
+ }
224
+
225
+ .chapter.active {
226
+ background: color-mix(in oklch, var(--chapter-accent) 22%, oklch(0.16 0.01 250));
227
+ box-shadow:
228
+ inset 0 0 0 2px color-mix(in oklch, var(--chapter-accent) 70%, transparent),
229
+ 0 0 12px color-mix(in oklch, var(--chapter-glow) 30%, transparent);
230
+ }
231
+
232
+ .chapter.active::after {
233
+ content: '';
234
+ position: absolute;
235
+ bottom: 0;
236
+ left: 0;
237
+ right: 0;
238
+ height: 2px;
239
+ background: var(--chapter-accent);
240
+ border-radius: 0 0 0.25rem 0.25rem;
241
+ }
242
+
243
+ .ch-glyph {
244
+ font-size: 0.95rem;
245
+ flex-shrink: 0;
246
+ }
247
+
248
+ .ch-label {
249
+ font-size: 0.7rem;
250
+ color: oklch(0.78 0.04 250);
251
+ overflow: hidden;
252
+ text-overflow: ellipsis;
253
+ min-width: 0;
254
+ }
255
+
256
+ .chapter.active .ch-label {
257
+ color: oklch(0.92 0.06 250);
258
+ font-weight: 600;
259
+ }
260
+
261
+ .ch-meta {
262
+ font-size: 0.6rem;
263
+ color: oklch(0.50 0.02 250);
264
+ margin-left: auto;
265
+ flex-shrink: 0;
266
+ font-family: monospace;
267
+ }
268
+
269
+ /* Detail strip under the track */
270
+ .ch-detail {
271
+ display: flex;
272
+ align-items: center;
273
+ gap: 0.6rem;
274
+ padding: 0.25rem 0.5rem;
275
+ font-size: 0.72rem;
276
+ color: oklch(0.70 0.04 250);
277
+ background: oklch(0.15 0.01 250);
278
+ border-radius: 0.375rem;
279
+ border: 1px solid oklch(0.22 0.02 250);
280
+ min-height: 1.75rem;
281
+ flex-wrap: wrap;
282
+ gap-y: 0.15rem;
283
+ }
284
+
285
+ .ch-detail-idx {
286
+ font-size: 0.65rem;
287
+ font-family: monospace;
288
+ color: oklch(0.50 0.02 250);
289
+ flex-shrink: 0;
290
+ }
291
+
292
+ .ch-detail-summary {
293
+ flex: 1;
294
+ overflow: hidden;
295
+ text-overflow: ellipsis;
296
+ white-space: nowrap;
297
+ }
298
+
299
+ .ch-detail-summary.muted {
300
+ color: oklch(0.55 0.02 250);
301
+ font-style: italic;
302
+ }
303
+
304
+ .ch-detail-files {
305
+ display: flex;
306
+ gap: 0.3rem;
307
+ flex-wrap: wrap;
308
+ }
309
+
310
+ .ch-file {
311
+ font-size: 0.64rem;
312
+ font-family: monospace;
313
+ background: oklch(0.19 0.02 250);
314
+ border: 1px solid oklch(0.25 0.02 250);
315
+ border-radius: 0.2rem;
316
+ padding: 0.1rem 0.35rem;
317
+ color: oklch(0.70 0.08 200);
318
+ }
319
+
320
+ .ch-detail-ts {
321
+ font-size: 0.62rem;
322
+ font-family: monospace;
323
+ color: oklch(0.48 0.02 250);
324
+ flex-shrink: 0;
325
+ }
326
+ </style>
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ChapterTimeline — horizontal chapter-marker scrubber for WorkBlocks.
3
+ *
4
+ * Renders one chapter marker per WorkBlock, colored by the block's signal
5
+ * type. Clicking a marker seeks to that block. The active block is
6
+ * highlighted. Keyboard left/right arrows step through chapters.
7
+ *
8
+ * Extracted from the JAT IDE (jat-jj79f.5 C1) and promoted into jatui
9
+ * so JST projects can embed session replay without taking a dependency
10
+ * on the full IDE codebase (jat-jj79f.16 G1).
11
+ *
12
+ * The IDE-specific `SESSION_STATE_VISUALS` import has been replaced with
13
+ * an inline `SIGNAL_COLORS` map that covers the same canonical signal types.
14
+ */
15
+ export interface ChapterBlock {
16
+ id: string;
17
+ signal: {
18
+ type: string;
19
+ label: string;
20
+ };
21
+ itemCount: number;
22
+ lineCount: number;
23
+ filesTouched: {
24
+ path: string;
25
+ how: string;
26
+ }[];
27
+ checkpoint: {
28
+ timestamp: string | null;
29
+ };
30
+ summary: string | null;
31
+ }
32
+ type $$ComponentProps = {
33
+ blocks: ChapterBlock[];
34
+ activeBlockId: string | null;
35
+ onSeek?: (blockId: string) => void;
36
+ };
37
+ declare const ChapterTimeline: import("svelte").Component<$$ComponentProps, {}, "">;
38
+ type ChapterTimeline = ReturnType<typeof ChapterTimeline>;
39
+ export default ChapterTimeline;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * transcriptModel.ts — transcript → linked (transcript items ↔ signal nodes) model
3
+ * for the session navigator. Extracted from sankey/sankeyData.ts (jat-w8tgz.1).
4
+ *
5
+ * THE CONCEPT (from ~/Documents/many_to_one.mp4): a two-column linked view like
6
+ * an academic article + its citations. LEFT = the session transcript (prose),
7
+ * RIGHT = the signal events (the "references"). Curved ribbons connect spans of
8
+ * the transcript to the signal they produced, and ink up as you scroll/hover.
9
+ *
10
+ * THE ANCHORING TRICK: a JAT agent emits state by running `jat-signal <type> …`,
11
+ * which appears in Claude's transcript as a Bash tool call. So we scan the
12
+ * transcript for those calls and treat each as a signal-emission boundary. Each
13
+ * signal then "owns" the run of transcript that led up to it — the genuine
14
+ * many-to-one mapping. The jat-signal lines themselves are plumbing, so we
15
+ * consume them as boundaries and do NOT render them.
16
+ *
17
+ * COMPACTION: raw transcript lines are noisy (every tool result, every "file
18
+ * updated successfully" notice). We group lines into ITEMS:
19
+ * - prompt — a user message (shown in full; it's the human's intent)
20
+ * - assistant — the agent's prose (shown in full; it's the narrative)
21
+ * - action — a tool call, with its result body tucked into `detail` so the
22
+ * transcript shows just the headline by default and the body is
23
+ * a drill-down. This is what keeps the left column readable.
24
+ */
25
+ export type ItemKind = 'prompt' | 'assistant' | 'action';
26
+ export interface TranscriptItem {
27
+ id: number;
28
+ kind: ItemKind;
29
+ /** the line shown when collapsed (prose, or the tool call) */
30
+ headline: string;
31
+ /** collapsible body — tool result lines; empty for prompt/assistant */
32
+ detail: string[];
33
+ /** how many raw transcript lines this item consumed (feeds the line-count badge) */
34
+ srcLines: number;
35
+ /** id of the signal node that owns this item (assigned after parse) */
36
+ signalId: string | null;
37
+ }
38
+ export interface FileTouchedEntry {
39
+ path: string;
40
+ how: 'written' | 'edited' | 'notebook';
41
+ }
42
+ export interface SignalNode {
43
+ id: string;
44
+ /** canonical SESSION_STATE_VISUALS key (e.g. 'working', 'needs-input', 'ready-for-review') */
45
+ state: string;
46
+ rawType: string;
47
+ /** short human label pulled from the signal payload, if any */
48
+ label: string;
49
+ /** inclusive transcript-item id range this signal owns */
50
+ rowStart: number;
51
+ rowEnd: number;
52
+ /** raw transcript lines feeding this signal — the "many" in many-to-one */
53
+ lineCount: number;
54
+ /** WorkBlock id (e.g. "blk-000"); null when built from raw transcript lines */
55
+ blockId: string | null;
56
+ /** One-line LLM summary of this block (A2); null if not yet populated */
57
+ summary: string | null;
58
+ /** Files touched in this block (A3 file correlation) */
59
+ filesTouched: FileTouchedEntry[];
60
+ /** Commit SHAs + subjects for this block (A3 diff data) */
61
+ commits: Array<{
62
+ shaShort: string;
63
+ subject: string;
64
+ }>;
65
+ /** Net lines changed across all diffs for this block */
66
+ linesAdded: number;
67
+ linesRemoved: number;
68
+ }
69
+ export interface LinkedSession {
70
+ items: TranscriptItem[];
71
+ signals: SignalNode[];
72
+ }
73
+ export declare function rawTypeToState(raw: string): string;
74
+ /** Build the linked model from rendered transcript lines. */
75
+ export declare function buildLinkedSession(lines: string[]): LinkedSession;
76
+ export interface WorkBlockClient {
77
+ id: string;
78
+ signal: {
79
+ type: string;
80
+ label: string;
81
+ payload: Record<string, unknown> | null;
82
+ };
83
+ itemRange: {
84
+ start: number;
85
+ end: number;
86
+ };
87
+ itemCount: number;
88
+ lineCount: number;
89
+ summary: string | null;
90
+ filesTouched: FileTouchedEntry[];
91
+ checkpoint: {
92
+ promptUuid: string;
93
+ timestamp: string | null;
94
+ };
95
+ /** Optional diff data (A3) — present when endpoint returns enriched data */
96
+ commits?: Array<{
97
+ shaShort: string;
98
+ subject: string;
99
+ }>;
100
+ linesAdded?: number;
101
+ linesRemoved?: number;
102
+ }
103
+ export interface WorkTranscriptItemClient {
104
+ id: number;
105
+ kind: 'prompt' | 'assistant' | 'action';
106
+ headline: string;
107
+ detail: string[];
108
+ srcLines: number;
109
+ blockId: string | null;
110
+ }
111
+ /**
112
+ * Build a LinkedSession from the WorkBlock substrate (B1).
113
+ *
114
+ * This is the preferred path for live sessions — it uses the richer data from
115
+ * `getSessionWork` (A1) including per-block summaries (A2) and file lists (A3)
116
+ * instead of re-parsing transcript lines.
117
+ *
118
+ * The `items` array from the server is used directly as `TranscriptItem[]` since
119
+ * both shapes share `id`, `kind`, `headline`, `detail`, `srcLines` with the
120
+ * `signalId` replaced by `blockId → signalId` mapping done here.
121
+ *
122
+ * @param blocks WorkBlock array from `/api/work/[sessionId]/navigator`
123
+ * @param items WorkTranscriptItem array from the same endpoint
124
+ */
125
+ export declare function buildLinkedSessionFromWork(blocks: WorkBlockClient[], items: WorkTranscriptItemClient[]): LinkedSession;
126
+ /**
127
+ * A baked, realistic sample so /sankey always demos beautifully even with no live
128
+ * session. Authored as raw transcript lines with real `jat-signal` calls, so it
129
+ * flows through the EXACT same parser as live data.
130
+ */
131
+ export declare const SAMPLE_TRANSCRIPT_LINES: string[];
132
+ export declare function sampleLinkedSession(): LinkedSession;