@xp266/dshtui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/bin/dshtui.js +65 -0
  2. package/cordis.patch.yml +81 -0
  3. package/lib/contract/index.d.mts +2 -0
  4. package/lib/contract/index.mjs +5 -0
  5. package/lib/dialog.d.mts +156 -0
  6. package/lib/dialog.mjs +3 -0
  7. package/lib/index-DqnaSXqP.d.mts +823 -0
  8. package/lib/index.d.mts +17 -0
  9. package/lib/index.mjs +11404 -0
  10. package/lib/prod-react-Dzof4qJx.mjs +4 -0
  11. package/lib/surface-DdxzIgIY.mjs +3126 -0
  12. package/lib/vendor.d.mts +3 -0
  13. package/lib/vendor.mjs +4 -0
  14. package/package.json +128 -0
  15. package/src/apply-theme.ts +8 -0
  16. package/src/boot-log.ts +35 -0
  17. package/src/chat/blocks.ts +41 -0
  18. package/src/chat/bridge.ts +1076 -0
  19. package/src/chat/builtin-tool-views.ts +53 -0
  20. package/src/chat/chat-face.ts +16 -0
  21. package/src/chat/chat-nodes.ts +53 -0
  22. package/src/chat/efforts.ts +4 -0
  23. package/src/chat/interactions.ts +329 -0
  24. package/src/chat/models.ts +263 -0
  25. package/src/chat/partial-json.ts +125 -0
  26. package/src/chat/presets.ts +26 -0
  27. package/src/chat/question-view.ts +106 -0
  28. package/src/chat/retry-status.ts +33 -0
  29. package/src/chat/session-groups.ts +34 -0
  30. package/src/chat/session-list.ts +311 -0
  31. package/src/chat/store.ts +636 -0
  32. package/src/chat/todo-view.ts +66 -0
  33. package/src/chat/tool-view.ts +311 -0
  34. package/src/chat/tool-views.ts +114 -0
  35. package/src/contract/index.ts +704 -0
  36. package/src/contract/upstream.ts +165 -0
  37. package/src/core/caret-nonce.ts +19 -0
  38. package/src/core/composer-layout.ts +65 -0
  39. package/src/core/edit.ts +152 -0
  40. package/src/core/field-view.ts +52 -0
  41. package/src/core/fields.ts +185 -0
  42. package/src/core/metrics.ts +26 -0
  43. package/src/core/paste.ts +149 -0
  44. package/src/core/segments.ts +112 -0
  45. package/src/core/text.ts +290 -0
  46. package/src/dialog.ts +11 -0
  47. package/src/env.ts +38 -0
  48. package/src/harness-home.ts +22 -0
  49. package/src/hot-theme.ts +56 -0
  50. package/src/index.tsx +185 -0
  51. package/src/kernel/registry.ts +102 -0
  52. package/src/kernel/surface.ts +33 -0
  53. package/src/log.ts +249 -0
  54. package/src/model/message.ts +89 -0
  55. package/src/model/selection.ts +63 -0
  56. package/src/performance-guard.ts +28 -0
  57. package/src/runtime/prod-react.ts +10 -0
  58. package/src/terminal/background.ts +66 -0
  59. package/src/terminal/capabilities.ts +54 -0
  60. package/src/terminal/clipboard-backends.ts +69 -0
  61. package/src/terminal/clipboard.ts +161 -0
  62. package/src/terminal/cursor-shape.ts +13 -0
  63. package/src/terminal/glyphs.ts +44 -0
  64. package/src/terminal/mouse.ts +154 -0
  65. package/src/terminal/probe.ts +61 -0
  66. package/src/terminal/screen.ts +349 -0
  67. package/src/theme-settings.ts +31 -0
  68. package/src/theme.ts +319 -0
  69. package/src/ui/app.tsx +454 -0
  70. package/src/ui/chrome/caps.tsx +9 -0
  71. package/src/ui/chrome/hint-service.ts +87 -0
  72. package/src/ui/chrome/input-status.ts +27 -0
  73. package/src/ui/chrome/key-gate.tsx +29 -0
  74. package/src/ui/chrome/status-bar.tsx +147 -0
  75. package/src/ui/contributions.ts +47 -0
  76. package/src/ui/dialog/defaults-dialog.tsx +152 -0
  77. package/src/ui/dialog/dialog-item.tsx +38 -0
  78. package/src/ui/dialog/dialog.tsx +386 -0
  79. package/src/ui/dialog/effort-dialog.tsx +45 -0
  80. package/src/ui/dialog/geometry.ts +131 -0
  81. package/src/ui/dialog/items.ts +124 -0
  82. package/src/ui/dialog/list-dialog.tsx +63 -0
  83. package/src/ui/dialog/models-dialog.tsx +203 -0
  84. package/src/ui/dialog/presets-dialog.tsx +46 -0
  85. package/src/ui/dialog/providers-dialog.tsx +313 -0
  86. package/src/ui/dialog/sessions-dialog.tsx +163 -0
  87. package/src/ui/dialog/sizes.ts +7 -0
  88. package/src/ui/dialog/status-lines.ts +11 -0
  89. package/src/ui/dialog/todo-dialog.tsx +29 -0
  90. package/src/ui/dialog/use-dialog-input.ts +186 -0
  91. package/src/ui/extension-point.ts +188 -0
  92. package/src/ui/home-logo.ts +132 -0
  93. package/src/ui/hooks/use-async-action.ts +25 -0
  94. package/src/ui/hooks/use-async-list.ts +71 -0
  95. package/src/ui/hooks/use-caret.ts +24 -0
  96. package/src/ui/hooks/use-chat-events.ts +153 -0
  97. package/src/ui/hooks/use-mouse-selection.ts +352 -0
  98. package/src/ui/hooks/use-scroll.ts +49 -0
  99. package/src/ui/hooks/use-terminal-size.ts +20 -0
  100. package/src/ui/input/commands.ts +118 -0
  101. package/src/ui/input/composer-bus.ts +14 -0
  102. package/src/ui/input/composer-fields.ts +111 -0
  103. package/src/ui/input/composer-keys.ts +21 -0
  104. package/src/ui/input/composer-paste.ts +20 -0
  105. package/src/ui/input/input-bar.tsx +279 -0
  106. package/src/ui/input/use-composer.ts +495 -0
  107. package/src/ui/key-arbiter.ts +37 -0
  108. package/src/ui/keymap.ts +15 -0
  109. package/src/ui/layout-service.ts +77 -0
  110. package/src/ui/message/layout.ts +763 -0
  111. package/src/ui/message/md/block.ts +247 -0
  112. package/src/ui/message/md/engine.ts +430 -0
  113. package/src/ui/message/md/extensions.ts +58 -0
  114. package/src/ui/message/md/highlight.ts +626 -0
  115. package/src/ui/message/md/index.ts +14 -0
  116. package/src/ui/message/md/inline.ts +97 -0
  117. package/src/ui/message/md/palette.ts +87 -0
  118. package/src/ui/message/message-list.tsx +69 -0
  119. package/src/ui/message/message-row.tsx +137 -0
  120. package/src/ui/message/message-views.ts +16 -0
  121. package/src/ui/message/renderers.ts +39 -0
  122. package/src/ui/message/tool-diff.ts +188 -0
  123. package/src/ui/message/warmup.ts +41 -0
  124. package/src/ui/message/wave.ts +98 -0
  125. package/src/ui/overlay.ts +22 -0
  126. package/src/ui/panels/approval-panel.tsx +193 -0
  127. package/src/ui/panels/exports.ts +3 -0
  128. package/src/ui/panels/question-model.ts +424 -0
  129. package/src/ui/panels/question-panel.tsx +154 -0
  130. package/src/ui/panels/surface.tsx +106 -0
  131. package/src/ui/panels-builtin.tsx +86 -0
  132. package/src/ui/pointer/builtins.ts +281 -0
  133. package/src/ui/pointer/registry.ts +25 -0
  134. package/src/ui/region.tsx +31 -0
  135. package/src/ui/selection/service.ts +92 -0
  136. package/src/ui/selection-registry.ts +88 -0
  137. package/src/ui/selection.tsx +148 -0
  138. package/src/ui/spinner-tick.tsx +24 -0
  139. package/src/ui/use-available-windows.ts +31 -0
  140. package/src/ui/widgets/actions.tsx +84 -0
  141. package/src/ui/widgets/button.tsx +57 -0
  142. package/src/ui/widgets/checkbox.tsx +58 -0
  143. package/src/ui/widgets/header.tsx +36 -0
  144. package/src/ui/widgets/index.ts +8 -0
  145. package/src/ui/widgets/input.tsx +76 -0
  146. package/src/ui/widgets/registry.ts +32 -0
  147. package/src/ui/widgets/search.tsx +59 -0
  148. package/src/ui/widgets/select.tsx +97 -0
  149. package/src/ui/widgets/static.tsx +37 -0
  150. package/src/ui/widgets/types.ts +1 -0
  151. package/src/ui/window-services-bridge.ts +71 -0
  152. package/src/ui/window-services.ts +20 -0
  153. package/src/ui/windows/models-window.tsx +15 -0
  154. package/src/ui/windows/providers-window.tsx +14 -0
  155. package/src/ui/windows/service-window.tsx +18 -0
  156. package/src/ui/windows/sessions-window.tsx +25 -0
  157. package/src/ui/windows-builtin.tsx +94 -0
  158. package/src/ui/windows.ts +18 -0
  159. package/src/vendor.ts +12 -0
