@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.
- package/bin/dshtui.js +65 -0
- package/cordis.patch.yml +81 -0
- package/lib/contract/index.d.mts +2 -0
- package/lib/contract/index.mjs +5 -0
- package/lib/dialog.d.mts +156 -0
- package/lib/dialog.mjs +3 -0
- package/lib/index-DqnaSXqP.d.mts +823 -0
- package/lib/index.d.mts +17 -0
- package/lib/index.mjs +11404 -0
- package/lib/prod-react-Dzof4qJx.mjs +4 -0
- package/lib/surface-DdxzIgIY.mjs +3126 -0
- package/lib/vendor.d.mts +3 -0
- package/lib/vendor.mjs +4 -0
- package/package.json +128 -0
- package/src/apply-theme.ts +8 -0
- package/src/boot-log.ts +35 -0
- package/src/chat/blocks.ts +41 -0
- package/src/chat/bridge.ts +1076 -0
- package/src/chat/builtin-tool-views.ts +53 -0
- package/src/chat/chat-face.ts +16 -0
- package/src/chat/chat-nodes.ts +53 -0
- package/src/chat/efforts.ts +4 -0
- package/src/chat/interactions.ts +329 -0
- package/src/chat/models.ts +263 -0
- package/src/chat/partial-json.ts +125 -0
- package/src/chat/presets.ts +26 -0
- package/src/chat/question-view.ts +106 -0
- package/src/chat/retry-status.ts +33 -0
- package/src/chat/session-groups.ts +34 -0
- package/src/chat/session-list.ts +311 -0
- package/src/chat/store.ts +636 -0
- package/src/chat/todo-view.ts +66 -0
- package/src/chat/tool-view.ts +311 -0
- package/src/chat/tool-views.ts +114 -0
- package/src/contract/index.ts +704 -0
- package/src/contract/upstream.ts +165 -0
- package/src/core/caret-nonce.ts +19 -0
- package/src/core/composer-layout.ts +65 -0
- package/src/core/edit.ts +152 -0
- package/src/core/field-view.ts +52 -0
- package/src/core/fields.ts +185 -0
- package/src/core/metrics.ts +26 -0
- package/src/core/paste.ts +149 -0
- package/src/core/segments.ts +112 -0
- package/src/core/text.ts +290 -0
- package/src/dialog.ts +11 -0
- package/src/env.ts +38 -0
- package/src/harness-home.ts +22 -0
- package/src/hot-theme.ts +56 -0
- package/src/index.tsx +185 -0
- package/src/kernel/registry.ts +102 -0
- package/src/kernel/surface.ts +33 -0
- package/src/log.ts +249 -0
- package/src/model/message.ts +89 -0
- package/src/model/selection.ts +63 -0
- package/src/performance-guard.ts +28 -0
- package/src/runtime/prod-react.ts +10 -0
- package/src/terminal/background.ts +66 -0
- package/src/terminal/capabilities.ts +54 -0
- package/src/terminal/clipboard-backends.ts +69 -0
- package/src/terminal/clipboard.ts +161 -0
- package/src/terminal/cursor-shape.ts +13 -0
- package/src/terminal/glyphs.ts +44 -0
- package/src/terminal/mouse.ts +154 -0
- package/src/terminal/probe.ts +61 -0
- package/src/terminal/screen.ts +349 -0
- package/src/theme-settings.ts +31 -0
- package/src/theme.ts +319 -0
- package/src/ui/app.tsx +454 -0
- package/src/ui/chrome/caps.tsx +9 -0
- package/src/ui/chrome/hint-service.ts +87 -0
- package/src/ui/chrome/input-status.ts +27 -0
- package/src/ui/chrome/key-gate.tsx +29 -0
- package/src/ui/chrome/status-bar.tsx +147 -0
- package/src/ui/contributions.ts +47 -0
- package/src/ui/dialog/defaults-dialog.tsx +152 -0
- package/src/ui/dialog/dialog-item.tsx +38 -0
- package/src/ui/dialog/dialog.tsx +386 -0
- package/src/ui/dialog/effort-dialog.tsx +45 -0
- package/src/ui/dialog/geometry.ts +131 -0
- package/src/ui/dialog/items.ts +124 -0
- package/src/ui/dialog/list-dialog.tsx +63 -0
- package/src/ui/dialog/models-dialog.tsx +203 -0
- package/src/ui/dialog/presets-dialog.tsx +46 -0
- package/src/ui/dialog/providers-dialog.tsx +313 -0
- package/src/ui/dialog/sessions-dialog.tsx +163 -0
- package/src/ui/dialog/sizes.ts +7 -0
- package/src/ui/dialog/status-lines.ts +11 -0
- package/src/ui/dialog/todo-dialog.tsx +29 -0
- package/src/ui/dialog/use-dialog-input.ts +186 -0
- package/src/ui/extension-point.ts +188 -0
- package/src/ui/home-logo.ts +132 -0
- package/src/ui/hooks/use-async-action.ts +25 -0
- package/src/ui/hooks/use-async-list.ts +71 -0
- package/src/ui/hooks/use-caret.ts +24 -0
- package/src/ui/hooks/use-chat-events.ts +153 -0
- package/src/ui/hooks/use-mouse-selection.ts +352 -0
- package/src/ui/hooks/use-scroll.ts +49 -0
- package/src/ui/hooks/use-terminal-size.ts +20 -0
- package/src/ui/input/commands.ts +118 -0
- package/src/ui/input/composer-bus.ts +14 -0
- package/src/ui/input/composer-fields.ts +111 -0
- package/src/ui/input/composer-keys.ts +21 -0
- package/src/ui/input/composer-paste.ts +20 -0
- package/src/ui/input/input-bar.tsx +279 -0
- package/src/ui/input/use-composer.ts +495 -0
- package/src/ui/key-arbiter.ts +37 -0
- package/src/ui/keymap.ts +15 -0
- package/src/ui/layout-service.ts +77 -0
- package/src/ui/message/layout.ts +763 -0
- package/src/ui/message/md/block.ts +247 -0
- package/src/ui/message/md/engine.ts +430 -0
- package/src/ui/message/md/extensions.ts +58 -0
- package/src/ui/message/md/highlight.ts +626 -0
- package/src/ui/message/md/index.ts +14 -0
- package/src/ui/message/md/inline.ts +97 -0
- package/src/ui/message/md/palette.ts +87 -0
- package/src/ui/message/message-list.tsx +69 -0
- package/src/ui/message/message-row.tsx +137 -0
- package/src/ui/message/message-views.ts +16 -0
- package/src/ui/message/renderers.ts +39 -0
- package/src/ui/message/tool-diff.ts +188 -0
- package/src/ui/message/warmup.ts +41 -0
- package/src/ui/message/wave.ts +98 -0
- package/src/ui/overlay.ts +22 -0
- package/src/ui/panels/approval-panel.tsx +193 -0
- package/src/ui/panels/exports.ts +3 -0
- package/src/ui/panels/question-model.ts +424 -0
- package/src/ui/panels/question-panel.tsx +154 -0
- package/src/ui/panels/surface.tsx +106 -0
- package/src/ui/panels-builtin.tsx +86 -0
- package/src/ui/pointer/builtins.ts +281 -0
- package/src/ui/pointer/registry.ts +25 -0
- package/src/ui/region.tsx +31 -0
- package/src/ui/selection/service.ts +92 -0
- package/src/ui/selection-registry.ts +88 -0
- package/src/ui/selection.tsx +148 -0
- package/src/ui/spinner-tick.tsx +24 -0
- package/src/ui/use-available-windows.ts +31 -0
- package/src/ui/widgets/actions.tsx +84 -0
- package/src/ui/widgets/button.tsx +57 -0
- package/src/ui/widgets/checkbox.tsx +58 -0
- package/src/ui/widgets/header.tsx +36 -0
- package/src/ui/widgets/index.ts +8 -0
- package/src/ui/widgets/input.tsx +76 -0
- package/src/ui/widgets/registry.ts +32 -0
- package/src/ui/widgets/search.tsx +59 -0
- package/src/ui/widgets/select.tsx +97 -0
- package/src/ui/widgets/static.tsx +37 -0
- package/src/ui/widgets/types.ts +1 -0
- package/src/ui/window-services-bridge.ts +71 -0
- package/src/ui/window-services.ts +20 -0
- package/src/ui/windows/models-window.tsx +15 -0
- package/src/ui/windows/providers-window.tsx +14 -0
- package/src/ui/windows/service-window.tsx +18 -0
- package/src/ui/windows/sessions-window.tsx +25 -0
- package/src/ui/windows-builtin.tsx +94 -0
- package/src/ui/windows.ts +18 -0
- 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'
|