dsh-code 0.9.1 → 1.0.1
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 +278 -249
- package/README.md +131 -102
- package/bin/deepseek.mjs +100 -6
- package/cordis.patch.yml +36 -1
- package/lib/index.mjs +3055 -819
- package/lib/startup.mjs +21 -11
- package/lib/{theme-BEi4i_aN.mjs → theme-DCT8Y2xf.mjs} +13 -9
- package/lib/types/app.d.ts +84 -16
- package/lib/types/attachments.d.ts +20 -0
- package/lib/types/authorization-panel.d.ts +22 -0
- package/lib/types/authorization.d.ts +36 -0
- package/lib/types/editor.d.ts +6 -0
- package/lib/types/fork.d.ts +8 -0
- package/lib/types/git-workflow.d.ts +23 -0
- package/lib/types/index.d.ts +6 -0
- package/lib/types/kernel-panels.d.ts +39 -0
- package/lib/types/keyboard.d.ts +41 -0
- package/lib/types/mentions.d.ts +30 -38
- package/lib/types/models.d.ts +3 -1
- package/lib/types/permissions.d.ts +4 -14
- package/lib/types/presets.d.ts +5 -20
- package/lib/types/provider-settings.d.ts +16 -0
- package/lib/types/render/animations.d.ts +10 -39
- package/lib/types/render/editor.d.ts +137 -0
- package/lib/types/render/export.d.ts +1 -1
- package/lib/types/render/lines.d.ts +6 -2
- package/lib/types/render/markdown.d.ts +3 -1
- package/lib/types/render/projection.d.ts +29 -3
- package/lib/types/render/status.d.ts +6 -13
- package/lib/types/session-directory.d.ts +1 -3
- package/lib/types/startup.d.ts +14 -11
- package/lib/types/store.d.ts +11 -9
- package/lib/types/subagents.d.ts +3 -3
- package/lib/types/theme.d.ts +14 -1
- package/lib/types/version.d.ts +15 -2
- package/package.json +159 -141
- package/src/app.ts +1490 -663
- package/src/attachments.ts +128 -0
- package/src/authorization-panel.ts +285 -0
- package/src/authorization.ts +147 -0
- package/src/editor.ts +51 -0
- package/src/fork.ts +31 -0
- package/src/git-workflow.ts +87 -0
- package/src/index.ts +1523 -1374
- package/src/internals.ts +14 -1
- package/src/kernel-panels.ts +914 -798
- package/src/keyboard.ts +126 -0
- package/src/mentions.ts +78 -117
- package/src/models.ts +20 -14
- package/src/permissions.ts +5 -13
- package/src/presets.ts +6 -22
- package/src/provider-settings.ts +95 -1
- package/src/render/animations.ts +420 -450
- package/src/render/editor.ts +398 -0
- package/src/render/export.ts +79 -79
- package/src/render/lines.ts +342 -236
- package/src/render/markdown.ts +99 -26
- package/src/render/projection.ts +106 -19
- package/src/render/status.ts +713 -650
- package/src/render/text.ts +150 -150
- package/src/render/tool-detail.ts +3 -1
- package/src/session-directory.ts +4 -4
- package/src/startup.ts +136 -119
- package/src/store.ts +23 -11
- package/src/subagents.ts +13 -5
- package/src/theme.ts +214 -206
- package/src/version.ts +58 -1
package/lib/types/mentions.d.ts
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Workspace @mention support: file and directory candidates from
|
|
3
|
-
*
|
|
4
|
-
* `sessionReferenceResolver` service, and submission
|
|
5
|
-
* `prepare()` API. Picked session mentions land as
|
|
6
|
-
* `@[label](dsh-session:…)` tokens; on submit the text is parsed
|
|
7
|
-
* readable `@label` text plus structured references, snapshots are
|
|
8
|
-
* via `agent.inject()` before the readable message wakes the driver
|
|
2
|
+
* Workspace @mention support: file and directory candidates from the
|
|
3
|
+
* `fileReferences` service (dsh-file-reference-local), session candidates
|
|
4
|
+
* from the opt-in `sessionReferenceResolver` service, and submission
|
|
5
|
+
* preparation through its `prepare()` API. Picked session mentions land as
|
|
6
|
+
* canonical `@[label](dsh-session:…)` tokens; on submit the text is parsed
|
|
7
|
+
* back into readable `@label` text plus structured references, snapshots are
|
|
8
|
+
* injected via `agent.inject()` before the readable message wakes the driver
|
|
9
9
|
* (`followup` idle, `steer` running) — exactly the upstream README's wiring.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* File discovery lives entirely in the Harness service (per-agent bounded
|
|
12
|
+
* index, `@dir/` listing, symlink guards, tool/result invalidation); this
|
|
13
|
+
* module only maps candidates to menu rows and never re-implements scanning.
|
|
14
|
+
* The service is agent-scoped (the agent supplies the session cwd and the
|
|
15
|
+
* cache key), so before the first session creates an agent the SAME official
|
|
16
|
+
* search class runs against the launch cwd — @ file completion works on a
|
|
17
|
+
* bare launch, model- and session-independent, and the agent-scoped service
|
|
18
|
+
* takes over once a session exists.
|
|
15
19
|
*
|
|
16
20
|
* @module @deepseek-ai/dsh-code/mentions
|
|
17
21
|
*/
|
|
@@ -20,13 +24,6 @@ import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
|
20
24
|
import { parseSessionReferenceText, type SessionReferenceCandidate, type SessionReferenceInput } from '@deepseek-ai/dsh-session-reference';
|
|
21
25
|
/** Parsed submission text: readable text plus structured references. */
|
|
22
26
|
type ParsedSessionReferenceText = ReturnType<typeof parseSessionReferenceText>;
|
|
23
|
-
/** One filesystem entry the @ menu can complete. */
|
|
24
|
-
export interface FileCandidate {
|
|
25
|
-
/** Workspace-relative path with forward slashes. */
|
|
26
|
-
path: string;
|
|
27
|
-
/** Entry kind; directories insert with a trailing slash. */
|
|
28
|
-
kind: 'file' | 'directory';
|
|
29
|
-
}
|
|
30
27
|
/** One merged menu candidate (files and sessions, already ranked). */
|
|
31
28
|
export interface MentionCandidate {
|
|
32
29
|
/** Text inserted after the `@` (directories carry a trailing slash). */
|
|
@@ -35,6 +32,8 @@ export interface MentionCandidate {
|
|
|
35
32
|
description: string;
|
|
36
33
|
/** Origin kind for icon/coloring decisions. */
|
|
37
34
|
kind: 'file' | 'directory' | 'session';
|
|
35
|
+
/** Absolute path for file candidates; never rendered or persisted directly. */
|
|
36
|
+
path?: string;
|
|
38
37
|
}
|
|
39
38
|
/** Prepared submission: readable content plus optional injected context. */
|
|
40
39
|
export interface PreparedMention {
|
|
@@ -45,18 +44,8 @@ export interface PreparedMention {
|
|
|
45
44
|
/** Aggregated snapshot for `agent.inject()`, undefined without references. */
|
|
46
45
|
additionalContext?: import('@deepseek-ai/dsh-session').UserMessage;
|
|
47
46
|
}
|
|
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
|
-
*/
|
|
55
|
-
export declare function scanWorkspaceFiles(root: string, signal?: AbortSignal): Promise<readonly FileCandidate[]>;
|
|
56
47
|
/** The mention API the input editor and the runner share. */
|
|
57
48
|
export interface MentionsApi {
|
|
58
|
-
/** Scanned workspace files and directories, cached across one session. */
|
|
59
|
-
files(): Promise<readonly FileCandidate[]>;
|
|
60
49
|
/** Ranked menu candidates for the typed `@` query. */
|
|
61
50
|
candidates(query: string, signal?: AbortSignal): Promise<readonly MentionCandidate[]>;
|
|
62
51
|
/** Parse submission text into readable text plus structured references. */
|
|
@@ -71,17 +60,20 @@ export interface MentionsApi {
|
|
|
71
60
|
}
|
|
72
61
|
/**
|
|
73
62
|
* Create the mention API for one agent's workspace. A missing
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* (a bare launch before any session
|
|
77
|
-
*
|
|
63
|
+
* `fileReferences` service (with an agent present) or `sessionReferenceResolver`
|
|
64
|
+
* degrades that half to empty rows; `prepare` passes text through untouched
|
|
65
|
+
* without references. An undefined agent (a bare launch before any session
|
|
66
|
+
* exists) runs the official WorkspaceFileSearch over the launch cwd — the
|
|
67
|
+
* same class the mounted service uses per agent — so `@` file completion
|
|
68
|
+
* works from the first keystroke; session references wait for the session.
|
|
78
69
|
*
|
|
79
|
-
* `
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* @param ctx - context carrying the optional `
|
|
83
|
-
*
|
|
84
|
-
* @param
|
|
70
|
+
* `candidates` never reaches for `this` — the runner hands it to the input
|
|
71
|
+
* editor as a detached callback, and a `this`-bound method would throw on
|
|
72
|
+
* every `@` key.
|
|
73
|
+
* @param ctx - context carrying the optional `fileReferences` and
|
|
74
|
+
* `sessionReferenceResolver` services.
|
|
75
|
+
* @param agent - the session owner; excluded from its own session candidates.
|
|
76
|
+
* @param cwd - launch working directory; bounds the pre-session search.
|
|
85
77
|
*/
|
|
86
78
|
export declare function createMentions(ctx: Context, agent: Agent | undefined, cwd: string): MentionsApi;
|
|
87
79
|
export {};
|
package/lib/types/models.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { Context } from '@deepseek-ai/cordis';
|
|
11
11
|
import type { ModelSelection } from '@deepseek-ai/dsh-agent';
|
|
12
|
-
import { type LlmCallConfig, type LlmModelReasoningInfo } from '@deepseek-ai/dsh-llm';
|
|
12
|
+
import { type LlmCallConfig, type LlmModelReasoningInfo, type ModelModality } from '@deepseek-ai/dsh-llm';
|
|
13
13
|
/** Display metadata for one adapter-owned reasoning effort (mirrors `LlmReasoningEffortInfo`). */
|
|
14
14
|
export interface ModelReasoningEffort {
|
|
15
15
|
/** Opaque value accepted by the model's `GenerateOptions.reasoningEffort`. */
|
|
@@ -36,6 +36,8 @@ export interface ModelRow {
|
|
|
36
36
|
model: string;
|
|
37
37
|
/** Human-readable model name. */
|
|
38
38
|
modelName: string;
|
|
39
|
+
/** Request modalities advertised for this exact route; absent means unknown. */
|
|
40
|
+
inputModalities?: readonly ModelModality[];
|
|
39
41
|
/** Adapter-owned selectable reasoning levels when the model exposes any. */
|
|
40
42
|
reasoning?: ModelReasoning;
|
|
41
43
|
}
|
|
@@ -1,24 +1,14 @@
|
|
|
1
1
|
/** Permission-preset policy for pending and active TUI sessions. */
|
|
2
2
|
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
-
import type {
|
|
3
|
+
import type { PermissionPresetService } from '@deepseek-ai/dsh-permission-presets';
|
|
4
|
+
import type { Session } from '@deepseek-ai/dsh-session';
|
|
4
5
|
/** One selectable permission preset row for the /permission panel. */
|
|
5
6
|
export interface PermissionRow {
|
|
6
7
|
readonly id: string;
|
|
7
8
|
readonly description?: string;
|
|
8
9
|
}
|
|
9
|
-
/**
|
|
10
|
-
export
|
|
11
|
-
readonly names: readonly string[];
|
|
12
|
-
readonly defaultPreset: string;
|
|
13
|
-
resolve(name: string): unknown;
|
|
14
|
-
current(events: readonly SessionEvent[]): string;
|
|
15
|
-
set(session: Session, preset: string): void;
|
|
16
|
-
/** Client presentation metadata for one preset; may reject unknown names. */
|
|
17
|
-
optionOf?(name: string): {
|
|
18
|
-
name: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
} | undefined;
|
|
21
|
-
}
|
|
10
|
+
/** Public compatibility alias for the official upstream permission service. */
|
|
11
|
+
export type PermissionPresetsService = PermissionPresetService;
|
|
22
12
|
/** Read the optional Harness service without importing its runtime package. */
|
|
23
13
|
export declare function permissionPresetsFrom(ctx: Context): PermissionPresetsService | undefined;
|
|
24
14
|
/** Effective label for either an active session or the not-yet-created first one. */
|
package/lib/types/presets.d.ts
CHANGED
|
@@ -1,25 +1,12 @@
|
|
|
1
1
|
/** Agent-preset policy kept independent from the Ink surface. */
|
|
2
2
|
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
-
import type { Agent
|
|
3
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
4
|
+
import type { AgentPreset, AgentPresets } from '@deepseek-ai/dsh-agent-presets';
|
|
4
5
|
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session';
|
|
5
6
|
/** One discoverable agent composition. */
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
readonly name?: string;
|
|
10
|
-
readonly description?: string;
|
|
11
|
-
readonly order?: number;
|
|
12
|
-
readonly broken?: string;
|
|
13
|
-
}
|
|
14
|
-
/** Structural boundary for the optional upstream AgentPresets service. */
|
|
15
|
-
export interface AgentPresetsService {
|
|
16
|
-
readonly defaultId: string;
|
|
17
|
-
list(): Promise<PresetRow[]>;
|
|
18
|
-
resolve(id?: string): Promise<PresetRow>;
|
|
19
|
-
mount(agentCtx: Context, id?: string): Promise<PresetRow>;
|
|
20
|
-
recompose(agentCtx: Context, id: string): Promise<PresetRow>;
|
|
21
|
-
composedPreset(agentCtx: Context): string | undefined;
|
|
22
|
-
}
|
|
7
|
+
export type PresetRow = AgentPreset;
|
|
8
|
+
/** Public compatibility alias for the official upstream service type. */
|
|
9
|
+
export type AgentPresetsService = AgentPresets;
|
|
23
10
|
/** Read an optional Cordis service without requiring its package at build time. */
|
|
24
11
|
export declare function agentPresetsFrom(ctx: Context): AgentPresetsService | undefined;
|
|
25
12
|
/** A preset may change only before the first durable turn begins. */
|
|
@@ -30,5 +17,3 @@ export declare function resolvePreset(session: Pick<Session, 'header' | 'events'
|
|
|
30
17
|
export declare function selectPreset(service: AgentPresetsService, agent: Agent | undefined, presetId: string): Promise<PresetRow>;
|
|
31
18
|
/** Recompose atomically from the caller's perspective, logging only success. */
|
|
32
19
|
export declare function switchPreset(service: AgentPresetsService, agent: Agent, presetId: string): Promise<PresetRow>;
|
|
33
|
-
/** Minimal handle shape used by lifecycle tests without exposing Agent internals. */
|
|
34
|
-
export type OwnedAgent = Pick<AgentHandle, 'agent' | 'dispose'>;
|
|
@@ -42,6 +42,18 @@ export type ProviderCredentialView = ({
|
|
|
42
42
|
readonly kind: 'error';
|
|
43
43
|
readonly message: string;
|
|
44
44
|
};
|
|
45
|
+
/** One explicit model enabled for a provider profile. */
|
|
46
|
+
export interface ProviderModelSettings {
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly name?: string;
|
|
49
|
+
readonly contextWindow?: number;
|
|
50
|
+
readonly maxTokens?: number;
|
|
51
|
+
}
|
|
52
|
+
/** The small, portable subset of a provider profile the terminal edits. */
|
|
53
|
+
export interface ProviderConfiguration {
|
|
54
|
+
readonly baseURL?: string;
|
|
55
|
+
readonly models: readonly ProviderModelSettings[];
|
|
56
|
+
}
|
|
45
57
|
/**
|
|
46
58
|
* One provider row in the TUI provider-management panel: the configurable
|
|
47
59
|
* directory entry joined with its settings profile and credential facts.
|
|
@@ -71,6 +83,8 @@ export interface ProviderTargetView {
|
|
|
71
83
|
readonly suggestedRef: string;
|
|
72
84
|
/** Credential facts, a bounded describe error, or undefined when there is no ref to describe. */
|
|
73
85
|
readonly credential: ProviderCredentialView | undefined;
|
|
86
|
+
/** Endpoint and explicit model overrides visible to the provider editor. */
|
|
87
|
+
readonly configuration: ProviderConfiguration;
|
|
74
88
|
/** The owning adapter reports this route as hand-declared (absent when it draws no distinction). */
|
|
75
89
|
readonly declared?: boolean;
|
|
76
90
|
}
|
|
@@ -120,6 +134,8 @@ export declare function loadProviderSettings(ctx: Context): Promise<ProviderSett
|
|
|
120
134
|
* @throws {@link ProviderSettingsError} with a single-line, key-free message.
|
|
121
135
|
*/
|
|
122
136
|
export declare function saveProviderCredential(ctx: Context, target: ProviderTargetView, rawKey: string): Promise<void>;
|
|
137
|
+
/** Save the endpoint and an explicit model allow-list without rebuilding the profile. */
|
|
138
|
+
export declare function saveProviderConfiguration(ctx: Context, target: ProviderTargetView, configuration: ProviderConfiguration): Promise<void>;
|
|
123
139
|
/**
|
|
124
140
|
* Remove the currently named credential without touching the provider
|
|
125
141
|
* profile. Only the resolved profile's own reference is unset; a dormant or
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Terminal animation frame tables derived from the web design language:
|
|
3
3
|
* the StateDot "ongoing" pixel chase (3×3 ring, 125ms flat-hold brightness
|
|
4
|
-
* steps, 1s cycle) becomes the
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* steps, 1s cycle) becomes the full-ring clockwise braille chase in
|
|
5
|
+
* {@link BUSY_CHASE_FRAMES}, and the streaming caret blink is the
|
|
6
|
+
* Claude-Code convention.
|
|
7
7
|
*
|
|
8
8
|
* The DeepSeek model-switch easter egg ports Codex's effort-ignition "Wave"
|
|
9
9
|
* style (`codex-rs/tui/src/bottom_pane/effort_ignition_styles.rs`): switching
|
|
@@ -17,10 +17,6 @@
|
|
|
17
17
|
* @module @deepseek-ai/dsh-code/render/animations
|
|
18
18
|
*/
|
|
19
19
|
import type { RgbTriple } from '../theme.ts';
|
|
20
|
-
/** Single-cell stepped pulse: flat holds mirroring the web's 125ms keyframes. */
|
|
21
|
-
export declare const PULSE_FRAMES: readonly ["█", "█", "▆", "▃", "▁", "▃", "▆", "█"];
|
|
22
|
-
/** Pulse frame for a monotonic tick. */
|
|
23
|
-
export declare function pulseFrame(tick: number): string;
|
|
24
20
|
/**
|
|
25
21
|
* The web StateDot "ongoing" chase in terminal form: three cells of the 3×3
|
|
26
22
|
* ring trail clockwise around the eight outer positions, one braille glyph
|
|
@@ -59,15 +55,8 @@ export type DeepseekWaveTier = 'flash' | 'deepseek' | 'unknown';
|
|
|
59
55
|
* One style is picked at random per trigger and never repeats the previous.
|
|
60
56
|
*/
|
|
61
57
|
export type DeepseekWaveStyle = 'wave' | 'aurora' | 'pulse';
|
|
62
|
-
/** All styles in canonical order, for random selection and tests. */
|
|
63
|
-
export declare const DEEPSEEK_WAVE_STYLES: readonly DeepseekWaveStyle[];
|
|
64
58
|
/** Wave half-width in columns — Codex WAVE_HALF_WIDTH (9). */
|
|
65
59
|
export declare const WAVE_HALF_WIDTH = 9;
|
|
66
|
-
/** Pulse ring half-width in columns — Codex PULSE_HALF_WIDTH (4.5). */
|
|
67
|
-
export declare const PULSE_HALF_WIDTH = 4.5;
|
|
68
|
-
/** Sparkle start and frame cadence — Codex SPARK_START / SPARK_FRAME. */
|
|
69
|
-
export declare const SPARK_START_MS = 900;
|
|
70
|
-
export declare const SPARK_FRAME_MS = 100;
|
|
71
60
|
/** Sparkle glyphs in frame order — Codex SPARK_GLYPHS (`· ✦ ✧`). */
|
|
72
61
|
export declare const SPARK_GLYPHS: readonly ["·", "✦", "✧"];
|
|
73
62
|
/**
|
|
@@ -77,8 +66,6 @@ export declare const SPARK_GLYPHS: readonly ["·", "✦", "✧"];
|
|
|
77
66
|
*/
|
|
78
67
|
export type DeepseekWaveBand = readonly [number, number, number];
|
|
79
68
|
export declare const DEEPSEEK_WAVE_BANDS: Readonly<Record<DeepseekWaveStyle, Readonly<Record<DeepseekWaveTier, readonly DeepseekWaveBand[]>>>>;
|
|
80
|
-
/** Extra display time applied to every Codex ignition style. */
|
|
81
|
-
export declare const DEEPSEEK_WAVE_DURATION_EXTENSION_MS = 200;
|
|
82
69
|
/**
|
|
83
70
|
* Total visible duration: the Codex ignition duration plus 200ms so its motion
|
|
84
71
|
* remains readable in a busy terminal.
|
|
@@ -94,14 +81,6 @@ export declare function deepseekWaveDuration(tier: DeepseekWaveTier, style?: Dee
|
|
|
94
81
|
* @returns a style different from `previous`.
|
|
95
82
|
*/
|
|
96
83
|
export declare function deepseekWaveStyleRandom(previous: DeepseekWaveStyle | undefined): DeepseekWaveStyle;
|
|
97
|
-
/**
|
|
98
|
-
* Blank-cell background the wave tint blends toward — fixed approximations
|
|
99
|
-
* of the terminal's default background, mirroring Codex's
|
|
100
|
-
* `user_message_bg_rgb` (which derives a near-black / near-white bubble tint
|
|
101
|
-
* from the terminal background). The Ink layer picks the active theme's one.
|
|
102
|
-
*/
|
|
103
|
-
export declare const WAVE_BASE_DARK: RgbTriple;
|
|
104
|
-
export declare const WAVE_BASE_LIGHT: RgbTriple;
|
|
105
84
|
/**
|
|
106
85
|
* Tier for a `provider/model` label: a model id containing `flash` runs the
|
|
107
86
|
* single-band flash tier; everything else (pro/reasoner/chat) runs the
|
|
@@ -137,14 +116,16 @@ export declare function easeInOut(progress: number): number;
|
|
|
137
116
|
*/
|
|
138
117
|
export declare function envelope(elapsed: number, total: number, fadeIn: number, fadeOut: number): number;
|
|
139
118
|
/**
|
|
140
|
-
* The background color for one composer-
|
|
119
|
+
* The background color for one composer-band column at a tick — Codex
|
|
141
120
|
* `paint_bands` + `Canvas::tint` for all three styles. Bands overlap with a
|
|
142
121
|
* max for Wave/Pulse and a SUM for Aurora (Codex differs by style), the
|
|
143
122
|
* weighted hues mix per column (Wave/Pulse always end on hue 0), the tint
|
|
144
123
|
* blends the mixed hue toward the blank-cell base at the style's alpha cap,
|
|
145
124
|
* and Aurora applies its own fade envelope. Returns `null` when the column
|
|
146
125
|
* should stay transparent, so the row returns to no `backgroundColor` on
|
|
147
|
-
* both ends.
|
|
126
|
+
* both ends. With `rows > 1` each row samples the same timeline shifted by a
|
|
127
|
+
* per-row phase offset, so the crest cascades down the band instead of
|
|
128
|
+
* painting every row identically.
|
|
148
129
|
* @param tick - wave frame (0, 1, … at DEEPSEEK_WAVE_TICK_MS).
|
|
149
130
|
* @param column - column index in the content row (0..width-1).
|
|
150
131
|
* @param width - content-row width in columns.
|
|
@@ -152,9 +133,11 @@ export declare function envelope(elapsed: number, total: number, fadeIn: number,
|
|
|
152
133
|
* @param style - the ignition style.
|
|
153
134
|
* @param hues - the tier's three hues.
|
|
154
135
|
* @param base - the blank-cell base color the tint blends toward.
|
|
136
|
+
* @param row - row index in the band (0..rows-1; default 0 = old single-row).
|
|
137
|
+
* @param rows - band height in rows (default 1).
|
|
155
138
|
* @returns the blended RGB background, or null for transparent.
|
|
156
139
|
*/
|
|
157
|
-
export declare function deepseekWaveColumnBg(tick: number, column: number, width: number, tier: DeepseekWaveTier, style: DeepseekWaveStyle, hues: readonly [RgbTriple, RgbTriple, RgbTriple], base: RgbTriple): RgbTriple | null;
|
|
140
|
+
export declare function deepseekWaveColumnBg(tick: number, column: number, width: number, tier: DeepseekWaveTier, style: DeepseekWaveStyle, hues: readonly [RgbTriple, RgbTriple, RgbTriple], base: RgbTriple, row?: number, rows?: number): RgbTriple | null;
|
|
158
141
|
/**
|
|
159
142
|
* The sparkle glyph for a tick — Codex `spark_frame`, sampled on the same
|
|
160
143
|
* proportionally slowed DeepSeek Wave timeline as the composer background.
|
|
@@ -163,18 +146,6 @@ export declare function deepseekWaveColumnBg(tick: number, column: number, width
|
|
|
163
146
|
* @returns the sparkle glyph, or null outside the stretched tail window.
|
|
164
147
|
*/
|
|
165
148
|
export declare function deepseekWaveSpark(tick: number): string | null;
|
|
166
|
-
/**
|
|
167
|
-
* The composer BORDER color at a tick: the frame breathes with the wave —
|
|
168
|
-
* the palette's static dim blends toward the tier accent as the crest is
|
|
169
|
-
* alive and back, so the frame glows up while the wave sweeps and settles to
|
|
170
|
-
* dim on both ends (first==last frame==dim, no hard jump).
|
|
171
|
-
* @param tick - wave frame at DEEPSEEK_WAVE_TICK_MS.
|
|
172
|
-
* @param tier - the wave tier.
|
|
173
|
-
* @param hues - the tier's three hues; the border blends toward hues[0].
|
|
174
|
-
* @param dim - the palette's static dim RGB (the resting border color).
|
|
175
|
-
* @returns the blended border RGB.
|
|
176
|
-
*/
|
|
177
|
-
export declare function deepseekWaveBorderColor(tick: number, tier: DeepseekWaveTier, style: DeepseekWaveStyle, hues: readonly [RgbTriple, RgbTriple, RgbTriple], dim: RgbTriple): RgbTriple;
|
|
178
149
|
/**
|
|
179
150
|
* Whether the `deepseek` wordmark rides the wave at this tick: it fades in
|
|
180
151
|
* shortly after the first crest launches and out before the wave settles,
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure composer editor model with Codex textarea semantics: a grapheme
|
|
3
|
+
* cursor over a column-safe multiline layout, word/piece motion, single-entry
|
|
4
|
+
* kill + yank, and the shell-recall boundary gate that keeps Up/Down usable
|
|
5
|
+
* inside a multiline draft.
|
|
6
|
+
*
|
|
7
|
+
* The model is intentionally string-offset based (UTF-16 indices clamped to
|
|
8
|
+
* grapheme boundaries) so the React state stays two primitives
|
|
9
|
+
* (value, cursor) and every operation here stays pure and testable.
|
|
10
|
+
*
|
|
11
|
+
* Word motion deviates from Codex's UAX#29 segmentation in one deliberate
|
|
12
|
+
* way: a run of same-class characters is ONE piece, so a CJK run moves as a
|
|
13
|
+
* single word (two hanzi are one Alt+B step, not two).
|
|
14
|
+
*
|
|
15
|
+
* @module @deepseek-ai/dsh-code/render/editor
|
|
16
|
+
*/
|
|
17
|
+
/** One grapheme cluster with its source span and display width in cells. */
|
|
18
|
+
export interface GraphemeSpan {
|
|
19
|
+
text: string;
|
|
20
|
+
start: number;
|
|
21
|
+
end: number;
|
|
22
|
+
width: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Split text into grapheme clusters. Falls back to code points when
|
|
26
|
+
* Intl.Segmenter is unavailable; the fallback still keeps surrogate pairs
|
|
27
|
+
* (emoji) atomic so the cursor can never split one.
|
|
28
|
+
*/
|
|
29
|
+
export declare function splitGraphemes(text: string): readonly GraphemeSpan[];
|
|
30
|
+
/** Clamp a cursor offset to the nearest grapheme boundary (surrogates, ZWJ, marks stay whole). */
|
|
31
|
+
export declare function clampCursor(value: string, offset: number): number;
|
|
32
|
+
/**
|
|
33
|
+
* Delete the final grapheme cluster (append-only drafts without a cursor).
|
|
34
|
+
* Surrogate pairs and multi-codepoint emoji stay whole instead of leaving a
|
|
35
|
+
* lone trailing code unit behind.
|
|
36
|
+
*/
|
|
37
|
+
export declare function deleteLastGrapheme(text: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* Step the cursor by whole graphemes (negative steps left). The cursor is
|
|
40
|
+
* assumed to sit on a boundary; any drift is clamped first.
|
|
41
|
+
*/
|
|
42
|
+
export declare function moveCursorBy(value: string, offset: number, delta: number): number;
|
|
43
|
+
/**
|
|
44
|
+
* Normalize text entering the draft: CRLF/CR become LF, tabs become two
|
|
45
|
+
* spaces (terminal tab stops are contextual and cannot join a deterministic
|
|
46
|
+
* row budget), and every other C0 control byte plus DEL is REMOVED — the
|
|
47
|
+
* draft is data, so a stray ESC (Windows Terminal file drops) disappears
|
|
48
|
+
* instead of rendering as literal backslash-x-1-b text. Newlines survive.
|
|
49
|
+
*/
|
|
50
|
+
export declare function sanitizeDraftText(text: string): string;
|
|
51
|
+
/** One physical editor row: wrapped text plus its boundary map. */
|
|
52
|
+
export interface EditorRowModel {
|
|
53
|
+
/** Display text of the row (never contains `\n`; sanitized upstream). */
|
|
54
|
+
readonly text: string;
|
|
55
|
+
/** Source offset of the first grapheme on the row. */
|
|
56
|
+
readonly start: number;
|
|
57
|
+
/** Source offset just past the last grapheme on the row (before its newline). */
|
|
58
|
+
readonly end: number;
|
|
59
|
+
/** Boundary offsets on the row, start to end inclusive. */
|
|
60
|
+
readonly offsets: readonly number[];
|
|
61
|
+
/** Display column of each boundary; `columns[i]` pairs with `offsets[i]`. */
|
|
62
|
+
readonly columns: readonly number[];
|
|
63
|
+
/** Code-unit cut in `text` at each boundary; `cuts[i]` pairs with `offsets[i]`. */
|
|
64
|
+
readonly cuts: readonly number[];
|
|
65
|
+
}
|
|
66
|
+
/** The wrapped physical-row model of one draft. */
|
|
67
|
+
export interface EditorModel {
|
|
68
|
+
readonly rows: readonly EditorRowModel[];
|
|
69
|
+
readonly length: number;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Hard-wrap the draft into column-safe physical rows. Wide graphemes never
|
|
73
|
+
* split across rows (a grapheme that does not fit flushes the row first) and
|
|
74
|
+
* explicit newlines end their row without occupying a cell.
|
|
75
|
+
*/
|
|
76
|
+
export declare function editorModel(value: string, columns: number): EditorModel;
|
|
77
|
+
/** Where a cursor offset renders: the physical row and its display column. */
|
|
78
|
+
export interface CaretSite {
|
|
79
|
+
row: number;
|
|
80
|
+
column: number;
|
|
81
|
+
}
|
|
82
|
+
/** Map a cursor offset to its caret site on the wrapped rows. */
|
|
83
|
+
export declare function caretSite(model: EditorModel, offset: number): CaretSite;
|
|
84
|
+
/**
|
|
85
|
+
* Move the caret across physical rows keeping a preferred display column
|
|
86
|
+
* (Codex `preferred_col`): horizontal moves reset the preference, vertical
|
|
87
|
+
* moves reuse it, clamped to each row's width.
|
|
88
|
+
*/
|
|
89
|
+
export declare function moveCursorVertically(model: EditorModel, offset: number, preferredColumn: number, delta: number): number;
|
|
90
|
+
/** The start/end offsets of the logical line containing the cursor. */
|
|
91
|
+
export declare function lineBounds(value: string, offset: number): {
|
|
92
|
+
start: number;
|
|
93
|
+
end: number;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Codex `beginning_of_previous_word`: skip whitespace left, then land on the
|
|
97
|
+
* START of the trailing non-space piece (extending over separator pieces).
|
|
98
|
+
*/
|
|
99
|
+
export declare function moveWordLeft(value: string, offset: number): number;
|
|
100
|
+
/**
|
|
101
|
+
* Codex `end_of_next_word`: skip whitespace right, then land on the END of
|
|
102
|
+
* the leading non-space piece (extending over separator pieces).
|
|
103
|
+
*/
|
|
104
|
+
export declare function moveWordRight(value: string, offset: number): number;
|
|
105
|
+
/** One edit outcome: the next draft value, cursor, and killed span (if any). */
|
|
106
|
+
export interface EditResult {
|
|
107
|
+
value: string;
|
|
108
|
+
cursor: number;
|
|
109
|
+
/** Text removed into the kill buffer; undefined when nothing was killed. */
|
|
110
|
+
killed: string | undefined;
|
|
111
|
+
}
|
|
112
|
+
/** Delete the grapheme cluster before the cursor. */
|
|
113
|
+
export declare function deleteBackward(value: string, cursor: number): EditResult;
|
|
114
|
+
/** Delete the grapheme cluster at the cursor. */
|
|
115
|
+
export declare function deleteForward(value: string, cursor: number): EditResult;
|
|
116
|
+
/** Delete back to the start of the previous word (fills the kill buffer). */
|
|
117
|
+
export declare function deleteWordBackward(value: string, cursor: number): EditResult;
|
|
118
|
+
/** Delete forward to the end of the next word (fills the kill buffer). */
|
|
119
|
+
export declare function deleteWordForward(value: string, cursor: number): EditResult;
|
|
120
|
+
/** Ctrl+U: kill from the line start to the cursor; at BOL, kill the newline. */
|
|
121
|
+
export declare function killToLineStart(value: string, cursor: number): EditResult;
|
|
122
|
+
/** Ctrl+K: kill from the cursor to the line end; at EOL, kill the newline. */
|
|
123
|
+
export declare function killToLineEnd(value: string, cursor: number): EditResult;
|
|
124
|
+
/** Insert sanitized text at the cursor. */
|
|
125
|
+
export declare function insertText(value: string, cursor: number, text: string): EditResult;
|
|
126
|
+
/**
|
|
127
|
+
* Composer editor row budget: the editor itself never grows past this many
|
|
128
|
+
* physical rows; deeper drafts scroll internally to keep the caret visible.
|
|
129
|
+
* Short terminals collapse toward one row so the live transcript keeps room.
|
|
130
|
+
*/
|
|
131
|
+
export declare function composerMaxRows(terminalRows: number): number;
|
|
132
|
+
/**
|
|
133
|
+
* Codex `should_handle_navigation`: Up/Down walk history only from an empty
|
|
134
|
+
* draft, or from a boundary of a draft that still exactly matches the last
|
|
135
|
+
* recalled entry. Any interior cursor position keeps vertical caret movement.
|
|
136
|
+
*/
|
|
137
|
+
export declare function shouldRecallNavigate(value: string, cursor: number, lastRecalled: string | null): boolean;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module @deepseek-ai/dsh-code/render/export
|
|
7
7
|
*/
|
|
8
|
-
import type
|
|
8
|
+
import { type TranscriptView } from './projection.ts';
|
|
9
9
|
/**
|
|
10
10
|
* Render the transcript as a standalone markdown document.
|
|
11
11
|
* @param view - the folded transcript view to export.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Width-safe styled physical rows for bounded terminal panels. */
|
|
2
|
-
import type
|
|
2
|
+
import { type TranscriptEntry } from './projection.ts';
|
|
3
3
|
import { type MdStyle } from './markdown.ts';
|
|
4
4
|
/** Presentation classes mapped to Ink colors by the app boundary. */
|
|
5
5
|
export type LineStyle = MdStyle | 'brand' | 'success' | 'error' | 'warn' | 'dimItalic';
|
|
@@ -33,5 +33,9 @@ export declare function reasoningLines(text: string, columns: number): readonly
|
|
|
33
33
|
/**
|
|
34
34
|
* Convert one durable transcript entry to its complete scrollable row model.
|
|
35
35
|
* The source entry stays intact; only the caller's visible slice is rendered.
|
|
36
|
+
* Wrapped continuations keep a hanging indent aligned under each row's
|
|
37
|
+
* content (Codex history-cell alignment) instead of resetting to column 0.
|
|
36
38
|
*/
|
|
37
|
-
export declare function transcriptEntryLines(entry: TranscriptEntry, columns: number): readonly StyledLine[];
|
|
39
|
+
export declare function transcriptEntryLines(entry: TranscriptEntry, columns: number, showReasoning?: boolean, reasoningToggleHint?: boolean): readonly StyledLine[];
|
|
40
|
+
/** Settled-history variant carrying the Ctrl+R reasoning fold. */
|
|
41
|
+
export declare function settledEntryLines(entry: TranscriptEntry, columns: number, showReasoning: boolean): readonly StyledLine[];
|
|
@@ -24,4 +24,6 @@ export interface MdLine {
|
|
|
24
24
|
/** Visible width of a run in columns (CJK counts double). */
|
|
25
25
|
export declare function visibleColumns(text: string): number;
|
|
26
26
|
/** Render markdown text into styled lines of at most `width` columns. */
|
|
27
|
-
export declare function renderMarkdown(text: string, width: number
|
|
27
|
+
export declare function renderMarkdown(text: string, width: number, options?: {
|
|
28
|
+
physicalWrap?: boolean;
|
|
29
|
+
}): readonly MdLine[];
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @deepseek-ai/dsh-tui/render/projection
|
|
8
8
|
*/
|
|
9
|
-
import { type MessageId } from '@deepseek-ai/dsh-llm';
|
|
9
|
+
import { type ImageBlock, type MessageId } from '@deepseek-ai/dsh-llm';
|
|
10
10
|
import type { SessionEvent, TodoItem } from '@deepseek-ai/dsh-session';
|
|
11
11
|
import { type ToolDetail } from './tool-detail.ts';
|
|
12
12
|
/** One user prompt line. */
|
|
@@ -17,6 +17,8 @@ export interface UserEntry {
|
|
|
17
17
|
/** True for collapsed injected context (plugin/continuation notices), which
|
|
18
18
|
* the renderer marks with a dim ↳ instead of the user ❯ prompt. */
|
|
19
19
|
notice: boolean;
|
|
20
|
+
/** Durable image references carried by this prompt. */
|
|
21
|
+
images?: readonly ImageBlock['attachment'][];
|
|
20
22
|
}
|
|
21
23
|
/** One user message waiting in the agent inbox (the web's queued-message row). */
|
|
22
24
|
export interface PendingEntry {
|
|
@@ -27,20 +29,32 @@ export interface PendingEntry {
|
|
|
27
29
|
target: 'next-turn' | 'next-step';
|
|
28
30
|
/** Full message text — Codex PendingSteer renders queued prompts exactly like user rows. */
|
|
29
31
|
text: string;
|
|
32
|
+
/** Durable image references queued with this prompt. */
|
|
33
|
+
images?: readonly ImageBlock['attachment'][];
|
|
30
34
|
}
|
|
31
|
-
/** One assembled assistant reply. */
|
|
35
|
+
/** One authoritative assembled assistant reply. */
|
|
32
36
|
export interface AssistantEntry {
|
|
33
37
|
kind: 'assistant';
|
|
34
38
|
/** Joined text blocks of the assistant message. */
|
|
35
39
|
text: string;
|
|
36
|
-
/** Joined reasoning blocks
|
|
40
|
+
/** Joined reasoning blocks from the same assembled message. */
|
|
37
41
|
reasoning: string;
|
|
42
|
+
/** True when a cancelled stream's delivered prefix was finalized as this
|
|
43
|
+
* entry (rc.8 `assistant/message.interrupted`) — rendered with a marker. */
|
|
44
|
+
interrupted?: true;
|
|
38
45
|
}
|
|
39
46
|
/** One model-requested tool invocation and its settled state. */
|
|
40
47
|
export interface ToolEntry {
|
|
41
48
|
kind: 'tool';
|
|
42
49
|
/** Correlation id shared with the matching `tool/result`. */
|
|
43
50
|
callId: string;
|
|
51
|
+
/**
|
|
52
|
+
* Global tool-call ordinal across the whole transcript (1, 2, 3…, never
|
|
53
|
+
* reset between turns). The tool-card badge and every error line that
|
|
54
|
+
* references the failed call share this number, so "call N" in an error
|
|
55
|
+
* always names the exact card the badge shows.
|
|
56
|
+
*/
|
|
57
|
+
ordinal: number;
|
|
44
58
|
/** Tool name as the model addressed it. */
|
|
45
59
|
name: string;
|
|
46
60
|
/** Raw arguments JSON string exactly as the model produced it. */
|
|
@@ -203,6 +217,12 @@ export interface TranscriptView {
|
|
|
203
217
|
streamingReasoning: string;
|
|
204
218
|
/** Latest whole-list todo snapshot from `todo/write`, empty when none. */
|
|
205
219
|
todos: readonly TodoItem[];
|
|
220
|
+
/**
|
|
221
|
+
* Global tool-call ordinal counter: the number the NEXT `tool/call` lands
|
|
222
|
+
* with (1-based). Never reset, so the counter and the badges/error lines
|
|
223
|
+
* stay consistent across turns and resumed sessions.
|
|
224
|
+
*/
|
|
225
|
+
toolCallOrdinal: number;
|
|
206
226
|
/** True while a durable turn is open (`turn/start` … `turn/end`). */
|
|
207
227
|
busy: boolean;
|
|
208
228
|
/** `turn/start` time of the open turn (0 while idle) — the web TurnStatus clock anchor. */
|
|
@@ -254,6 +274,10 @@ export interface TranscriptView {
|
|
|
254
274
|
turnTools: Map<number, Set<string>>;
|
|
255
275
|
};
|
|
256
276
|
}
|
|
277
|
+
/** Human-readable bounded image labels for transcript, inspector, and export surfaces. */
|
|
278
|
+
export declare function imageLabels(images: readonly ImageBlock['attachment'][] | undefined): string;
|
|
279
|
+
/** Prompt text with its durable image labels, without exposing local paths or bytes. */
|
|
280
|
+
export declare function promptDisplayText(entry: Pick<UserEntry | PendingEntry, 'text' | 'images'>): string;
|
|
257
281
|
/** A fresh, empty transcript view. */
|
|
258
282
|
export declare function createTranscriptView(): TranscriptView;
|
|
259
283
|
/**
|
|
@@ -302,6 +326,8 @@ export interface ReplayAccumulator {
|
|
|
302
326
|
streaming: string;
|
|
303
327
|
streamingReasoning: string;
|
|
304
328
|
todos: readonly TodoItem[];
|
|
329
|
+
/** Global tool-call ordinal counter (see `TranscriptView.toolCallOrdinal`). */
|
|
330
|
+
toolCallOrdinal: number;
|
|
305
331
|
busy: boolean;
|
|
306
332
|
busySince: number;
|
|
307
333
|
model: string;
|
|
@@ -23,17 +23,10 @@ export declare function formatTokens(n: number): string;
|
|
|
23
23
|
* @returns display string.
|
|
24
24
|
*/
|
|
25
25
|
export declare function formatDuration(ms: number): string;
|
|
26
|
-
/**
|
|
27
|
-
* Compact decode rate: one decimal under a hundred, whole below a thousand,
|
|
28
|
-
* then thousands (15.3 / 124 / 1.2K).
|
|
29
|
-
* @param n - tokens per second.
|
|
30
|
-
* @returns display string.
|
|
31
|
-
*/
|
|
32
|
-
export declare function formatRate(n: number): string;
|
|
33
26
|
/**
|
|
34
27
|
* Cache-hit share of billed prompt-side input.
|
|
35
28
|
* @param usage - cumulative token totals.
|
|
36
|
-
* @returns rounded
|
|
29
|
+
* @returns percent rounded to one decimal place, or null when no input was billed.
|
|
37
30
|
*/
|
|
38
31
|
export declare function cacheHitPercent(usage: TranscriptStats['usage']): number | null;
|
|
39
32
|
/**
|
|
@@ -80,13 +73,12 @@ export declare const STATUS_ITEM_SEPARATOR = " \u00B7 ";
|
|
|
80
73
|
export declare const STATUS_CYCLE_HINT = " (shift+tab to cycle)";
|
|
81
74
|
/**
|
|
82
75
|
* Interior columns of the segmented context bar (content-type segments plus
|
|
83
|
-
* the free tail whose right edge carries the usage readout).
|
|
84
|
-
*
|
|
85
|
-
*
|
|
76
|
+
* the free tail whose right edge carries the usage readout). The layout
|
|
77
|
+
* starts every bar at this width so the drop ladder can pre-measure the
|
|
78
|
+
* group, then shrinks the bar inside a tighter budget before dropping it
|
|
79
|
+
* (see CONTEXT_MIN_WIDTH) rather than asking the layout for more room.
|
|
86
80
|
*/
|
|
87
81
|
export declare const CONTEXT_BAR_WIDTH = 24;
|
|
88
|
-
/** Occupancy at which the usage readout flips from brand blue to amber. */
|
|
89
|
-
export declare const CONTEXT_WARN_PERCENT = 90;
|
|
90
82
|
/**
|
|
91
83
|
* Render context occupancy as ONE stepless bar: a solid DeepSeek-blue fill
|
|
92
84
|
* run, a dim dotted free track, and the usage readout riding the track's
|
|
@@ -193,4 +185,5 @@ export declare function permissionTone(permission: string): StatusTone;
|
|
|
193
185
|
export declare function layoutStatusBar(facts: StatusFacts, stats: TranscriptStats, columns: number, options?: {
|
|
194
186
|
busy?: boolean;
|
|
195
187
|
items?: readonly string[];
|
|
188
|
+
contextWidth?: number;
|
|
196
189
|
}): StatusLayout;
|