dsh-code 1.0.1 → 1.0.3
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 +21 -13
- package/README.md +21 -13
- package/lib/index.mjs +1902 -1092
- package/lib/types/app.d.ts +4 -13
- package/lib/types/attachments.d.ts +1 -1
- package/lib/types/editor-keys.d.ts +105 -0
- package/lib/types/git-workflow.d.ts +6 -2
- package/lib/types/keyboard.d.ts +31 -0
- package/lib/types/mentions.d.ts +2 -0
- package/lib/types/model-capabilities.d.ts +82 -0
- package/lib/types/provider-settings.d.ts +7 -0
- package/lib/types/render/animations.d.ts +27 -11
- package/lib/types/render/editor.d.ts +32 -7
- package/lib/types/render/lines.d.ts +26 -1
- package/lib/types/render/markdown.d.ts +1 -1
- package/lib/types/render/projection.d.ts +15 -1
- package/lib/types/render/text.d.ts +15 -9
- package/lib/types/render/width.d.ts +29 -0
- package/lib/types/session-directory.d.ts +27 -0
- package/lib/types/settings-file.d.ts +33 -0
- package/lib/types/store.d.ts +10 -0
- package/lib/types/subagents.d.ts +13 -3
- package/package.json +159 -159
- package/src/app.ts +920 -764
- package/src/attachments.ts +7 -0
- package/src/editor-keys.ts +371 -0
- package/src/git-workflow.ts +10 -6
- package/src/index.ts +1637 -1523
- package/src/internals.ts +26 -9
- package/src/keyboard.ts +131 -7
- package/src/mentions.ts +6 -1
- package/src/model-capabilities.ts +318 -0
- package/src/provider-settings.ts +16 -0
- package/src/render/animations.ts +64 -17
- package/src/render/editor.ts +125 -25
- package/src/render/lines.ts +403 -342
- package/src/render/markdown.ts +4 -7
- package/src/render/projection.ts +63 -40
- package/src/render/text.ts +152 -150
- package/src/render/width.ts +189 -0
- package/src/session-directory.ts +56 -0
- package/src/settings-file.ts +56 -0
- package/src/store.ts +26 -7
- package/src/subagents.ts +39 -6
package/lib/types/app.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import type { ModelDirectory, ModelRow } from './models.ts';
|
|
|
26
26
|
import type { ProviderConfiguration, ProviderSettingsDirectory, ProviderTargetView } from './provider-settings.ts';
|
|
27
27
|
import type { QuestionStore } from './questions.ts';
|
|
28
28
|
import type { SkillsView, SkillRow } from './skills.ts';
|
|
29
|
-
import type
|
|
29
|
+
import { type MentionCandidate } from './mentions.ts';
|
|
30
30
|
import type { SubagentFeedView } from './subagents.ts';
|
|
31
31
|
import { type JobRow } from './kernel-panels.ts';
|
|
32
32
|
import type { PresetRow } from './presets.ts';
|
|
@@ -85,7 +85,7 @@ export interface AppProps {
|
|
|
85
85
|
/** Validate draft image paths without committing attachment objects. */
|
|
86
86
|
inspectImages(paths: readonly string[]): Promise<readonly ImagePathInspection[]>;
|
|
87
87
|
/** Validate, normalize and persist images immediately before submission. */
|
|
88
|
-
prepareImages(paths: readonly string[]): Promise<readonly ImageBlock[]>;
|
|
88
|
+
prepareImages(paths: readonly string[], signal?: AbortSignal): Promise<readonly ImageBlock[]>;
|
|
89
89
|
/** Apply one /model selection (with an advertised reasoning effort, when picked); returns the display label. */
|
|
90
90
|
selectModel(row: ModelRow, effortId?: string): string;
|
|
91
91
|
/** The /subagent override label, '' when delegated agents follow the current model. */
|
|
@@ -163,18 +163,9 @@ export interface AppProps {
|
|
|
163
163
|
recordHistory(text: string): void;
|
|
164
164
|
/** Cancel one queued inbox message by identity (Delete on the empty composer). */
|
|
165
165
|
cancelQueued(messageId: string): void;
|
|
166
|
+
/** Apply the Ctrl+R terminal passthrough to the detected editor (/vscode-keys); resolves to a one-line summary. */
|
|
167
|
+
applyEditorKeys(): Promise<string>;
|
|
166
168
|
}
|
|
167
|
-
/**
|
|
168
|
-
* One-row editor window keeping the logical cursor visible in long drafts.
|
|
169
|
-
* The caret and its surroundings slice at grapheme boundaries: splitting a
|
|
170
|
-
* star-plane surrogate pair would render an isolated half under the block
|
|
171
|
-
* caret with a width the terminal never draws.
|
|
172
|
-
*/
|
|
173
|
-
export declare function editorWindow(value: string, cursor: number, columns: number): {
|
|
174
|
-
before: string;
|
|
175
|
-
caret: string;
|
|
176
|
-
after: string;
|
|
177
|
-
};
|
|
178
169
|
/** One completion candidate row. */
|
|
179
170
|
interface CompletionCandidate {
|
|
180
171
|
/** Insertion text for the command name (with leading slash). */
|
|
@@ -17,4 +17,4 @@ export declare function parsePastedImagePaths(input: string): readonly string[];
|
|
|
17
17
|
/** Validate path, byte size and encoded signature without writing an attachment object. */
|
|
18
18
|
export declare function inspectImagePaths(paths: readonly string[], attachments: AttachmentStore | undefined, cwd?: string): Promise<readonly ImagePathInspection[]>;
|
|
19
19
|
/** Read, validate, and persist an ordered image path list as model content blocks. */
|
|
20
|
-
export declare function saveImagePaths(paths: readonly string[], attachments: AttachmentStore | undefined): Promise<readonly ImageBlock[]>;
|
|
20
|
+
export declare function saveImagePaths(paths: readonly string[], attachments: AttachmentStore | undefined, signal?: AbortSignal): Promise<readonly ImageBlock[]>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VS Code-family terminal keybinding repair. VS Code hands Ctrl+R to the
|
|
3
|
+
* workbench (Open Recent) even while an integrated terminal owns focus, so
|
|
4
|
+
* the reasoning-fold key never reaches the TUI. Workspace-scoped keybindings
|
|
5
|
+
* do not exist, so the fix is one user-level keybindings.json rule that
|
|
6
|
+
* forwards the raw Ctrl byte via sendSequence under terminalFocus. This
|
|
7
|
+
* module detects the hosting editor variant, resolves its user
|
|
8
|
+
* keybindings.json, and merges the rule idempotently; pure merge/detect
|
|
9
|
+
* helpers are separated from the fs orchestration so both stay testable.
|
|
10
|
+
* @module @deepseek-ai/dsh-code/editor-keys
|
|
11
|
+
*/
|
|
12
|
+
/** Integrated-terminal editor variants this module can repair. */
|
|
13
|
+
export type EditorTerminalFamily = 'vscode' | 'cursor' | 'vscodium' | 'windsurf';
|
|
14
|
+
/**
|
|
15
|
+
* Detect the editor hosting this integrated terminal.
|
|
16
|
+
* @param env - process environment (TERM_PROGRAM decides; case/whitespace tolerant).
|
|
17
|
+
* @returns the family, or undefined outside VS Code-family terminals.
|
|
18
|
+
*/
|
|
19
|
+
export declare function detectEditorTerminalFamily(env?: NodeJS.ProcessEnv): EditorTerminalFamily | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Whether the pty is hosted away from the editor UI (ssh/container/tunnel).
|
|
22
|
+
* Keybindings live on the client machine, so a remote session must never
|
|
23
|
+
* write them server-side.
|
|
24
|
+
*/
|
|
25
|
+
export declare function isRemoteTerminalEnv(env?: NodeJS.ProcessEnv): boolean;
|
|
26
|
+
/** Filesystem anchors used to resolve editor config paths (injectable for tests). */
|
|
27
|
+
export interface EditorPathContext {
|
|
28
|
+
/** User home directory. */
|
|
29
|
+
homedir: string;
|
|
30
|
+
/** %APPDATA% on Windows; only read for win32 resolution. */
|
|
31
|
+
appdata?: string;
|
|
32
|
+
/** Node platform qualifier. */
|
|
33
|
+
platform: NodeJS.Platform;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the user keybindings.json candidates for one family, most likely
|
|
37
|
+
* install first. Only paths that exist on disk are repaired.
|
|
38
|
+
*/
|
|
39
|
+
export declare function editorKeybindingCandidates(family: EditorTerminalFamily, context: EditorPathContext): readonly string[];
|
|
40
|
+
/** The one workbench rule that hands Ctrl+R to the focused terminal. */
|
|
41
|
+
export declare const CTRL_R_PASSTHROUGH_RULE: {
|
|
42
|
+
readonly key: "ctrl+r";
|
|
43
|
+
readonly command: "workbench.action.terminal.sendSequence";
|
|
44
|
+
readonly args: {
|
|
45
|
+
readonly text: "\u0012";
|
|
46
|
+
};
|
|
47
|
+
readonly when: "terminalFocus";
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Remove // and block comments from one JSONC document. Double-quoted strings
|
|
51
|
+
* survive untouched, so comment markers inside string values are preserved.
|
|
52
|
+
*/
|
|
53
|
+
export declare function stripJsoncComments(text: string): string;
|
|
54
|
+
/** Parse one JSONC document; trailing commas are tolerated. */
|
|
55
|
+
export declare function parseJsonc(text: string): unknown;
|
|
56
|
+
/** Outcome of merging the passthrough rule into one keybindings document. */
|
|
57
|
+
export type KeybindingsMerge = {
|
|
58
|
+
readonly status: 'present';
|
|
59
|
+
} | {
|
|
60
|
+
readonly status: 'updated';
|
|
61
|
+
readonly text: string;
|
|
62
|
+
} | {
|
|
63
|
+
readonly status: 'created';
|
|
64
|
+
readonly text: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Merge the Ctrl+R passthrough into one keybindings.json document. The raw
|
|
68
|
+
* text is preserved verbatim (comments included); the rule is inserted right
|
|
69
|
+
* after the array opener so it cannot be shadowed by later conflicting user
|
|
70
|
+
* rules. Missing files resolve to a fresh template.
|
|
71
|
+
* @throws when the document does not carry a rule array.
|
|
72
|
+
*/
|
|
73
|
+
export declare function mergeCtrlRPassthrough(raw: string | undefined): KeybindingsMerge;
|
|
74
|
+
/** User-level marker file content: the startup hint fires at most once per install. */
|
|
75
|
+
export interface EditorKeysFlag {
|
|
76
|
+
hintShownAt?: string;
|
|
77
|
+
}
|
|
78
|
+
/** Parse one flag file snapshot; missing or corrupt content degrades to unshown. */
|
|
79
|
+
export declare function parseEditorKeysFlag(raw: string | undefined): EditorKeysFlag;
|
|
80
|
+
/** Persist the shown marker; best-effort, the hint is cosmetic and never a gate. */
|
|
81
|
+
export declare function markEditorKeysHintShown(path: string): Promise<void>;
|
|
82
|
+
/** Inputs shared by the apply and startup-hint flows. */
|
|
83
|
+
export interface EditorKeysEnv {
|
|
84
|
+
/** Process environment (TERM_PROGRAM / VSCODE_IPC_HOOK_CLI). */
|
|
85
|
+
env: NodeJS.ProcessEnv;
|
|
86
|
+
/** Filesystem anchors for editor config resolution. */
|
|
87
|
+
paths: EditorPathContext;
|
|
88
|
+
/** Absolute path of the one-shot hint marker under the DSH home. */
|
|
89
|
+
flagPath: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Apply the Ctrl+R passthrough to every local keybindings.json of the hosting
|
|
93
|
+
* editor and mark the startup hint shown. Existing files get a .dsh-bak
|
|
94
|
+
* backup before the first write.
|
|
95
|
+
* @returns a one-line user-facing summary.
|
|
96
|
+
* @throws with an actionable message when the environment cannot be repaired.
|
|
97
|
+
*/
|
|
98
|
+
export declare function applyCtrlRPassthrough({ env, paths, flagPath }: EditorKeysEnv): Promise<string>;
|
|
99
|
+
/**
|
|
100
|
+
* Resolve the one-shot startup hint for VS Code-family terminals. Fires at
|
|
101
|
+
* most once per install (flag file), never when the passthrough rule is
|
|
102
|
+
* already present, and never in remote ptys where the repair cannot run.
|
|
103
|
+
* @returns the hint line, or undefined to stay silent.
|
|
104
|
+
*/
|
|
105
|
+
export declare function resolveEditorKeysStartupHint({ env, paths, flagPath }: EditorKeysEnv): Promise<string | undefined>;
|
|
@@ -17,7 +17,11 @@ export interface GitDiffView {
|
|
|
17
17
|
export declare function parseGitDiffFiles(text: string): readonly GitDiffFile[];
|
|
18
18
|
/** Parse the intentionally small, option-safe /diff argument vocabulary. */
|
|
19
19
|
export declare function parseGitDiffSpec(argument: string): GitDiffSpec;
|
|
20
|
-
/**
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Load one complete textual diff without invoking external diff drivers.
|
|
22
|
+
* @param signal - aborted by the caller on session switches/quit, killing the
|
|
23
|
+
* git subprocess instead of letting a stale repository's diff land later.
|
|
24
|
+
*/
|
|
25
|
+
export declare function loadGitDiff(cwd: string, argument: string, signal?: AbortSignal): Promise<GitDiffView>;
|
|
22
26
|
/** Review prompt capped before it reaches a provider context window. */
|
|
23
27
|
export declare function buildReviewPrompt(diff: string, label: string, maxChars?: number): string;
|
package/lib/types/keyboard.d.ts
CHANGED
|
@@ -19,10 +19,26 @@
|
|
|
19
19
|
export declare const KEYBOARD_ENHANCE_ENABLE = "\u001B[>4;0m\u001B[>5u";
|
|
20
20
|
/** Pop the enhancement stack and reset modifyOtherKeys (exit path). */
|
|
21
21
|
export declare const KEYBOARD_ENHANCE_DISABLE = "\u001B[<u\u001B[>4;0m";
|
|
22
|
+
/** Explicit environment overrides for terminal keyboard enhancement. */
|
|
23
|
+
export declare const DSH_DISABLE_KEYBOARD_ENHANCEMENT = "DSH_DISABLE_KEYBOARD_ENHANCEMENT";
|
|
24
|
+
export declare const DSH_ENABLE_KEYBOARD_ENHANCEMENT = "DSH_ENABLE_KEYBOARD_ENHANCEMENT";
|
|
25
|
+
/** True when the process is running inside the VS Code integrated terminal. */
|
|
26
|
+
export declare function isVsCodeTerminalEnv(env?: NodeJS.ProcessEnv): boolean;
|
|
27
|
+
/** Whether to push Kitty keyboard enhancement for the current terminal. */
|
|
28
|
+
export declare function shouldEnableKeyboardEnhancement(env?: NodeJS.ProcessEnv): boolean;
|
|
22
29
|
/** Enable bracketed paste reporting. */
|
|
23
30
|
export declare const BRACKETED_PASTE_ENABLE = "\u001B[?2004h";
|
|
24
31
|
/** Disable bracketed paste reporting. */
|
|
25
32
|
export declare const BRACKETED_PASTE_DISABLE = "\u001B[?2004l";
|
|
33
|
+
/** Enable terminal focus-in/focus-out reporting (xterm focus protocol). */
|
|
34
|
+
export declare const TERMINAL_FOCUS_REPORT_ENABLE = "\u001B[?1004h";
|
|
35
|
+
/** Disable terminal focus-in/focus-out reporting. */
|
|
36
|
+
export declare const TERMINAL_FOCUS_REPORT_DISABLE = "\u001B[?1004l";
|
|
37
|
+
/**
|
|
38
|
+
* Remove xterm focus reports from one input chunk and update the caller's
|
|
39
|
+
* focus state. Focus reports are terminal protocol, not composer text.
|
|
40
|
+
*/
|
|
41
|
+
export declare function stripTerminalFocusEvents(chunk: string, onFocus: (focused: boolean) => void): string;
|
|
26
42
|
/** Bracketed paste markers as Ink delivers them (it strips the leading ESC). */
|
|
27
43
|
export declare const PASTE_START_MARKER = "[200~";
|
|
28
44
|
export declare const PASTE_END_MARKER = "[201~";
|
|
@@ -39,3 +55,18 @@ export declare function stripPasteMarkers(text: string): string;
|
|
|
39
55
|
* unaffected.
|
|
40
56
|
*/
|
|
41
57
|
export declare function normalizeKeyboardChunk(chunk: string): string;
|
|
58
|
+
/** Editor actions Ink cannot distinguish reliably when terminal bytes batch. */
|
|
59
|
+
export type RawEditorToken = {
|
|
60
|
+
readonly kind: 'text';
|
|
61
|
+
readonly text: string;
|
|
62
|
+
} | {
|
|
63
|
+
readonly kind: 'home' | 'end' | 'delete-backward' | 'delete-word-backward' | 'delete-forward' | 'delete-word-forward';
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Tokenize a stdin chunk containing at least one editor-only key. Ink calls
|
|
67
|
+
* `useInput` once for a pasted/batched chunk, so preserving each action here
|
|
68
|
+
* prevents repeated Backspace/Home/End/Delete presses from collapsing into
|
|
69
|
+
* one blurred key event. Unknown escape sequences return undefined and stay
|
|
70
|
+
* under Ink's ownership.
|
|
71
|
+
*/
|
|
72
|
+
export declare function tokenizeRawEditorChunk(chunk: string): readonly RawEditorToken[] | undefined;
|
package/lib/types/mentions.d.ts
CHANGED
|
@@ -44,6 +44,8 @@ export interface PreparedMention {
|
|
|
44
44
|
/** Aggregated snapshot for `agent.inject()`, undefined without references. */
|
|
45
45
|
additionalContext?: import('@deepseek-ai/dsh-session').UserMessage;
|
|
46
46
|
}
|
|
47
|
+
/** Whether a mention token is already navigating a filesystem path. */
|
|
48
|
+
export declare function isPathLikeMentionQuery(query: string): boolean;
|
|
47
49
|
/** The mention API the input editor and the runner share. */
|
|
48
50
|
export interface MentionsApi {
|
|
49
51
|
/** Ranked menu candidates for the typed `@` query. */
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Same-id reasoning-capability inheritance for hand-declared pi-ai routes:
|
|
3
|
+
* the model catalog inherits capabilities by route key, not by model id, so a
|
|
4
|
+
* relay route listing `gpt-5.5` reads nothing from the installed `openai`
|
|
5
|
+
* catalog entry and materializes as `reasoning: false` until its settings
|
|
6
|
+
* entry declares `reasoningEfforts`. This adapter closes that gap without
|
|
7
|
+
* touching upstream: whenever a pi-ai profile's model entry carries no
|
|
8
|
+
* declaration and its live row advertises no efforts, but the same model id
|
|
9
|
+
* is declared (in a sibling settings entry) or advertised (on another route)
|
|
10
|
+
* elsewhere, the declaration is materialized into settings — verbatim from a
|
|
11
|
+
* sibling declaration when one exists, otherwise as an identity level map
|
|
12
|
+
* (`off` maps to null, every other level to its own name), which is the
|
|
13
|
+
* correct wire spelling for OpenAI-compatible relays. Writes ride the same
|
|
14
|
+
* `settings.mutate` path as the provider panel, so the upstream
|
|
15
|
+
* `assertServiceable` gate still rejects anything invalid atomically.
|
|
16
|
+
*
|
|
17
|
+
* @module @deepseek-ai/dsh-tui/model-capabilities
|
|
18
|
+
*/
|
|
19
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
20
|
+
import { type ModelRow } from './models.ts';
|
|
21
|
+
/** A notice sink structurally compatible with the app bridge's `notify`. */
|
|
22
|
+
export type CapabilityNotice = (text: string, tone?: 'info' | 'warning' | 'error') => void;
|
|
23
|
+
/** One pi-ai provider profile as stored in settings, addressed for mutation. */
|
|
24
|
+
export interface CapabilityProfileSource {
|
|
25
|
+
/** Settings namespace owning the profile (`llm-pi-ai`). */
|
|
26
|
+
readonly settingsNs: string;
|
|
27
|
+
/** Path from the section root to this provider's profile. */
|
|
28
|
+
readonly settingsPath: readonly string[];
|
|
29
|
+
/** Revision of the owning section at read time. */
|
|
30
|
+
readonly revision: number;
|
|
31
|
+
/** Raw model entries, exactly as stored (declaration fields included). */
|
|
32
|
+
readonly models: readonly Record<string, unknown>[];
|
|
33
|
+
}
|
|
34
|
+
/** One planned per-provider models rewrite. */
|
|
35
|
+
export interface CapabilitySyncPlan {
|
|
36
|
+
/** Provider route the plan targets. */
|
|
37
|
+
readonly provider: string;
|
|
38
|
+
/** Settings namespace owning the profile. */
|
|
39
|
+
readonly settingsNs: string;
|
|
40
|
+
/** Path from the section root to the provider profile. */
|
|
41
|
+
readonly settingsPath: readonly string[];
|
|
42
|
+
/** Raw model entries, exactly as stored (declaration fields included). */
|
|
43
|
+
readonly models: readonly Record<string, unknown>[];
|
|
44
|
+
/** Fingerprint of the source models array this plan was derived from. */
|
|
45
|
+
readonly sourceFingerprint: string;
|
|
46
|
+
/** Document revision of the owning section when the plan was derived. */
|
|
47
|
+
readonly sourceRevision: number;
|
|
48
|
+
/** Model ids that gained a declaration, notice-facing. */
|
|
49
|
+
readonly inherited: readonly string[];
|
|
50
|
+
/** `provider/model` labels the declarations came from, notice-facing. */
|
|
51
|
+
readonly sources: readonly string[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Plan the reasoning declarations to materialize. A model entry inherits
|
|
55
|
+
* when it declares nothing (`reasoningEfforts` absent — a dict or `false` is
|
|
56
|
+
* an explicit choice and is never touched) and its live row advertises no
|
|
57
|
+
* efforts; the donor is the first sibling settings declaration for the same
|
|
58
|
+
* id, copied verbatim so dialect wire spellings survive, otherwise the first
|
|
59
|
+
* other-route row advertising efforts for that id, mapped by identity.
|
|
60
|
+
* Entries never lose fields and keep their key order; a provider appears in
|
|
61
|
+
* the result only when at least one entry changes.
|
|
62
|
+
* @param input - the live model rows and the raw pi-ai profiles from settings.
|
|
63
|
+
* @returns one plan per provider with at least one inheritance.
|
|
64
|
+
*/
|
|
65
|
+
export declare function planCapabilitySync(input: {
|
|
66
|
+
readonly rows: readonly ModelRow[];
|
|
67
|
+
readonly profiles: ReadonlyMap<string, CapabilityProfileSource>;
|
|
68
|
+
}): readonly CapabilitySyncPlan[];
|
|
69
|
+
/**
|
|
70
|
+
* Materialize same-id reasoning declarations once per provider. Reads the
|
|
71
|
+
* configurable directory, the redacted settings document, and the live model
|
|
72
|
+
* rows; plans; then writes each provider's merged models array through
|
|
73
|
+
* `settings.mutate` under a fresh revision (writes bump the section
|
|
74
|
+
* revision, so per-plan revisions are re-read). Every failure converges to a
|
|
75
|
+
* single-line notice — the caller's promise never rejects and the session
|
|
76
|
+
* keeps running on the previous configuration.
|
|
77
|
+
* @param ctx - context carrying the `llm` and `settings` services (optional).
|
|
78
|
+
* @param notify - the app bridge's notice sink, when one is live.
|
|
79
|
+
*/
|
|
80
|
+
export declare function syncModelCapabilities(ctx: Context, notify?: CapabilityNotice): Promise<void>;
|
|
81
|
+
/** Test seam: forget the applied-write fingerprints. */
|
|
82
|
+
export declare function resetCapabilitySyncState(): void;
|
|
@@ -48,6 +48,13 @@ export interface ProviderModelSettings {
|
|
|
48
48
|
readonly name?: string;
|
|
49
49
|
readonly contextWindow?: number;
|
|
50
50
|
readonly maxTokens?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Remaining entry fields the editor does not model (`reasoningEfforts`,
|
|
53
|
+
* `compat`, `input`, …), carried verbatim so a save preserves them.
|
|
54
|
+
* Populated by {@link loadProviderSettings}; never contains the four
|
|
55
|
+
* modelled keys.
|
|
56
|
+
*/
|
|
57
|
+
readonly extras?: Readonly<Record<string, unknown>>;
|
|
51
58
|
}
|
|
52
59
|
/** The small, portable subset of a provider profile the terminal edits. */
|
|
53
60
|
export interface ProviderConfiguration {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Terminal animation
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* Claude-Code convention.
|
|
2
|
+
* Terminal animation helpers derived from the web design language:
|
|
3
|
+
* thinking uses Codex's slow shimmer sweep, the busy composer marker uses the
|
|
4
|
+
* original braille chase, and the streaming caret blink is the Claude-Code
|
|
5
|
+
* convention.
|
|
7
6
|
*
|
|
8
7
|
* The DeepSeek model-switch easter egg ports Codex's effort-ignition "Wave"
|
|
9
8
|
* style (`codex-rs/tui/src/bottom_pane/effort_ignition_styles.rs`): switching
|
|
@@ -17,14 +16,31 @@
|
|
|
17
16
|
* @module @deepseek-ai/dsh-code/render/animations
|
|
18
17
|
*/
|
|
19
18
|
import type { RgbTriple } from '../theme.ts';
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* per step — 8 frames × 125ms = the web's 1s cycle.
|
|
24
|
-
*/
|
|
19
|
+
/** Cadence for the original busy braille chase (8 frames × 125ms = 1s). */
|
|
20
|
+
export declare const BUSY_CHASE_TICK_MS = 125;
|
|
21
|
+
/** Original terminal StateDot chase frames. */
|
|
25
22
|
export declare const BUSY_CHASE_FRAMES: readonly ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"];
|
|
26
|
-
/** Chase frame for a monotonic tick
|
|
23
|
+
/** Chase frame for a monotonic tick. */
|
|
27
24
|
export declare function busyChaseFrame(tick: number): string;
|
|
25
|
+
/** Clock cadence for the Codex-style Deep diving shimmer. */
|
|
26
|
+
export declare const DEEP_DIVING_SHIMMER_TICK_MS = 33;
|
|
27
|
+
/** Codex shimmer timing and geometry. */
|
|
28
|
+
export declare const DEEP_DIVING_SHIMMER_DURATION_MS = 2000;
|
|
29
|
+
export declare const DEEP_DIVING_SHIMMER_PADDING = 10;
|
|
30
|
+
export declare const DEEP_DIVING_SHIMMER_HALF_WIDTH = 5;
|
|
31
|
+
export declare const DEEP_DIVING_SPARK_BREATH_DURATION_MS = 2000;
|
|
32
|
+
/**
|
|
33
|
+
* Codex's 2-second shimmer sweep, expressed in terminal ticks. The sweep has
|
|
34
|
+
* ten virtual columns of padding on either side and a five-column cosine
|
|
35
|
+
* highlight band, so the text changes gently rather than cycling rapidly.
|
|
36
|
+
*/
|
|
37
|
+
export declare function deepDivingShimmerIntensity(index: number, tick: number, graphemeCount: number): number;
|
|
38
|
+
/** Blue RGB color for one grapheme in the Codex-style shimmer. */
|
|
39
|
+
export declare function deepDivingGradientColor(index: number, tick: number, graphemeCount: number, base: RgbTriple, highlight: RgbTriple): RgbTriple;
|
|
40
|
+
/** Smooth breathing intensity for the always-visible Deep diving sparkle. */
|
|
41
|
+
export declare function deepDivingSparkIntensity(tick: number): number;
|
|
42
|
+
/** Blue RGB color for the breathing Deep diving sparkle. */
|
|
43
|
+
export declare function deepDivingSparkColor(tick: number, base: RgbTriple, highlight: RgbTriple): RgbTriple;
|
|
28
44
|
/** Caret visibility: half the ticks on, half off (530ms blink). */
|
|
29
45
|
export declare function caretVisible(tick: number): boolean;
|
|
30
46
|
/**
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
* grapheme boundaries) so the React state stays two primitives
|
|
9
9
|
* (value, cursor) and every operation here stays pure and testable.
|
|
10
10
|
*
|
|
11
|
-
* Word motion
|
|
12
|
-
*
|
|
13
|
-
* single word (two hanzi are one Alt+B step, not two).
|
|
11
|
+
* Word motion follows Codex's piece semantics: whitespace separates runs,
|
|
12
|
+
* punctuation runs stay atomic, and each Han grapheme is its own boundary.
|
|
14
13
|
*
|
|
15
14
|
* @module @deepseek-ai/dsh-code/render/editor
|
|
16
15
|
*/
|
|
@@ -79,6 +78,15 @@ export interface CaretSite {
|
|
|
79
78
|
row: number;
|
|
80
79
|
column: number;
|
|
81
80
|
}
|
|
81
|
+
/** Text slices for rendering one physical row with at most one caret. */
|
|
82
|
+
export interface EditorRowParts {
|
|
83
|
+
readonly before: string;
|
|
84
|
+
readonly caret: string;
|
|
85
|
+
readonly after: string;
|
|
86
|
+
readonly hasCaret: boolean;
|
|
87
|
+
}
|
|
88
|
+
/** Split one row around the authoritative caret; every other row stays whole. */
|
|
89
|
+
export declare function editorRowParts(row: EditorRowModel, rowIndex: number, caretRow: number, cursor: number, caretEnabled?: boolean): EditorRowParts;
|
|
82
90
|
/** Map a cursor offset to its caret site on the wrapped rows. */
|
|
83
91
|
export declare function caretSite(model: EditorModel, offset: number): CaretSite;
|
|
84
92
|
/**
|
|
@@ -123,6 +131,23 @@ export declare function killToLineStart(value: string, cursor: number): EditResu
|
|
|
123
131
|
export declare function killToLineEnd(value: string, cursor: number): EditResult;
|
|
124
132
|
/** Insert sanitized text at the cursor. */
|
|
125
133
|
export declare function insertText(value: string, cursor: number, text: string): EditResult;
|
|
134
|
+
/** Ctrl+A: current logical line start, then the previous line start on repeat. */
|
|
135
|
+
export declare function moveToLineStart(value: string, cursor: number, crossOnRepeat: boolean): number;
|
|
136
|
+
/** Ctrl+E: current logical line end, then the next line end on repeat. */
|
|
137
|
+
export declare function moveToLineEnd(value: string, cursor: number, crossOnRepeat: boolean): number;
|
|
138
|
+
/** One stable text range captured before an asynchronous draft operation. */
|
|
139
|
+
export interface DraftRange {
|
|
140
|
+
readonly start: number;
|
|
141
|
+
readonly end: number;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Remap a captured range when all intervening edits are wholly before or
|
|
145
|
+
* wholly after it. An edit overlapping either boundary invalidates the
|
|
146
|
+
* anchor instead of guessing and inserting content at a surprising place.
|
|
147
|
+
*/
|
|
148
|
+
export declare function remapStableRange(original: string, current: string, range: DraftRange): DraftRange | undefined;
|
|
149
|
+
/** Replace a current range while preserving a cursor moved after capture. */
|
|
150
|
+
export declare function replaceRangePreservingCursor(value: string, cursor: number, range: DraftRange, replacement: string): EditResult;
|
|
126
151
|
/**
|
|
127
152
|
* Composer editor row budget: the editor itself never grows past this many
|
|
128
153
|
* physical rows; deeper drafts scroll internally to keep the caret visible.
|
|
@@ -130,8 +155,8 @@ export declare function insertText(value: string, cursor: number, text: string):
|
|
|
130
155
|
*/
|
|
131
156
|
export declare function composerMaxRows(terminalRows: number): number;
|
|
132
157
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
158
|
+
* History navigation starts with Up on an empty draft, or after visual
|
|
159
|
+
* movement has reached the directional text edge of an unchanged recalled
|
|
160
|
+
* entry. Every other position remains under textarea movement.
|
|
136
161
|
*/
|
|
137
|
-
export declare function shouldRecallNavigate(value: string, cursor: number, lastRecalled: string | null): boolean;
|
|
162
|
+
export declare function shouldRecallNavigate(value: string, cursor: number, lastRecalled: string | null, direction: -1 | 1): boolean;
|
|
@@ -36,6 +36,31 @@ export declare function reasoningLines(text: string, columns: number): readonly
|
|
|
36
36
|
* Wrapped continuations keep a hanging indent aligned under each row's
|
|
37
37
|
* content (Codex history-cell alignment) instead of resetting to column 0.
|
|
38
38
|
*/
|
|
39
|
-
export declare function transcriptEntryLines(entry: TranscriptEntry, columns: number, showReasoning?: boolean, reasoningToggleHint?: boolean): readonly StyledLine[];
|
|
39
|
+
export declare function transcriptEntryLines(entry: TranscriptEntry, columns: number, showReasoning?: boolean, reasoningToggleHint?: boolean, showToolDetails?: boolean): readonly StyledLine[];
|
|
40
40
|
/** Settled-history variant carrying the Ctrl+R reasoning fold. */
|
|
41
41
|
export declare function settledEntryLines(entry: TranscriptEntry, columns: number, showReasoning: boolean): readonly StyledLine[];
|
|
42
|
+
/** The flexible rows of the live region; chrome (composer/notice/status) is never reduced. */
|
|
43
|
+
export interface LiveAllocation {
|
|
44
|
+
/** Settled tail rows currently rendered in the live tree. */
|
|
45
|
+
readonly live: number;
|
|
46
|
+
/** Rows reserved for the streaming reasoning tail or its marker. */
|
|
47
|
+
readonly reasoning: number;
|
|
48
|
+
/** Rows reserved for the streaming answer tail. */
|
|
49
|
+
readonly answer: number;
|
|
50
|
+
}
|
|
51
|
+
/** A clamped allocation plus the invariant-trip warning that triggered it. */
|
|
52
|
+
export interface LiveAllocationAudit {
|
|
53
|
+
readonly allocation: LiveAllocation;
|
|
54
|
+
readonly warning?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Clamp the live-region allocation so the flexible dynamic rows never exceed
|
|
58
|
+
* the post-chrome budget. By construction the caller derives these rows from
|
|
59
|
+
* the same budget; this is the runtime tripwire for a future edit that breaks
|
|
60
|
+
* that derivation. Reduction order: answer first (the freshest content is the
|
|
61
|
+
* live tail), then reasoning, then settled live rows; nothing goes negative.
|
|
62
|
+
* @param allocation - the intended row allocation.
|
|
63
|
+
* @param dynamicRows - the post-chrome row budget.
|
|
64
|
+
* @returns the clamped allocation and a warning string when clamping fired.
|
|
65
|
+
*/
|
|
66
|
+
export declare function clampLiveAllocation(allocation: LiveAllocation, dynamicRows: number): LiveAllocationAudit;
|
|
@@ -21,7 +21,7 @@ export interface MdSegment {
|
|
|
21
21
|
export interface MdLine {
|
|
22
22
|
segments: readonly MdSegment[];
|
|
23
23
|
}
|
|
24
|
-
/** Visible width of a run in columns (
|
|
24
|
+
/** Visible width of a run in columns (grapheme-cluster and emoji aware). */
|
|
25
25
|
export declare function visibleColumns(text: string): number;
|
|
26
26
|
/** Render markdown text into styled lines of at most `width` columns. */
|
|
27
27
|
export declare function renderMarkdown(text: string, width: number, options?: {
|
|
@@ -357,8 +357,11 @@ export declare function createReplayAccumulator(): ReplayAccumulator;
|
|
|
357
357
|
* to a sequential fold; only the `entries` container operations are mutable.
|
|
358
358
|
*
|
|
359
359
|
* @internal Test-instrumentation path; `projectEvents` is the public entry.
|
|
360
|
+
* @returns whether the event changed the accumulated state — the live store
|
|
361
|
+
* stays silent and keeps its snapshot identity for ignored events, exactly
|
|
362
|
+
* like the copy-on-write reducer returning its input view unchanged.
|
|
360
363
|
*/
|
|
361
|
-
export declare function replayProjectEvent(acc: ReplayAccumulator, event: SessionEvent):
|
|
364
|
+
export declare function replayProjectEvent(acc: ReplayAccumulator, event: SessionEvent): boolean;
|
|
362
365
|
/**
|
|
363
366
|
* Materialize the accumulated fold as a `TranscriptView`, compacting any
|
|
364
367
|
* retired tombstones. The anchors maps are handed through as-is (their
|
|
@@ -367,6 +370,17 @@ export declare function replayProjectEvent(acc: ReplayAccumulator, event: Sessio
|
|
|
367
370
|
* @internal Test-instrumentation path; `projectEvents` is the public entry.
|
|
368
371
|
*/
|
|
369
372
|
export declare function finishReplay(acc: ReplayAccumulator): TranscriptView;
|
|
373
|
+
/**
|
|
374
|
+
* Materialize the accumulated fold as a fresh immutable snapshot for the
|
|
375
|
+
* live store. Unlike {@link finishReplay} — the one-shot replay entry, which
|
|
376
|
+
* hands the accumulator's own arrays through because the accumulator is
|
|
377
|
+
* discarded — every array a renderer can hold is copied here, so later
|
|
378
|
+
* folds never mutate a snapshot already handed out. Same fields, same
|
|
379
|
+
* tombstone compaction.
|
|
380
|
+
*
|
|
381
|
+
* @internal Live-store path; `projectEvents` is the public entry.
|
|
382
|
+
*/
|
|
383
|
+
export declare function snapshotReplayView(acc: ReplayAccumulator): TranscriptView;
|
|
370
384
|
/**
|
|
371
385
|
* Fold a replayed event history into one view.
|
|
372
386
|
*
|
|
@@ -25,8 +25,8 @@ export declare function singleLineText(text: string): string;
|
|
|
25
25
|
/**
|
|
26
26
|
* Truncate one display-safe row without ever exceeding its physical-column
|
|
27
27
|
* budget. The ellipsis is included inside the budget, matching Codex's popup
|
|
28
|
-
* truncation contract; the
|
|
29
|
-
*
|
|
28
|
+
* truncation contract; the cut walks grapheme clusters so emoji and
|
|
29
|
+
* combining sequences never split mid-cluster.
|
|
30
30
|
*/
|
|
31
31
|
export declare function truncateColumns(text: string, columns: number): string;
|
|
32
32
|
/** A display-safe suffix bounded by terminal rows and columns. */
|
|
@@ -37,13 +37,19 @@ export interface DisplayTail {
|
|
|
37
37
|
truncated: boolean;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* Keep
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
40
|
+
* Keep the newest display-safe text that fits a terminal rectangle, wrapping
|
|
41
|
+
* FORWARD from the start of the text and slicing the tail rows.
|
|
42
|
+
*
|
|
43
|
+
* Forward wrapping is what keeps a streaming tail calm: rows already produced
|
|
44
|
+
* never re-wrap as tokens append (a backward scan recomputes every wrap point
|
|
45
|
+
* per chunk and the whole visible block jumps), and the wrap rules match the
|
|
46
|
+
* settled text's renderer so the flush at turn end does not reflow the block
|
|
47
|
+
* a second time. CJK kinsoku applies at both edges: closing punctuation
|
|
48
|
+
* overhangs up to two cells onto the filled row instead of starting the next
|
|
49
|
+
* one (within the caret column the caller reserves), and opening punctuation
|
|
50
|
+
* moves down instead of dangling at a row end. Tabs expand to two spaces so
|
|
51
|
+
* terminal tab stops cannot inflate the physical row count; clusters carry
|
|
52
|
+
* emoji presentation and combining marks whole.
|
|
47
53
|
* @param text - raw externally sourced text.
|
|
48
54
|
* @param columns - available terminal columns.
|
|
49
55
|
* @param rows - available terminal rows.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Precise terminal-cell width measurement — the single authority every
|
|
3
|
+
* budget, wrap, and truncation path shares. The previous heuristic
|
|
4
|
+
* (`codePoint > 0x2e7f ? 2 : 1)) mis-sized Hangul Jamo (narrow), high
|
|
5
|
+
* non-CJK code points (wide), and emoji: text-default glyphs like ✳ ⚠ ❤
|
|
6
|
+
* counted 2 while terminals draw 1, and VS16 sequences counted 1 while
|
|
7
|
+
* terminals draw 2 — the exact drift class the community dsh-TUI string
|
|
8
|
+
* engine documents (a spinner glyph drifting one column per frame). This
|
|
9
|
+
* module adapts that engine's rules without its Ink-fork renderer: an ASCII
|
|
10
|
+
* fast path, grapheme-cluster iteration via Intl.Segmenter (code-point
|
|
11
|
+
* fallback), a merged East-Asian-Wide/Fullwidth + Emoji_Presentation range
|
|
12
|
+
* table, text-default emoji = 1, VS16 = 2, marks/selectors/ZWJ = 0.
|
|
13
|
+
* @module @deepseek-ai/dsh-code/render/width
|
|
14
|
+
*/
|
|
15
|
+
/** Split text into grapheme clusters (code points when Segmenter is absent). */
|
|
16
|
+
export declare function splitGraphemes(text: string): string[];
|
|
17
|
+
/** Terminal-cell width of one grapheme cluster. */
|
|
18
|
+
export declare function graphemeWidth(cluster: string): number;
|
|
19
|
+
/** Terminal-cell width of one code point (surrogate pairs must stay paired). */
|
|
20
|
+
export declare function codePointWidth(char: string): number;
|
|
21
|
+
/**
|
|
22
|
+
* Terminal-cell width of a string: an ASCII fast path avoids the segmenter
|
|
23
|
+
* for the overwhelmingly common case; everything else sums grapheme clusters.
|
|
24
|
+
* Control characters occupy no cells (display sanitization makes them
|
|
25
|
+
* visible escapes before they ever reach a budget).
|
|
26
|
+
* @param text - display-safe or raw text to measure.
|
|
27
|
+
* @returns the column count the terminal will draw.
|
|
28
|
+
*/
|
|
29
|
+
export declare function stringWidth(text: string): number;
|
|
@@ -102,6 +102,33 @@ export declare function sessionArtifactDirectory(artifact: string, id: string):
|
|
|
102
102
|
* @returns the ids to delete, root first.
|
|
103
103
|
*/
|
|
104
104
|
export declare function collectDeletionSubtree(records: readonly SessionRecord[], id: string): string[];
|
|
105
|
+
/** One validated node of a deletion plan. */
|
|
106
|
+
export interface DeletionPlanNode {
|
|
107
|
+
/** Session id to remove. */
|
|
108
|
+
readonly id: string;
|
|
109
|
+
/** Distance from the deletion root (0 for the root itself). */
|
|
110
|
+
readonly depth: number;
|
|
111
|
+
}
|
|
112
|
+
/** A fully preflighted subtree deletion, or the refusal that produced none. */
|
|
113
|
+
export type SessionDeletionPlan = {
|
|
114
|
+
readonly ok: true;
|
|
115
|
+
readonly nodes: readonly DeletionPlanNode[];
|
|
116
|
+
} | {
|
|
117
|
+
readonly ok: false;
|
|
118
|
+
readonly reason: string;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Plan one session-subtree deletion with NO filesystem side effects: collect
|
|
122
|
+
* the doomed lineage, refuse when the root or ANY member is live (a live
|
|
123
|
+
* child would outlive its deleted parent) or missing from the listing, and
|
|
124
|
+
* order the result children-first so the executor can never leave a deleted
|
|
125
|
+
* parent behind surviving children. Artifact-location guards stay at the
|
|
126
|
+
* call site; this is the pure preflight they complete.
|
|
127
|
+
* @param records - the full directory listing.
|
|
128
|
+
* @param id - the root session id to delete.
|
|
129
|
+
* @returns the ordered plan, or a user-facing refusal reason.
|
|
130
|
+
*/
|
|
131
|
+
export declare function planSessionDeletion(records: readonly SessionRecord[], id: string): SessionDeletionPlan;
|
|
105
132
|
/**
|
|
106
133
|
* Codex-style relative time for session rows ("now", "5m ago", "3h ago",
|
|
107
134
|
* "2d ago"; older than a week falls back to the local date).
|