@djangocfg/ui-tools 2.1.431 → 2.1.432

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 (171) hide show
  1. package/package.json +28 -14
  2. package/src/common/CodePanelHeader/CodePanelHeader.tsx +121 -0
  3. package/src/common/CodePanelHeader/index.ts +1 -0
  4. package/src/common/blocks/BlockError.tsx +46 -0
  5. package/src/common/blocks/index.ts +34 -0
  6. package/src/common/blocks/schemas.ts +143 -0
  7. package/src/common/blocks/types.ts +76 -0
  8. package/src/common/chips/README.md +67 -0
  9. package/src/common/chips/UrlChip.tsx +105 -0
  10. package/src/common/chips/index.ts +20 -0
  11. package/src/common/link-preview/LinkPreviewCard.tsx +178 -0
  12. package/src/common/link-preview/index.ts +12 -0
  13. package/src/common/link-preview/types.ts +44 -0
  14. package/src/common/tiptap/index.ts +10 -0
  15. package/src/common/tiptap/useNodeAttrs.ts +128 -0
  16. package/src/lib/page-snapshot/react/PageSnapshotChip.tsx +1 -1
  17. package/src/tools/chat/README.md +88 -2
  18. package/src/tools/chat/composer/Composer.tsx +45 -12
  19. package/src/tools/chat/composer/ComposerRichTextarea.tsx +16 -4
  20. package/src/tools/chat/constants.ts +9 -2
  21. package/src/tools/chat/core/extractFirstUrl.ts +45 -0
  22. package/src/tools/chat/core/reducer.ts +32 -8
  23. package/src/tools/chat/core/transport/mappers/pydantic-ai.ts +1 -1
  24. package/src/tools/chat/core/transport/mock.ts +4 -1
  25. package/src/tools/chat/hooks/useChat.ts +20 -20
  26. package/src/tools/chat/lazy.tsx +10 -0
  27. package/src/tools/chat/messages/Attachments.tsx +267 -42
  28. package/src/tools/chat/messages/BubbleContextMenu.tsx +203 -0
  29. package/src/tools/chat/messages/MessageBubble.tsx +175 -31
  30. package/src/tools/chat/messages/MessageList.tsx +107 -21
  31. package/src/tools/chat/messages/ToolCalls.tsx +28 -13
  32. package/src/tools/chat/messages/blocks/MessageBlocks.tsx +70 -17
  33. package/src/tools/chat/messages/blocks/builtin.tsx +6 -3
  34. package/src/tools/chat/messages/blocks/renderers/AudioBlock.tsx +6 -0
  35. package/src/tools/chat/messages/blocks/renderers/ChatImageAlbum.tsx +154 -0
  36. package/src/tools/chat/messages/blocks/renderers/CodeBlock.tsx +94 -6
  37. package/src/tools/chat/messages/blocks/renderers/DiffBlock.tsx +29 -0
  38. package/src/tools/chat/messages/blocks/renderers/GalleryBlock.tsx +17 -10
  39. package/src/tools/chat/messages/blocks/renderers/ImageBlock.tsx +37 -16
  40. package/src/tools/chat/messages/blocks/renderers/JsonBlock.tsx +11 -6
  41. package/src/tools/chat/messages/blocks/renderers/LinkBlock.tsx +32 -0
  42. package/src/tools/chat/messages/blocks/renderers/MapBlock.tsx +181 -19
  43. package/src/tools/chat/messages/blocks/renderers/VideoBlock.tsx +2 -5
  44. package/src/tools/chat/messages/index.ts +13 -0
  45. package/src/tools/chat/public.ts +5 -1
  46. package/src/tools/chat/shell/ChatRoot.tsx +29 -0
  47. package/src/tools/chat/styles/bubbleTokens.ts +18 -0
  48. package/src/tools/chat/styles/index.ts +1 -0
  49. package/src/tools/chat/types/attachment.ts +26 -2
  50. package/src/tools/chat/types/block.ts +56 -9
  51. package/src/tools/chat/types/blockSchemas.ts +197 -0
  52. package/src/tools/chat/types/config.ts +33 -0
  53. package/src/tools/chat/types/events.ts +14 -7
  54. package/src/tools/chat/types/index.ts +4 -2
  55. package/src/tools/chat/types/message.ts +7 -0
  56. package/src/tools/data/JsonTree/JsonViewer.tsx +7 -7
  57. package/src/tools/dev/Map/README.md +30 -345
  58. package/src/tools/dev/Map/cards/index.ts +6 -0
  59. package/src/tools/dev/Map/cards/types.ts +67 -0
  60. package/src/tools/dev/Map/components/CompassChip.tsx +67 -0
  61. package/src/tools/dev/Map/components/DraggableMarkers.tsx +79 -0
  62. package/src/tools/dev/Map/components/GeocoderControl.tsx +148 -63
  63. package/src/tools/dev/Map/components/GeocoderInput.tsx +181 -0
  64. package/src/tools/dev/Map/components/MapContainer/FullscreenDialog.tsx +140 -0
  65. package/src/tools/dev/Map/components/MapContainer/GeolocateHandle.tsx +70 -0
  66. package/src/tools/dev/Map/components/MapContainer/MapInner.tsx +326 -0
  67. package/src/tools/dev/Map/components/MapContainer/MapToolbar.tsx +286 -0
  68. package/src/tools/dev/Map/components/MapContainer/RotateHint.tsx +25 -0
  69. package/src/tools/dev/Map/components/MapContainer/ScrollLockOverlay.tsx +52 -0
  70. package/src/tools/dev/Map/components/MapContainer/constants.ts +15 -0
  71. package/src/tools/dev/Map/components/MapContainer/hooks/useAutoReset.ts +51 -0
  72. package/src/tools/dev/Map/components/MapContainer/hooks/useBasemapState.ts +48 -0
  73. package/src/tools/dev/Map/components/MapContainer/hooks/useExternalMapsUrl.ts +29 -0
  74. package/src/tools/dev/Map/components/MapContainer/index.tsx +29 -0
  75. package/src/tools/dev/Map/components/MapContainer/types.ts +198 -0
  76. package/src/tools/dev/Map/components/MapControls.tsx +27 -0
  77. package/src/tools/dev/Map/components/MapMarkers.tsx +94 -0
  78. package/src/tools/dev/Map/components/MarkerCard.tsx +102 -0
  79. package/src/tools/dev/Map/components/MeasureControl.tsx +219 -0
  80. package/src/tools/dev/Map/components/index.ts +35 -1
  81. package/src/tools/dev/Map/geocode/index.ts +2 -0
  82. package/src/tools/dev/Map/geocode/photon.ts +265 -0
  83. package/src/tools/dev/Map/hooks/index.ts +37 -0
  84. package/src/tools/dev/Map/hooks/useCompass.ts +44 -0
  85. package/src/tools/dev/Map/hooks/useDraggableMarkers.ts +112 -0
  86. package/src/tools/dev/Map/hooks/useGeocoder.ts +155 -0
  87. package/src/tools/dev/Map/hooks/useGeolocation.ts +174 -0
  88. package/src/tools/dev/Map/hooks/useMapControl.ts +5 -1
  89. package/src/tools/dev/Map/hooks/useMapImages.ts +112 -0
  90. package/src/tools/dev/Map/hooks/useMapScrollProtection.ts +155 -0
  91. package/src/tools/dev/Map/hooks/useMapTouchMode.ts +21 -0
  92. package/src/tools/dev/Map/hooks/useMarkerCard.ts +39 -0
  93. package/src/tools/dev/Map/hooks/useMarkers.ts +9 -3
  94. package/src/tools/dev/Map/hooks/useMeasure.ts +169 -0
  95. package/src/tools/dev/Map/hooks/useTerrain.ts +123 -0
  96. package/src/tools/dev/Map/index.ts +59 -2
  97. package/src/tools/dev/Map/layers/index.ts +1 -0
  98. package/src/tools/dev/Map/layers/symbol.ts +93 -0
  99. package/src/tools/dev/Map/lazy.tsx +120 -0
  100. package/src/tools/dev/Map/protocols/pmtiles.ts +73 -0
  101. package/src/tools/dev/Map/styles/index.ts +62 -0
  102. package/src/tools/dev/Map/styles/terrain.ts +29 -0
  103. package/src/tools/dev/Map/types.ts +31 -3
  104. package/src/tools/dev/api/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +1 -1
  105. package/src/tools/dev/api/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/StatusTag.tsx +5 -12
  106. package/src/tools/dev/api/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/SectionHeader.tsx +1 -1
  107. package/src/tools/dev/api/OpenapiViewer/components/DocsLayout/SchemaCopyMenu.tsx +1 -1
  108. package/src/tools/dev/api/OpenapiViewer/components/shared/SendButton.tsx +1 -1
  109. package/src/tools/dev/api/OpenapiViewer/components/shared/http-colors.ts +61 -0
  110. package/src/tools/dev/api/OpenapiViewer/components/shared/ui.tsx +4 -21
  111. package/src/tools/dev/code/MarkdownMessage/MarkdownMessage.tsx +150 -58
  112. package/src/tools/dev/code/MarkdownMessage/README.md +38 -0
  113. package/src/tools/dev/code/MarkdownMessage/builtinRules.tsx +92 -0
  114. package/src/tools/dev/code/MarkdownMessage/index.ts +3 -0
  115. package/src/tools/dev/code/MarkdownMessage/streamingBlocks.ts +164 -0
  116. package/src/tools/dev/code/MarkdownMessage/types.ts +20 -0
  117. package/src/tools/dev/code/PrettyCode/PrettyCode.client.tsx +27 -30
  118. package/src/tools/dev/code/PrettyCode/index.tsx +4 -1
  119. package/src/tools/dev/code/PrettyCode/lazy.tsx +3 -1
  120. package/src/tools/forms/MarkdownEditor/MarkdownEditor.tsx +168 -3
  121. package/src/tools/forms/MarkdownEditor/README.md +84 -0
  122. package/src/tools/forms/MarkdownEditor/chip/ChipNode.ts +454 -0
  123. package/src/tools/forms/MarkdownEditor/chip/syncChips.ts +80 -0
  124. package/src/tools/forms/MarkdownEditor/filePath/FilePathChip.tsx +68 -0
  125. package/src/tools/forms/MarkdownEditor/filePath/__tests__/detect.test.ts +161 -0
  126. package/src/tools/forms/MarkdownEditor/filePath/detect.ts +289 -0
  127. package/src/tools/forms/MarkdownEditor/filePath/index.ts +15 -0
  128. package/src/tools/forms/MarkdownEditor/index.ts +19 -0
  129. package/src/tools/forms/MarkdownEditor/lazy.tsx +10 -0
  130. package/src/tools/forms/MarkdownEditor/styles.css +134 -2
  131. package/src/tools/forms/MarkdownEditor/url/__tests__/detect.test.ts +121 -0
  132. package/src/tools/forms/MarkdownEditor/url/detect.ts +227 -0
  133. package/src/tools/forms/NotionEditor/BookmarkNode.ts +158 -0
  134. package/src/tools/forms/NotionEditor/BookmarkView.tsx +45 -0
  135. package/src/tools/forms/NotionEditor/MapEditLayer.tsx +199 -0
  136. package/src/tools/forms/NotionEditor/MapNode.ts +218 -0
  137. package/src/tools/forms/NotionEditor/MapView.tsx +103 -0
  138. package/src/tools/forms/NotionEditor/NotionEditor.tsx +7 -2
  139. package/src/tools/forms/NotionEditor/README.md +109 -188
  140. package/src/tools/forms/NotionEditor/extensions.ts +15 -0
  141. package/src/tools/forms/NotionEditor/slashItems.ts +19 -0
  142. package/src/tools/forms/NotionEditor/styles.css +25 -0
  143. package/src/tools/forms/NotionEditor/types.ts +9 -0
  144. package/src/tools/input/SpeechRecognition/core/engine/external.ts +5 -0
  145. package/src/tools/input/SpeechRecognition/types.ts +10 -0
  146. package/src/tools/media/Gallery/components/lightbox/GalleryLightbox.tsx +39 -22
  147. package/src/tools/media/ImageViewer/README.md +9 -0
  148. package/src/tools/media/ImageViewer/components/ImageViewer.tsx +165 -79
  149. package/src/tools/media/ImageViewer/components/LightboxChrome.tsx +137 -0
  150. package/src/tools/media/ImageViewer/components/MiddleEllipsis.tsx +46 -0
  151. package/src/tools/media/ImageViewer/components/index.ts +2 -0
  152. package/src/tools/media/ImageViewer/hooks/index.ts +3 -0
  153. package/src/tools/media/ImageViewer/hooks/useIdleChrome.ts +73 -0
  154. package/src/tools/media/ImageViewer/types.ts +10 -0
  155. package/src/tools/media/ImageViewer/utils/constants.ts +28 -0
  156. package/src/tools/media/ImageViewer/utils/index.ts +4 -0
  157. package/src/tools/media/VideoPlayer/VideoPlayer.tsx +34 -40
  158. package/src/tools/media/VideoPlayer/canvas/youtube-canvas.tsx +13 -13
  159. package/src/tools/media/VideoPlayer/parts/center-play.tsx +26 -0
  160. package/src/tools/media/VideoPlayer/parts/index.ts +2 -0
  161. package/src/tools/media/VideoPlayer/styles/video-player.css +102 -53
  162. package/src/tools/visual/charts/SmoothLine/README.md +97 -0
  163. package/src/tools/visual/charts/SmoothLine/SmoothLine.tsx +177 -0
  164. package/src/tools/visual/charts/SmoothLine/index.ts +21 -0
  165. package/src/tools/visual/charts/SmoothLine/lazy.tsx +23 -0
  166. package/src/tools/visual/charts/SmoothLine/smoothPath.ts +209 -0
  167. package/src/tools/visual/charts/SmoothLine/types.ts +98 -0
  168. package/src/tools/visual/charts/SmoothLine/useSmoothSeries.ts +429 -0
  169. package/src/tools/visual/indicators/Fps/types.ts +1 -1
  170. package/src/tools/chat/messages/blocks/renderers/LottieBlock.tsx +0 -11
  171. package/src/tools/dev/Map/components/MapContainer.tsx +0 -198
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Zod schemas for the chat `MessageBlock` discriminated union (`./block.ts`).
3
+ *
4
+ * These are the runtime VALIDATORS used at the single registry dispatch point
5
+ * (`messages/blocks/MessageBlocks.tsx`): every block flows through
6
+ * `safeParseBlock(BLOCK_SCHEMAS[kind], block)` BEFORE its renderer runs, so a
7
+ * malformed wire / hand-authored payload renders a safe `<BlockError>` card
8
+ * instead of throwing inside a renderer and blanking the transcript.
9
+ *
10
+ * Design rules (mirroring `common/blocks/schemas.ts`):
11
+ * - The TS types in `./block.ts` stay the source of truth; these schemas are
12
+ * kept in sync and `Extends<…>`-checked below so they can't silently drift.
13
+ * - The map / link shapes are COMPOSED from the shared
14
+ * `MapBlockPayloadSchema` / `LinkPreviewDataSchema` in `common/blocks` — we
15
+ * never re-describe a map/link payload here.
16
+ * - LENIENT where the renderer is tolerant: optional fields stay optional,
17
+ * `json.data` is `unknown` (JsonTree is throw-safe), extra keys are stripped.
18
+ * - STRICT on the load-bearing shape: a field whose wrong type would THROW in
19
+ * the renderer is required + typed (gallery `items` must be an array;
20
+ * image/video/audio `src` must be a string; map coords are finite
21
+ * numbers via the shared schema).
22
+ * - `custom` and the prose kinds (`text`/`markdown`) carry no schema in
23
+ * `BLOCK_SCHEMAS` — `custom` is host-defined (don't over-constrain) and the
24
+ * prose kinds can't throw on a wrong-typed field worth guarding. They render
25
+ * as-is. (`text`/`markdown` schemas are still defined for completeness.)
26
+ */
27
+
28
+ import { z } from 'zod';
29
+
30
+ import { MapBlockPayloadSchema, LinkPreviewDataSchema } from '../../../common/blocks';
31
+ import type {
32
+ TextBlock,
33
+ MarkdownBlock,
34
+ AudioBlock,
35
+ VideoBlock,
36
+ ImageBlock,
37
+ GalleryBlock,
38
+ MapBlock,
39
+ JsonBlock,
40
+ MermaidBlock,
41
+ CodeBlock,
42
+ DiffBlock,
43
+ LinkBlock,
44
+ MessageBlock,
45
+ } from './block';
46
+
47
+ /**
48
+ * Base fields shared by every block (`BlockBase` in `./block.ts`). We validate
49
+ * the WHOLE block object (incl. `kind` / `id` / `caption`), so each schema
50
+ * composes this. `id` is required (it is the React key + memo discriminant).
51
+ */
52
+ const BlockBaseSchema = z.object({
53
+ id: z.string(),
54
+ caption: z.string().optional(),
55
+ });
56
+
57
+ const TextBlockSchema = BlockBaseSchema.extend({
58
+ kind: z.literal('text'),
59
+ text: z.string(),
60
+ });
61
+
62
+ const MarkdownBlockSchema = BlockBaseSchema.extend({
63
+ kind: z.literal('markdown'),
64
+ markdown: z.string(),
65
+ });
66
+
67
+ const AudioBlockSchema = BlockBaseSchema.extend({
68
+ kind: z.literal('audio'),
69
+ src: z.string(),
70
+ title: z.string().optional(),
71
+ artist: z.string().optional(),
72
+ cover: z.string().optional(),
73
+ variant: z.enum(['default', 'compact']).optional(),
74
+ });
75
+
76
+ const VideoBlockSchema = BlockBaseSchema.extend({
77
+ kind: z.literal('video'),
78
+ src: z.string(),
79
+ poster: z.string().optional(),
80
+ title: z.string().optional(),
81
+ aspectRatio: z.union([z.number(), z.literal('auto'), z.literal('fill')]).optional(),
82
+ });
83
+
84
+ const ImageBlockSchema = BlockBaseSchema.extend({
85
+ kind: z.literal('image'),
86
+ src: z.string(),
87
+ alt: z.string().optional(),
88
+ interactive: z.boolean().optional(),
89
+ });
90
+
91
+ const GalleryItemSchema = z.object({
92
+ id: z.string(),
93
+ src: z.string(),
94
+ thumbnail: z.string().optional(),
95
+ alt: z.string().optional(),
96
+ });
97
+
98
+ const GalleryBlockSchema = BlockBaseSchema.extend({
99
+ kind: z.literal('gallery'),
100
+ // STRICT: `items` must be an array — the renderer maps over it.
101
+ items: z.array(GalleryItemSchema),
102
+ });
103
+
104
+ /**
105
+ * Map block = the shared serializable map payload (`MapBlockPayloadSchema`,
106
+ * which already enforces finite-number `center.lat/lng` etc.) + the base block
107
+ * fields + the chat-only `userLocation` opt-in flag. Composed, never
108
+ * duplicated.
109
+ */
110
+ const MapBlockSchema = MapBlockPayloadSchema.merge(BlockBaseSchema).extend({
111
+ kind: z.literal('map'),
112
+ userLocation: z.boolean().optional(),
113
+ });
114
+
115
+ const JsonBlockSchema = BlockBaseSchema.extend({
116
+ kind: z.literal('json'),
117
+ // LENIENT: JsonTree renders anything safely — accept any payload.
118
+ data: z.unknown(),
119
+ mode: z.enum(['full', 'compact', 'inline']).optional(),
120
+ });
121
+
122
+ const MermaidBlockSchema = BlockBaseSchema.extend({
123
+ kind: z.literal('mermaid'),
124
+ chart: z.string(),
125
+ });
126
+
127
+ const CodeBlockSchema = BlockBaseSchema.extend({
128
+ kind: z.literal('code'),
129
+ code: z.string(),
130
+ language: z.string(),
131
+ });
132
+
133
+ const DiffBlockSchema = BlockBaseSchema.extend({
134
+ kind: z.literal('diff'),
135
+ oldCode: z.string().optional(),
136
+ newCode: z.string().optional(),
137
+ patch: z.string().optional(),
138
+ language: z.string().optional(),
139
+ layout: z.enum(['split', 'unified']).optional(),
140
+ });
141
+
142
+ /**
143
+ * Link block = base fields + `url` + optional pre-resolved `data` (validated
144
+ * against the shared `LinkPreviewDataSchema`). The renderer also tolerates a
145
+ * resolver-only block (no `data`), so `data` stays optional.
146
+ */
147
+ const LinkBlockSchema = BlockBaseSchema.extend({
148
+ kind: z.literal('link'),
149
+ url: z.string(),
150
+ data: LinkPreviewDataSchema.optional(),
151
+ });
152
+
153
+ // ── Drift guards ──────────────────────────────────────────────────────────
154
+ // Type-level assertion that each schema's inferred output stays assignable to
155
+ // the canonical block type in `./block.ts`. If a schema and the type diverge,
156
+ // the corresponding assignment fails to compile.
157
+ type Extends<A, B> = A extends B ? true : never;
158
+ const _textOk: Extends<z.infer<typeof TextBlockSchema>, TextBlock> = true;
159
+ const _markdownOk: Extends<z.infer<typeof MarkdownBlockSchema>, MarkdownBlock> = true;
160
+ const _audioOk: Extends<z.infer<typeof AudioBlockSchema>, AudioBlock> = true;
161
+ const _videoOk: Extends<z.infer<typeof VideoBlockSchema>, VideoBlock> = true;
162
+ const _imageOk: Extends<z.infer<typeof ImageBlockSchema>, ImageBlock> = true;
163
+ const _galleryOk: Extends<z.infer<typeof GalleryBlockSchema>, GalleryBlock> = true;
164
+ const _mapOk: Extends<z.infer<typeof MapBlockSchema>, MapBlock> = true;
165
+ const _jsonOk: Extends<z.infer<typeof JsonBlockSchema>, JsonBlock> = true;
166
+ const _mermaidOk: Extends<z.infer<typeof MermaidBlockSchema>, MermaidBlock> = true;
167
+ const _codeOk: Extends<z.infer<typeof CodeBlockSchema>, CodeBlock> = true;
168
+ const _diffOk: Extends<z.infer<typeof DiffBlockSchema>, DiffBlock> = true;
169
+ const _linkOk: Extends<z.infer<typeof LinkBlockSchema>, LinkBlock> = true;
170
+ void [
171
+ _textOk, _markdownOk, _audioOk, _videoOk, _imageOk, _galleryOk, _mapOk,
172
+ _jsonOk, _mermaidOk, _codeOk, _diffOk, _linkOk,
173
+ ];
174
+
175
+ /**
176
+ * `kind` → schema. Validated at the registry dispatch point. Kinds WITHOUT an
177
+ * entry are skipped (rendered as-is):
178
+ * - `custom` — host-defined `payload`; over-constraining it would break hosts.
179
+ *
180
+ * Every built-in data block is covered so a malformed payload is caught
181
+ * centrally — and any FUTURE kind is guarded the moment it's added here.
182
+ */
183
+ export const BLOCK_SCHEMAS: Partial<Record<MessageBlock['kind'], z.ZodType>> = {
184
+ text: TextBlockSchema,
185
+ markdown: MarkdownBlockSchema,
186
+ audio: AudioBlockSchema,
187
+ video: VideoBlockSchema,
188
+ image: ImageBlockSchema,
189
+ gallery: GalleryBlockSchema,
190
+ map: MapBlockSchema,
191
+ json: JsonBlockSchema,
192
+ mermaid: MermaidBlockSchema,
193
+ code: CodeBlockSchema,
194
+ diff: DiffBlockSchema,
195
+ link: LinkBlockSchema,
196
+ // `custom` intentionally omitted — host-defined, rendered as-is.
197
+ };
@@ -4,6 +4,7 @@
4
4
 
5
5
  import type { ChatAssistantContext, ChatUserContext } from './persona';
6
6
  import type { ChatLabels } from './labels';
7
+ import type { ResolveLinkPreview } from '../../../common/link-preview';
7
8
 
8
9
  export type ChatDisplayMode = 'closed' | 'embedded' | 'floating' | 'sidebar' | 'fullscreen';
9
10
 
@@ -40,4 +41,36 @@ export interface ChatConfig {
40
41
  prefs?: ChatPrefs;
41
42
  /** Visual labels (i18n is the host's job). */
42
43
  labels?: Partial<ChatLabels>;
44
+ /** Link-preview (URL unfurl) wiring — the host owns metadata fetching. */
45
+ linkPreview?: ChatLinkPreviewConfig;
46
+ /**
47
+ * Render bare web URLs inside message bubbles as compact "URL chips"
48
+ * (favicon + domain + middle-ellipsis path, e.g.
49
+ * `github.com/wailsapp/…/README.md`) instead of a plain blue link —
50
+ * matching the composer's URL chip. Only bare `http(s)` autolinks are
51
+ * chipped; a `[labeled](url)` link keeps its author-chosen text as a
52
+ * normal link.
53
+ *
54
+ * Off by default (the host opts in). When on, the chip rule is appended
55
+ * AFTER any host-supplied `linkRules`, so host-specific rules win.
56
+ * @default false
57
+ */
58
+ linkChips?: boolean;
59
+ }
60
+
61
+ export interface ChatLinkPreviewConfig {
62
+ /**
63
+ * Host resolver that turns a URL into preview metadata. Required for any
64
+ * link card to appear (the browser cannot fetch meta tags cross-origin).
65
+ * In Wails/Go this wraps a bound `FetchLinkMetadata` method; on the web,
66
+ * a `/api/unfurl` route.
67
+ */
68
+ resolve?: ResolveLinkPreview;
69
+ /**
70
+ * Auto-render a preview card under a message bubble when its text
71
+ * contains a link (Telegram-style — at most ONE card per message).
72
+ * Off by default; needs `resolve` to be set.
73
+ * @default false
74
+ */
75
+ autoDetect?: boolean;
43
76
  }
@@ -5,12 +5,12 @@
5
5
  * into this union by `mapPydanticAIEvent` and friends.
6
6
  */
7
7
 
8
- import type { ChatSource } from './attachment';
8
+ import type { ChatMessage } from './message';
9
9
 
10
10
  /**
11
- * Per-run metrics block. Emitted once per assistant turn alongside the
12
- * terminal `message_end` event (a separate `message_metrics` event so
13
- * `message_end` keeps its lean `tokensIn`/`tokensOut`/`sources` shape).
11
+ * Per-run metrics block. Emitted once per assistant turn, carried into the
12
+ * message either by a non-terminal `message_metrics` event mid-run or via
13
+ * the terminal `message_end` event's generic `patch` slot.
14
14
  *
15
15
  * All fields optional — the backend omits anything it could not measure.
16
16
  */
@@ -54,9 +54,16 @@ export type ChatStreamEvent =
54
54
  }
