dsh-code 1.2.0 → 1.4.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 +5 -5
- package/README.md +5 -5
- package/lib/index.mjs +6679 -5232
- package/lib/startup.mjs +1 -1
- package/lib/{theme-7u5Qo3dF.mjs → theme-B3orFUYz.mjs} +8 -0
- package/lib/types/app.d.ts +32 -45
- package/lib/types/attachments.d.ts +16 -7
- package/lib/types/completion.d.ts +29 -0
- package/lib/types/composer.d.ts +150 -0
- package/lib/types/git-workflow.d.ts +6 -0
- package/lib/types/index.d.ts +6 -188
- package/lib/types/locales/en.d.ts +65 -5
- package/lib/types/{authorization-panel.d.ts → panels/authorization-panel.d.ts} +6 -1
- package/lib/types/panels/completion-panel.d.ts +13 -0
- package/lib/types/panels/interaction-bars.d.ts +38 -0
- package/lib/types/{kernel-panels.d.ts → panels/kernel-panels.d.ts} +19 -38
- package/lib/types/{language-panel.d.ts → panels/language-panel.d.ts} +1 -1
- package/lib/types/panels/model-panels.d.ts +86 -0
- package/lib/types/{theme-panel.d.ts → panels/theme-panel.d.ts} +1 -1
- package/lib/types/{update-panel.d.ts → panels/update-panel.d.ts} +22 -1
- package/lib/types/provider-settings.d.ts +11 -0
- package/lib/types/render/inspector.d.ts +8 -0
- package/lib/types/render/status.d.ts +4 -1
- package/lib/types/render/text.d.ts +4 -0
- package/lib/types/runner/harness-gate.d.ts +83 -0
- package/lib/types/runner/input-history.d.ts +31 -0
- package/lib/types/runner/mode-cycle.d.ts +44 -0
- package/lib/types/runner/preferences.d.ts +40 -0
- package/lib/types/runner/quit.d.ts +27 -0
- package/lib/types/runner/search-rows.d.ts +38 -0
- package/lib/types/runner/session-io.d.ts +46 -0
- package/lib/types/runner/session-target.d.ts +42 -0
- package/lib/types/runner/startup-config.d.ts +33 -0
- package/lib/types/runner/submissions.d.ts +87 -0
- package/lib/types/session/attach.d.ts +39 -0
- package/lib/types/{history.d.ts → session/history.d.ts} +10 -0
- package/lib/types/{session-directory.d.ts → session/session-directory.d.ts} +25 -1
- package/lib/types/{session-switch.d.ts → session/session-switch.d.ts} +8 -0
- package/lib/types/{store.d.ts → session/store.d.ts} +1 -1
- package/lib/types/{subagents.d.ts → session/subagents.d.ts} +8 -1
- package/lib/types/settings-file.d.ts +10 -0
- package/lib/types/{panel-accent.d.ts → ui/panel-accent.d.ts} +1 -1
- package/lib/types/ui/panel-gap.d.ts +6 -0
- package/lib/types/ui/query-editor.d.ts +10 -0
- package/lib/types/ui/styled-rows.d.ts +8 -0
- package/lib/types/{terminal-title.d.ts → ui/terminal-title.d.ts} +1 -1
- package/lib/types/ui/ui-contract.d.ts +12 -0
- package/lib/types/ui/use-frames.d.ts +6 -0
- package/lib/types/ui/use-stable-input.d.ts +7 -0
- package/lib/types/version.d.ts +2 -0
- package/package.json +7 -5
- package/src/app.ts +707 -4108
- package/src/attachments.ts +65 -19
- package/src/completion.ts +117 -0
- package/src/composer.ts +1956 -0
- package/src/git-workflow.ts +18 -0
- package/src/index.ts +164 -645
- package/src/input-split.ts +24 -4
- package/src/internals.ts +1 -1
- package/src/locales/en.ts +66 -5
- package/src/locales/zh.ts +66 -5
- package/src/{authorization-panel.ts → panels/authorization-panel.ts} +30 -8
- package/src/panels/completion-panel.ts +79 -0
- package/src/panels/interaction-bars.ts +567 -0
- package/src/{kernel-panels.ts → panels/kernel-panels.ts} +142 -90
- package/src/{language-panel.ts → panels/language-panel.ts} +4 -4
- package/src/panels/model-panels.ts +1021 -0
- package/src/{theme-panel.ts → panels/theme-panel.ts} +5 -5
- package/src/{update-panel.ts → panels/update-panel.ts} +117 -10
- package/src/provider-settings.ts +38 -0
- package/src/rainbow.ts +13 -3
- package/src/render/inspector.ts +23 -0
- package/src/render/status.ts +80 -29
- package/src/render/text.ts +10 -1
- package/src/runner/harness-gate.ts +168 -0
- package/src/runner/input-history.ts +77 -0
- package/src/runner/mode-cycle.ts +49 -0
- package/src/runner/preferences.ts +67 -0
- package/src/runner/quit.ts +53 -0
- package/src/runner/search-rows.ts +55 -0
- package/src/runner/session-io.ts +206 -0
- package/src/runner/session-target.ts +81 -0
- package/src/runner/startup-config.ts +54 -0
- package/src/runner/submissions.ts +157 -0
- package/src/session/attach.ts +87 -0
- package/src/{fork.ts → session/fork.ts} +11 -7
- package/src/{history.ts → session/history.ts} +14 -0
- package/src/{session-directory.ts → session/session-directory.ts} +83 -4
- package/src/{session-switch.ts → session/session-switch.ts} +14 -0
- package/src/{store.ts → session/store.ts} +20 -2
- package/src/{subagents.ts → session/subagents.ts} +12 -1
- package/src/settings-file.ts +19 -1
- package/src/{panel-accent.ts → ui/panel-accent.ts} +1 -1
- package/src/ui/panel-gap.ts +9 -0
- package/src/ui/query-editor.ts +16 -0
- package/src/ui/styled-rows.ts +124 -0
- package/src/{terminal-title.ts → ui/terminal-title.ts} +1 -1
- package/src/ui/ui-contract.ts +10 -0
- package/src/ui/use-frames.ts +23 -0
- package/src/ui/use-stable-input.ts +17 -0
- package/src/version.ts +5 -0
- /package/lib/types/{fork.d.ts → session/fork.d.ts} +0 -0
package/src/settings-file.ts
CHANGED
|
@@ -17,8 +17,9 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { randomUUID } from 'node:crypto'
|
|
20
|
+
import { readFileSync } from 'node:fs'
|
|
20
21
|
import { mkdir, rename, writeFile } from 'node:fs/promises'
|
|
21
|
-
import { dirname } from 'node:path'
|
|
22
|
+
import { basename, dirname } from 'node:path'
|
|
22
23
|
|
|
23
24
|
/**
|
|
24
25
|
* Run one file operation with a bounded retry: one initial try plus at
|
|
@@ -86,3 +87,20 @@ export function createUserSettingsPersistence(): UserSettingsPersistence {
|
|
|
86
87
|
},
|
|
87
88
|
}
|
|
88
89
|
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Read one user-level settings file as a plain object. The callers all treat a
|
|
93
|
+
* missing file as "unset" and a corrupt one as "warn and fall back", so this
|
|
94
|
+
* helper owns the one distinction they share: readable JSON that is not an
|
|
95
|
+
* object is corruption, not an absent preference, and must not surface as a
|
|
96
|
+
* cryptic property access on `null`.
|
|
97
|
+
* @param path - absolute path of the settings file.
|
|
98
|
+
* @returns the parsed object; the caller narrows each field itself.
|
|
99
|
+
*/
|
|
100
|
+
export function readSettingsObject(path: string): Record<string, unknown> {
|
|
101
|
+
const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'))
|
|
102
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
103
|
+
throw new Error(`${basename(path)} must contain a JSON object`)
|
|
104
|
+
}
|
|
105
|
+
return parsed as Record<string, unknown>
|
|
106
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @module @deepseek-ai/dsh-tui/panel-accent
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { surfaceAccent, type RgbTriple } from '
|
|
14
|
+
import { surfaceAccent, type RgbTriple } from '../theme.ts'
|
|
15
15
|
|
|
16
16
|
/** First-seen slot per panel id; stable for the process lifetime. */
|
|
17
17
|
const panelSlots = new Map<string, number>()
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Shared optional spacer row used by bounded panel layouts. */
|
|
2
|
+
|
|
3
|
+
import { createElement, type ReactElement } from 'react'
|
|
4
|
+
import { Text } from 'ink'
|
|
5
|
+
|
|
6
|
+
/** Codex-style panel rhythm that still participates in the row budget. */
|
|
7
|
+
export function PanelGap({ visible }: { visible: boolean }): ReactElement | undefined {
|
|
8
|
+
return visible ? createElement(Text, null, ' ') : undefined
|
|
9
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Shared one-line query editor for keyboard-owned filtering panels. */
|
|
2
|
+
|
|
3
|
+
import { stripPasteMarkers } from '../keyboard.ts'
|
|
4
|
+
import { deleteLastGrapheme } from '../render/editor.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Apply one keystroke to a panel search query. IME commits arrive as one
|
|
8
|
+
* multi-character chunk, so the whole printable run is appended; paste
|
|
9
|
+
* markers are stripped and control-laden chunks are ignored.
|
|
10
|
+
*/
|
|
11
|
+
export function editQuery(query: string, input: string, key: { backspace?: boolean; delete?: boolean }): string | undefined {
|
|
12
|
+
if (key.backspace || key.delete) return deleteLastGrapheme(query)
|
|
13
|
+
const text = stripPasteMarkers(input)
|
|
14
|
+
if (text !== '' && !/[\u0000-\u001f\u007f]/u.test(text)) return query + text
|
|
15
|
+
return undefined
|
|
16
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/** Ink renderer for width-safe styled terminal rows. */
|
|
2
|
+
|
|
3
|
+
import { createElement, type ReactElement } from 'react'
|
|
4
|
+
import { Box, Text } from 'ink'
|
|
5
|
+
import type { MdSegment } from '../render/markdown.ts'
|
|
6
|
+
import type { LineStyle, StyledLine } from '../render/lines.ts'
|
|
7
|
+
import {
|
|
8
|
+
diffBackground,
|
|
9
|
+
getPalette,
|
|
10
|
+
inkColor,
|
|
11
|
+
promptRowTokens,
|
|
12
|
+
rowBackground,
|
|
13
|
+
} from '../theme.ts'
|
|
14
|
+
|
|
15
|
+
function segmentProps(style: MdSegment['style']): {
|
|
16
|
+
color: string | undefined
|
|
17
|
+
bold: boolean | undefined
|
|
18
|
+
italic: boolean | undefined
|
|
19
|
+
strikethrough: boolean | undefined
|
|
20
|
+
} {
|
|
21
|
+
switch (style) {
|
|
22
|
+
case 'accent':
|
|
23
|
+
return { color: inkColor(getPalette().brandBright), bold: undefined, italic: undefined, strikethrough: undefined }
|
|
24
|
+
case 'accentBold':
|
|
25
|
+
return { color: inkColor(getPalette().brandBright), bold: true, italic: undefined, strikethrough: undefined }
|
|
26
|
+
case 'code':
|
|
27
|
+
return { color: inkColor(getPalette().code), bold: undefined, italic: undefined, strikethrough: undefined }
|
|
28
|
+
case 'dim':
|
|
29
|
+
return { color: inkColor(getPalette().dim), bold: undefined, italic: undefined, strikethrough: undefined }
|
|
30
|
+
case 'bold':
|
|
31
|
+
return { color: undefined, bold: true, italic: undefined, strikethrough: undefined }
|
|
32
|
+
case 'italic':
|
|
33
|
+
return { color: undefined, bold: undefined, italic: true, strikethrough: undefined }
|
|
34
|
+
case 'boldItalic':
|
|
35
|
+
return { color: undefined, bold: true, italic: true, strikethrough: undefined }
|
|
36
|
+
case 'strike':
|
|
37
|
+
return { color: inkColor(getPalette().dim), bold: undefined, italic: undefined, strikethrough: true }
|
|
38
|
+
case 'diffAdd':
|
|
39
|
+
case 'diffDel':
|
|
40
|
+
// Inline-markdown twin of lineStyleProps' diff cases: tinted rows for
|
|
41
|
+
// ```diff fences rendered through the markdown span path. The diff
|
|
42
|
+
// foreground tokens stay AA-legible both on the row tints (when the
|
|
43
|
+
// background rides along) and on the plain terminal background.
|
|
44
|
+
return {
|
|
45
|
+
color: inkColor(style === 'diffAdd' ? getPalette().diffAddFg : getPalette().diffDelFg),
|
|
46
|
+
bold: undefined,
|
|
47
|
+
italic: undefined,
|
|
48
|
+
strikethrough: undefined,
|
|
49
|
+
}
|
|
50
|
+
default:
|
|
51
|
+
return { color: undefined, bold: undefined, italic: undefined, strikethrough: undefined }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Ink props for the richer line model used by bounded scrolling panels. */
|
|
56
|
+
function lineStyleProps(style: LineStyle): {
|
|
57
|
+
color: string | undefined
|
|
58
|
+
bold: boolean | undefined
|
|
59
|
+
italic: boolean | undefined
|
|
60
|
+
strikethrough: boolean | undefined
|
|
61
|
+
dimColor: boolean | undefined
|
|
62
|
+
backgroundColor: string | undefined
|
|
63
|
+
} {
|
|
64
|
+
switch (style) {
|
|
65
|
+
case 'brand':
|
|
66
|
+
return { color: inkColor(getPalette().brandBright), bold: undefined, italic: undefined, strikethrough: undefined, dimColor: undefined, backgroundColor: undefined }
|
|
67
|
+
case 'success':
|
|
68
|
+
return { color: inkColor(getPalette().success), bold: undefined, italic: undefined, strikethrough: undefined, dimColor: undefined, backgroundColor: undefined }
|
|
69
|
+
case 'error':
|
|
70
|
+
return { color: inkColor(getPalette().error), bold: undefined, italic: undefined, strikethrough: undefined, dimColor: undefined, backgroundColor: undefined }
|
|
71
|
+
case 'warn':
|
|
72
|
+
return { color: inkColor(getPalette().warn), bold: undefined, italic: undefined, strikethrough: undefined, dimColor: undefined, backgroundColor: undefined }
|
|
73
|
+
case 'dimItalic':
|
|
74
|
+
return { color: inkColor(getPalette().dim), bold: undefined, italic: true, strikethrough: undefined, dimColor: undefined, backgroundColor: undefined }
|
|
75
|
+
// Codex diff rendering: added/removed lines carry a theme tint behind
|
|
76
|
+
// the sign and text, with the AA-tuned diff foreground tokens on top; the
|
|
77
|
+
// depth gate turns this into plain foreground styling on 16-color
|
|
78
|
+
// terminals.
|
|
79
|
+
case 'diffAdd':
|
|
80
|
+
return { color: inkColor(getPalette().diffAddFg), bold: undefined, italic: undefined, strikethrough: undefined, dimColor: undefined, backgroundColor: diffBackground('diffAdd') }
|
|
81
|
+
case 'diffDel':
|
|
82
|
+
return { color: inkColor(getPalette().diffDelFg), bold: undefined, italic: undefined, strikethrough: undefined, dimColor: undefined, backgroundColor: diffBackground('diffDel') }
|
|
83
|
+
// Prompt rows: one full-width bar per delivery kind. The tint comes from
|
|
84
|
+
// the row token and the foreground from its AA-tuned twin; the depth gate
|
|
85
|
+
// inside rowBackground degrades this to foreground-only on 16-color
|
|
86
|
+
// terminals, matching the diff rows.
|
|
87
|
+
case 'promptRow':
|
|
88
|
+
case 'promptQueuedRow':
|
|
89
|
+
case 'promptSteeredRow': {
|
|
90
|
+
const tokens = promptRowTokens(style === 'promptQueuedRow' ? 'queued' : style === 'promptSteeredRow' ? 'steered' : undefined)
|
|
91
|
+
return {
|
|
92
|
+
color: inkColor(getPalette()[tokens.fg]),
|
|
93
|
+
bold: undefined,
|
|
94
|
+
italic: undefined,
|
|
95
|
+
strikethrough: undefined,
|
|
96
|
+
dimColor: undefined,
|
|
97
|
+
backgroundColor: rowBackground(tokens.fg),
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
default:
|
|
101
|
+
return { ...segmentProps(style), dimColor: undefined, backgroundColor: undefined }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Render width-safe rows; every child is exactly one terminal row. */
|
|
106
|
+
export function StyledRows({ lines }: { lines: readonly StyledLine[] }): ReactElement {
|
|
107
|
+
return createElement(
|
|
108
|
+
Box,
|
|
109
|
+
{ flexDirection: 'column' },
|
|
110
|
+
...lines.map((line, index) => createElement(
|
|
111
|
+
Text,
|
|
112
|
+
{ key: index, wrap: 'truncate-end' },
|
|
113
|
+
line.segments.length === 0
|
|
114
|
+
? ' '
|
|
115
|
+
: line.segments.map((segment, at) => createElement(
|
|
116
|
+
Text,
|
|
117
|
+
{ key: at, ...lineStyleProps(segment.style) },
|
|
118
|
+
segment.text,
|
|
119
|
+
)),
|
|
120
|
+
)),
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** File-oriented, color-coded unified diff viewport. */
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* an empty OSC payload and the terminal falls back to its own default; the
|
|
11
11
|
* previously set title is not portable to read back and is never restored.
|
|
12
12
|
*
|
|
13
|
-
* @module @deepseek-ai/dsh-code/terminal-title
|
|
13
|
+
* @module @deepseek-ai/dsh-code/ui/terminal-title
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Shared terminal UI contracts used by the runner, App, and composer. */
|
|
2
|
+
|
|
3
|
+
/** Visual priority for one bounded local notice. */
|
|
4
|
+
export type NoticeTone = 'info' | 'warning' | 'error'
|
|
5
|
+
|
|
6
|
+
/** One mutation the terminal may request for a pending next-turn inbox item. */
|
|
7
|
+
export type QueueMutation =
|
|
8
|
+
| { readonly kind: 'remove' }
|
|
9
|
+
| { readonly kind: 'edit'; readonly text: string }
|
|
10
|
+
| { readonly kind: 'steer' }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Wall-clock animation frame hook shared by independent terminal leaves. */
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Derive ticks from elapsed wall time so a stretched interval skips ahead
|
|
7
|
+
* instead of slowing the animation under a busy event loop or remote shell.
|
|
8
|
+
*/
|
|
9
|
+
export function useFrames(intervalMs: number, active = true): number {
|
|
10
|
+
const [tick, setTick] = useState(0)
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
if (!active) return
|
|
13
|
+
const startedAt = Date.now()
|
|
14
|
+
setTick(0)
|
|
15
|
+
const id = setInterval(() => {
|
|
16
|
+
setTick(Math.max(0, Math.floor((Date.now() - startedAt) / intervalMs)))
|
|
17
|
+
}, intervalMs)
|
|
18
|
+
return () => {
|
|
19
|
+
clearInterval(id)
|
|
20
|
+
}
|
|
21
|
+
}, [active, intervalMs])
|
|
22
|
+
return tick
|
|
23
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Stable Ink input subscription for stateful keyboard-owned surfaces. */
|
|
2
|
+
|
|
3
|
+
import { useCallback, useRef } from 'react'
|
|
4
|
+
import { useInput, type Key } from 'ink'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ink re-subscribes its input effect whenever the handler identity changes.
|
|
8
|
+
* Keep ownership stable while a surface updates cursor, scroll, or draft state.
|
|
9
|
+
*/
|
|
10
|
+
export function useStableInput(handler: (input: string, key: Key) => void, active: boolean): void {
|
|
11
|
+
const handlerRef = useRef(handler)
|
|
12
|
+
handlerRef.current = handler
|
|
13
|
+
const stableHandler = useCallback((input: string, key: Key): void => {
|
|
14
|
+
handlerRef.current(input, key)
|
|
15
|
+
}, [])
|
|
16
|
+
useInput(stableHandler, { isActive: active })
|
|
17
|
+
}
|
package/src/version.ts
CHANGED
|
@@ -17,6 +17,11 @@ function readPackageVersion(manifest = new URL('../package.json', import.meta.ur
|
|
|
17
17
|
/** Version of the installed dsh-code package. */
|
|
18
18
|
export const DSH_CODE_VERSION = readPackageVersion()
|
|
19
19
|
|
|
20
|
+
/** Header brand line: hide a missing-manifest fallback so we never paint v0.0.0. */
|
|
21
|
+
export function headerBrandTitle(version: string = DSH_CODE_VERSION): string {
|
|
22
|
+
return version === '0.0.0' ? 'DeepSeek Harness' : `DeepSeek Harness · v${version}`
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
/** The harness host package: the dsh CLI whose process runs the TUI plugin. */
|
|
21
26
|
const DSH_HOST_PACKAGE_NAME = '@deepseek-ai/dsh'
|
|
22
27
|
|
|
File without changes
|