@deepseek-harness-tui/dsh-tui 0.8.4 → 0.8.6
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/lib/types/activityPrefs.d.ts +10 -0
- package/lib/types/activityPrefs.d.ts.map +1 -1
- package/lib/types/activityPrefs.js +17 -0
- package/lib/types/cc/figures.d.ts +7 -0
- package/lib/types/cc/figures.d.ts.map +1 -1
- package/lib/types/cc/figures.js +23 -0
- package/lib/types/cc/markdown.d.ts.map +1 -1
- package/lib/types/cc/markdown.js +47 -14
- package/lib/types/cc/syntaxTheme.d.ts +12 -0
- package/lib/types/cc/syntaxTheme.d.ts.map +1 -0
- package/lib/types/cc/syntaxTheme.js +76 -0
- package/lib/types/components/EffortChargeGlyph.d.ts +26 -0
- package/lib/types/components/EffortChargeGlyph.d.ts.map +1 -0
- package/lib/types/components/EffortChargeGlyph.js +70 -0
- package/lib/types/components/EffortInputBorder.d.ts +36 -0
- package/lib/types/components/EffortInputBorder.d.ts.map +1 -0
- package/lib/types/components/EffortInputBorder.js +73 -0
- package/lib/types/components/EffortTierBadge.d.ts +26 -0
- package/lib/types/components/EffortTierBadge.d.ts.map +1 -0
- package/lib/types/components/EffortTierBadge.js +97 -0
- package/lib/types/components/LoadedContextPanel.js +1 -1
- package/lib/types/components/MessageList.d.ts +4 -1
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +9 -11
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +8 -3
- package/lib/types/components/SplitDiffView.d.ts +4 -5
- package/lib/types/components/SplitDiffView.d.ts.map +1 -1
- package/lib/types/components/SplitDiffView.js +18 -65
- package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
- package/lib/types/components/StreamingMarkdown.js +10 -2
- package/lib/types/components/activityFrames.d.ts +7 -28
- package/lib/types/components/activityFrames.d.ts.map +1 -1
- package/lib/types/components/activityFrames.js +7 -94
- package/lib/types/components/design-system/ThemedBox.d.ts.map +1 -1
- package/lib/types/components/design-system/ThemedBox.js +4 -1
- package/lib/types/components/design-system/ThemedText.js +2 -2
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts +12 -6
- package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantThinkingMessage.js +25 -9
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts +4 -1
- package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
- package/lib/types/components/messages/AssistantToolUseMessage.js +45 -17
- package/lib/types/components/messages/UserPromptMessage.d.ts +4 -6
- package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
- package/lib/types/components/messages/UserPromptMessage.js +5 -9
- package/lib/types/dsh-adapter/channel.d.ts +22 -0
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +204 -19
- package/lib/types/dsh-adapter/index.d.ts +5 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +19 -0
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +176 -4
- package/lib/types/dsh-adapter/settings-sections.d.ts +13 -0
- package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
- package/lib/types/dsh-adapter/settings-sections.js +31 -6
- package/lib/types/ink/ink.d.ts +9 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +24 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +35 -6
- package/lib/types/screens/Settings.d.ts.map +1 -1
- package/lib/types/screens/Settings.js +109 -50
- package/lib/types/screens/StatusLine.d.ts +8 -0
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +62 -39
- package/lib/types/settings-sections.d.ts +1 -1
- package/lib/types/settings-sections.d.ts.map +1 -1
- package/lib/types/theme.d.ts +12 -0
- package/lib/types/theme.d.ts.map +1 -1
- package/lib/types/theme.js +51 -21
- package/lib/types/tips.js +2 -2
- package/lib/types/trajectory/effortIgnition.d.ts +66 -0
- package/lib/types/trajectory/effortIgnition.d.ts.map +1 -0
- package/lib/types/trajectory/effortIgnition.js +142 -0
- package/lib/types/tuiDisplayPrefs.d.ts +47 -0
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -0
- package/lib/types/tuiDisplayPrefs.js +60 -0
- package/package.json +6 -3
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* unknown preset left behind by an older version) just falls back to the
|
|
8
8
|
* default preset.
|
|
9
9
|
*/
|
|
10
|
+
import { type WorkingActivityConfig } from 'dsh-working-activity/config';
|
|
10
11
|
/**
|
|
11
12
|
* Parse a persisted `{ frames }` value; anything else yields undefined.
|
|
12
13
|
* @param text - Raw file contents.
|
|
@@ -19,6 +20,15 @@ export declare function parseActivityFrames(text: string): string | undefined;
|
|
|
19
20
|
* @returns The persisted preset name, if any.
|
|
20
21
|
*/
|
|
21
22
|
export declare function readActivityFrames(dir?: string): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Read the full pi-style working-activity config (frames / mode / features /
|
|
25
|
+
* customPhrases / customActions / narrate / thresholds) from the same JSON
|
|
26
|
+
* file the `/activity` command writes, parsed by the wa package. Best-effort:
|
|
27
|
+
* a missing or corrupt file yields undefined (callers use their defaults).
|
|
28
|
+
* @param dir - Prefs directory (injectable for tests).
|
|
29
|
+
* @returns The parsed config, or undefined when unreadable.
|
|
30
|
+
*/
|
|
31
|
+
export declare function readActivityConfig(dir?: string): WorkingActivityConfig | undefined;
|
|
22
32
|
/**
|
|
23
33
|
* Persist the chosen indicator preset (best effort).
|
|
24
34
|
* @param name - Preset name to persist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activityPrefs.d.ts","sourceRoot":"","sources":["../../src/activityPrefs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"activityPrefs.d.ts","sourceRoot":"","sources":["../../src/activityPrefs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAA8B,KAAK,qBAAqB,EAAE,MAAM,6BAA6B,CAAA;AAKpG;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CASpE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAkB,GAAG,MAAM,GAAG,SAAS,CAM9E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAkB,GAAG,qBAAqB,GAAG,SAAS,CAM7F;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,MAAkB,GAAG,OAAO,CAQlF"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
import { isPresetName } from './components/activityFrames.js';
|
|
13
|
+
import { parseWorkingActivityConfig } from 'dsh-working-activity/config';
|
|
13
14
|
import { DATA_DIR } from './utils/paths.js';
|
|
14
15
|
const PREFS_DIR = DATA_DIR;
|
|
15
16
|
/**
|
|
@@ -42,6 +43,22 @@ export function readActivityFrames(dir = PREFS_DIR) {
|
|
|
42
43
|
return undefined;
|
|
43
44
|
}
|
|
44
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Read the full pi-style working-activity config (frames / mode / features /
|
|
48
|
+
* customPhrases / customActions / narrate / thresholds) from the same JSON
|
|
49
|
+
* file the `/activity` command writes, parsed by the wa package. Best-effort:
|
|
50
|
+
* a missing or corrupt file yields undefined (callers use their defaults).
|
|
51
|
+
* @param dir - Prefs directory (injectable for tests).
|
|
52
|
+
* @returns The parsed config, or undefined when unreadable.
|
|
53
|
+
*/
|
|
54
|
+
export function readActivityConfig(dir = PREFS_DIR) {
|
|
55
|
+
try {
|
|
56
|
+
return parseWorkingActivityConfig(readFileSync(join(dir, 'working-activity.json'), 'utf8')).config;
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
45
62
|
/**
|
|
46
63
|
* Persist the chosen indicator preset (best effort).
|
|
47
64
|
* @param name - Preset name to persist.
|
|
@@ -67,4 +67,11 @@ export declare const BRIDGE_SPINNER_FRAMES: string[];
|
|
|
67
67
|
export declare const BRIDGE_READY_INDICATOR = "\u00B7\u2714\uFE0E\u00B7";
|
|
68
68
|
/** Bridge failed indicator (`×`). */
|
|
69
69
|
export declare const BRIDGE_FAILED_INDICATOR = "\u00D7";
|
|
70
|
+
export declare const THINKING_SPINNER_FRAMES: string[];
|
|
71
|
+
export declare const THINKING_SPINNER_INTERVAL_MS = 80;
|
|
72
|
+
/** Thinking settled marker: anchor (`⚓`) — the static end-state glyph after
|
|
73
|
+
* the reasoning block stops streaming. U+2693 is Emoji_Presentation in
|
|
74
|
+
* ink/stringWidth, so it measures 2 columns; the braille spinner frames
|
|
75
|
+
* above are padded to the same width to keep the label stationary. */
|
|
76
|
+
export declare const THINKING_SETTLED_MARKER = "\u2693";
|
|
70
77
|
//# sourceMappingURL=figures.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"figures.d.ts","sourceRoot":"","sources":["../../../src/cc/figures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,uEAAuE;AACvE,eAAO,MAAM,YAAY,QAA4C,CAAA;AAGrE,kDAAkD;AAClD,eAAO,MAAM,OAAO,WAAW,CAAA;AAC/B,+BAA+B;AAC/B,eAAO,MAAM,IAAI,WAAW,CAAA;AAC5B,gDAAgD;AAChD,eAAO,MAAM,eAAe,WAAW,CAAA;AACvC,qEAAqE;AACrE,eAAO,MAAM,MAAM,WAAW,CAAA;AAC9B,gCAAgC;AAChC,eAAO,MAAM,gBAAgB,WAAW,CAAA;AACxC,uDAAuD;AACvD,eAAO,MAAM,iBAAiB,WAAW,CAAA;AACzC,iDAAiD;AACjD,eAAO,MAAM,cAAc,WAAW,CAAA;AAGtC,sBAAsB;AACtB,eAAO,MAAM,QAAQ,WAAW,CAAA;AAChC,wBAAwB;AACxB,eAAO,MAAM,UAAU,WAAW,CAAA;AAClC,2CAA2C;AAC3C,eAAO,MAAM,aAAa,WAAW,CAAA;AACrC,qDAAqD;AACrD,eAAO,MAAM,cAAc,WAAW,CAAA;AAGtC,qCAAqC;AACrC,eAAO,MAAM,UAAU,WAAW,CAAA;AAClC,+CAA+C;AAC/C,eAAO,MAAM,aAAa,WAAW,CAAA;AACrC,wCAAwC;AACxC,eAAO,MAAM,WAAW,WAAW,CAAA;AACnC,sCAAsC;AACtC,eAAO,MAAM,UAAU,WAAW,CAAA;AAGlC,0BAA0B;AAC1B,eAAO,MAAM,SAAS,WAAW,CAAA;AACjC,2BAA2B;AAC3B,eAAO,MAAM,UAAU,WAAW,CAAA;AAGlC,uDAAuD;AACvD,eAAO,MAAM,aAAa,WAAW,CAAA;AACrC,wCAAwC;AACxC,eAAO,MAAM,UAAU,WAAW,CAAA;AAGlC,mDAAmD;AACnD,eAAO,MAAM,YAAY,WAAW,CAAA;AACpC,qDAAqD;AACrD,eAAO,MAAM,cAAc,WAAW,CAAA;AACtC,iDAAiD;AACjD,eAAO,MAAM,cAAc,WAAW,CAAA;AAGtC,wCAAwC;AACxC,eAAO,MAAM,SAAS,WAAW,CAAA;AACjC,oDAAoD;AACpD,eAAO,MAAM,cAAc,WAAW,CAAA;AACtC,mCAAmC;AACnC,eAAO,MAAM,gBAAgB,WAAW,CAAA;AAGxC,4DAA4D;AAC5D,eAAO,MAAM,qBAAqB,UAKjC,CAAA;AACD,uCAAuC;AACvC,eAAO,MAAM,sBAAsB,6BAA6B,CAAA;AAChE,qCAAqC;AACrC,eAAO,MAAM,uBAAuB,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"figures.d.ts","sourceRoot":"","sources":["../../../src/cc/figures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,uEAAuE;AACvE,eAAO,MAAM,YAAY,QAA4C,CAAA;AAGrE,kDAAkD;AAClD,eAAO,MAAM,OAAO,WAAW,CAAA;AAC/B,+BAA+B;AAC/B,eAAO,MAAM,IAAI,WAAW,CAAA;AAC5B,gDAAgD;AAChD,eAAO,MAAM,eAAe,WAAW,CAAA;AACvC,qEAAqE;AACrE,eAAO,MAAM,MAAM,WAAW,CAAA;AAC9B,gCAAgC;AAChC,eAAO,MAAM,gBAAgB,WAAW,CAAA;AACxC,uDAAuD;AACvD,eAAO,MAAM,iBAAiB,WAAW,CAAA;AACzC,iDAAiD;AACjD,eAAO,MAAM,cAAc,WAAW,CAAA;AAGtC,sBAAsB;AACtB,eAAO,MAAM,QAAQ,WAAW,CAAA;AAChC,wBAAwB;AACxB,eAAO,MAAM,UAAU,WAAW,CAAA;AAClC,2CAA2C;AAC3C,eAAO,MAAM,aAAa,WAAW,CAAA;AACrC,qDAAqD;AACrD,eAAO,MAAM,cAAc,WAAW,CAAA;AAGtC,qCAAqC;AACrC,eAAO,MAAM,UAAU,WAAW,CAAA;AAClC,+CAA+C;AAC/C,eAAO,MAAM,aAAa,WAAW,CAAA;AACrC,wCAAwC;AACxC,eAAO,MAAM,WAAW,WAAW,CAAA;AACnC,sCAAsC;AACtC,eAAO,MAAM,UAAU,WAAW,CAAA;AAGlC,0BAA0B;AAC1B,eAAO,MAAM,SAAS,WAAW,CAAA;AACjC,2BAA2B;AAC3B,eAAO,MAAM,UAAU,WAAW,CAAA;AAGlC,uDAAuD;AACvD,eAAO,MAAM,aAAa,WAAW,CAAA;AACrC,wCAAwC;AACxC,eAAO,MAAM,UAAU,WAAW,CAAA;AAGlC,mDAAmD;AACnD,eAAO,MAAM,YAAY,WAAW,CAAA;AACpC,qDAAqD;AACrD,eAAO,MAAM,cAAc,WAAW,CAAA;AACtC,iDAAiD;AACjD,eAAO,MAAM,cAAc,WAAW,CAAA;AAGtC,wCAAwC;AACxC,eAAO,MAAM,SAAS,WAAW,CAAA;AACjC,oDAAoD;AACpD,eAAO,MAAM,cAAc,WAAW,CAAA;AACtC,mCAAmC;AACnC,eAAO,MAAM,gBAAgB,WAAW,CAAA;AAGxC,4DAA4D;AAC5D,eAAO,MAAM,qBAAqB,UAKjC,CAAA;AACD,uCAAuC;AACvC,eAAO,MAAM,sBAAsB,6BAA6B,CAAA;AAChE,qCAAqC;AACrC,eAAO,MAAM,uBAAuB,WAAW,CAAA;AAO/C,eAAO,MAAM,uBAAuB,UAWnC,CAAA;AACD,eAAO,MAAM,4BAA4B,KAAK,CAAA;AAC9C;;;uEAGuE;AACvE,eAAO,MAAM,uBAAuB,WAAW,CAAA"}
|
package/lib/types/cc/figures.js
CHANGED
|
@@ -80,3 +80,26 @@ export const BRIDGE_SPINNER_FRAMES = [
|
|
|
80
80
|
export const BRIDGE_READY_INDICATOR = '\u00b7\u2714\ufe0e\u00b7';
|
|
81
81
|
/** Bridge failed indicator (`×`). */
|
|
82
82
|
export const BRIDGE_FAILED_INDICATOR = '\u00d7';
|
|
83
|
+
// Thinking spinner (Kimi Code style braille cycle, shown while reasoning
|
|
84
|
+
// streams; the static anchor takes over once the step settles). Each frame
|
|
85
|
+
// is padded to 2 columns so it matches the settled ⚓ anchor exactly — a
|
|
86
|
+
// 1-col frame would shift the whole label right by one column the moment
|
|
87
|
+
// the step settles.
|
|
88
|
+
export const THINKING_SPINNER_FRAMES = [
|
|
89
|
+
'\u280b ', // ⠋
|
|
90
|
+
'\u2819 ', // ⠙
|
|
91
|
+
'\u2839 ', // ⠹
|
|
92
|
+
'\u2838 ', // ⠸
|
|
93
|
+
'\u283c ', // ⠼
|
|
94
|
+
'\u2834 ', // ⠴
|
|
95
|
+
'\u2826 ', // ⠦
|
|
96
|
+
'\u2827 ', // ⠧
|
|
97
|
+
'\u2807 ', // ⠇
|
|
98
|
+
'\u280f ', // ⠏
|
|
99
|
+
];
|
|
100
|
+
export const THINKING_SPINNER_INTERVAL_MS = 80;
|
|
101
|
+
/** Thinking settled marker: anchor (`⚓`) — the static end-state glyph after
|
|
102
|
+
* the reasoning block stops streaming. U+2693 is Emoji_Presentation in
|
|
103
|
+
* ink/stringWidth, so it measures 2 columns; the braille spinner frames
|
|
104
|
+
* above are padded to the same width to keep the label stationary. */
|
|
105
|
+
export const THINKING_SETTLED_MARKER = '\u2693'; // ⚓
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/cc/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAA4B,KAAK,KAAK,EAAe,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/cc/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAA4B,KAAK,KAAK,EAAe,MAAM,QAAQ,CAAA;AAO1E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AA4BrD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE1D;AAID;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAWtC;AAuCD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,KAAK,EACZ,SAAS,SAAI,EACb,iBAAiB,GAAE,MAAM,GAAG,IAAW,EACvC,MAAM,GAAE,KAAK,GAAG,IAAW,EAC3B,SAAS,GAAE,YAAY,GAAG,IAAW,GACpC,MAAM,CAER;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,SAAS,GAAE,YAAY,GAAG,IAAW,GACpC,MAAM,CAgBR;AA2SD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,GACpD,MAAM,CAUR"}
|
package/lib/types/cc/markdown.js
CHANGED
|
@@ -18,6 +18,7 @@ import { stringWidth } from '../ink/stringWidth.js';
|
|
|
18
18
|
import { supportsHyperlinks } from '../ink/supports-hyperlinks.js';
|
|
19
19
|
import { colorize } from '../ink/colorize.js';
|
|
20
20
|
import { getActiveTheme } from '../theme.js';
|
|
21
|
+
import { buildSyntaxTheme } from './syntaxTheme.js';
|
|
21
22
|
import { logForDebugging } from '../utils/debug.js';
|
|
22
23
|
import { createHyperlink } from './hyperlink.js';
|
|
23
24
|
// '\n' is used unconditionally — os.EOL is '\r\n' on Windows, and the stray
|
|
@@ -112,7 +113,10 @@ export function applyMarkdown(content, highlight = null) {
|
|
|
112
113
|
.lexer(stripPromptXMLTags(content))
|
|
113
114
|
.map(token => dispatch(token, rootState))
|
|
114
115
|
.join('')
|
|
115
|
-
|
|
116
|
+
// trimEnd only: the input is already trimmed, so leading whitespace in
|
|
117
|
+
// the output is renderer-intended (e.g. the code block's 2-space indent
|
|
118
|
+
// on its first line). A full trim() would eat that first-line indent.
|
|
119
|
+
.trimEnd();
|
|
116
120
|
}
|
|
117
121
|
/**
|
|
118
122
|
* Type guard that narrows to the concrete marked token of `kind`.
|
|
@@ -176,19 +180,39 @@ function renderBlockquote(token, state) {
|
|
|
176
180
|
.join(EOL);
|
|
177
181
|
}
|
|
178
182
|
function renderCodeBlock(token, state) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
// Kimi Code style: a muted ```lang opening line (language tag + boundary
|
|
184
|
+
// for unhighlighted blocks) + 2-space indent; no closing fence (syntax
|
|
185
|
+
// colors or the indent already mark the end, it only cost vertical space).
|
|
186
|
+
const theme = getActiveTheme();
|
|
187
|
+
const openFence = colorize('```' + (token.lang ?? ''), theme.subtle, 'foreground');
|
|
188
|
+
const indent = ' ';
|
|
189
|
+
const renderBody = () => {
|
|
190
|
+
if (!state.highlight) {
|
|
191
|
+
return token.text;
|
|
186
192
|
}
|
|
187
|
-
|
|
188
|
-
|
|
193
|
+
let language = 'plaintext';
|
|
194
|
+
if (token.lang) {
|
|
195
|
+
if (state.highlight.supportsLanguage(token.lang)) {
|
|
196
|
+
language = token.lang;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
logForDebugging(`Language not supported while highlighting code, falling back to plaintext: ${token.lang}`);
|
|
200
|
+
}
|
|
189
201
|
}
|
|
202
|
+
return state.highlight.highlight(token.text, { language, theme: buildSyntaxTheme(theme) });
|
|
203
|
+
};
|
|
204
|
+
// Strip ALL trailing newlines: trailing blank lines would otherwise leak a
|
|
205
|
+
// stray blank line at the end of the block.
|
|
206
|
+
const body = renderBody().replace(/\n+$/, '');
|
|
207
|
+
if (body === '') {
|
|
208
|
+
return `${openFence}${EOL}`;
|
|
190
209
|
}
|
|
191
|
-
return
|
|
210
|
+
return (openFence +
|
|
211
|
+
EOL +
|
|
212
|
+
body
|
|
213
|
+
.split(EOL)
|
|
214
|
+
.map(line => (line === '' ? line : indent + line))
|
|
215
|
+
.join(EOL) + EOL);
|
|
192
216
|
}
|
|
193
217
|
function renderEmphasis(token, state) {
|
|
194
218
|
const inner = token.tokens.map(child => dispatch(child, inlineChildren(state))).join('');
|
|
@@ -200,8 +224,14 @@ function renderStrong(token, state) {
|
|
|
200
224
|
}
|
|
201
225
|
function renderHeading(token, state) {
|
|
202
226
|
const text = token.tokens.map(child => dispatch(child, fresh(state))).join('');
|
|
203
|
-
// H1 gets the
|
|
204
|
-
|
|
227
|
+
// Blue-primary progression: H1 gets the mist brand blue + underline, H2 the
|
|
228
|
+
// lighter border blue, deeper levels stay bold near-text (kimi-style).
|
|
229
|
+
const theme = getActiveTheme();
|
|
230
|
+
const styled = token.depth === 1
|
|
231
|
+
? chalk.bold.underline(colorize(text, theme.claude, 'foreground'))
|
|
232
|
+
: token.depth === 2
|
|
233
|
+
? chalk.bold(colorize(text, theme.permission, 'foreground'))
|
|
234
|
+
: chalk.bold(text);
|
|
205
235
|
return styled + EOL + EOL;
|
|
206
236
|
}
|
|
207
237
|
function renderLink(token, state) {
|
|
@@ -251,7 +281,10 @@ function renderText(token, state) {
|
|
|
251
281
|
const body = token.tokens
|
|
252
282
|
? token.tokens.map(child => dispatch(child, withParent(state, token))).join('')
|
|
253
283
|
: linkifyIssueReferences(token.text);
|
|
254
|
-
|
|
284
|
+
// Blue bullet marker: list structure gets a tint without loading the
|
|
285
|
+
// whole item (kimi-style `•` in the accent color).
|
|
286
|
+
const tinted = colorize(bullet, getActiveTheme().permission, 'foreground');
|
|
287
|
+
return `${tinted} ${body}${EOL}`;
|
|
255
288
|
}
|
|
256
289
|
return linkifyIssueReferences(token.text);
|
|
257
290
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Theme } from '../theme.js';
|
|
2
|
+
/** highlight.js token classes -> theme syntax tokens. `default` catches
|
|
3
|
+
* every unmapped class so cli-highlight's own yellow never leaks through
|
|
4
|
+
* (issue #250, P2-6). */
|
|
5
|
+
export declare const SYNTAX_CLASS_TO_TOKEN: Record<string, string>;
|
|
6
|
+
/** chalk style for one raw theme value. Accepts every form the theme
|
|
7
|
+
* loader documents: #rgb, #rrggbb, #rrggbbaa (alpha stripped), rgb() with
|
|
8
|
+
* or without spaces, ansi256(n), ansi:name (issue #250, P2-5). */
|
|
9
|
+
export declare function chalkFromToken(token: string): (text: string) => string;
|
|
10
|
+
/** Build the cli-highlight `theme` option from an active theme palette. */
|
|
11
|
+
export declare function buildSyntaxTheme(theme: Theme): Record<string, (text: string) => string>;
|
|
12
|
+
//# sourceMappingURL=syntaxTheme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syntaxTheme.d.ts","sourceRoot":"","sources":["../../../src/cc/syntaxTheme.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAQxC;;0BAE0B;AAC1B,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA4BxD,CAAA;AAED;;mEAEmE;AACnE,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAmBtE;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,GACX,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,CAQ1C"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
// --- syntax highlighting theme bridge ----------------------------------------
|
|
3
|
+
// Shared by SplitDiffView (diff panes) and the markdown renderer (fenced
|
|
4
|
+
// code blocks). Both feed cli-highlight's `theme` option so code colors come
|
|
5
|
+
// from the active dsh-tui theme instead of cli-highlight's own sparse
|
|
6
|
+
// default palette.
|
|
7
|
+
/** highlight.js token classes -> theme syntax tokens. `default` catches
|
|
8
|
+
* every unmapped class so cli-highlight's own yellow never leaks through
|
|
9
|
+
* (issue #250, P2-6). */
|
|
10
|
+
export const SYNTAX_CLASS_TO_TOKEN = {
|
|
11
|
+
keyword: 'syntaxKeyword',
|
|
12
|
+
built_in: 'syntaxKeyword',
|
|
13
|
+
literal: 'syntaxKeyword',
|
|
14
|
+
string: 'syntaxString',
|
|
15
|
+
subst: 'syntaxString',
|
|
16
|
+
quote: 'syntaxString',
|
|
17
|
+
comment: 'syntaxComment',
|
|
18
|
+
number: 'syntaxNumber',
|
|
19
|
+
title: 'syntaxFunction',
|
|
20
|
+
'title.function_': 'syntaxFunction',
|
|
21
|
+
function: 'syntaxFunction',
|
|
22
|
+
'title.class_': 'syntaxType',
|
|
23
|
+
type: 'syntaxType',
|
|
24
|
+
class: 'syntaxType',
|
|
25
|
+
tag: 'syntaxType',
|
|
26
|
+
name: 'syntaxType',
|
|
27
|
+
attr: 'syntaxVariable',
|
|
28
|
+
attribute: 'syntaxVariable',
|
|
29
|
+
variable: 'syntaxVariable',
|
|
30
|
+
'template-variable': 'syntaxVariable',
|
|
31
|
+
params: 'syntaxVariable',
|
|
32
|
+
operator: 'syntaxOperator',
|
|
33
|
+
punctuation: 'syntaxPunctuation',
|
|
34
|
+
meta: 'syntaxPunctuation',
|
|
35
|
+
symbol: 'syntaxConstant',
|
|
36
|
+
regexp: 'syntaxConstant',
|
|
37
|
+
default: 'syntaxVariable',
|
|
38
|
+
};
|
|
39
|
+
/** chalk style for one raw theme value. Accepts every form the theme
|
|
40
|
+
* loader documents: #rgb, #rrggbb, #rrggbbaa (alpha stripped), rgb() with
|
|
41
|
+
* or without spaces, ansi256(n), ansi:name (issue #250, P2-5). */
|
|
42
|
+
export function chalkFromToken(token) {
|
|
43
|
+
let match = /^#([0-9a-fA-F]{3})$/.exec(token);
|
|
44
|
+
if (match !== null) {
|
|
45
|
+
const [r, g, b] = match[1].split('').map(c => parseInt(c + c, 16));
|
|
46
|
+
return chalk.rgb(r, g, b);
|
|
47
|
+
}
|
|
48
|
+
match = /^#([0-9a-fA-F]{6})(?:[0-9a-fA-F]{2})?$/.exec(token);
|
|
49
|
+
if (match !== null)
|
|
50
|
+
return chalk.hex(`#${match[1]}`);
|
|
51
|
+
match = /^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/.exec(token);
|
|
52
|
+
if (match !== null)
|
|
53
|
+
return chalk.rgb(Number(match[1]), Number(match[2]), Number(match[3]));
|
|
54
|
+
match = /^ansi256\((\d+)\)$/.exec(token);
|
|
55
|
+
if (match !== null)
|
|
56
|
+
return chalk.ansi256(Number(match[1]));
|
|
57
|
+
match = /^ansi:(\w+)$/.exec(token);
|
|
58
|
+
if (match !== null) {
|
|
59
|
+
const name = match[1] === 'blackBright' ? 'gray' : match[1];
|
|
60
|
+
const style = chalk[name];
|
|
61
|
+
if (style !== undefined)
|
|
62
|
+
return style;
|
|
63
|
+
}
|
|
64
|
+
return (text) => text;
|
|
65
|
+
}
|
|
66
|
+
/** Build the cli-highlight `theme` option from an active theme palette. */
|
|
67
|
+
export function buildSyntaxTheme(theme) {
|
|
68
|
+
const palette = theme;
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const [tokenClass, tokenKey] of Object.entries(SYNTAX_CLASS_TO_TOKEN)) {
|
|
71
|
+
const value = palette[tokenKey];
|
|
72
|
+
if (value !== undefined)
|
|
73
|
+
out[tokenClass] = chalkFromToken(value);
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EffortChargeGlyph — 输入提示前缀 `❯ ` 的最高档强调与充能。
|
|
3
|
+
*
|
|
4
|
+
* 思考强度处于当前路线最高档期间,前缀换为点火强调色(加粗,与点焰波
|
|
5
|
+
* 共用 hues[0]——同一瞬间前缀与波是同一种橙);切到最高档的瞬间做一次
|
|
6
|
+
* 150ms「充能」渐变(accentRamp 的暗端 → 全值)。冷启动已在最高档时
|
|
7
|
+
* 不充能(充能只属于切换瞬间),离开最高档恢复原样的 dim 行为。
|
|
8
|
+
*
|
|
9
|
+
* 触发判定在渲染期做(React 官方的「props 变化即调整 state」模式,
|
|
10
|
+
* 与 EffortInputBorder 同一模式)——放 effect 会晚一帧,effort 变化
|
|
11
|
+
* 的首帧以全亮闪现、下一帧才跌回暗端重来。充能只动颜色,glyph 恒为
|
|
12
|
+
* `❯ `,SGR-only 规则天然成立。
|
|
13
|
+
*
|
|
14
|
+
* 时钟复用 Ink core 共享时钟,且只在充能未满的那 150ms 内订阅;稳态
|
|
15
|
+
* 零定时器、零重渲染,前缀色取记忆化常量。
|
|
16
|
+
*/
|
|
17
|
+
import React from 'react';
|
|
18
|
+
export declare function EffortChargeGlyph({ effort, levels, working, }: {
|
|
19
|
+
/** 当前思考强度档 id;`undefined` 表示路线未声明。 */
|
|
20
|
+
effort: string | undefined;
|
|
21
|
+
/** 当前路线的档位表(低→高,末位为最高档)。 */
|
|
22
|
+
levels: readonly string[] | undefined;
|
|
23
|
+
/** 模型工作中时前缀照旧压暗(既有语义)。 */
|
|
24
|
+
working: boolean;
|
|
25
|
+
}): React.ReactNode;
|
|
26
|
+
//# sourceMappingURL=EffortChargeGlyph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EffortChargeGlyph.d.ts","sourceRoot":"","sources":["../../../src/components/EffortChargeGlyph.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAsD,MAAM,OAAO,CAAA;AAW1E,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,MAAM,EACN,OAAO,GACR,EAAE;IACD,sCAAsC;IACtC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,4BAA4B;IAC5B,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IACrC,0BAA0B;IAC1B,OAAO,EAAE,OAAO,CAAA;CACjB,GAAG,KAAK,CAAC,SAAS,CAuDlB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* EffortChargeGlyph — 输入提示前缀 `❯ ` 的最高档强调与充能。
|
|
4
|
+
*
|
|
5
|
+
* 思考强度处于当前路线最高档期间,前缀换为点火强调色(加粗,与点焰波
|
|
6
|
+
* 共用 hues[0]——同一瞬间前缀与波是同一种橙);切到最高档的瞬间做一次
|
|
7
|
+
* 150ms「充能」渐变(accentRamp 的暗端 → 全值)。冷启动已在最高档时
|
|
8
|
+
* 不充能(充能只属于切换瞬间),离开最高档恢复原样的 dim 行为。
|
|
9
|
+
*
|
|
10
|
+
* 触发判定在渲染期做(React 官方的「props 变化即调整 state」模式,
|
|
11
|
+
* 与 EffortInputBorder 同一模式)——放 effect 会晚一帧,effort 变化
|
|
12
|
+
* 的首帧以全亮闪现、下一帧才跌回暗端重来。充能只动颜色,glyph 恒为
|
|
13
|
+
* `❯ `,SGR-only 规则天然成立。
|
|
14
|
+
*
|
|
15
|
+
* 时钟复用 Ink core 共享时钟,且只在充能未满的那 150ms 内订阅;稳态
|
|
16
|
+
* 零定时器、零重渲染,前缀色取记忆化常量。
|
|
17
|
+
*/
|
|
18
|
+
import { useContext, useEffect, useReducer, useState } from 'react';
|
|
19
|
+
import { Text, useTheme } from '../ui.js';
|
|
20
|
+
import { ClockContext } from '../ink/components/ClockContext.js';
|
|
21
|
+
import { accentRamp } from '../trajectory/effortIgnition.js';
|
|
22
|
+
import { rgbString } from '../trajectory/motion.js';
|
|
23
|
+
import { interpolateColor } from './Spinner/spinnerUtils.js';
|
|
24
|
+
import { isLightThemeActive } from '../theme.js';
|
|
25
|
+
/** 充能时长(ms)。 */
|
|
26
|
+
const CHARGE_MS = 150;
|
|
27
|
+
export function EffortChargeGlyph({ effort, levels, working, }) {
|
|
28
|
+
const clock = useContext(ClockContext);
|
|
29
|
+
const [themeName] = useTheme();
|
|
30
|
+
const [chargeStartedAt, setChargeStartedAt] = useState(null);
|
|
31
|
+
const [prevEffort, setPrevEffort] = useState(effort);
|
|
32
|
+
const [, forceRender] = useReducer((tick) => tick + 1, 0);
|
|
33
|
+
const topActive = effort !== undefined && levels !== undefined && levels.length > 1 && effort === levels[levels.length - 1];
|
|
34
|
+
// Render-phase trigger (same pattern as EffortInputBorder): switching from
|
|
35
|
+
// an existing tier onto the top tier starts the charge NOW, not one effect
|
|
36
|
+
// later — the first frame of the new effort must not flash fully lit.
|
|
37
|
+
// Cold mounts enter the steady state directly; without a shared clock
|
|
38
|
+
// (headless embeds) there is no charge either — no frames would ever arrive.
|
|
39
|
+
if (effort !== prevEffort) {
|
|
40
|
+
setPrevEffort(effort);
|
|
41
|
+
if (topActive && clock !== null) {
|
|
42
|
+
setChargeStartedAt(clock.now());
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// Only the 150ms charge window subscribes to the shared clock; steady
|
|
46
|
+
// state has zero timers and zero re-renders.
|
|
47
|
+
const chargeElapsed = chargeStartedAt === null
|
|
48
|
+
? Infinity
|
|
49
|
+
: // Clamp at zero: the shared clock can hand back a stale tickTime for
|
|
50
|
+
// one frame after waking from pause.
|
|
51
|
+
Math.max(0, (clock?.now() ?? Date.now()) - chargeStartedAt);
|
|
52
|
+
const charging = chargeElapsed < CHARGE_MS;
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (!charging || clock === null)
|
|
55
|
+
return;
|
|
56
|
+
return clock.subscribe(() => forceRender(), /* keepAlive */ true);
|
|
57
|
+
}, [charging, clock]);
|
|
58
|
+
if (!topActive)
|
|
59
|
+
return _jsx(Text, { dimColor: working, children: "\u276F " });
|
|
60
|
+
const ramp = accentRamp(isLightThemeActive(themeName));
|
|
61
|
+
if (!charging) {
|
|
62
|
+
// Steady state re-derives on every render (two allocations + one blend
|
|
63
|
+
// per keystroke — negligible) instead of caching: a cached colour would
|
|
64
|
+
// go stale across a light/dark theme flip while the tier stays active.
|
|
65
|
+
const color = rgbString(ramp.full);
|
|
66
|
+
return (_jsxs(Text, { bold: true, color: color, dimColor: working, children: ["\u276F", ' '] }));
|
|
67
|
+
}
|
|
68
|
+
const charge = Math.min(1, chargeElapsed / CHARGE_MS);
|
|
69
|
+
return (_jsxs(Text, { bold: true, color: rgbString(interpolateColor(ramp.dim, ramp.full, charge)), dimColor: working, children: ["\u276F", ' '] }));
|
|
70
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EffortInputBorder — 输入框层上的三幕点焰叠加(对齐 Codex 的完整
|
|
3
|
+
* 语义:光扫过、档位字样浮现、整体渐隐)。
|
|
4
|
+
*
|
|
5
|
+
* 输入框只有顶/底两条横边框(round、无左右)——本组件自绘这两行,
|
|
6
|
+
* **同步**承载动画;档位字样由输入行尾的 EffortTierBadge 短暂显示
|
|
7
|
+
* (见 PromptInput),动画全程行数恒定。切到最高思考强度档时:
|
|
8
|
+
*
|
|
9
|
+
* 1. 扫光 [0, 1s)——一段高亮彩色光带沿顶/底边框同步自左向右扫过
|
|
10
|
+
* (wave 波形逐列变色),期间输入框完全正常可用;
|
|
11
|
+
* 2. 档位字样 [600ms, ~1.1s)——光带行至中段时,输入行居中浮现
|
|
12
|
+
* 档名大写(由暗渐亮加粗、间距聚拢,见 EffortTierBadge);
|
|
13
|
+
* 3. 渐隐 [1.5s, 2s)——字样连同光色一起向主题色淡出,末帧归零:
|
|
14
|
+
* 静止时顶/底边框就是原主题色,内容区无任何附加物。
|
|
15
|
+
*
|
|
16
|
+
* glyph 变化仅限字样行的出现/让位(一次性);其余帧间变化全部是既
|
|
17
|
+
* 有 `─` 的前景色。触发判定在渲染期做(props-变化-调整模式);从
|
|
18
|
+
* 「已有档位」切到档位表末位最高档才触发,冷启动恢复偏好/单档表/
|
|
19
|
+
* 无档位表/无共享时钟均不触发。时钟复用 Ink core 共享时钟,仅动画
|
|
20
|
+
* 窗口订阅(keepAlive),播完回到零开销静止边框。
|
|
21
|
+
*/
|
|
22
|
+
import React from 'react';
|
|
23
|
+
import type { Color } from '../ink/styles.js';
|
|
24
|
+
import type { Theme } from '../theme.js';
|
|
25
|
+
export declare function EffortInputBorder({ effort, levels, columns, onLight, idleColor, children, }: {
|
|
26
|
+
/** 当前思考强度档 id;`undefined` 表示路线未声明。 */
|
|
27
|
+
effort: string | undefined;
|
|
28
|
+
/** 当前路线的档位表(低→高,末位为最高档);未知时传 `undefined`。 */
|
|
29
|
+
levels: readonly string[] | undefined;
|
|
30
|
+
columns: number;
|
|
31
|
+
onLight: boolean;
|
|
32
|
+
/** 静止边框色(主题 token 名,如 'promptBorder' / 'planMode')。 */
|
|
33
|
+
idleColor: keyof Theme | Color;
|
|
34
|
+
children: React.ReactNode;
|
|
35
|
+
}): React.ReactNode;
|
|
36
|
+
//# sourceMappingURL=EffortInputBorder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EffortInputBorder.d.ts","sourceRoot":"","sources":["../../../src/components/EffortInputBorder.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAA8D,MAAM,OAAO,CAAA;AAGlF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AA8BxC,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,MAAM,EACN,OAAO,EACP,OAAO,EACP,SAAS,EACT,QAAQ,GACT,EAAE;IACD,sCAAsC;IACtC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,6CAA6C;IAC7C,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,uDAAuD;IACvD,SAAS,EAAE,MAAM,KAAK,GAAG,KAAK,CAAA;IAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,GAAG,KAAK,CAAC,SAAS,CA0DlB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* EffortInputBorder — 输入框层上的三幕点焰叠加(对齐 Codex 的完整
|
|
4
|
+
* 语义:光扫过、档位字样浮现、整体渐隐)。
|
|
5
|
+
*
|
|
6
|
+
* 输入框只有顶/底两条横边框(round、无左右)——本组件自绘这两行,
|
|
7
|
+
* **同步**承载动画;档位字样由输入行尾的 EffortTierBadge 短暂显示
|
|
8
|
+
* (见 PromptInput),动画全程行数恒定。切到最高思考强度档时:
|
|
9
|
+
*
|
|
10
|
+
* 1. 扫光 [0, 1s)——一段高亮彩色光带沿顶/底边框同步自左向右扫过
|
|
11
|
+
* (wave 波形逐列变色),期间输入框完全正常可用;
|
|
12
|
+
* 2. 档位字样 [600ms, ~1.1s)——光带行至中段时,输入行居中浮现
|
|
13
|
+
* 档名大写(由暗渐亮加粗、间距聚拢,见 EffortTierBadge);
|
|
14
|
+
* 3. 渐隐 [1.5s, 2s)——字样连同光色一起向主题色淡出,末帧归零:
|
|
15
|
+
* 静止时顶/底边框就是原主题色,内容区无任何附加物。
|
|
16
|
+
*
|
|
17
|
+
* glyph 变化仅限字样行的出现/让位(一次性);其余帧间变化全部是既
|
|
18
|
+
* 有 `─` 的前景色。触发判定在渲染期做(props-变化-调整模式);从
|
|
19
|
+
* 「已有档位」切到档位表末位最高档才触发,冷启动恢复偏好/单档表/
|
|
20
|
+
* 无档位表/无共享时钟均不触发。时钟复用 Ink core 共享时钟,仅动画
|
|
21
|
+
* 窗口订阅(keepAlive),播完回到零开销静止边框。
|
|
22
|
+
*/
|
|
23
|
+
import { useContext, useEffect, useReducer, useState } from 'react';
|
|
24
|
+
import { Box, Text } from '../ui.js';
|
|
25
|
+
import { ClockContext } from '../ink/components/ClockContext.js';
|
|
26
|
+
import { IGNITION_TIMELINE, ignitionLineColors } from '../trajectory/effortIgnition.js';
|
|
27
|
+
/** 边框行(顶/底共用同一色段序列——同步变色)。 */
|
|
28
|
+
function BorderRow({ left, right, runs, idleColor, }) {
|
|
29
|
+
return (_jsxs(Text, { children: [_jsx(Text, { color: idleColor, children: left }), runs.map((run, i) => (_jsx(Text, { color: run.color, children: run.glyph }, i))), _jsx(Text, { color: idleColor, children: right })] }));
|
|
30
|
+
}
|
|
31
|
+
export function EffortInputBorder({ effort, levels, columns, onLight, idleColor, children, }) {
|
|
32
|
+
const clock = useContext(ClockContext);
|
|
33
|
+
const [overlay, setOverlay] = useState(null);
|
|
34
|
+
const [prevEffort, setPrevEffort] = useState(effort);
|
|
35
|
+
const [, forceRender] = useReducer((tick) => tick + 1, 0);
|
|
36
|
+
// 渲染期触发:effort 变化的首帧就以新状态渲染(effect 会晚一帧)。
|
|
37
|
+
if (effort !== prevEffort) {
|
|
38
|
+
setPrevEffort(effort);
|
|
39
|
+
if (clock !== null &&
|
|
40
|
+
effort !== undefined &&
|
|
41
|
+
levels !== undefined &&
|
|
42
|
+
levels.length > 1 &&
|
|
43
|
+
effort === levels[levels.length - 1]) {
|
|
44
|
+
setOverlay({ label: effort.toUpperCase(), startedAtMs: clock.now() });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// 仅动画窗口订阅共享时钟;静止边框零定时器零重渲染。
|
|
48
|
+
const elapsedMs = overlay === null ? Infinity : Math.max(0, (clock?.now() ?? Date.now()) - overlay.startedAtMs);
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (overlay === null || clock === null)
|
|
51
|
+
return;
|
|
52
|
+
return clock.subscribe(() => forceRender(), /* keepAlive */ true);
|
|
53
|
+
}, [overlay, clock]);
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (overlay !== null && elapsedMs >= IGNITION_TIMELINE.fadeEndMs)
|
|
56
|
+
setOverlay(null);
|
|
57
|
+
}, [overlay, elapsedMs]);
|
|
58
|
+
const midWidth = Math.max(0, columns - 2);
|
|
59
|
+
const sweepColors = overlay !== null && elapsedMs < IGNITION_TIMELINE.sweepMs && midWidth > 0
|
|
60
|
+
? ignitionLineColors({ elapsedMs, width: midWidth, onLight })
|
|
61
|
+
: [];
|
|
62
|
+
// 顶/底共用的色段序列(同步):扫光列取波形色,其余列回主题色。
|
|
63
|
+
const runs = [];
|
|
64
|
+
for (let index = 0; index < midWidth; index++) {
|
|
65
|
+
const color = sweepColors[index] ?? idleColor;
|
|
66
|
+
const last = runs[runs.length - 1];
|
|
67
|
+
if (last !== undefined && last.color === color)
|
|
68
|
+
last.glyph += '─';
|
|
69
|
+
else
|
|
70
|
+
runs.push({ glyph: '─', color });
|
|
71
|
+
}
|
|
72
|
+
return (_jsxs(Box, { flexDirection: "column", alignItems: "flex-start", justifyContent: "flex-start", width: "100%", flexShrink: 0, children: [_jsx(BorderRow, { left: "\u256D", right: "\u256E", runs: runs, idleColor: idleColor }), children, _jsx(BorderRow, { left: "\u2570", right: "\u256F", runs: runs, idleColor: idleColor })] }));
|
|
73
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EffortTierBadge — 输入行尾的档位字样徽标:三幕点焰的第二幕载体
|
|
3
|
+
* (与 EffortInputBorder 的双框扫光同一时间轴、同一触发),切到最高
|
|
4
|
+
* 思考强度档时光带行至中段,输入行居中浮现档名(当前档名大写,字母间距从
|
|
5
|
+
* 10 个空格减速聚拢到 1 个——连续位置逐字母取整保证帧帧有移动,
|
|
6
|
+
* 明亮蓝加粗,由暗渐亮),随后随图层整体渐隐让位——行数恒定,静
|
|
7
|
+
* 止时完全不渲染;输入行有文字时不显示(绝不遮挡内容)。
|
|
8
|
+
*
|
|
9
|
+
* 触发判定在渲染期做(props-变化-调整模式,与边框/充能组件同模
|
|
10
|
+
* 式);冷启动恢复偏好/单档表/无档位表/无共享时钟均不触发。时钟
|
|
11
|
+
* 复用 Ink core 共享时钟,仅动画窗口订阅(keepAlive),播完归零。
|
|
12
|
+
*/
|
|
13
|
+
import React from 'react';
|
|
14
|
+
export declare function EffortTierBadge({ effort, levels, onLight, columns, leadingColumns, }: {
|
|
15
|
+
/** 当前思考强度档 id;`undefined` 表示路线未声明。 */
|
|
16
|
+
effort: string | undefined;
|
|
17
|
+
/** 当前路线的档位表(低→高,末位为最高档);未知时传 `undefined`。 */
|
|
18
|
+
levels: readonly string[] | undefined;
|
|
19
|
+
onLight: boolean;
|
|
20
|
+
/** 终端列数——居中锚点按终端几何中心计算(纯文本流,不引入嵌套 Box)。 */
|
|
21
|
+
columns: number;
|
|
22
|
+
/** badge 文本流之前该行已被占据的列数(❯ 与块光标等)——居中换算成
|
|
23
|
+
* badge 流内列时要扣掉,否则整体偏右一个前缀宽。 */
|
|
24
|
+
leadingColumns: number;
|
|
25
|
+
}): React.ReactNode;
|
|
26
|
+
//# sourceMappingURL=EffortTierBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EffortTierBadge.d.ts","sourceRoot":"","sources":["../../../src/components/EffortTierBadge.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAsD,MAAM,OAAO,CAAA;AAc1E,wBAAgB,eAAe,CAAC,EAC9B,MAAM,EACN,MAAM,EACN,OAAO,EACP,OAAO,EACP,cAAc,GACf,EAAE;IACD,sCAAsC;IACtC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,6CAA6C;IAC7C,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;IACrC,OAAO,EAAE,OAAO,CAAA;IAChB,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAA;IACf;qCACiC;IACjC,cAAc,EAAE,MAAM,CAAA;CACvB,GAAG,KAAK,CAAC,SAAS,CAkFlB"}
|