55
55
  | {
56
56
  type: 'message_end';
57
- tokensIn?: number;
58
- tokensOut?: number;
59
- sources?: ChatSource[];
57
+ /**
58
+ * Generic finalize patch merged into the message on done — the ONE
59
+ * slot every finalize feature rides (tokens, sources, blocks, OCR
60
+ * text, attachments-finalized, local-preview metadata). `content`
61
+ * here REPLACES the accumulated bubble body (the append-only gap).
62
+ * The reducer applies it as a `Partial<ChatMessage>` merge — the same
63
+ * mechanism `message_metrics`/`resolved_model` already use. Absent →
64
+ * `message_end` is a pure terminal no-op (just clears `isStreaming`).
65
+ */
66
+ patch?: Partial<ChatMessage>;
60
67
  }
61
68
  | {
62
69
  /**
@@ -33,13 +33,15 @@ export type {
33
33
  JsonBlock,
34
34
  MermaidBlock,
35
35
  CodeBlock,
36
- LottieBlock,
36
+ DiffBlock,
37
+ LinkBlock,
37
38
  CustomBlock,
38
39
  } from './block';
40
+ export type { LinkPreviewData, ResolveLinkPreview } from '../../../common/link-preview';
39
41
  export type { ChatMessage } from './message';
40
42
  export { DEFAULT_LABELS } from './labels';
41
43
  export type { ChatLabels } from './labels';
42
- export type { ChatConfig, ChatPrefs, ChatDisplayMode } from './config';
44
+ export type { ChatConfig, ChatPrefs, ChatDisplayMode, ChatLinkPreviewConfig } from './config';
43
45
  export type { ChatStreamEvent, ChatMessageMetrics } from './events';
44
46
  export type {
45
47
  CreateSessionOptions,
@@ -17,6 +17,13 @@ export interface ChatMessage {
17
17
  content: string;
18
18
  /** epoch ms — serializable. */
19
19
  createdAt: number;
20
+ /**
21
+ * Stable per-session transcript position, supplied by hosts whose backend
22
+ * keys messages by an absolute sequence (e.g. a monotonic `chat_history.seq`).
23
+ * The synthetic `id` is a UI key only; `seq` is the durable handle the host
24
+ * passes back to an authoritative edit/truncate API. Optional — engines that
25
+ * don't track a backend seq leave it undefined. */
26
+ seq?: number;
20
27
  isStreaming?: boolean;
21
28
  isError?: boolean;
22
29
  /** Bumps on edit so memo keys invalidate. */
@@ -104,8 +104,8 @@ const TOKEN_CLASS: Record<string, string> = {
104
104
  string: 'text-[#a31515] dark:text-emerald-400',
105
105
  number: 'text-[#098658] dark:text-sky-400',
106
106
  boolean: 'text-[#0550ae] dark:text-amber-400',
107
- null: 'text-neutral-500 dark:text-muted-foreground/60',
108
- punctuation: 'text-neutral-700 dark:text-muted-foreground',
107
+ null: 'text-muted-foreground/70 dark:text-muted-foreground/60',
108
+ punctuation: 'text-muted-foreground dark:text-muted-foreground',
109
109
  };
110
110
 
111
111
  function TokenSpan({
@@ -131,7 +131,7 @@ function HighlightMatch({ text, query }: { text: string; query: string }) {
131
131
  return (
132
132
  <>
133
133
  {text.slice(0, idx)}
134
- <mark className="rounded-sm bg-amber-200/60 px-0.5 text-inherit dark:bg-amber-500/30">
134
+ <mark className="rounded-sm bg-warning/30 px-0.5 text-inherit">
135
135
  {text.slice(idx, idx + query.length)}
136
136
  </mark>
137
137
  {text.slice(idx + query.length)}
@@ -189,7 +189,7 @@ function JsonNode({
189
189
 
190
190
  const rowClass = cn(
191
191
  'group flex items-center gap-0 py-px hover:bg-muted/40 rounded-sm',
192
- nodeMatches && 'bg-amber-100/40 dark:bg-amber-900/20',
192
+ nodeMatches && 'bg-warning/15',
193
193
  );
194
194
 
195
195
  const rowStyle: CSSProperties = { paddingLeft: `${depth * 20 + 8}px` };
@@ -264,7 +264,7 @@ function JsonNode({
264
264
  className={copyIconClass}
265
265
  >
266
266
  {pathCopied ? (
267
- <CopyPlus className="size-3 text-emerald-500" />
267
+ <CopyPlus className="size-3 text-success" />
268
268
  ) : (
269
269
  <CopyPlus className="size-3" />
270
270
  )}
@@ -317,7 +317,7 @@ function JsonNode({
317
317
  className={copyIconClass}
318
318
  >
319
319
  {pathCopied ? (
320
- <CopyPlus className="size-3 text-emerald-500" />
320
+ <CopyPlus className="size-3 text-success" />
321
321
  ) : (
322
322
  <CopyPlus className="size-3" />
323
323
  )}
@@ -489,7 +489,7 @@ function Toolbar({
489
489
  'copy',
490
490
  copiedAll ? 'Copied!' : 'Copy JSON',
491
491
  copiedAll ? (
492
- <Check className={cn(iconCls, 'text-emerald-500')} />
492
+ <Check className={cn(iconCls, 'text-success')} />
493
493
  ) : (
494
494
  <Copy className={iconCls} />
495
495
  ),