@askdkc/dsh-cli 0.12.0 → 0.12.2
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.md +12 -9
- package/lib/types/components/ModelDialogLayer.d.ts +12 -0
- package/lib/types/components/ModelDialogLayer.d.ts.map +1 -0
- package/lib/types/components/ModelDialogLayer.js +39 -0
- package/lib/types/components/ModelPicker.d.ts +12 -41
- package/lib/types/components/ModelPicker.d.ts.map +1 -1
- package/lib/types/components/ModelPicker.js +15 -44
- package/lib/types/components/design-system/ListItem.d.ts.map +1 -1
- package/lib/types/components/design-system/ListItem.js +1 -1
- package/lib/types/dsh-adapter/channel/command-completions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/command-completions.js +11 -2
- package/lib/types/dsh-adapter/contract.d.ts +4 -3
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +6 -3
- package/lib/types/i18n.d.ts +18 -26
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +5 -7
- package/lib/types/modelFavorites.d.ts +8 -0
- package/lib/types/modelFavorites.d.ts.map +1 -0
- package/lib/types/modelFavorites.js +59 -0
- package/lib/types/modelGroups.d.ts +3 -6
- package/lib/types/modelGroups.d.ts.map +1 -1
- package/lib/types/modelGroups.js +4 -7
- package/lib/types/modelPickerRows.d.ts +19 -0
- package/lib/types/modelPickerRows.d.ts.map +1 -0
- package/lib/types/modelPickerRows.js +54 -0
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +186 -237
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/screens/chatOverlay.js +2 -0
- package/node_modules/@askdkc/dsh-auth/package.json +10 -10
- package/node_modules/dsh-working-activity/package.json +9 -9
- package/package.json +92 -92
package/README.md
CHANGED
|
@@ -17,9 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
> An interactive terminal UI plugin for DeepSeek Harness.
|
|
19
19
|
|
|
20
|
-
This is
|
|
21
|
-
maintained by dkc. Releases and development decisions are made in this
|
|
22
|
-
repository. Compatibility with DeepSeek Harness remains a separate requirement.
|
|
20
|
+
This is dkc's independently developed fork of [dsh-TUI by chimney](https://github.com/ccch1mneyyy/dsh-TUI).
|
|
23
21
|
|
|
24
22
|
## Preview
|
|
25
23
|
|
|
@@ -36,19 +34,19 @@ repository. Compatibility with DeepSeek Harness remains a separate requirement.
|
|
|
36
34
|
- A pixel whale, live work status, context bar, TPS gauge, and clickable timeline.
|
|
37
35
|
- Session management with resume, fork, rewind, background work, and export.
|
|
38
36
|
- DSH presets, skills, MCP, goals, subagents, provider authentication, and extensions.
|
|
37
|
+
- Authentication for OpenAI, Claude, OpenCode, OpenRouter, Hermes Agent, Infron, and OrcaRouter.
|
|
39
38
|
- Virtualized rendering and bounded caches for long sessions.
|
|
40
39
|
|
|
41
40
|
## Quick Start
|
|
42
41
|
|
|
43
42
|
Requires [Node.js](https://nodejs.org/en) `^22.19 || >=24`, pnpm 11, a
|
|
44
43
|
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) source
|
|
45
|
-
checkout with dependencies installed, and `DEEPSEEK_API_KEY`.
|
|
46
|
-
|
|
44
|
+
checkout with dependencies installed, and `DEEPSEEK_API_KEY`. We aim to support
|
|
45
|
+
the latest DSH release. The primary validated Harness version is `0.2.0-rc.2`;
|
|
46
|
+
older supported versions are listed in [ADAPTER.md](ADAPTER.md).
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`git submodule update --init --recursive` before installation. Builds use the
|
|
51
|
-
checked-out auth source and do not fetch `dsh-auth-fork`.
|
|
48
|
+
Clone recursively to fetch submodules. In an existing checkout, run
|
|
49
|
+
`git submodule update --init --recursive` before installation.
|
|
52
50
|
|
|
53
51
|
```sh
|
|
54
52
|
git clone --recurse-submodules https://github.com/askdkc/dsh-cli.git
|
|
@@ -82,6 +80,11 @@ Chinese. Ambiguous short requests keep that session's previous progress language
|
|
|
82
80
|
selects a model. `/bg` keeps a session running until the TUI exits. See
|
|
83
81
|
[Interaction and commands](docs/interaction.en.md) for the full reference.
|
|
84
82
|
|
|
83
|
+
`/model` opens a centered picker with Favorites, Recent, and provider sections.
|
|
84
|
+
`/model seek deep` opens it with an order-independent search; an exact
|
|
85
|
+
`/model provider/model-id` switches directly. In the picker, use `Ctrl+F` to
|
|
86
|
+
toggle a favorite, `Ctrl+A` to connect a provider, and `Esc` to close.
|
|
87
|
+
|
|
85
88
|
To import Claude Code, Codex, OMP, zcode, or Grok Build conversations, run
|
|
86
89
|
`dsh-cli migrate` to see available histories, then
|
|
87
90
|
`dsh-cli migrate <agent> [--dry-run]`. Tool traffic is not imported.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/** Root-level, zero-layout-height modal. Its catcher and card are siblings. */
|
|
3
|
+
export declare function ModelDialogLayer({ children, onClose, onInsufficientSpace, fullscreen }: {
|
|
4
|
+
children(size: {
|
|
5
|
+
columns: number;
|
|
6
|
+
rows: number;
|
|
7
|
+
}): React.ReactNode;
|
|
8
|
+
onClose(): void;
|
|
9
|
+
onInsufficientSpace?(): void;
|
|
10
|
+
fullscreen: boolean;
|
|
11
|
+
}): React.ReactNode;
|
|
12
|
+
//# sourceMappingURL=ModelDialogLayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ModelDialogLayer.d.ts","sourceRoot":"","sources":["../../../src/components/ModelDialogLayer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,EAAE;IACvF,QAAQ,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC,SAAS,CAAA;IAClE,OAAO,IAAI,IAAI,CAAA;IACf,mBAAmB,CAAC,IAAI,IAAI,CAAA;IAC5B,UAAU,EAAE,OAAO,CAAA;CACpB,GAAG,KAAK,CAAC,SAAS,CAoClB"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, useApp, useTerminalSize } from '../ui.js';
|
|
4
|
+
import measureElement from '../ink/measure-element.js';
|
|
5
|
+
/** Root-level, zero-layout-height modal. Its catcher and card are siblings. */
|
|
6
|
+
export function ModelDialogLayer({ children, onClose, onInsufficientSpace, fullscreen }) {
|
|
7
|
+
const terminal = useTerminalSize();
|
|
8
|
+
const { stdout } = useApp();
|
|
9
|
+
const ref = React.useRef(null);
|
|
10
|
+
const [region, setRegion] = React.useState({ columns: terminal.columns, rows: terminal.rows });
|
|
11
|
+
React.useLayoutEffect(() => {
|
|
12
|
+
const node = ref.current;
|
|
13
|
+
const parent = node?.parentNode;
|
|
14
|
+
if (!node || !parent)
|
|
15
|
+
return;
|
|
16
|
+
const { width } = measureElement(node);
|
|
17
|
+
const { height } = measureElement(parent);
|
|
18
|
+
let bottom = height;
|
|
19
|
+
let rootHeight = height;
|
|
20
|
+
for (let ancestor = parent; ancestor; ancestor = ancestor.parentNode) {
|
|
21
|
+
bottom += ancestor.yogaNode?.getComputedTop() ?? 0;
|
|
22
|
+
rootHeight = ancestor.yogaNode?.getComputedHeight() ?? rootHeight;
|
|
23
|
+
}
|
|
24
|
+
const terminalRows = stdout.rows ?? terminal.rows;
|
|
25
|
+
const visibleTop = rootHeight > terminalRows ? rootHeight - terminalRows + 1 : 0;
|
|
26
|
+
const rows = fullscreen ? terminalRows : Math.max(1, Math.min(terminalRows, bottom - visibleTop));
|
|
27
|
+
if (!fullscreen && rows < 8)
|
|
28
|
+
onInsufficientSpace?.();
|
|
29
|
+
const columns = Math.max(1, width || terminal.columns);
|
|
30
|
+
setRegion(previous => previous.columns === columns && previous.rows === rows ? previous : { columns, rows });
|
|
31
|
+
});
|
|
32
|
+
const visible = region;
|
|
33
|
+
const columns = Math.max(1, Math.min(72, visible.columns - 2));
|
|
34
|
+
const rows = Math.max(1, Math.min(26, visible.rows - 2));
|
|
35
|
+
const left = Math.max(0, Math.floor((visible.columns - columns) / 2));
|
|
36
|
+
const bottom = Math.max(0, Math.ceil((visible.rows - rows) / 2));
|
|
37
|
+
const layer = _jsxs(_Fragment, { children: [_jsx(Box, { ref: ref, position: "absolute", bottom: 0, left: 0, width: "100%", height: visible.rows, backdrop: "dim", onClick: event => { event.stopImmediatePropagation(); onClose(); } }), _jsx(Box, { position: "absolute", bottom: bottom, left: left, width: columns, height: rows, backgroundColor: "toolCardBackground", opaque: true, overflow: "hidden", onClick: event => event.stopImmediatePropagation(), children: children({ columns, rows }) })] });
|
|
38
|
+
return layer;
|
|
39
|
+
}
|
|
@@ -1,46 +1,17 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* the rows as Select entries (❯ focus pointer, ✓ on the active row,
|
|
7
|
-
* descriptions), plus the Enter/Esc hint line. Selection switches through
|
|
8
|
-
* the Chat coordinator's live-fork path.
|
|
9
|
-
*
|
|
10
|
-
* Opens on the complete searchable model list. Tab can show provider groups
|
|
11
|
-
* (registry display name + model count), where Enter drills into one
|
|
12
|
-
* provider. Enter on a model switches through the live-fork path.
|
|
13
|
-
*
|
|
14
|
-
* 长列表按焦点窗口化(Select 同款):picker 经 OverlayAbove 浮层挂载后有
|
|
15
|
-
* maxHeight 裁剪,全量渲染会让焦点行被裁掉(看不到焦点按 Enter)。
|
|
16
|
-
*/
|
|
17
|
-
export declare function ModelPicker(props: {
|
|
18
|
-
/** Top level: provider groups; Enter/click drills into one. */
|
|
19
|
-
groups: readonly ModelGroupRow[];
|
|
2
|
+
import type { ModelPickerRow } from '../modelPickerRows.js';
|
|
3
|
+
import type { ModelRef } from '../modelGroups.js';
|
|
4
|
+
export declare function ModelPicker({ rows, focusIndex, currentModel, favorites, query, cursor, height, status, switching, onPick, onWheelStep }: {
|
|
5
|
+
rows: readonly ModelPickerRow[];
|
|
20
6
|
focusIndex: number;
|
|
21
|
-
/** Current route key — its group row carries the ✓ marker. */
|
|
22
|
-
currentProvider: string;
|
|
23
|
-
onPick?: (index: number) => void;
|
|
24
|
-
query?: string;
|
|
25
|
-
cursor?: number;
|
|
26
|
-
emptyReason?: 'loading' | 'empty' | 'no-match' | 'error';
|
|
27
|
-
} | {
|
|
28
|
-
/** Flat catalog or one group's models (`showProviderPrefix` for mixed providers). */
|
|
29
|
-
models: readonly LlmModelInfo[];
|
|
30
|
-
/** The group's display label as this pane's title (default: "Model"). */
|
|
31
|
-
groupLabel?: string;
|
|
32
|
-
/** A drilled-in provider or recents group can return to the group list. */
|
|
33
|
-
showBack: boolean;
|
|
34
|
-
/** Flat catalog can switch to provider groups with Tab. */
|
|
35
|
-
showGroups?: boolean;
|
|
36
|
-
/** Prefix each row with its provider (the recents group mixes providers). */
|
|
37
|
-
showProviderPrefix?: boolean;
|
|
38
|
-
focusIndex: number;
|
|
39
|
-
/** `provider/model` of the current model — its row carries the ✓ marker. */
|
|
40
7
|
currentModel: string;
|
|
41
|
-
|
|
42
|
-
query
|
|
43
|
-
cursor
|
|
44
|
-
|
|
8
|
+
favorites: readonly ModelRef[];
|
|
9
|
+
query: string;
|
|
10
|
+
cursor: number;
|
|
11
|
+
height: number;
|
|
12
|
+
status: 'loading' | 'ready' | 'error';
|
|
13
|
+
switching: boolean;
|
|
14
|
+
onPick(index: number): void;
|
|
15
|
+
onWheelStep(step: number): void;
|
|
45
16
|
}): React.ReactNode;
|
|
46
17
|
//# sourceMappingURL=ModelPicker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModelPicker.d.ts","sourceRoot":"","sources":["../../../src/components/ModelPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"ModelPicker.d.ts","sourceRoot":"","sources":["../../../src/components/ModelPicker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAMjD,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,EAAE;IACxI,IAAI,EAAE,SAAS,cAAc,EAAE,CAAA;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAA;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;IACrC,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAChC,GAAG,KAAK,CAAC,SAAS,CA4BlB"}
|
|
@@ -1,51 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { t } from '../i18n.js';
|
|
3
2
|
import { Box, Text } from '../ui.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { ListItem } from './design-system/ListItem.js';
|
|
7
|
-
import { HintLine } from './design-system/HintLine.js';
|
|
3
|
+
import { t } from '../i18n.js';
|
|
4
|
+
import { focusedModelRow } from '../modelPickerRows.js';
|
|
8
5
|
import { listWindow } from './listWindow.js';
|
|
9
|
-
import { useOverlayListRows } from './OverlayAbove.js';
|
|
10
6
|
import { SearchBox } from './SearchBox.js';
|
|
7
|
+
import { ListItem } from './design-system/ListItem.js';
|
|
11
8
|
import { useTerminalFocus } from '../ink/hooks/use-terminal-focus.js';
|
|
12
|
-
|
|
13
|
-
* Model picker: a permission-colored Pane with
|
|
14
|
-
* the rows as Select entries (❯ focus pointer, ✓ on the active row,
|
|
15
|
-
* descriptions), plus the Enter/Esc hint line. Selection switches through
|
|
16
|
-
* the Chat coordinator's live-fork path.
|
|
17
|
-
*
|
|
18
|
-
* Opens on the complete searchable model list. Tab can show provider groups
|
|
19
|
-
* (registry display name + model count), where Enter drills into one
|
|
20
|
-
* provider. Enter on a model switches through the live-fork path.
|
|
21
|
-
*
|
|
22
|
-
* 长列表按焦点窗口化(Select 同款):picker 经 OverlayAbove 浮层挂载后有
|
|
23
|
-
* maxHeight 裁剪,全量渲染会让焦点行被裁掉(看不到焦点按 Enter)。
|
|
24
|
-
*/
|
|
25
|
-
export function ModelPicker(props) {
|
|
26
|
-
const inGroups = 'groups' in props;
|
|
9
|
+
export function ModelPicker({ rows, focusIndex, currentModel, favorites, query, cursor, height, status, switching, onPick, onWheelStep }) {
|
|
27
10
|
const terminalFocused = useTerminalFocus();
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
const rows = inGroups ? props.groups : props.models;
|
|
40
|
-
const listRows = useOverlayListRows(8);
|
|
41
|
-
const { start, end } = listWindow(rowHeights, props.focusIndex, listRows);
|
|
42
|
-
const hint = inGroups
|
|
43
|
-
? t('hint-model-groups')
|
|
44
|
-
: props.showBack ? t('hint-model-back') : props.showGroups ? t('hint-model-all') : t('hint-confirm-exit');
|
|
45
|
-
return (_jsxs(Pane, { color: "permission", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "remember", bold: true, children: inGroups || props.groupLabel === undefined ? t('picker-title-model') : props.groupLabel }) }), _jsx(SearchBox, { query: props.query ?? '', cursorOffset: props.cursor ?? 0, isFocused: true, isTerminalFocused: terminalFocused, borderless: true, placeholder: t('picker-model-search') }), _jsx(Text, { dimColor: true, children: t('picker-model-results', { count: rows.length }) }), rows.length === 0 && _jsx(Text, { dimColor: true, children: t(props.emptyReason === 'loading' ? 'model-loading' : props.emptyReason === 'error' ? 'picker-model-error' : props.emptyReason === 'no-match' ? 'picker-model-no-match' : 'picker-model-empty') }), rows.slice(start, end).map((row, index) => {
|
|
46
|
-
const absoluteIndex = start + index;
|
|
47
|
-
return inGroups ? (_jsx(ListItem, { isFocused: absoluteIndex === props.focusIndex, declareCursor: false, isSelected: row.provider === props.currentProvider, description: t('picker-group-count', { count: row.count }), showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < rows.length, onClick: onPick ? () => onPick(absoluteIndex) : undefined, children: row.label === RECENTS_LABEL_PLACEHOLDER && row.provider === RECENTS_GROUP_PROVIDER
|
|
48
|
-
? t('picker-group-recent')
|
|
49
|
-
: row.label }, row.provider)) : (_jsx(ListItem, { isFocused: absoluteIndex === props.focusIndex, declareCursor: false, isSelected: `${row.provider}/${row.id}` === props.currentModel, description: row.description, showScrollUp: absoluteIndex === start && start > 0, showScrollDown: absoluteIndex === end - 1 && end < rows.length, onClick: onPick ? () => onPick(absoluteIndex) : undefined, children: props.showProviderPrefix === true ? `${row.provider} / ${row.name}` : row.name }, `${row.provider}/${row.id}`));
|
|
50
|
-
})] }), _jsx(Text, { dimColor: true, italic: true, children: _jsx(HintLine, { text: hint }) })] }));
|
|
11
|
+
const available = Math.max(1, height - 6);
|
|
12
|
+
const window = listWindow(rows.map(() => 1), focusIndex, available);
|
|
13
|
+
const focused = focusedModelRow(rows, focusIndex);
|
|
14
|
+
const route = focused ? `${focused.model.provider}/${focused.model.id}` : '';
|
|
15
|
+
const hint = switching ? t('model-switching', { name: focused?.model.name ?? '' }) : t('hint-model-dialog');
|
|
16
|
+
return (_jsxs(Box, { flexDirection: "column", width: "100%", height: height, paddingX: 1, overflow: "hidden", onWheel: (event) => {
|
|
17
|
+
event.stopImmediatePropagation();
|
|
18
|
+
onWheelStep(event.deltaY < 0 ? -1 : 1);
|
|
19
|
+
}, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsx(Text, { bold: true, children: t('picker-title-model') }), _jsx(Text, { dimColor: true, children: "esc" })] }), _jsx(SearchBox, { query: query, cursorOffset: cursor, isFocused: true, isTerminalFocused: terminalFocused, borderless: true, placeholder: t('picker-model-search') }), _jsx(Text, { dimColor: true, children: t('picker-model-results', { count: rows.filter(row => row.kind === 'model').length }) }), rows.length === 0 && _jsx(Text, { dimColor: true, children: t(status === 'loading' ? 'model-loading' : status === 'error' ? 'picker-model-error' : query.trim() ? 'picker-model-no-match' : 'picker-model-empty') }), rows.slice(window.start, window.end).map((row, offset) => row.kind === 'heading'
|
|
20
|
+
? _jsx(Text, { color: "warning", bold: true, wrap: "truncate-end", children: row.key === 'heading:favorites' ? t('picker-model-favorites') : row.key === 'heading:recent' ? t('picker-group-recent') : row.label }, row.key)
|
|
21
|
+
: _jsx(Box, { width: "100%", height: 1, overflow: "hidden", backgroundColor: window.start + offset === focusIndex ? 'userMessageBackgroundHover' : undefined, children: _jsxs(ListItem, { isFocused: window.start + offset === focusIndex, isSelected: `${row.model.provider}/${row.model.id}` === currentModel, declareCursor: false, onClick: () => onPick(window.start + offset), children: [favorites.some(ref => ref.provider === row.model.provider && ref.id === row.model.id) ? '★ ' : '', row.model.name, _jsx(Text, { dimColor: true, children: ` ${row.providerName}` })] }) }, row.key)), _jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: route }), _jsx(Text, { dimColor: true, wrap: "truncate-end", children: hint })] }));
|
|
51
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ListItem.d.ts","sourceRoot":"","sources":["../../../../src/components/design-system/ListItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAGjE,MAAM,MAAM,aAAa,GAAG;IAC1B;qDACiD;IACjD,SAAS,EAAE,OAAO,CAAA;IAClB;uDACmD;IACnD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,4CAA4C;IAC5C,QAAQ,EAAE,SAAS,CAAA;IACnB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CACtC,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACvB,SAAS,EACT,UAAkB,EAClB,QAAQ,EACR,WAAW,EACX,cAAc,EACd,YAAY,EACZ,MAAa,EACb,QAAgB,EAChB,aAAa,EACb,OAAO,GACR,EAAE,aAAa,GAAG,KAAK,CAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"ListItem.d.ts","sourceRoot":"","sources":["../../../../src/components/design-system/ListItem.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAGvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAGjE,MAAM,MAAM,aAAa,GAAG;IAC1B;qDACiD;IACjD,SAAS,EAAE,OAAO,CAAA;IAClB;uDACmD;IACnD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,4CAA4C;IAC5C,QAAQ,EAAE,SAAS,CAAA;IACnB,kEAAkE;IAClE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;CACtC,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EACvB,SAAS,EACT,UAAkB,EAClB,QAAQ,EACR,WAAW,EACX,cAAc,EACd,YAAY,EACZ,MAAa,EACb,QAAgB,EAChB,aAAa,EACb,OAAO,GACR,EAAE,aAAa,GAAG,KAAK,CAAC,SAAS,CAmFjC"}
|
|
@@ -58,7 +58,7 @@ export function ListItem({ isFocused, isSelected = false, children, description,
|
|
|
58
58
|
// 非 Fragment 的 element(如调用方自己的 <Text>)保留原样——其子树高度
|
|
59
59
|
// 由调用方负责。
|
|
60
60
|
const flatChildren = flattenDeep(children);
|
|
61
|
-
return (_jsxs(Box, { ref: cursorRef, flexDirection: "column", onClick: clickable ? onClick : undefined, onMouseEnter: clickable ? () => setHovered(true) : undefined, onMouseLeave: clickable ? () => setHovered(false) : undefined, backgroundColor: clickable && hovered ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { flexDirection: "row", gap: 1, height: 1, flexShrink: 0, overflow: "hidden", width: "100%", children: [renderIndicator(), styled ? (_jsx(Text, { color: getTextColor(), dimColor: disabled, wrap: "truncate-end", children: flatChildren })) : (flatChildren), isSelected && !disabled && _jsx(Text, { color: "success", children: TICK })] }), description && (_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: "inactive", wrap: "truncate", children: flattenLine(description) }) }))] }));
|
|
61
|
+
return (_jsxs(Box, { ref: cursorRef, flexDirection: "column", onClick: clickable ? onClick : undefined, onMouseEnter: clickable ? () => setHovered(true) : undefined, onMouseLeave: clickable ? () => setHovered(false) : undefined, backgroundColor: clickable && hovered ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { flexDirection: "row", gap: 1, height: 1, flexShrink: 0, overflow: "hidden", width: "100%", paddingRight: isSelected && !disabled ? 2 : 0, children: [renderIndicator(), styled ? (_jsx(Text, { color: getTextColor(), dimColor: disabled, wrap: "truncate-end", children: flatChildren })) : (flatChildren), isSelected && !disabled && _jsx(Box, { position: "absolute", right: 1, width: 1, children: _jsx(Text, { color: "success", children: TICK }) })] }), description && (_jsx(Box, { paddingLeft: 2, children: _jsx(Text, { color: "inactive", wrap: "truncate", children: flattenLine(description) }) }))] }));
|
|
62
62
|
}
|
|
63
63
|
/** 单行化:内嵌换行折叠为空格(行尾/行首换行随之消除)。 */
|
|
64
64
|
function flattenLine(s) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-completions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/command-completions.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAI1G,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,KAAK,EAAE,MAAM,YAAY,CAAA;IACzB,SAAS,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAA;IACjD,YAAY,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,qBAAqB,EAAE,CAAA;KAAE,CAAA;IACtF,iBAAiB,IAAI,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACnG,KAAK,EAAE;QACL,cAAc,IAAI,IAAI,CAAA;QACtB,UAAU,IAAI,SAAS,qBAAqB,EAAE,CAAA;QAC9C,iBAAiB,IAAI,IAAI,CAAA;QACzB,aAAa,IAAI,SAAS;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,OAAO,CAAA;SAAE,EAAE,CAAA;QACpG,gBAAgB,IAAI,IAAI,CAAA;KACzB,CAAA;CACF,IAES,OAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"command-completions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/command-completions.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8C,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAI1G,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAExD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,wFAAwF;AACxF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,KAAK,EAAE,MAAM,YAAY,CAAA;IACzB,SAAS,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAA;IACjD,YAAY,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,qBAAqB,EAAE,CAAA;KAAE,CAAA;IACtF,iBAAiB,IAAI,SAAS;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACnG,KAAK,EAAE;QACL,cAAc,IAAI,IAAI,CAAA;QACtB,UAAU,IAAI,SAAS,qBAAqB,EAAE,CAAA;QAC9C,iBAAiB,IAAI,IAAI,CAAA;QACzB,aAAa,IAAI,SAAS;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,OAAO,CAAA;SAAE,EAAE,CAAA;QACpG,gBAAgB,IAAI,IAAI,CAAA;KACzB,CAAA;CACF,IAES,OAAO,MAAM,qDAmFtB"}
|
|
@@ -9,10 +9,19 @@ export function createCommandCompletions(deps) {
|
|
|
9
9
|
const { themeHost, commandTrees, workspaceCommands, model } = deps;
|
|
10
10
|
return (input) => {
|
|
11
11
|
const state = deps.state();
|
|
12
|
+
// Model arguments are search text, not a hierarchy of route completions.
|
|
13
|
+
// Keep the original line so Enter, Tab and click cannot select the first model.
|
|
14
|
+
if (/^\/model(?:[\t ]|$)/iu.test(input) && !/[\r\n]/u.test(input)) {
|
|
15
|
+
return [{
|
|
16
|
+
name: input.slice(1),
|
|
17
|
+
description: 'Open model picker',
|
|
18
|
+
descriptionKey: 'sugg-model-open-desc',
|
|
19
|
+
replacement: input,
|
|
20
|
+
commandLine: input,
|
|
21
|
+
}];
|
|
22
|
+
}
|
|
12
23
|
const head = input.slice(1).split(/[\t ]/)[0]?.toLowerCase() ?? '';
|
|
13
24
|
if (head !== '') {
|
|
14
|
-
if ('model'.startsWith(head))
|
|
15
|
-
model.warmModelNodes();
|
|
16
25
|
if ('preset'.startsWith(head))
|
|
17
26
|
model.warmPresetOptions();
|
|
18
27
|
if ('effort'.startsWith(head))
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/** Primary validated upstream line (newest). */
|
|
2
|
-
export declare const UPSTREAM_VALIDATED_VERSION = "0.
|
|
2
|
+
export declare const UPSTREAM_VALIDATED_VERSION = "0.2.0-rc.2";
|
|
3
3
|
/**
|
|
4
4
|
* Explicitly supported upstream prerelease lines, oldest first.
|
|
5
5
|
*
|
|
6
|
-
* 0.
|
|
6
|
+
* 0.2.0-rc.2 = primary continuous-CI line; 0.2.0-rc.1 = previous primary;
|
|
7
|
+
* 0.1.7-rc.2/rc.1 = previous
|
|
7
8
|
* build (mapped compatibility); 0.1.5-rc.1/alpha.2/alpha.1 = mapped
|
|
8
9
|
* compatibility lines (source-checked when the primary line moves);
|
|
9
10
|
* 0.1.3-alpha.2 = compatibility line (the only 0.1.3 build on npm);
|
|
@@ -16,7 +17,7 @@ export declare const UPSTREAM_VALIDATED_VERSION = "0.1.7-rc.2";
|
|
|
16
17
|
* The peer range in package.json is deliberately wider than this list: an
|
|
17
18
|
* install on an older or newer line is allowed but reports drift at boot.
|
|
18
19
|
*/
|
|
19
|
-
export declare const UPSTREAM_VALIDATED_VERSIONS: readonly ["0.1.0-rc.6", "0.1.0-rc.7", "0.1.0-rc.8", "0.1.1-rc.1", "0.1.1-rc.2", "0.1.2-alpha.3", "0.1.2-alpha.4", "0.1.2-alpha.5", "0.1.2-rc.1", "0.1.3-alpha.2", "0.1.5-alpha.1", "0.1.5-alpha.2", "0.1.5-rc.1", "0.1.7-rc.1", "0.1.7-rc.2"];
|
|
20
|
+
export declare const UPSTREAM_VALIDATED_VERSIONS: readonly ["0.1.0-rc.6", "0.1.0-rc.7", "0.1.0-rc.8", "0.1.1-rc.1", "0.1.1-rc.2", "0.1.2-alpha.3", "0.1.2-alpha.4", "0.1.2-alpha.5", "0.1.2-rc.1", "0.1.3-alpha.2", "0.1.5-alpha.1", "0.1.5-alpha.2", "0.1.5-rc.1", "0.1.7-rc.1", "0.1.7-rc.2", "0.2.0-rc.1", "0.2.0-rc.2"];
|
|
20
21
|
/**
|
|
21
22
|
* Framework packages version on their own lines; the contract validates
|
|
22
23
|
* their MAJOR (breaking surface), not the harness prerelease number.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/contract.ts"],"names":[],"mappings":"AAkBA,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,eAAe,CAAA;AAEtD
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/contract.ts"],"names":[],"mappings":"AAkBA,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,eAAe,CAAA;AAEtD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,2BAA2B,2QAkB9B,CAAA;AAEV;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAG5D,CAAA;AAED,qEAAqE;AACrE,eAAO,MAAM,yBAAyB,o8BA+B5B,CAAA;AAEV,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;CAClB;AAUD,4EAA4E;AAC5E,MAAM,MAAM,yBAAyB,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,CAAA;AAE/D,4FAA4F;AAC5F,MAAM,MAAM,oBAAoB,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,yBAAyB,EAAE,MAAM,CAAC,CAAA;AAEvG,wEAAwE;AACxE,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,oBAAoB,GAAG,SAAS,CAKlG;AAED,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,CAAC,EAAE,oBAAoB,EAAE,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAQxF;AAkBD,sEAAsE;AACtE,eAAO,MAAM,wBAAwB,QAA6D,CAAA;AAYlG,wBAAgB,yBAAyB,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAqB9E;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,oBAAoB,GAAG,SAAS,CAE9F;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAKnF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,iBAAiB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAA+B,GAC5F,MAAM,EAAE,CAQV;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,iBAAiB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAA+B,GAC5F,kBAAkB,EAAE,CAuBtB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAA;AAEtE,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,iBAAiB,CAAA;IACvB,sFAAsF;IACtF,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,iBAAiB,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAA+B,GAC5F,oBAAoB,GAAG,SAAS,CA0BlC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Upstream compatibility contract.
|
|
3
3
|
*
|
|
4
4
|
* The TUI is validated against a set of upstream prerelease lines — the
|
|
5
|
-
* current primary (0.
|
|
5
|
+
* current primary (0.2.0-rc.2) plus older lines kept in backward
|
|
6
6
|
* compatibility across the 0.1.5, 0.1.3, 0.1.2, 0.1.1 and 0.1.0 release
|
|
7
7
|
* families.
|
|
8
8
|
* Every official package this adapter touches is blessed here; anything
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
import { readFileSync } from 'node:fs';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
/** Primary validated upstream line (newest). */
|
|
19
|
-
export const UPSTREAM_VALIDATED_VERSION = '0.
|
|
19
|
+
export const UPSTREAM_VALIDATED_VERSION = '0.2.0-rc.2';
|
|
20
20
|
/**
|
|
21
21
|
* Explicitly supported upstream prerelease lines, oldest first.
|
|
22
22
|
*
|
|
23
|
-
* 0.
|
|
23
|
+
* 0.2.0-rc.2 = primary continuous-CI line; 0.2.0-rc.1 = previous primary;
|
|
24
|
+
* 0.1.7-rc.2/rc.1 = previous
|
|
24
25
|
* build (mapped compatibility); 0.1.5-rc.1/alpha.2/alpha.1 = mapped
|
|
25
26
|
* compatibility lines (source-checked when the primary line moves);
|
|
26
27
|
* 0.1.3-alpha.2 = compatibility line (the only 0.1.3 build on npm);
|
|
@@ -49,6 +50,8 @@ export const UPSTREAM_VALIDATED_VERSIONS = [
|
|
|
49
50
|
'0.1.5-rc.1',
|
|
50
51
|
'0.1.7-rc.1',
|
|
51
52
|
'0.1.7-rc.2',
|
|
53
|
+
'0.2.0-rc.1',
|
|
54
|
+
'0.2.0-rc.2',
|
|
52
55
|
];
|
|
53
56
|
/**
|
|
54
57
|
* Framework packages version on their own lines; the contract validates
|
package/lib/types/i18n.d.ts
CHANGED
|
@@ -874,14 +874,6 @@ declare const dict: {
|
|
|
874
874
|
readonly zh: "模型已切换,但工作区挂载失败 · {{err}}";
|
|
875
875
|
readonly en: "Model switched, but workspace attachment failed · {{err}}";
|
|
876
876
|
};
|
|
877
|
-
readonly 'model-usage': {
|
|
878
|
-
readonly zh: "用法:/model <provider/model>(如 deepseek/deepseek-flash)";
|
|
879
|
-
readonly en: "Usage: /model <provider/model> (e.g. deepseek/deepseek-flash)";
|
|
880
|
-
};
|
|
881
|
-
readonly 'model-unknown': {
|
|
882
|
-
readonly zh: "未知模型「{{spec}}」· /model 查看全部";
|
|
883
|
-
readonly en: "Unknown model \"{{spec}}\" · /model to view all";
|
|
884
|
-
};
|
|
885
877
|
readonly 'compact-unavailable': {
|
|
886
878
|
readonly zh: "压缩不可用——当前 leaf 没有压缩服务";
|
|
887
879
|
readonly en: "Compaction unavailable · no compaction service in this leaf";
|
|
@@ -2880,14 +2872,26 @@ declare const dict: {
|
|
|
2880
2872
|
readonly zh: "最近使用";
|
|
2881
2873
|
readonly en: "Recently used";
|
|
2882
2874
|
};
|
|
2883
|
-
readonly 'picker-group-count': {
|
|
2884
|
-
readonly zh: "{{count}} 个模型";
|
|
2885
|
-
readonly en: "{{count}} models";
|
|
2886
|
-
};
|
|
2887
2875
|
readonly 'picker-model-search': {
|
|
2888
2876
|
readonly zh: "输入关键词筛选模型";
|
|
2889
2877
|
readonly en: "Type keywords to filter models";
|
|
2890
2878
|
};
|
|
2879
|
+
readonly 'picker-model-favorites': {
|
|
2880
|
+
readonly zh: "收藏";
|
|
2881
|
+
readonly en: "Favorites";
|
|
2882
|
+
};
|
|
2883
|
+
readonly 'picker-model-favorite-save-error': {
|
|
2884
|
+
readonly zh: "无法保存模型收藏;本次会话仍可使用";
|
|
2885
|
+
readonly en: "Could not save model favorites; this session keeps the change";
|
|
2886
|
+
};
|
|
2887
|
+
readonly 'sugg-model-open-desc': {
|
|
2888
|
+
readonly zh: "打开模型选择界面";
|
|
2889
|
+
readonly en: "Open model picker";
|
|
2890
|
+
};
|
|
2891
|
+
readonly 'hint-model-dialog': {
|
|
2892
|
+
readonly zh: "Enter 切换 · Ctrl+F 收藏 · Ctrl+A 添加提供方 · Ctrl+R 重试 · Esc 关闭";
|
|
2893
|
+
readonly en: "Enter switch · Ctrl+F favorite · Ctrl+A connect · Ctrl+R retry · Esc close";
|
|
2894
|
+
};
|
|
2891
2895
|
readonly 'picker-model-results': {
|
|
2892
2896
|
readonly zh: "{{count}} 个结果";
|
|
2893
2897
|
readonly en: "{{count}} results";
|
|
@@ -2901,20 +2905,8 @@ declare const dict: {
|
|
|
2901
2905
|
readonly en: "No matching models";
|
|
2902
2906
|
};
|
|
2903
2907
|
readonly 'picker-model-error': {
|
|
2904
|
-
readonly zh: "
|
|
2905
|
-
readonly en: "Could not load models;
|
|
2906
|
-
};
|
|
2907
|
-
readonly 'hint-model-groups': {
|
|
2908
|
-
readonly zh: "**Enter** 查看模型 · **Tab** 全部模型 · Esc 退出";
|
|
2909
|
-
readonly en: "**Enter** to view models · **Tab** all models · Esc to exit";
|
|
2910
|
-
};
|
|
2911
|
-
readonly 'hint-model-all': {
|
|
2912
|
-
readonly zh: "**Enter** 切换模型 · **Tab** 提供方分组 · Esc 退出";
|
|
2913
|
-
readonly en: "**Enter** to switch · **Tab** provider groups · Esc to exit";
|
|
2914
|
-
};
|
|
2915
|
-
readonly 'hint-model-back': {
|
|
2916
|
-
readonly zh: "**Enter** 切换模型 · Esc/⌫ 返回上级";
|
|
2917
|
-
readonly en: "**Enter** to switch · Esc/⌫ to go back";
|
|
2908
|
+
readonly zh: "模型列表获取失败,按 Ctrl+R 重试";
|
|
2909
|
+
readonly en: "Could not load models; press Ctrl+R to retry";
|
|
2918
2910
|
};
|
|
2919
2911
|
readonly 'picker-title-skills': {
|
|
2920
2912
|
readonly zh: "技能";
|
package/lib/types/i18n.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAI9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAE1C;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,QAAA,MAAM,IAAI
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAI9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAE1C;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,QAAA,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
|
package/lib/types/i18n.js
CHANGED
|
@@ -280,8 +280,6 @@ const dict = {
|
|
|
280
280
|
'model-switch-fork-failed': { zh: '无法切换模型 · {{err}}', en: 'Cannot switch models · {{err}}' },
|
|
281
281
|
'model-switch-failed': { zh: '模型切换失败 · {{err}}', en: 'Model switch failed · {{err}}' },
|
|
282
282
|
'model-switch-attach-failed': { zh: '模型已切换,但工作区挂载失败 · {{err}}', en: 'Model switched, but workspace attachment failed · {{err}}' },
|
|
283
|
-
'model-usage': { zh: '用法:/model <provider/model>(如 deepseek/deepseek-flash)', en: 'Usage: /model <provider/model> (e.g. deepseek/deepseek-flash)' },
|
|
284
|
-
'model-unknown': { zh: '未知模型「{{spec}}」· /model 查看全部', en: 'Unknown model "{{spec}}" · /model to view all' },
|
|
285
283
|
'compact-unavailable': { zh: '压缩不可用——当前 leaf 没有压缩服务', en: 'Compaction unavailable · no compaction service in this leaf' },
|
|
286
284
|
'compact-while-working': { zh: '回合运行中,无法压缩会话', en: 'Cannot compact while a turn is running' },
|
|
287
285
|
'compact-working': { zh: '正在压缩会话…', en: 'Summarizing earlier turns…' },
|
|
@@ -899,15 +897,15 @@ const dict = {
|
|
|
899
897
|
// ── components/ModelPicker.tsx / ThemePicker.tsx / ActivityPicker.tsx / EffortSlider.tsx ──
|
|
900
898
|
'picker-title-model': { zh: '模型', en: 'Model' },
|
|
901
899
|
'picker-group-recent': { zh: '最近使用', en: 'Recently used' },
|
|
902
|
-
'picker-group-count': { zh: '{{count}} 个模型', en: '{{count}} models' },
|
|
903
900
|
'picker-model-search': { zh: '输入关键词筛选模型', en: 'Type keywords to filter models' },
|
|
901
|
+
'picker-model-favorites': { zh: '收藏', en: 'Favorites' },
|
|
902
|
+
'picker-model-favorite-save-error': { zh: '无法保存模型收藏;本次会话仍可使用', en: 'Could not save model favorites; this session keeps the change' },
|
|
903
|
+
'sugg-model-open-desc': { zh: '打开模型选择界面', en: 'Open model picker' },
|
|
904
|
+
'hint-model-dialog': { zh: 'Enter 切换 · Ctrl+F 收藏 · Ctrl+A 添加提供方 · Ctrl+R 重试 · Esc 关闭', en: 'Enter switch · Ctrl+F favorite · Ctrl+A connect · Ctrl+R retry · Esc close' },
|
|
904
905
|
'picker-model-results': { zh: '{{count}} 个结果', en: '{{count}} results' },
|
|
905
906
|
'picker-model-empty': { zh: '没有可用模型', en: 'No models available' },
|
|
906
907
|
'picker-model-no-match': { zh: '没有匹配的模型', en: 'No matching models' },
|
|
907
|
-
'picker-model-error': { zh: '
|
|
908
|
-
'hint-model-groups': { zh: '**Enter** 查看模型 · **Tab** 全部模型 · Esc 退出', en: '**Enter** to view models · **Tab** all models · Esc to exit' },
|
|
909
|
-
'hint-model-all': { zh: '**Enter** 切换模型 · **Tab** 提供方分组 · Esc 退出', en: '**Enter** to switch · **Tab** provider groups · Esc to exit' },
|
|
910
|
-
'hint-model-back': { zh: '**Enter** 切换模型 · Esc/⌫ 返回上级', en: '**Enter** to switch · Esc/⌫ to go back' },
|
|
908
|
+
'picker-model-error': { zh: '模型列表获取失败,按 Ctrl+R 重试', en: 'Could not load models; press Ctrl+R to retry' },
|
|
911
909
|
'picker-title-skills': { zh: '技能', en: 'Skills' },
|
|
912
910
|
'skills-loading': { zh: '正在加载技能', en: 'Loading skills' },
|
|
913
911
|
'skills-loading-subtitle': { zh: '正在查询技能注册表…', en: 'Querying the skill registry…' },
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ModelRef } from './modelGroups.js';
|
|
2
|
+
export declare function parseModelFavorites(text: string): readonly ModelRef[];
|
|
3
|
+
export declare function readModelFavorites(dir?: string): readonly ModelRef[];
|
|
4
|
+
export declare function toggleModelFavorite(current: readonly ModelRef[], ref: ModelRef, dir?: string): {
|
|
5
|
+
favorites: readonly ModelRef[];
|
|
6
|
+
saved: boolean;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=modelFavorites.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelFavorites.d.ts","sourceRoot":"","sources":["../../src/modelFavorites.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEhD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,QAAQ,EAAE,CAgBrE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAiB,GAAG,SAAS,QAAQ,EAAE,CAG9E;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,GAAG,EAAE,QAAQ,EACb,GAAG,GAAE,MAAiB,GACrB;IAAE,SAAS,EAAE,SAAS,QAAQ,EAAE,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAepD"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** Persisted model favorites. Catalog availability never deletes a saved ref. */
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { DATA_DIR } from './utils/paths.js';
|
|
6
|
+
export function parseModelFavorites(text) {
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = JSON.parse(text);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
15
|
+
return [];
|
|
16
|
+
const models = parsed.models;
|
|
17
|
+
if (!Array.isArray(models))
|
|
18
|
+
return [];
|
|
19
|
+
const result = [];
|
|
20
|
+
for (const value of models) {
|
|
21
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
22
|
+
continue;
|
|
23
|
+
const item = value;
|
|
24
|
+
if (typeof item.provider !== 'string' || !item.provider || typeof item.id !== 'string' || !item.id)
|
|
25
|
+
continue;
|
|
26
|
+
if (!result.some(ref => ref.provider === item.provider && ref.id === item.id)) {
|
|
27
|
+
result.push({ provider: item.provider, id: item.id });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
export function readModelFavorites(dir = DATA_DIR) {
|
|
33
|
+
try {
|
|
34
|
+
return parseModelFavorites(readFileSync(join(dir, 'model-favorites.json'), 'utf8'));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function toggleModelFavorite(current, ref, dir = DATA_DIR) {
|
|
41
|
+
const exists = current.some(item => item.provider === ref.provider && item.id === ref.id);
|
|
42
|
+
const favorites = exists
|
|
43
|
+
? current.filter(item => item.provider !== ref.provider || item.id !== ref.id)
|
|
44
|
+
: [...current, ref];
|
|
45
|
+
const temporary = join(dir, `model-favorites.${randomUUID()}.tmp`);
|
|
46
|
+
try {
|
|
47
|
+
mkdirSync(dir, { recursive: true });
|
|
48
|
+
writeFileSync(temporary, `${JSON.stringify({ models: favorites }, null, 2)}\n`, { flag: 'wx' });
|
|
49
|
+
renameSync(temporary, join(dir, 'model-favorites.json'));
|
|
50
|
+
return { favorites, saved: true };
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
try {
|
|
54
|
+
unlinkSync(temporary);
|
|
55
|
+
}
|
|
56
|
+
catch { /* no temporary file */ }
|
|
57
|
+
return { favorites, saved: false };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* `scripts/verify-model-picker-groups.mjs` can drive it headless; the
|
|
6
|
-
* recents row's localized label is resolved at render time (its `label`
|
|
7
|
-
* field is the {@link RECENTS_LABEL_PLACEHOLDER} sentinel).
|
|
2
|
+
* Shared literal model filtering and the retained group-derivation helpers.
|
|
3
|
+
* The centered picker builds its display rows in modelPickerRows.ts;
|
|
4
|
+
* older group consumers and their regression tests still use the helpers here.
|
|
8
5
|
*
|
|
9
6
|
* @module dsh-tui/modelGroups
|
|
10
7
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modelGroups.d.ts","sourceRoot":"","sources":["../../src/modelGroups.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"modelGroups.d.ts","sourceRoot":"","sources":["../../src/modelGroups.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEpF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,gBAAgB,CAAA;AAEnD,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,eAAe,CAAA;AAErD,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,kFAAkF;AAClF,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,eAAe,EAAE,GACpC,SAAS,YAAY,EAAE,CAQzB;AAED,sDAAsD;AACtD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GAAG,SAAS,GAC/B,MAAM,CAGR;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,MAAM,EAAE,SAAS,YAAY,EAAE,GAC9B,SAAS,YAAY,EAAE,CAUzB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,aAAa,EAAE,SAAS,eAAe,EAAE,EACzC,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,GAC5B,SAAS,aAAa,EAAE,CAsB1B;AAED,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,kDAAkD;IAClD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,YAAY,EAAE,EAC/B,eAAe,EAAE,MAAM,GAAG,SAAS,EACnC,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,OAAO,CAAC,EAAE,SAAS,QAAQ,EAAE,GAC5B,kBAAkB,CAiBpB"}
|