dsh-code 0.6.0 → 0.7.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/README.en.md +4 -2
- package/README.md +4 -2
- package/bin/deepseek.mjs +38 -3
- package/lib/index.mjs +1485 -906
- package/lib/startup.mjs +21 -9
- package/lib/theme-BEi4i_aN.mjs +624 -0
- package/lib/types/app.d.ts +31 -3
- package/lib/types/index.d.ts +1 -0
- package/lib/types/kernel-panels.d.ts +21 -0
- package/lib/types/mentions.d.ts +29 -12
- package/lib/types/models.d.ts +66 -0
- package/lib/types/render/animations.d.ts +175 -2
- package/lib/types/render/projection.d.ts +38 -7
- package/lib/types/render/status.d.ts +34 -13
- package/lib/types/startup.d.ts +12 -4
- package/lib/types/theme-panel.d.ts +24 -0
- package/lib/types/theme.d.ts +158 -2
- package/package.json +1 -1
- package/src/app.ts +510 -130
- package/src/index.ts +964 -900
- package/src/kernel-panels.ts +481 -419
- package/src/mentions.ts +57 -27
- package/src/models.ts +200 -66
- package/src/render/animations.ts +359 -2
- package/src/render/projection.ts +764 -655
- package/src/render/status.ts +744 -603
- package/src/startup.ts +119 -109
- package/src/theme-panel.ts +72 -0
- package/src/theme.ts +206 -70
package/lib/types/app.d.ts
CHANGED
|
@@ -14,12 +14,14 @@
|
|
|
14
14
|
* @module @deepseek-ai/dsh-code/app
|
|
15
15
|
*/
|
|
16
16
|
import { type ReactElement } from 'react';
|
|
17
|
+
import type { CommandDescriptor } from '@deepseek-ai/dsh-commands';
|
|
18
|
+
import { type ThemeName } from './theme.ts';
|
|
17
19
|
import type { TranscriptStore } from './store.ts';
|
|
18
20
|
import type { ApprovalStore } from './approval.ts';
|
|
19
21
|
import type { CommandsView } from './commands.ts';
|
|
20
22
|
import type { ModelDirectory, ModelRow } from './models.ts';
|
|
21
23
|
import type { QuestionStore } from './questions.ts';
|
|
22
|
-
import type { SkillsView } from './skills.ts';
|
|
24
|
+
import type { SkillsView, SkillRow } from './skills.ts';
|
|
23
25
|
import type { MentionCandidate } from './mentions.ts';
|
|
24
26
|
import type { PresetRow } from './presets.ts';
|
|
25
27
|
import type { PluginRow } from './plugin-inventory.ts';
|
|
@@ -40,6 +42,8 @@ export interface AppProps {
|
|
|
40
42
|
skills: SkillsView;
|
|
41
43
|
/** `provider/model` selection serving this session (updated on /model). */
|
|
42
44
|
model: string;
|
|
45
|
+
/** Effective reasoning effort in force ('' when none), for the /model picker mark. */
|
|
46
|
+
effort?: string;
|
|
43
47
|
/** Working-directory basename the session serves. */
|
|
44
48
|
cwd: string;
|
|
45
49
|
/** Absolute working directory used by session filters and references. */
|
|
@@ -64,8 +68,8 @@ export interface AppProps {
|
|
|
64
68
|
loadModels(): Promise<ModelDirectory>;
|
|
65
69
|
/** Load @mention candidates for the typed query (files + sessions). */
|
|
66
70
|
loadMentions(query: string, signal?: AbortSignal): Promise<readonly MentionCandidate[]>;
|
|
67
|
-
/** Apply one /model selection; returns the display label. */
|
|
68
|
-
selectModel(row: ModelRow): string;
|
|
71
|
+
/** Apply one /model selection (with an advertised reasoning effort, when picked); returns the display label. */
|
|
72
|
+
selectModel(row: ModelRow, effortId?: string): string;
|
|
69
73
|
/** Cycle to the next permission preset (Shift+Tab); returns the new label. */
|
|
70
74
|
cyclePermission(): string;
|
|
71
75
|
/** Export the transcript to a markdown file (/export [path]); reports via notices. */
|
|
@@ -89,6 +93,8 @@ export interface AppProps {
|
|
|
89
93
|
statusline: readonly string[];
|
|
90
94
|
/** Persist a new statusline item set; the runner surfaces IO failures as notices. */
|
|
91
95
|
saveStatusline(items: readonly string[]): void;
|
|
96
|
+
/** Apply and persist one /theme selection; the runner owns the theme.json file. */
|
|
97
|
+
saveTheme?(name: ThemeName): void;
|
|
92
98
|
/** Persistent cross-session input history (oldest first); the runner owns the file. */
|
|
93
99
|
history: readonly string[];
|
|
94
100
|
/** Persist one submitted prompt to the global history file. */
|
|
@@ -96,5 +102,27 @@ export interface AppProps {
|
|
|
96
102
|
/** Cancel one queued inbox message by identity (Delete on the empty composer). */
|
|
97
103
|
cancelQueued(messageId: string): void;
|
|
98
104
|
}
|
|
105
|
+
/** One completion candidate row. */
|
|
106
|
+
interface CompletionCandidate {
|
|
107
|
+
/** Insertion text for the command name (with leading slash). */
|
|
108
|
+
label: string;
|
|
109
|
+
/** Human-readable description shown beside the label. */
|
|
110
|
+
description: string;
|
|
111
|
+
/** Candidate origin; skills land the same literal text but route through the prompt. */
|
|
112
|
+
origin: 'command' | 'skill' | 'mention' | 'path';
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Resolve completion candidates for the current input: TUI-local commands,
|
|
116
|
+
* the live registry descriptors, and user-invocable skills, filtered by the
|
|
117
|
+
* typed prefix. Command names win collisions (the dispatch tries the
|
|
118
|
+
* registry first and only then falls through to the skill gesture), and a
|
|
119
|
+
* later duplicate name never renders twice.
|
|
120
|
+
*
|
|
121
|
+
* A bare `/` returns the FULL merged list — Codex's command popup shows every
|
|
122
|
+
* command inside a scroll window on an empty filter, and the menu's own
|
|
123
|
+
* selection window bounds the visible rows, so no slice cap is needed.
|
|
124
|
+
*/
|
|
125
|
+
export declare function completionCandidates(value: string, descriptors: readonly CommandDescriptor[], skills: readonly SkillRow[]): readonly CompletionCandidate[];
|
|
99
126
|
/** The whole terminal app; state arrives via the store, output via Ink. */
|
|
100
127
|
export declare function App(props: AppProps): ReactElement;
|
|
128
|
+
export {};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/** Bounded, composer-safe panels for preset, session, and plugin kernel views. */
|
|
2
2
|
import { type ReactElement } from 'react';
|
|
3
|
+
import type { ModelRow } from './models.ts';
|
|
3
4
|
import type { PresetRow } from './presets.ts';
|
|
4
5
|
import type { PluginRow } from './plugin-inventory.ts';
|
|
5
6
|
import type { SessionDirectoryOptions, SessionRow } from './session-directory.ts';
|
|
@@ -46,3 +47,23 @@ export declare function StatuslinePanel({ enabled, change, close }: {
|
|
|
46
47
|
change(items: readonly StatusItemId[]): void;
|
|
47
48
|
close(): void;
|
|
48
49
|
}): ReactElement;
|
|
50
|
+
/**
|
|
51
|
+
* The `/model` reasoning-effort stage (the Codex model → reasoning popup
|
|
52
|
+
* contract): one bounded list over the selected model's adapter-advertised
|
|
53
|
+
* effort levels, with the effective effort and the model default marked.
|
|
54
|
+
* A model WITHOUT an adapter-declared default leads with a "Default"
|
|
55
|
+
* (provider-default) row — the web effort pane's first entry — so the user
|
|
56
|
+
* can clear a picked level back to provider behavior instead of being forced
|
|
57
|
+
* to choose an advertised one. Enter applies one level; Esc returns to the
|
|
58
|
+
* model list without applying.
|
|
59
|
+
*/
|
|
60
|
+
export declare function EffortPanel({ row, current, select, back }: {
|
|
61
|
+
/** The model row whose advertised levels this stage lists. */
|
|
62
|
+
row: ModelRow;
|
|
63
|
+
/** Effective effort currently in force ('' when none), for the ● mark. */
|
|
64
|
+
current: string | undefined;
|
|
65
|
+
/** Accept one advertised effort id, or '' for the provider default. */
|
|
66
|
+
select(effortId: string): void;
|
|
67
|
+
/** Return to the model list without applying. */
|
|
68
|
+
back(): void;
|
|
69
|
+
}): ReactElement;
|
package/lib/types/mentions.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workspace @mention support: file candidates from a bounded
|
|
3
|
-
* the session cwd, session candidates from the opt-in
|
|
4
|
-
* service, and submission preparation through its
|
|
5
|
-
* session mentions land as canonical
|
|
6
|
-
* submit the text is parsed back into
|
|
7
|
-
* references, snapshots are injected
|
|
8
|
-
*
|
|
9
|
-
* upstream README's wiring.
|
|
2
|
+
* Workspace @mention support: file and directory candidates from a bounded
|
|
3
|
+
* async scan of the session cwd, session candidates from the opt-in
|
|
4
|
+
* `sessionReferenceResolver` service, and submission preparation through its
|
|
5
|
+
* `prepare()` API. Picked session mentions land as canonical
|
|
6
|
+
* `@[label](dsh-session:…)` tokens; on submit the text is parsed back into
|
|
7
|
+
* readable `@label` text plus structured references, snapshots are injected
|
|
8
|
+
* via `agent.inject()` before the readable message wakes the driver
|
|
9
|
+
* (`followup` idle, `steer` running) — exactly the upstream README's wiring.
|
|
10
|
+
*
|
|
11
|
+
* Harness exposes no workspace-file mention service (only session references
|
|
12
|
+
* plus a post-hoc produced-file linker), so the file index is the lightweight
|
|
13
|
+
* bounded scan below, kept deliberately smaller than Codex's streaming
|
|
14
|
+
* gitignore-aware walker.
|
|
10
15
|
*
|
|
11
16
|
* @module @deepseek-ai/dsh-code/mentions
|
|
12
17
|
*/
|
|
@@ -40,11 +45,17 @@ export interface PreparedMention {
|
|
|
40
45
|
/** Aggregated snapshot for `agent.inject()`, undefined without references. */
|
|
41
46
|
additionalContext?: import('@deepseek-ai/dsh-session').UserMessage;
|
|
42
47
|
}
|
|
43
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Bounded async BFS scan of a workspace; unreadable entries are skipped.
|
|
50
|
+
* Both files and directories are indexed (directories insert with a trailing
|
|
51
|
+
* slash), mirroring Codex's `MatchType::{File,Directory}` index. Dotfiles and
|
|
52
|
+
* the {@link SKIP_DIRS} list are excluded, which is a coarser filter than
|
|
53
|
+
* Codex's gitignore-aware walker but stays dependency-free and bounded.
|
|
54
|
+
*/
|
|
44
55
|
export declare function scanWorkspaceFiles(root: string, signal?: AbortSignal): Promise<readonly FileCandidate[]>;
|
|
45
56
|
/** The mention API the input editor and the runner share. */
|
|
46
57
|
export interface MentionsApi {
|
|
47
|
-
/** Scanned workspace files, cached across one session. */
|
|
58
|
+
/** Scanned workspace files and directories, cached across one session. */
|
|
48
59
|
files(): Promise<readonly FileCandidate[]>;
|
|
49
60
|
/** Ranked menu candidates for the typed `@` query. */
|
|
50
61
|
candidates(query: string, signal?: AbortSignal): Promise<readonly MentionCandidate[]>;
|
|
@@ -61,10 +72,16 @@ export interface MentionsApi {
|
|
|
61
72
|
/**
|
|
62
73
|
* Create the mention API for one agent's workspace. A missing
|
|
63
74
|
* session-reference service degrades to file mentions only (the scan still
|
|
64
|
-
* works); `prepare` then passes text through untouched.
|
|
75
|
+
* works); `prepare` then passes text through untouched. An undefined agent
|
|
76
|
+
* (a bare launch before any session exists) also degrades to file-only
|
|
77
|
+
* mentions, so `@` file completion works before the first message.
|
|
78
|
+
*
|
|
79
|
+
* `files` is hoisted into the closure so `candidates` never reaches for
|
|
80
|
+
* `this` — the runner hands `mentions.candidates` to the input editor as a
|
|
81
|
+
* detached callback, and a `this`-bound method would throw on every `@` key.
|
|
65
82
|
* @param ctx - context carrying the optional `sessionReferenceResolver`.
|
|
66
83
|
* @param agent - the session owner; excluded from its own candidates.
|
|
67
84
|
* @param cwd - workspace root to scan.
|
|
68
85
|
*/
|
|
69
|
-
export declare function createMentions(ctx: Context, agent: Agent, cwd: string): MentionsApi;
|
|
86
|
+
export declare function createMentions(ctx: Context, agent: Agent | undefined, cwd: string): MentionsApi;
|
|
70
87
|
export {};
|
package/lib/types/models.d.ts
CHANGED
|
@@ -8,6 +8,24 @@
|
|
|
8
8
|
* @module @deepseek-ai/dsh-tui/models
|
|
9
9
|
*/
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { ModelSelection } from '@deepseek-ai/dsh-agent';
|
|
12
|
+
import { type LlmModelReasoningInfo } from '@deepseek-ai/dsh-llm';
|
|
13
|
+
/** Display metadata for one adapter-owned reasoning effort (mirrors `LlmReasoningEffortInfo`). */
|
|
14
|
+
export interface ModelReasoningEffort {
|
|
15
|
+
/** Opaque value accepted by the model's `GenerateOptions.reasoningEffort`. */
|
|
16
|
+
id: string;
|
|
17
|
+
/** Human-readable effort name for selectors. */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Optional user-facing distinction from otherwise similar efforts. */
|
|
20
|
+
description?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Selectable reasoning efforts for one model (mirrors `LlmModelReasoningInfo`). */
|
|
23
|
+
export interface ModelReasoning {
|
|
24
|
+
/** Supported efforts in adapter-preferred display order. */
|
|
25
|
+
efforts: readonly ModelReasoningEffort[];
|
|
26
|
+
/** Adapter-configured default materialized when callers omit an effort. */
|
|
27
|
+
defaultEffort?: string;
|
|
28
|
+
}
|
|
11
29
|
/** One selectable row in the `/model` panel. */
|
|
12
30
|
export interface ModelRow {
|
|
13
31
|
/** Registered provider route. */
|
|
@@ -18,6 +36,8 @@ export interface ModelRow {
|
|
|
18
36
|
model: string;
|
|
19
37
|
/** Human-readable model name. */
|
|
20
38
|
modelName: string;
|
|
39
|
+
/** Adapter-owned selectable reasoning levels when the model exposes any. */
|
|
40
|
+
reasoning?: ModelReasoning;
|
|
21
41
|
}
|
|
22
42
|
/** The resolved directory: rows plus per-provider discovery failures. */
|
|
23
43
|
export interface ModelDirectory {
|
|
@@ -25,12 +45,58 @@ export interface ModelDirectory {
|
|
|
25
45
|
rows: readonly ModelRow[];
|
|
26
46
|
/** Provider ids whose model listing failed; those providers contribute no rows. */
|
|
27
47
|
failures: readonly string[];
|
|
48
|
+
/**
|
|
49
|
+
* `provider/model` labels whose per-model capability lookup failed. Those
|
|
50
|
+
* rows still appear (advisory degrade, mirroring the web catalog), but
|
|
51
|
+
* without an effort picker — a picker caller must not misread the absence
|
|
52
|
+
* as "this model exposes no reasoning" (e.g. deepseek-v4-flash always
|
|
53
|
+
* advertises off/high/max unless thinking is disabled). Optional for
|
|
54
|
+
* callers that shape a directory by hand; {@link loadModelDirectory}
|
|
55
|
+
* always populates it (empty when nothing failed).
|
|
56
|
+
*/
|
|
57
|
+
reasoningFailures?: readonly string[];
|
|
28
58
|
}
|
|
59
|
+
/** Map an adapter's reasoning capability onto the panel's plain-id shape. */
|
|
60
|
+
export declare function mapReasoning(reasoning: LlmModelReasoningInfo): ModelReasoning;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the effective model selection for one live session, in the
|
|
63
|
+
* documented precedence: the in-process explicit pick, then the session's
|
|
64
|
+
* last `request/header` config, then the deployment default.
|
|
65
|
+
* @param picked - the explicit selection made in this process, when any.
|
|
66
|
+
* @param logged - the last logged request header config, when any.
|
|
67
|
+
* @param defaults - the deployment default selection.
|
|
68
|
+
* @returns the effective selection, carrying a reasoning effort when one is in force.
|
|
69
|
+
*/
|
|
70
|
+
export declare function resolveEffectiveSelection(picked: ModelSelection | undefined, logged: {
|
|
71
|
+
provider: string;
|
|
72
|
+
model: string;
|
|
73
|
+
reasoningEffort?: string;
|
|
74
|
+
} | undefined, defaults: ModelSelection): ModelSelection;
|
|
75
|
+
/**
|
|
76
|
+
* Build the selection one `/model` pick applies, rejecting an effort the
|
|
77
|
+
* row does not advertise. The row is the picker's source of truth, so a
|
|
78
|
+
* stale directory cannot smuggle an unsupported effort into the next step
|
|
79
|
+
* (the request pipeline would reject it before network I/O regardless). The
|
|
80
|
+
* empty string is the picker's "provider default" sentinel — an explicit
|
|
81
|
+
* choice to leave the effort to the model's own default, exactly like an
|
|
82
|
+
* absent effort.
|
|
83
|
+
* @param row - the picked model row.
|
|
84
|
+
* @param effortId - the chosen advertised effort, '' or undefined for the model default.
|
|
85
|
+
* @returns the selection the runner records for the next assembled step.
|
|
86
|
+
*/
|
|
87
|
+
export declare function buildModelSelection(row: ModelRow, effortId?: string): ModelSelection;
|
|
88
|
+
/** Display label for one applied selection: `provider/model` or `provider/model@effort`. */
|
|
89
|
+
export declare function modelSelectionLabel(selection: ModelSelection): string;
|
|
29
90
|
/**
|
|
30
91
|
* Load the selectable model directory from the live `ctx.llm` registry.
|
|
31
92
|
* Providers are listed synchronously; each provider's models are discovered
|
|
32
93
|
* with a bounded parallel fan-out whose failures degrade to that provider
|
|
33
94
|
* contributing no rows (mirrors the web catalog's per-provider failures).
|
|
95
|
+
* Each row's reasoning levels are resolved per exact model like the web
|
|
96
|
+
* catalog (`buildModelCatalog`); a single model's capability lookup failure
|
|
97
|
+
* degrades to that row having no effort picker rather than hiding the model,
|
|
98
|
+
* and the failure rides `reasoningFailures` so the caller can tell "no
|
|
99
|
+
* advertised reasoning" from "capability lookup failed".
|
|
34
100
|
* @param ctx - context carrying the `llm` service.
|
|
35
101
|
* @returns the resolved directory; empty rows when `llm` is unavailable.
|
|
36
102
|
*/
|
|
@@ -3,11 +3,20 @@
|
|
|
3
3
|
* the StateDot "ongoing" pixel chase (3×3 ring, 125ms flat-hold brightness
|
|
4
4
|
* steps, 1s cycle) becomes the single-cell stepped pulse below and the
|
|
5
5
|
* full-ring clockwise braille chase in {@link BUSY_CHASE_FRAMES}, and the
|
|
6
|
-
* streaming caret blink is the Claude-Code convention.
|
|
7
|
-
*
|
|
6
|
+
* streaming caret blink is the Claude-Code convention.
|
|
7
|
+
*
|
|
8
|
+
* The DeepSeek model-switch easter egg ports Codex's effort-ignition "Wave"
|
|
9
|
+
* style (`codex-rs/tui/src/bottom_pane/effort_ignition_styles.rs`): switching
|
|
10
|
+
* INTO an official DeepSeek route sweeps a blue wave across the composer's
|
|
11
|
+
* input row — one column per cell, `backgroundColor` = the sampled wave
|
|
12
|
+
* color — then, on the deepseek (Ultra-equivalent) tier, drops the `· ✦ ✧`
|
|
13
|
+
* sparkle sequence into the rightmost blank cell before fading. The prompt
|
|
14
|
+
* marker keeps the tier accent afterwards (persistent, like Codex's prompt
|
|
15
|
+
* charge). Pure functions only — the Ink layer owns timers and colors.
|
|
8
16
|
*
|
|
9
17
|
* @module @deepseek-ai/dsh-code/render/animations
|
|
10
18
|
*/
|
|
19
|
+
import type { RgbTriple } from '../theme.ts';
|
|
11
20
|
/** Single-cell stepped pulse: flat holds mirroring the web's 125ms keyframes. */
|
|
12
21
|
export declare const PULSE_FRAMES: readonly ["█", "█", "▆", "▃", "▁", "▃", "▆", "█"];
|
|
13
22
|
/** Pulse frame for a monotonic tick. */
|
|
@@ -22,3 +31,167 @@ export declare const BUSY_CHASE_FRAMES: readonly ["⣾", "⣽", "⣻", "⢿", "
|
|
|
22
31
|
export declare function busyChaseFrame(tick: number): string;
|
|
23
32
|
/** Caret visibility: half the ticks on, half off (530ms blink). */
|
|
24
33
|
export declare function caretVisible(tick: number): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The one-shot DeepSeek model-switch easter egg: when the status bar model
|
|
36
|
+
* label switches to an official DeepSeek route, the composer's input row
|
|
37
|
+
* plays Codex's effort-ignition "Wave" — a blue crest sweeping the content
|
|
38
|
+
* row column by column (background tint ≤ 0.55 under the draft), plus the
|
|
39
|
+
* Ultra-style `· ✦ ✧` sparkles on the deepseek tier — and the prompt marker
|
|
40
|
+
* keeps the tier accent afterwards. The Ink layer owns the timer and reads
|
|
41
|
+
* the ACTIVE palette anchors (`getPalette`); everything below is pure
|
|
42
|
+
* interpolation over the colors it is given.
|
|
43
|
+
*/
|
|
44
|
+
/** Frame cadence of the DeepSeek wave: Codex's IGNITION_FRAME_TICK (33ms ≈ 30fps). */
|
|
45
|
+
export declare const DEEPSEEK_WAVE_TICK_MS = 33;
|
|
46
|
+
/**
|
|
47
|
+
* The two DeepSeek wave tiers. The concept maps Codex's reasoning tiers to
|
|
48
|
+
* model ids: `flash` runs the Max parameters, `deepseek` (pro models) runs
|
|
49
|
+
* the Ultra parameters (dual band + tail sparkles on the Wave style).
|
|
50
|
+
*/
|
|
51
|
+
export type DeepseekWaveTier = 'flash' | 'deepseek';
|
|
52
|
+
/**
|
|
53
|
+
* The three ignition styles — Codex `IgnitionStyle`: a traveling crest
|
|
54
|
+
* (Wave), a drifting multi-hue band (Aurora), and an expanding ring (Pulse).
|
|
55
|
+
* One style is picked at random per trigger and never repeats the previous.
|
|
56
|
+
*/
|
|
57
|
+
export type DeepseekWaveStyle = 'wave' | 'aurora' | 'pulse';
|
|
58
|
+
/** All styles in canonical order, for random selection and tests. */
|
|
59
|
+
export declare const DEEPSEEK_WAVE_STYLES: readonly DeepseekWaveStyle[];
|
|
60
|
+
/** Wave half-width in columns — Codex WAVE_HALF_WIDTH (9). */
|
|
61
|
+
export declare const WAVE_HALF_WIDTH = 9;
|
|
62
|
+
/** Pulse ring half-width in columns — Codex PULSE_HALF_WIDTH (4.5). */
|
|
63
|
+
export declare const PULSE_HALF_WIDTH = 4.5;
|
|
64
|
+
/** Sparkle start and frame cadence — Codex SPARK_START / SPARK_FRAME. */
|
|
65
|
+
export declare const SPARK_START_MS = 900;
|
|
66
|
+
export declare const SPARK_FRAME_MS = 100;
|
|
67
|
+
/** Sparkle glyphs in frame order — Codex SPARK_GLYPHS (`· ✦ ✧`). */
|
|
68
|
+
export declare const SPARK_GLYPHS: readonly ["·", "✦", "✧"];
|
|
69
|
+
/**
|
|
70
|
+
* Band tables — Codex `bands(style, tier)`. Each entry is a triple whose
|
|
71
|
+
* meaning depends on the style: Wave/Pulse use `(launch, travel, strength)`;
|
|
72
|
+
* Aurora uses `(speed, phase, hueIndex)`.
|
|
73
|
+
*/
|
|
74
|
+
export type DeepseekWaveBand = readonly [number, number, number];
|
|
75
|
+
export declare const DEEPSEEK_WAVE_BANDS: Readonly<Record<DeepseekWaveStyle, Readonly<Record<DeepseekWaveTier, readonly DeepseekWaveBand[]>>>>;
|
|
76
|
+
/**
|
|
77
|
+
* Total animation duration — Codex `IgnitionStyle::total_duration`: three
|
|
78
|
+
* styles × two tiers.
|
|
79
|
+
* @param tier - the active wave tier.
|
|
80
|
+
* @param style - the active ignition style.
|
|
81
|
+
* @returns the duration in milliseconds.
|
|
82
|
+
*/
|
|
83
|
+
export declare function deepseekWaveDuration(tier: DeepseekWaveTier, style?: DeepseekWaveStyle): number;
|
|
84
|
+
/**
|
|
85
|
+
* Pick one ignition style at random, never repeating the previous one —
|
|
86
|
+
* Codex `IgnitionStyle::random`. Falls back to the remaining styles.
|
|
87
|
+
* @param previous - the style of the last trigger, if any.
|
|
88
|
+
* @returns a style different from `previous`.
|
|
89
|
+
*/
|
|
90
|
+
export declare function deepseekWaveStyleRandom(previous: DeepseekWaveStyle | undefined): DeepseekWaveStyle;
|
|
91
|
+
/**
|
|
92
|
+
* Blank-cell background the wave tint blends toward — fixed approximations
|
|
93
|
+
* of the terminal's default background, mirroring Codex's
|
|
94
|
+
* `user_message_bg_rgb` (which derives a near-black / near-white bubble tint
|
|
95
|
+
* from the terminal background). The Ink layer picks the active theme's one.
|
|
96
|
+
*/
|
|
97
|
+
export declare const WAVE_BASE_DARK: RgbTriple;
|
|
98
|
+
export declare const WAVE_BASE_LIGHT: RgbTriple;
|
|
99
|
+
/**
|
|
100
|
+
* Tier for a `provider/model` label: a model id containing `flash` runs the
|
|
101
|
+
* single-band flash tier; everything else (pro/reasoner/chat) runs the
|
|
102
|
+
* dual-band deepseek tier. Mirrors Codex's Max→Ultra mapping.
|
|
103
|
+
* @param model - the `provider/model` label of the applied model.
|
|
104
|
+
* @returns the wave tier for that model.
|
|
105
|
+
*/
|
|
106
|
+
export declare function deepseekWaveTier(model: string): DeepseekWaveTier;
|
|
107
|
+
/**
|
|
108
|
+
* Cosine window — Codex `crest`: 1 exactly under the wave center, 0 from
|
|
109
|
+
* one half-width away.
|
|
110
|
+
* @param distance - distance from the crest center in half-widths.
|
|
111
|
+
* @returns the crest strength in 0..1.
|
|
112
|
+
*/
|
|
113
|
+
export declare function crest(distance: number): number;
|
|
114
|
+
/**
|
|
115
|
+
* Cubic ease-in-out — Codex `ease_in_out`: flat at both ends, steepest in
|
|
116
|
+
* the middle, so the crest accelerates and eases instead of sliding linearly.
|
|
117
|
+
* @param progress - raw progress (clamped to 0..1).
|
|
118
|
+
* @returns the eased progress in 0..1.
|
|
119
|
+
*/
|
|
120
|
+
export declare function easeInOut(progress: number): number;
|
|
121
|
+
/**
|
|
122
|
+
* Fade-in/fade-out envelope — Codex `envelope`: linear ramp over `fadeIn`
|
|
123
|
+
* at the start and `fadeOut` at the end, plateau at 1 between, 0 outside the
|
|
124
|
+
* total. The Wave style keeps the envelope at 1 (Codex paints Wave without
|
|
125
|
+
* an envelope); exported for the Aurora-style fades and for tests.
|
|
126
|
+
* @param elapsed - seconds since the animation started.
|
|
127
|
+
* @param total - total duration in seconds.
|
|
128
|
+
* @param fadeIn - seconds of fade-in.
|
|
129
|
+
* @param fadeOut - seconds of fade-out.
|
|
130
|
+
* @returns the envelope value in 0..1.
|
|
131
|
+
*/
|
|
132
|
+
export declare function envelope(elapsed: number, total: number, fadeIn: number, fadeOut: number): number;
|
|
133
|
+
/**
|
|
134
|
+
* The background color for one composer-row column at a tick — Codex
|
|
135
|
+
* `paint_bands` + `Canvas::tint` for all three styles. Bands overlap with a
|
|
136
|
+
* max for Wave/Pulse and a SUM for Aurora (Codex differs by style), the
|
|
137
|
+
* weighted hues mix per column (Wave/Pulse always end on hue 0), the tint
|
|
138
|
+
* blends the mixed hue toward the blank-cell base at the style's alpha cap,
|
|
139
|
+
* and Aurora applies its own fade envelope. Returns `null` when the column
|
|
140
|
+
* should stay transparent, so the row returns to no `backgroundColor` on
|
|
141
|
+
* both ends.
|
|
142
|
+
* @param tick - wave frame (0, 1, … at DEEPSEEK_WAVE_TICK_MS).
|
|
143
|
+
* @param column - column index in the content row (0..width-1).
|
|
144
|
+
* @param width - content-row width in columns.
|
|
145
|
+
* @param tier - the wave tier (flash = Max, deepseek = Ultra parameters).
|
|
146
|
+
* @param style - the ignition style.
|
|
147
|
+
* @param hues - the tier's three hues.
|
|
148
|
+
* @param base - the blank-cell base color the tint blends toward.
|
|
149
|
+
* @returns the blended RGB background, or null for transparent.
|
|
150
|
+
*/
|
|
151
|
+
export declare function deepseekWaveColumnBg(tick: number, column: number, width: number, tier: DeepseekWaveTier, style: DeepseekWaveStyle, hues: readonly [RgbTriple, RgbTriple, RgbTriple], base: RgbTriple): RgbTriple | null;
|
|
152
|
+
/**
|
|
153
|
+
* The sparkle glyph for a tick — Codex `spark_frame`: from 900ms on, one
|
|
154
|
+
* glyph every 100ms through `· ✦ ✧`, then silent. The deepseek (Ultra) tier
|
|
155
|
+
* only; the Ink layer still must skip occupied cells.
|
|
156
|
+
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
157
|
+
* @returns the sparkle glyph, or null outside the 900..1200ms window.
|
|
158
|
+
*/
|
|
159
|
+
export declare function deepseekWaveSpark(tick: number): string | null;
|
|
160
|
+
/**
|
|
161
|
+
* The composer BORDER color at a tick: the frame breathes with the wave —
|
|
162
|
+
* the palette's static dim blends toward the tier accent as the crest is
|
|
163
|
+
* alive and back, so the frame glows up while the wave sweeps and settles to
|
|
164
|
+
* dim on both ends (first==last frame==dim, no hard jump).
|
|
165
|
+
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
166
|
+
* @param tier - the wave tier.
|
|
167
|
+
* @param hues - the tier's three hues; the border blends toward hues[0].
|
|
168
|
+
* @param dim - the palette's static dim RGB (the resting border color).
|
|
169
|
+
* @returns the blended border RGB.
|
|
170
|
+
*/
|
|
171
|
+
export declare function deepseekWaveBorderColor(tick: number, tier: DeepseekWaveTier, style: DeepseekWaveStyle, hues: readonly [RgbTriple, RgbTriple, RgbTriple], dim: RgbTriple): RgbTriple;
|
|
172
|
+
/**
|
|
173
|
+
* Whether the `deepseek` wordmark rides the wave at this tick: it fades in
|
|
174
|
+
* shortly after the first crest launches and out before the wave settles,
|
|
175
|
+
* so the brand name surfaces through the sweep's middle. The Ink layer
|
|
176
|
+
* places it in the row's blank mid-section (never over real draft text).
|
|
177
|
+
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
178
|
+
* @param tier - the wave tier.
|
|
179
|
+
* @returns true while the wordmark should be visible.
|
|
180
|
+
*/
|
|
181
|
+
export declare function deepseekWaveWordVisible(tick: number, tier: DeepseekWaveTier, style?: DeepseekWaveStyle): boolean;
|
|
182
|
+
/**
|
|
183
|
+
* The per-character color for the `deepseek` wordmark: the tier's hues
|
|
184
|
+
* cycled per character (d→hue0, e→hue1, e→hue2, …), a brand-gradient text.
|
|
185
|
+
* @param index - character index in the wordmark.
|
|
186
|
+
* @param hues - the tier's three hues.
|
|
187
|
+
* @returns the hue for that character.
|
|
188
|
+
*/
|
|
189
|
+
export declare function deepseekWaveWordHue(index: number, hues: readonly [RgbTriple, RgbTriple, RgbTriple]): RgbTriple;
|
|
190
|
+
/**
|
|
191
|
+
* True when a `provider/model` status label addresses the official DeepSeek
|
|
192
|
+
* route: either segment contains `deepseek` (case-insensitive), covering the
|
|
193
|
+
* `deepseek-official` provider route and its `deepseek-*` model ids.
|
|
194
|
+
* @param label - the status bar model label (`provider/model`).
|
|
195
|
+
* @returns whether the label names an official DeepSeek model.
|
|
196
|
+
*/
|
|
197
|
+
export declare function isOfficialDeepSeekLabel(label: string): boolean;
|
|
@@ -138,6 +138,24 @@ export interface UsageTotals {
|
|
|
138
138
|
/** Cache-read tokens over the whole log (0 when the adapter reports none). */
|
|
139
139
|
cacheReadTokens: number;
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Estimated used tokens per context content type, folded from transcript
|
|
143
|
+
* events via {@link estimateTokens}. The segmented context bar's composition
|
|
144
|
+
* source: proportions across types are meaningful, absolute values are not
|
|
145
|
+
* (they never touch billing or the reported `lastPromptTokens`).
|
|
146
|
+
*/
|
|
147
|
+
export interface ContextSegments {
|
|
148
|
+
/** Rendered system-prompt text (latest `request/header`) plus injected-context notices. */
|
|
149
|
+
system: number;
|
|
150
|
+
/** Direct human prompts (durable `user/message` rows). */
|
|
151
|
+
prompt: number;
|
|
152
|
+
/** Assistant text blocks (visible replies). */
|
|
153
|
+
assistant: number;
|
|
154
|
+
/** Assistant reasoning blocks (hidden thinking). */
|
|
155
|
+
thinking: number;
|
|
156
|
+
/** Tool call arguments plus result text. */
|
|
157
|
+
tools: number;
|
|
158
|
+
}
|
|
141
159
|
/** Window-scoped figures the status line shows; timing uses event timestamps. */
|
|
142
160
|
export interface TranscriptStats {
|
|
143
161
|
/** Durable turns opened (`turn/start` events). */
|
|
@@ -154,6 +172,8 @@ export interface TranscriptStats {
|
|
|
154
172
|
lastPromptTokens: number;
|
|
155
173
|
/** Newest advertised route capacity, 0 when no adapter ever advertised one. */
|
|
156
174
|
contextWindow: number;
|
|
175
|
+
/** Estimated used tokens per content type (the segmented bar's composition). */
|
|
176
|
+
contextSegments: ContextSegments;
|
|
157
177
|
/** Summed first-token waits: `step/start` → first non-empty chunk, in ms. */
|
|
158
178
|
ttftMs: number;
|
|
159
179
|
/** Steps that produced a first chunk (the TTFT average's denominator). */
|
|
@@ -162,6 +182,14 @@ export interface TranscriptStats {
|
|
|
162
182
|
decodeMs: number;
|
|
163
183
|
/** Completion tokens over timed decode spans (the tok/s numerator). */
|
|
164
184
|
decodeTokens: number;
|
|
185
|
+
/**
|
|
186
|
+
* Adapter-owned reasoning effort of the latest `request/header` config —
|
|
187
|
+
* the EFFECTIVE effort the session actually uses (a materialized model
|
|
188
|
+
* default is included, exactly as the adapter resolved it). Empty when the
|
|
189
|
+
* header carried none (provider-default behavior). The status line appends
|
|
190
|
+
* it to the model segment as `provider/model@effort`.
|
|
191
|
+
*/
|
|
192
|
+
reasoningEffort: string;
|
|
165
193
|
}
|
|
166
194
|
/** The complete TUI transcript view for one session. */
|
|
167
195
|
export interface TranscriptView {
|
|
@@ -235,13 +263,16 @@ export declare function projectEvent(view: TranscriptView, event: SessionEvent):
|
|
|
235
263
|
*/
|
|
236
264
|
export declare function projectEvents(events: readonly SessionEvent[]): TranscriptView;
|
|
237
265
|
/**
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
266
|
+
* The append-only flush boundary for a transcript view: the count of entries
|
|
267
|
+
* no later event can remove. Entries at or beyond this index are mutable and
|
|
268
|
+
* must stay in the live tree.
|
|
269
|
+
*
|
|
270
|
+
* `pending` rows are excluded even though they are not a running tool/retry:
|
|
271
|
+
* the inbox claims or cancels them durably (`agent/inbox/spliced` removals,
|
|
272
|
+
* `user/message` retirement), and an append-only `<Static>` flush cannot
|
|
273
|
+
* erase a row that vanishes from the view — the retired row would ghost on
|
|
274
|
+
* screen until the next source-backed replay. Everything else (including a
|
|
275
|
+
* completed tail) is final: later events only APPEND new rows.
|
|
245
276
|
* @param entries - the view's transcript entries in order.
|
|
246
277
|
* @returns the count of entries safe to flush (0 for an empty transcript).
|
|
247
278
|
*/
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module @deepseek-ai/dsh-tui/render/status
|
|
11
11
|
*/
|
|
12
|
-
import type { TranscriptStats } from './projection.ts';
|
|
12
|
+
import type { ContextSegments, TranscriptStats } from './projection.ts';
|
|
13
13
|
/**
|
|
14
14
|
* Compact token count: 517 / 12.2K / 517K / 1.2M (one decimal under three
|
|
15
15
|
* digits), mirroring the web composer's StatsLine format.
|
|
@@ -40,7 +40,7 @@ export declare function cacheHitPercent(usage: TranscriptStats['usage']): number
|
|
|
40
40
|
* Presentation tones for status spans; the footer maps each to a theme color
|
|
41
41
|
* (Codex status-line accents: model/path/branch/state/usage categories).
|
|
42
42
|
*/
|
|
43
|
-
export type StatusTone = 'model' | 'live' | 'path' | 'branch' | 'value' | 'label' | 'meta' | 'accent' | 'success' | 'warn' | 'error';
|
|
43
|
+
export type StatusTone = 'model' | 'live' | 'path' | 'branch' | 'value' | 'label' | 'meta' | 'accent' | 'success' | 'warn' | 'error' | 'ctxSystem' | 'ctxPrompt' | 'ctxAssistant' | 'ctxThinking' | 'ctxTools';
|
|
44
44
|
/** One colored run inside the status bar. */
|
|
45
45
|
export interface StatusSpan {
|
|
46
46
|
text: string;
|
|
@@ -80,21 +80,42 @@ export declare const STATUS_GROUP_SEPARATOR = " | ";
|
|
|
80
80
|
export declare const STATUS_ITEM_SEPARATOR = " \u00B7 ";
|
|
81
81
|
/** The Codex-style mode cycle hint appended to the permission badge. */
|
|
82
82
|
export declare const STATUS_CYCLE_HINT = " (shift+tab to cycle)";
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Interior columns of the segmented context bar (content-type segments plus
|
|
85
|
+
* the free tail whose right edge carries the usage readout). Fixed so the
|
|
86
|
+
* row-2 drop ladder can pre-measure the group; the bar shrinks its labels and
|
|
87
|
+
* readout inside this budget rather than asking the layout for more room.
|
|
88
|
+
*/
|
|
89
|
+
export declare const CONTEXT_BAR_WIDTH = 24;
|
|
90
|
+
/** Occupancy at which the usage readout flips from brand blue to amber. */
|
|
86
91
|
export declare const CONTEXT_WARN_PERCENT = 90;
|
|
92
|
+
/** One content-type segment of the context bar (pure data; colors live in app.ts). */
|
|
93
|
+
export interface ContextSegmentSpec {
|
|
94
|
+
key: keyof ContextSegments;
|
|
95
|
+
/** Tone the footer maps to a DeepSeek blue shade. */
|
|
96
|
+
tone: StatusTone;
|
|
97
|
+
/** Labels longest → shortest; the first one fitting the segment width wins. */
|
|
98
|
+
labels: readonly string[];
|
|
99
|
+
}
|
|
100
|
+
/** The five content types in conversation order, dark → light blue. */
|
|
101
|
+
export declare const CONTEXT_SEGMENTS: readonly ContextSegmentSpec[];
|
|
87
102
|
/**
|
|
88
|
-
* Render
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
103
|
+
* Render context occupancy as a segmented bar: one DeepSeek-blue run per
|
|
104
|
+
* content type (system/prompt/assistant/thinking/tools), column widths
|
|
105
|
+
* proportional to their estimated token share, each with a centered label
|
|
106
|
+
* that shortens to fit (system→sys→s). The remaining free tail is a dim
|
|
107
|
+
* track whose right edge carries the usage readout (`12.3K/1.0M 25%`,
|
|
108
|
+
* shrinking to the bare percent as the tail narrows). The readout flips to
|
|
109
|
+
* amber once occupancy reaches the warning threshold; the segment blues stay
|
|
110
|
+
* untouched so the composition remains readable at full context. The used
|
|
111
|
+
* total comes from the reported `lastPromptTokens`, never from the estimates.
|
|
112
|
+
* @param segments - estimated used tokens per content type.
|
|
113
|
+
* @param usedTokens - reported used tokens (drives the readout and percent).
|
|
114
|
+
* @param contextWindow - route capacity.
|
|
115
|
+
* @param width - total bar interior columns.
|
|
95
116
|
* @returns tone-split spans for the footer to paint.
|
|
96
117
|
*/
|
|
97
|
-
export declare function contextBar(
|
|
118
|
+
export declare function contextBar(segments: ContextSegments, usedTokens: number, contextWindow: number, width: number): readonly StatusSpan[];
|
|
98
119
|
/**
|
|
99
120
|
* One customizable status item (the Codex /statusline picker contract).
|
|
100
121
|
* 'left' items render as pipe-separated clusters after the identity dot;
|