@@ -0,0 +1,58 @@
1
+ import { keyedRegistry } from '../../../kernel/registry.ts'
2
+ import Prism from 'prismjs'
3
+ import type { MarkdownBlockContribution, MarkdownInlineContribution, PrismGrammarContribution } from '../../../contract/index.ts'
4
+
5
+ const blocks = keyedRegistry<MarkdownBlockContribution>()
6
+ const inline = keyedRegistry<MarkdownInlineContribution>()
7
+ const languages = keyedRegistry<PrismGrammarContribution>()
8
+
9
+ /** Grammars contributed by plugins resolve ahead of Prism.languages lookups. */
10
+ const pluginGrammars = new Map<string, Prism.Grammar>()
11
+ const asGrammar = (grammar: unknown): Prism.Grammar => grammar as Prism.Grammar
12
+
13
+ export function registerMarkdownBlock(contribution: MarkdownBlockContribution): () => void {
14
+ return blocks.register(contribution.type, contribution, { order: contribution.order })
15
+ }
16
+
17
+ export function registerMarkdownInline(contribution: MarkdownInlineContribution): () => void {
18
+ return inline.register(contribution.type, contribution, { order: contribution.order })
19
+ }
20
+
21
+ export function registerPrismGrammar(contribution: PrismGrammarContribution): () => void {
22
+ const dispose = languages.register(contribution.id, contribution)
23
+ const touched = applyPrismGrammar(contribution)
24
+ for (const name of touched) pluginGrammars.set(name, asGrammar(contribution.grammar))
25
+ return () => {
26
+ dispose()
27
+ for (const name of touched) {
28
+ if (pluginGrammars.get(name) === contribution.grammar) pluginGrammars.delete(name)
29
+ }
30
+ revertPrismGrammar(touched)
31
+ }
32
+ }
33
+
34
+ export function pluginGrammarOf(lang: string): Prism.Grammar | undefined {
35
+ return pluginGrammars.get(lang.toLowerCase())
36
+ }
37
+
38
+ function markdownBlockOf(token: { type: string }) {
39
+ return blocks.get(token.type)
40
+ }
41
+
42
+ function markdownInlineOf(token: { type: string }) {
43
+ return inline.get(token.type)
44
+ }
45
+
46
+ export const markdownExtensions = { markdownBlockOf, markdownInlineOf }
47
+
48
+ function applyPrismGrammar(contribution: PrismGrammarContribution): string[] {
49
+ const prism = Prism as unknown as { languages: Record<string, unknown> }
50
+ const touched = [contribution.id, ...(contribution.aliases ?? [])]
51
+ for (const name of touched) prism.languages[name] = contribution.grammar
52
+ return touched
53
+ }
54
+
55
+ function revertPrismGrammar(names: string[]): void {
56
+ const prism = Prism as unknown as { languages: Record<string, unknown> }
57
+ for (const name of names) delete prism.languages[name]
58
+ }
@@ -0,0 +1,626 @@
1
+ import { createRequire } from 'node:module'
2
+ import { performance } from 'node:perf_hooks'
3
+ import Prism from 'prismjs'
4
+ import { COLORS } from '../../../theme.ts'
5
+ import type { MarkStyle, Segment } from '../../../core/segments.ts'
6
+ import { pluginGrammarOf } from './extensions.ts'
7
+
8
+ type CodeRole =
9
+ | 'comment'
10
+ | 'string'
11
+ | 'number'
12
+ | 'constant'
13
+ | 'keyword'
14
+ | 'function'
15
+ | 'type'
16
+ | 'variable'
17
+ | 'operator'
18
+ | 'added'
19
+ | 'removed'
20
+ | 'boldFlag'
21
+ | 'italicFlag'
22
+ | 'strikeFlag'
23
+
24
+ const TOKEN_ROLES: Record<string, CodeRole> = {
25
+ comment: 'comment',
26
+ shebang: 'comment',
27
+ hashbang: 'comment',
28
+ prolog: 'comment',
29
+ doctype: 'comment',
30
+ cdata: 'comment',
31
+ 'included-cdata': 'comment',
32
+ 'internal-subset': 'comment',
33
+ doc: 'comment',
34
+ 'front-matter': 'comment',
35
+ 'multiline-comment': 'comment',
36
+ 'comment-block': 'comment',
37
+
38
+ string: 'string',
39
+ char: 'string',
40
+ regex: 'string',
41
+ url: 'string',
42
+ scalar: 'string',
43
+ heredoc: 'string',
44
+ 'raw-string': 'string',
45
+ 'multiline-string': 'string',
46
+ 'string-literal': 'string',
47
+ pystring: 'string',
48
+ quoted: 'string',
49
+ value: 'string',
50
+ 'template-string': 'string',
51
+ 'triple-quoted-string': 'string',
52
+ 'interpolation-string': 'string',
53
+ 'string-interpolation': 'string',
54
+ 'attr-value': 'string',
55
+ 'regex-source': 'string',
56
+ 'header-value': 'string',
57
+ 'reason-phrase': 'string',
58
+ 'url-reference': 'string',
59
+ 'template-punctuation': 'string',
60
+
61
+ number: 'number',
62
+ literal: 'number',
63
+ datetime: 'number',
64
+ date: 'number',
65
+ time: 'number',
66
+ version: 'number',
67
+ 'status-code': 'number',
68
+ 'http-version': 'number',
69
+ 'quoted-number': 'number',
70
+
71
+ boolean: 'constant',
72
+ symbol: 'constant',
73
+ constant: 'constant',
74
+ null: 'constant',
75
+ escape: 'constant',
76
+ 'special-escape': 'constant',
77
+ nil: 'constant',
78
+ color: 'constant',
79
+ 'format-spec': 'constant',
80
+ 'conversion-option': 'constant',
81
+ 'regex-flags': 'constant',
82
+ 'file-descriptor': 'constant',
83
+ 'special-attr': 'constant',
84
+ options: 'constant',
85
+ 'command-line-option': 'constant',
86
+
87
+ keyword: 'keyword',
88
+ atrule: 'keyword',
89
+ rule: 'keyword',
90
+ directive: 'keyword',
91
+ preprocessor: 'keyword',
92
+ import: 'keyword',
93
+ 'module-declaration': 'keyword',
94
+ module: 'keyword',
95
+ package: 'keyword',
96
+ namespace: 'keyword',
97
+ attribute: 'keyword',
98
+ instruction: 'keyword',
99
+ target: 'keyword',
100
+ declare: 'keyword',
101
+ statement: 'keyword',
102
+ feature: 'keyword',
103
+ scenario: 'keyword',
104
+ section: 'keyword',
105
+ 'section-name': 'keyword',
106
+ heading: 'keyword',
107
+ title: 'keyword',
108
+ diff: 'keyword',
109
+
110
+ annotation: 'function',
111
+ decorator: 'function',
112
+ function: 'function',
113
+ 'function-variable': 'function',
114
+ 'function-name': 'function',
115
+ macro: 'function',
116
+ 'macro-name': 'function',
117
+ mixin: 'function',
118
+ 'mixin-usage': 'function',
119
+ method: 'function',
120
+ 'c-style-function': 'function',
121
+ 'generic-function': 'function',
122
+ 'function-definition': 'function',
123
+ 'method-definition': 'function',
124
+ 'constructor-invocation': 'function',
125
+ 'generic-method': 'function',
126
+ 'selector-function-argument': 'function',
127
+ lambda: 'function',
128
+ defun: 'function',
129
+ 'quoted-function': 'function',
130
+ command: 'function',
131
+
132
+ 'class-name': 'type',
133
+ type: 'type',
134
+ generics: 'type',
135
+ generic: 'type',
136
+ 'builtin-type': 'type',
137
+ 'type-definition': 'type',
138
+ 'type-expression': 'type',
139
+ 'type-list': 'type',
140
+ 'return-type': 'type',
141
+ 'fragment-specifier': 'type',
142
+ 'class-name-definition': 'type',
143
+
144
+ variable: 'variable',
145
+ parameter: 'variable',
146
+ property: 'variable',
147
+ key: 'variable',
148
+ identifier: 'variable',
149
+ environment: 'variable',
150
+ field: 'variable',
151
+ argument: 'variable',
152
+ args: 'variable',
153
+ arguments: 'variable',
154
+ 'argument-name': 'variable',
155
+ 'named-parameter': 'variable',
156
+ 'literal-property': 'variable',
157
+ 'string-property': 'variable',
158
+ 'attr-name': 'variable',
159
+ 'assign-left': 'variable',
160
+ 'closure-params': 'variable',
161
+ 'lambda-parameter': 'variable',
162
+ interpolation: 'variable',
163
+ hvariable: 'variable',
164
+ 'property-literal': 'variable',
165
+ 'property-declaration': 'variable',
166
+ 'variable-declaration': 'variable',
167
+ 'variable-line': 'variable',
168
+ 'header-name': 'variable',
169
+ label: 'variable',
170
+ user: 'variable',
171
+ keys: 'variable',
172
+ placeholder: 'variable',
173
+
174
+ operator: 'operator',
175
+ punctuation: 'operator',
176
+ name: 'operator',
177
+ selector: 'operator',
178
+ entity: 'operator',
179
+ important: 'operator',
180
+ script: 'operator',
181
+ style: 'operator',
182
+ at: 'operator',
183
+ arrow: 'operator',
184
+ spread: 'operator',
185
+ ellipsis: 'operator',
186
+ range: 'operator',
187
+ delimiter: 'operator',
188
+ dot: 'operator',
189
+ 'double-colon': 'operator',
190
+ 'percent-operator': 'operator',
191
+ brackets: 'operator',
192
+ tag: 'operator',
193
+ 'tag-name': 'operator',
194
+ 'closure-punctuation': 'operator',
195
+ 'interpolation-punctuation': 'operator',
196
+ 'range-punctuation': 'operator',
197
+ 'script-punctuation': 'operator',
198
+ 'flow-punctuation': 'operator',
199
+ 'operator-like-punctuation': 'operator',
200
+ 'regex-delimiter': 'operator',
201
+ 'record-arguments': 'operator',
202
+ 'attribute-arguments': 'operator',
203
+ 'deleted-arrow': 'removed',
204
+ 'inserted-arrow': 'added',
205
+
206
+ insertion: 'added',
207
+ inserted: 'added',
208
+ 'inserted-sign': 'added',
209
+ deleted: 'removed',
210
+ 'deleted-sign': 'removed',
211
+
212
+ bold: 'boldFlag',
213
+ italic: 'italicFlag',
214
+ strike: 'strikeFlag',
215
+ blockquote: 'italicFlag',
216
+ code: 'string',
217
+
218
+ coord: 'comment',
219
+ }
220
+
221
+ const ROLE_STYLE_LIGHT: Record<CodeRole, () => MarkStyle> = {
222
+ comment: () => ({ color: COLORS.codeComment, italic: true }),
223
+ string: () => ({ color: COLORS.codeString }),
224
+ number: () => ({ color: COLORS.codeNumber }),
225
+ constant: () => ({ color: COLORS.codeConstant }),
226
+ keyword: () => ({ color: COLORS.codeKeyword }),
227
+ function: () => ({ color: COLORS.codeFunction }),
228
+ type: () => ({ color: COLORS.codeType }),
229
+ variable: () => ({ color: COLORS.codeVariable }),
230
+ operator: () => ({ color: COLORS.codeOperator }),
231
+ added: () => ({ color: COLORS.diffAdded }),
232
+ removed: () => ({ color: COLORS.diffRemoved }),
233
+ boldFlag: () => ({ bold: true }),
234
+ italicFlag: () => ({ italic: true }),
235
+ strikeFlag: () => ({ strike: true }),
236
+ }
237
+
238
+ const ROLE_STYLE_THINKING: Record<CodeRole, () => MarkStyle> = {
239
+ comment: () => ({ color: COLORS.thinkCodeComment, italic: true }),
240
+ string: () => ({ color: COLORS.thinkCodeString }),
241
+ number: () => ({ color: COLORS.thinkCodeNumber }),
242
+ constant: () => ({ color: COLORS.thinkCodeConstant }),
243
+ keyword: () => ({ color: COLORS.thinkCodeKeyword }),
244
+ function: () => ({ color: COLORS.thinkCodeFunction }),
245
+ type: () => ({ color: COLORS.thinkCodeType }),
246
+ variable: () => ({ color: COLORS.thinkCodeVariable }),
247
+ operator: () => ({ color: COLORS.thinkCodePlain }),
248
+ added: () => ({ color: COLORS.diffAdded }),
249
+ removed: () => ({ color: COLORS.diffRemoved }),
250
+ boldFlag: () => ({ bold: true }),
251
+ italicFlag: () => ({ italic: true }),
252
+ strikeFlag: () => ({ strike: true }),
253
+ }
254
+
255
+ function buildStyles(roleFns: Record<CodeRole, () => MarkStyle>): Record<string, MarkStyle> {
256
+ const out: Record<string, MarkStyle> = {}
257
+ for (const [token, role] of Object.entries(TOKEN_ROLES)) {
258
+ Object.defineProperty(out, token, {
259
+ enumerable: true,
260
+ get: roleFns[role],
261
+ })
262
+ }
263
+ return out
264
+ }
265
+
266
+ export const codeStyles = buildStyles(ROLE_STYLE_LIGHT)
267
+ export const codeStylesThinking = buildStyles(ROLE_STYLE_THINKING)
268
+
269
+ export const LANGUAGE_ALIASES: Record<string, string> = {
270
+ py: 'python',
271
+ python3: 'python',
272
+ js: 'javascript',
273
+ node: 'javascript',
274
+ nodejs: 'javascript',
275
+ mjs: 'javascript',
276
+ cjs: 'javascript',
277
+ jsx: 'javascript',
278
+ ts: 'typescript',
279
+ tsx: 'typescript',
280
+ sh: 'bash',
281
+ shell: 'bash',
282
+ zsh: 'bash',
283
+ ksh: 'bash',
284
+ rb: 'ruby',
285
+ rs: 'rust',
286
+ golang: 'go',
287
+ kt: 'kotlin',
288
+ kts: 'kotlin',
289
+ 'c++': 'cpp',
290
+ c: 'c',
291
+ h: 'c',
292
+ cpp: 'cpp',
293
+ cc: 'cpp',
294
+ cxx: 'cpp',
295
+ hpp: 'cpp',
296
+ cs: 'csharp',
297
+ 'c#': 'csharp',
298
+ objc: 'objectivec',
299
+ 'objective-c': 'objectivec',
300
+ vb: 'vbnet',
301
+ 'visual-basic': 'vbnet',
302
+ fs: 'fsharp',
303
+ pl: 'perl',
304
+ pm: 'perl',
305
+ ps1: 'powershell',
306
+ pwsh: 'powershell',
307
+ bat: 'batch',
308
+ cmd: 'batch',
309
+ htm: 'markup',
310
+ html: 'markup',
311
+ xhtml: 'markup',
312
+ svg: 'markup',
313
+ xml: 'markup',
314
+ vue: 'markup',
315
+ xsl: 'markup',
316
+ plist: 'markup',
317
+ styl: 'stylus',
318
+ yml: 'yaml',
319
+ cfg: 'ini',
320
+ tsv: 'csv',
321
+ mysql: 'sql',
322
+ pgsql: 'sql',
323
+ sqlite: 'sql',
324
+ sql: 'sql',
325
+ gql: 'graphql',
326
+ md: 'markdown',
327
+ mdwn: 'markdown',
328
+ patch: 'diff',
329
+ dockerfile: 'docker',
330
+ containerfile: 'docker',
331
+ make: 'makefile',
332
+ mk: 'makefile',
333
+ gitignore: 'ignore',
334
+ terraform: 'hcl',
335
+ tf: 'hcl',
336
+ apache: 'apacheconf',
337
+ ex: 'elixir',
338
+ exs: 'elixir',
339
+ erl: 'erlang',
340
+ hs: 'haskell',
341
+ ml: 'ocaml',
342
+ pas: 'pascal',
343
+ f90: 'fortran',
344
+ clj: 'clojure',
345
+ scm: 'scheme',
346
+ el: 'lisp',
347
+ tex: 'latex',
348
+ rst: 'rest',
349
+ adoc: 'asciidoc',
350
+ viml: 'vim',
351
+ sol: 'solidity',
352
+ feature: 'gherkin',
353
+ plantuml: 'plant-uml',
354
+ console: 'shell-session',
355
+ jade: 'pug',
356
+ hbs: 'handlebars',
357
+ mustache: 'handlebars',
358
+ jinja: 'django',
359
+ jinja2: 'django',
360
+ coffee: 'coffeescript',
361
+ json: 'json',
362
+ jsonc: 'json',
363
+ java: 'java',
364
+ swift: 'swift',
365
+ php: 'php',
366
+ lua: 'lua',
367
+ toml: 'toml',
368
+ css: 'css',
369
+ scss: 'scss',
370
+ less: 'less',
371
+ }
372
+
373
+ const LANGUAGE_COMPONENTS: string[] = [
374
+ 'clike', 'markup', 'css', 'markup-templating', 'javascript', 'json', 'ruby', 'c', 'bash',
375
+ 'python', 'typescript', 'jsx', 'tsx', 'coffeescript', 'flow', 'json5', 'jsonp', 'cpp',
376
+ 'objectivec', 'hlsl', 'php', 'handlebars', 'django', 'ejs', 'crystal', 'haml', 'gradle',
377
+ 'markdown', 'go', 'rust', 'java', 'csharp', 'kotlin', 'scala', 'swift', 'dart', 'lua',
378
+ 'perl', 'r', 'groovy', 'powershell', 'sql', 'yaml', 'toml', 'ini', 'properties', 'csv',
379
+ 'docker', 'makefile', 'git', 'diff', 'regex', 'graphql', 'http', 'protobuf', 'elixir',
380
+ 'erlang', 'haskell', 'ocaml', 'pascal', 'basic', 'vbnet', 'fsharp', 'clojure', 'scheme',
381
+ 'lisp', 'julia', 'matlab', 'fortran', 'latex', 'rest', 'asciidoc', 'vim', 'wasm', 'zig',
382
+ 'nim', 'nix', 'v', 'd', 'qml', 'glsl', 'elm', 'solidity', 'verilog', 'vhdl', 'nginx',
383
+ 'apacheconf', 'hcl', 'cmake', 'systemd', 'editorconfig', 'ignore', 'gherkin', 'mermaid',
384
+ 'plant-uml', 'shell-session', 'batch', 'sass', 'scss', 'less', 'stylus', 'twig', 'liquid',
385
+ 'pug', 'awk', 'jq', 'applescript', 'autohotkey', 'autoit', 'haxe', 'cobol', 'ada',
386
+ 'turtle', 'sparql', 'mongodb',
387
+ ]
388
+
389
+ let ensured = false
390
+
391
+ export function ensureGrammars(): void {
392
+ if (ensured) return
393
+ ensured = true
394
+ try {
395
+ const req = createRequire(import.meta.url)
396
+ for (const name of LANGUAGE_COMPONENTS) {
397
+ try {
398
+ req(`prismjs/components/prism-${name}.js`)
399
+ } catch {
400
+ // A grammar that is not shipped in this prismjs build is skipped silently.
401
+ }
402
+ }
403
+ } catch {
404
+ // Bundled prismjs without component files: highlighting falls back to plain text.
405
+ }
406
+ }
407
+
408
+ const WARM_SLICE_MS = 6
409
+
410
+ let warmStarted = false
411
+ const warmListeners: Array<() => void> = []
412
+
413
+ const warmState = { done: false, remaining: LANGUAGE_COMPONENTS.length }
414
+
415
+ function loadNext(names: string[]): void {
416
+ const t0 = performance.now()
417
+ while (names.length > 0) {
418
+ const name = names.shift()!
419
+ try {
420
+ createRequireOnce()(loadSpec(name))
421
+ } catch {
422
+ // A grammar that is not shipped in this prismjs build is skipped silently.
423
+ }
424
+ warmState.remaining = names.length
425
+ if (performance.now() - t0 >= WARM_SLICE_MS) break
426
+ }
427
+ if (names.length > 0) {
428
+ setTimeout(() => loadNext(names), 0)
429
+ return
430
+ }
431
+ warmState.done = true
432
+ for (const cb of warmListeners.splice(0)) cb()
433
+ }
434
+
435
+ let requireFn: ReturnType<typeof createRequire> | undefined
436
+
437
+ function createRequireOnce(): ReturnType<typeof createRequire> {
438
+ if (requireFn === undefined) requireFn = createRequire(import.meta.url)
439
+ return requireFn
440
+ }
441
+
442
+ function loadSpec(name: string): string {
443
+ return `prismjs/components/prism-${name}.js`
444
+ }
445
+
446
+ export function warmLanguages(): void {
447
+ // Must not bail out when ensureGrammars() already ran synchronously: the
448
+ // async warm loop is what flips warmState.done and fires onLanguagesWarm.
449
+ if (warmStarted) return
450
+ warmStarted = true
451
+ const queue = [...LANGUAGE_COMPONENTS]
452
+ setTimeout(() => loadNext(queue), 0)
453
+ }
454
+
455
+ export function onLanguagesWarm(cb: () => void): void {
456
+ if (warmState.done) cb()
457
+ else warmListeners.push(cb)
458
+ }
459
+
460
+ const HIGHLIGHT_CACHE_MAX = 4000
461
+ const HIGHLIGHT_CACHE_MAX_BYTES = 16 * 1024 * 1024
462
+ // Segment objects cost far more heap than their text; the constant keeps the
463
+ // byte accounting within ~2x of actual heap usage.
464
+ const SEGMENT_HEAP_BYTES = 128
465
+
466
+ function segmentsHeapBytes(segments: Segment[] | null): number {
467
+ if (segments === null) return 0
468
+ let bytes = 0
469
+ for (const segment of segments) bytes += SEGMENT_HEAP_BYTES + segment.text.length
470
+ return bytes
471
+ }
472
+
473
+ interface HighlightCacheEntry {
474
+ segments: Segment[] | null
475
+ bytes: number
476
+ }
477
+
478
+ const highlightCache = new Map<string, HighlightCacheEntry>()
479
+ let highlightCacheBytes = 0
480
+
481
+ // Streaming states retain the full source plus one segment per token, so a
482
+ // long session would otherwise pin every streamed code block forever. Both
483
+ // caps evict oldest-first; the entry count keeps small states from pinning
484
+ // slots indefinitely.
485
+ const STREAM_STATE_MAX = 64
486
+ const STREAM_STATE_MAX_BYTES = 8 * 1024 * 1024
487
+
488
+ interface StreamState {
489
+ source: string
490
+ segments: Segment[]
491
+ bytes: number
492
+ }
493
+
494
+ const streamStates = new Map<string, StreamState>()
495
+ let streamStateBytes = 0
496
+
497
+ export function clearHighlightCache(): void {
498
+ highlightCache.clear()
499
+ highlightCacheBytes = 0
500
+ streamStates.clear()
501
+ streamStateBytes = 0
502
+ }
503
+
504
+ function trackStreamState(stateKey: string, state: StreamState): void {
505
+ const existing = streamStates.get(stateKey)
506
+ if (existing !== undefined) streamStateBytes -= existing.bytes
507
+ streamStates.delete(stateKey)
508
+ streamStates.set(stateKey, state)
509
+ streamStateBytes += state.bytes
510
+ while (streamStates.size > 1 && (streamStates.size > STREAM_STATE_MAX || streamStateBytes > STREAM_STATE_MAX_BYTES)) {
511
+ const oldest = streamStates.keys().next()
512
+ if (oldest.done) break
513
+ const evicted = streamStates.get(oldest.value)
514
+ if (evicted !== undefined) streamStateBytes -= evicted.bytes
515
+ streamStates.delete(oldest.value)
516
+ }
517
+ }
518
+
519
+ function resolveGrammar(lang: string): Prism.Grammar | undefined {
520
+ const plugin = pluginGrammarOf(lang.toLowerCase())
521
+ if (plugin !== undefined) return plugin
522
+ const name = LANGUAGE_ALIASES[lang.toLowerCase()] ?? lang.toLowerCase()
523
+ return Prism.languages[name] as Prism.Grammar | undefined
524
+ }
525
+
526
+ export function highlightCodeBlock(text: string, lang: string, thinking = false, streamId = ''): Segment[] | null {
527
+ if (text === '') return []
528
+ const key = `${thinking ? 'd' : 'l'}\x00${lang}\x00${streamId}\x00${text}`
529
+ const cached = highlightCache.get(key)
530
+ if (cached !== undefined) return cached.segments
531
+ let grammar = resolveGrammar(lang)
532
+ if (grammar === undefined && lang !== '') {
533
+ ensureGrammars()
534
+ grammar = resolveGrammar(lang)
535
+ }
536
+ let result: Segment[] | null
537
+ let incremental = false
538
+ if (grammar === undefined) {
539
+ result = null
540
+ } else {
541
+ const stateKey = `${thinking ? 'd' : 'l'}\x00${streamId}\x00${lang}`
542
+ const state = streamStates.get(stateKey)
543
+ incremental = state !== undefined
544
+ && text.length > state.source.length
545
+ && text.startsWith(state.source)
546
+ && state.source.endsWith('\n')
547
+ try {
548
+ const styles = thinking ? codeStylesThinking : codeStyles
549
+ const plain: MarkStyle = { color: thinking ? COLORS.thinkCodePlain : COLORS.mdCodePlain }
550
+ if (incremental) {
551
+ // The state's segment array is appended in place and shared with the
552
+ // current frame's render result; per-frame cache entries for every
553
+ // snapshot would retain the whole history, so incremental frames
554
+ // skip the cache and only the stream state holds the segments.
555
+ const segments = state!.segments
556
+ appendTokens(segments, Prism.tokenize(text.slice(state!.source.length), grammar!), styles, plain)
557
+ trackStreamState(stateKey, { source: text, segments, bytes: text.length + segmentsHeapBytes(segments) })
558
+ result = segments
559
+ } else {
560
+ const segments: Segment[] = []
561
+ appendTokens(segments, Prism.tokenize(text, grammar!), styles, plain)
562
+ trackStreamState(stateKey, { source: text, segments, bytes: text.length + segmentsHeapBytes(segments) })
563
+ result = segments
564
+ }
565
+ } catch {
566
+ const existing = streamStates.get(stateKey)
567
+ if (existing !== undefined) streamStateBytes -= existing.bytes
568
+ streamStates.delete(stateKey)
569
+ result = null
570
+ }
571
+ }
572
+ const bytes = key.length + segmentsHeapBytes(result)
573
+ evictHighlightCacheIfNeeded()
574
+ highlightCache.set(key, { segments: result, bytes })
575
+ highlightCacheBytes += bytes
576
+ return result
577
+ }
578
+
579
+ function evictHighlightCacheIfNeeded(): void {
580
+ while (highlightCache.size > 0 && (highlightCache.size >= HIGHLIGHT_CACHE_MAX || highlightCacheBytes >= HIGHLIGHT_CACHE_MAX_BYTES)) {
581
+ const oldest = highlightCache.keys().next()
582
+ if (oldest.done) return
583
+ const entry = highlightCache.get(oldest.value)
584
+ if (entry !== undefined) highlightCacheBytes -= entry.bytes
585
+ highlightCache.delete(oldest.value)
586
+ }
587
+ }
588
+
589
+ function styleForToken(token: Prism.Token, styles: Record<string, MarkStyle>): MarkStyle | undefined {
590
+ const direct = styles[token.type]
591
+ if (direct !== undefined) return direct
592
+ const alias = (token as unknown as { alias?: string | string[] }).alias
593
+ if (alias !== undefined) {
594
+ for (const name of Array.isArray(alias) ? alias : [alias]) {
595
+ const style = styles[name]
596
+ if (style !== undefined) return style
597
+ }
598
+ }
599
+ return undefined
600
+ }
601
+
602
+ function appendTokens(segments: Segment[], tokens: Iterable<string | Prism.Token>, styles: Record<string, MarkStyle>, plain: MarkStyle): void {
603
+ for (const token of tokens) {
604
+ if (typeof token === 'string') {
605
+ if (token !== '') segments.push({ text: token, style: plain })
606
+ } else {
607
+ appendToken(segments, token, styles, plain)
608
+ }
609
+ }
610
+ }
611
+
612
+ function appendToken(segments: Segment[], token: Prism.Token, styles: Record<string, MarkStyle>, plain: MarkStyle): void {
613
+ const style = styleForToken(token, styles) ?? plain
614
+ const content = token.content
615
+ if (typeof content === 'string') {
616
+ if (content !== '') segments.push({ text: content, style })
617
+ return
618
+ }
619
+ for (const child of content as (string | Prism.Token)[]) {
620
+ if (typeof child === 'string') {
621
+ if (child !== '') segments.push({ text: child, style })
622
+ } else {
623
+ appendToken(segments, child, styles, plain)
624
+ }
625
+ }
626
+ }
@@ -0,0 +1,14 @@
1
+ export {
2
+ clearHighlightCache,
3
+ codeStyles,
4
+ codeStylesThinking,
5
+ highlightCodeBlock,
6
+ } from './highlight.ts'
7
+ export { createMdPalette } from './palette.ts'
8
+ export type { MdPalette } from './palette.ts'
9
+ export { renderInline } from './inline.ts'
10
+ export { createMarkdownRenderer, renderMarkdown, renderMarkdownStreaming, clearMarkdownStreamStates } from './engine.ts'
11
+ export type {
12
+ MarkdownRenderResult,
13
+ MarkdownRenderer,
14
+ } from './engine.ts'