@xiaoso/dsh-tool-plus 0.1.8-beta.3 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +4 -7
- package/README.md +123 -126
- package/lib/client.js +461 -151
- package/lib/index.mjs +12 -12
- package/lib/{scrapers-DU4u4-Co.mjs → scrapers-D1uU6YiZ.mjs} +1 -1
- package/lib/types/client/ToolPlusSection.d.ts +3 -2
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/config/fields.d.ts +1 -1
- package/lib/types/web/client/ToolCardShell.d.ts +15 -5
- package/lib/types/web/client/labels.d.ts +1 -1
- package/lib/types/web/client/row-utils.d.ts +28 -8
- package/lib/types/web/client/rows/card-leading.d.ts +37 -0
- package/lib/types/web/client/rows/fallback-card-facts.d.ts +52 -0
- package/lib/{utils-C-Lo_JC6.mjs → utils-CdLvJy_P.mjs} +1 -1
- package/package.json +151 -151
- package/presets/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -12,6 +12,7 @@ window.__ModuleLoader__.load({
|
|
|
12
12
|
//#region src/config/fields.ts
|
|
13
13
|
/** Field group ids -> locale key of the group heading. */
|
|
14
14
|
const TOOL_PLUS_GROUP_LABELS = {
|
|
15
|
+
cards: "groupCards",
|
|
15
16
|
timing: "groupTiming",
|
|
16
17
|
output: "groupOutput",
|
|
17
18
|
truncation: "groupTruncation",
|
|
@@ -32,6 +33,15 @@ window.__ModuleLoader__.load({
|
|
|
32
33
|
* single source; settings.ts reads from here.
|
|
33
34
|
*/
|
|
34
35
|
const TOOL_PLUS_FIELDS = [
|
|
36
|
+
{
|
|
37
|
+
name: "webCards",
|
|
38
|
+
kind: "boolean",
|
|
39
|
+
default: true,
|
|
40
|
+
labelKey: "webCards",
|
|
41
|
+
hintKey: "webCardsHint",
|
|
42
|
+
group: "cards",
|
|
43
|
+
tool: "web"
|
|
44
|
+
},
|
|
35
45
|
{
|
|
36
46
|
name: "enableRunInBackground",
|
|
37
47
|
kind: "boolean",
|
|
@@ -95,15 +105,6 @@ window.__ModuleLoader__.load({
|
|
|
95
105
|
group: "behavior",
|
|
96
106
|
tool: "bash"
|
|
97
107
|
},
|
|
98
|
-
{
|
|
99
|
-
name: "webCards",
|
|
100
|
-
kind: "boolean",
|
|
101
|
-
default: true,
|
|
102
|
-
labelKey: "webCards",
|
|
103
|
-
hintKey: "webCardsHint",
|
|
104
|
-
group: "behavior",
|
|
105
|
-
tool: "bash"
|
|
106
|
-
},
|
|
107
108
|
{
|
|
108
109
|
name: "autoBackgroundMs",
|
|
109
110
|
kind: "number",
|
|
@@ -740,6 +741,11 @@ window.__ModuleLoader__.load({
|
|
|
740
741
|
];
|
|
741
742
|
/** Tool tabs in nav order; empty `fields` = no configurable settings. */
|
|
742
743
|
const TOOL_PLUS_TABS = [
|
|
744
|
+
{
|
|
745
|
+
id: "web",
|
|
746
|
+
labelKey: "tabWeb",
|
|
747
|
+
fields: TOOL_PLUS_FIELDS.filter((f) => f.tool === "web").map((f) => f.name)
|
|
748
|
+
},
|
|
743
749
|
{
|
|
744
750
|
id: "bash",
|
|
745
751
|
labelKey: "tabBash",
|
|
@@ -2105,8 +2111,9 @@ window.__ModuleLoader__.load({
|
|
|
2105
2111
|
/**
|
|
2106
2112
|
* Tool Plus settings section — the plugin's own page in the Settings panel,
|
|
2107
2113
|
* registered into the `settings.section` slot (nav id `tool-plus`). The page
|
|
2108
|
-
* renders one tab per tool (Bash / Read / Write & Edit / Grep /
|
|
2109
|
-
* Ast
|
|
2114
|
+
* renders one tab per tool (Bash / Read / Write & Edit / Grep / Glob /
|
|
2115
|
+
* Ast Grep / Ast Edit) plus a leading `Web 卡片` tab for the plugin-wide
|
|
2116
|
+
* browser-card switch; switching a tab shows that tool's configurable
|
|
2110
2117
|
* fields, while tools without global settings show a placeholder. Only the
|
|
2111
2118
|
* active tool's panel is rendered; the shared staged form over the
|
|
2112
2119
|
* `tool-plus` namespace lives at the page level, so drafts survive switching
|
|
@@ -2637,6 +2644,7 @@ window.__ModuleLoader__.load({
|
|
|
2637
2644
|
nav: "Tool Plus",
|
|
2638
2645
|
pageTitle: "Tool Plus 工具配置",
|
|
2639
2646
|
pageDescription: "按工具切换,调整每个工具的全局配置;保存后对新调用生效。",
|
|
2647
|
+
tabWeb: "Web 卡片",
|
|
2640
2648
|
tabBash: "Bash",
|
|
2641
2649
|
tabRead: "Read",
|
|
2642
2650
|
tabWriteEdit: "Write & Edit",
|
|
@@ -2645,6 +2653,7 @@ window.__ModuleLoader__.load({
|
|
|
2645
2653
|
tabAstGrep: "Ast Grep",
|
|
2646
2654
|
tabAstEdit: "Ast Edit",
|
|
2647
2655
|
tabReadImage: "Read Image",
|
|
2656
|
+
groupCards: "卡片",
|
|
2648
2657
|
groupTiming: "超时与后台",
|
|
2649
2658
|
groupOutput: "输出",
|
|
2650
2659
|
groupTruncation: "完成消息截断",
|
|
@@ -2829,6 +2838,7 @@ window.__ModuleLoader__.load({
|
|
|
2829
2838
|
nav: "Tool Plus",
|
|
2830
2839
|
pageTitle: "Tool Plus tool configuration",
|
|
2831
2840
|
pageDescription: "Switch by tool to adjust its global settings; values apply …",
|
|
2841
|
+
tabWeb: "Web cards",
|
|
2832
2842
|
tabBash: "Bash",
|
|
2833
2843
|
tabRead: "Read",
|
|
2834
2844
|
tabWriteEdit: "Write & Edit",
|
|
@@ -2837,6 +2847,7 @@ window.__ModuleLoader__.load({
|
|
|
2837
2847
|
tabAstGrep: "Ast Grep",
|
|
2838
2848
|
tabAstEdit: "Ast Edit",
|
|
2839
2849
|
tabReadImage: "Read Image",
|
|
2850
|
+
groupCards: "Cards",
|
|
2840
2851
|
groupTiming: "Timeouts & backgrounding",
|
|
2841
2852
|
groupOutput: "Output",
|
|
2842
2853
|
groupTruncation: "Completion truncation",
|
|
@@ -3349,6 +3360,7 @@ window.__ModuleLoader__.load({
|
|
|
3349
3360
|
running: "运行中",
|
|
3350
3361
|
done: "完成",
|
|
3351
3362
|
failed: "失败",
|
|
3363
|
+
stopped: "已停止",
|
|
3352
3364
|
cancelled: "已取消",
|
|
3353
3365
|
timedOut: "已超时",
|
|
3354
3366
|
noOutput: "无输出",
|
|
@@ -3412,6 +3424,7 @@ window.__ModuleLoader__.load({
|
|
|
3412
3424
|
running: "Running",
|
|
3413
3425
|
done: "Done",
|
|
3414
3426
|
failed: "Failed",
|
|
3427
|
+
stopped: "Stopped",
|
|
3415
3428
|
cancelled: "Cancelled",
|
|
3416
3429
|
timedOut: "Timed out",
|
|
3417
3430
|
noOutput: "No output",
|
|
@@ -3470,7 +3483,7 @@ window.__ModuleLoader__.load({
|
|
|
3470
3483
|
running: "running",
|
|
3471
3484
|
ok: null,
|
|
3472
3485
|
error: "failed",
|
|
3473
|
-
warning: "
|
|
3486
|
+
warning: "stopped"
|
|
3474
3487
|
};
|
|
3475
3488
|
/** Parsed-argument cache: the node is immutable, so one parse serves every render. */
|
|
3476
3489
|
const parsedArgs = /* @__PURE__ */ new WeakMap();
|
|
@@ -3493,6 +3506,21 @@ window.__ModuleLoader__.load({
|
|
|
3493
3506
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3494
3507
|
}
|
|
3495
3508
|
/**
|
|
3509
|
+
* The call's original argument text.
|
|
3510
|
+
*
|
|
3511
|
+
* A running call carries it directly; a settled result carries the head
|
|
3512
|
+
* backfilled from the in-window `tool/call`, which is absent when window
|
|
3513
|
+
* truncation left that event outside. The text outlives the parse: a call whose
|
|
3514
|
+
* arguments are still streaming is not valid JSON yet, and the shell states its
|
|
3515
|
+
* first line rather than pretending the call said nothing.
|
|
3516
|
+
* @param block - the frozen running-or-settled call node.
|
|
3517
|
+
* @returns the argument text, or `''` when no head survives.
|
|
3518
|
+
*/
|
|
3519
|
+
function cardArgsRaw(block) {
|
|
3520
|
+
const raw = callHead(block)?.argsRaw;
|
|
3521
|
+
return typeof raw === "string" ? raw : "";
|
|
3522
|
+
}
|
|
3523
|
+
/**
|
|
3496
3524
|
* Parse the call's raw argument JSON.
|
|
3497
3525
|
*
|
|
3498
3526
|
* The parsed object is memoized per node (the node is immutable), so repeated
|
|
@@ -3521,30 +3549,26 @@ window.__ModuleLoader__.load({
|
|
|
3521
3549
|
/**
|
|
3522
3550
|
* Run state of one call.
|
|
3523
3551
|
*
|
|
3524
|
-
* A
|
|
3525
|
-
*
|
|
3526
|
-
*
|
|
3527
|
-
*
|
|
3528
|
-
*
|
|
3552
|
+
* A turn stopped mid-call is projected as a settled `isError` block carrying
|
|
3553
|
+
* `error.code === 'interrupted'`; the shipped row model reads that before
|
|
3554
|
+
* `isError`, so a stopped call is amber and named "stopped" rather than failed.
|
|
3555
|
+
* Any other settled `isError` result is an error. A foreground command that
|
|
3556
|
+
* timed out or was cancelled is amber rather than green (the shipped terminal
|
|
3557
|
+
* model reads only the trailing exit marker, which is exactly what the card
|
|
3558
|
+
* owner was told not to trust). A non-zero exit stays `ok`: bash reports a
|
|
3559
|
+
* failing command as result data, and the card's own status pill carries the
|
|
3560
|
+
* red.
|
|
3529
3561
|
* @param block - the frozen running-or-settled call node.
|
|
3530
3562
|
* @returns the card run state.
|
|
3531
3563
|
*/
|
|
3532
3564
|
function cardState(block) {
|
|
3533
3565
|
if (!isSettled(block)) return "running";
|
|
3566
|
+
if (isRecord$2(block.error) && block.error.code === "interrupted") return "warning";
|
|
3534
3567
|
if (block.isError === true) return "error";
|
|
3535
3568
|
const terminal = narrowTerminalCardMeta(cardMeta(block));
|
|
3536
3569
|
if (terminal !== null && terminal.mode === "foreground" && (terminal.timedOut || terminal.aborted)) return "warning";
|
|
3537
3570
|
return "ok";
|
|
3538
3571
|
}
|
|
3539
|
-
/** Map a card run state onto the primitive's dot semantics. */
|
|
3540
|
-
function dotState(state) {
|
|
3541
|
-
switch (state) {
|
|
3542
|
-
case "running": return "ongoing";
|
|
3543
|
-
case "error": return "error";
|
|
3544
|
-
case "warning": return "warning";
|
|
3545
|
-
default: return "done";
|
|
3546
|
-
}
|
|
3547
|
-
}
|
|
3548
3572
|
/**
|
|
3549
3573
|
* The assistive-technology status word for a run state, or `null` when the
|
|
3550
3574
|
* state is already carried by the row's content.
|
|
@@ -3788,15 +3812,339 @@ window.__ModuleLoader__.load({
|
|
|
3788
3812
|
};
|
|
3789
3813
|
}
|
|
3790
3814
|
//#endregion
|
|
3815
|
+
//#region src/web/client/rows/card-leading.ts
|
|
3816
|
+
/**
|
|
3817
|
+
* Wire tool name to glyph family: the shipped name table for the six keys this
|
|
3818
|
+
* plugin shadows, plus its own two tools.
|
|
3819
|
+
*/
|
|
3820
|
+
const GLYPH_BY_TOOL = {
|
|
3821
|
+
bash: "bash",
|
|
3822
|
+
read: "read",
|
|
3823
|
+
write: "edit",
|
|
3824
|
+
edit: "edit",
|
|
3825
|
+
grep: "search",
|
|
3826
|
+
glob: "search",
|
|
3827
|
+
ast_grep: "search",
|
|
3828
|
+
ast_edit: "edit"
|
|
3829
|
+
};
|
|
3830
|
+
/**
|
|
3831
|
+
* Glyph family a row leads with.
|
|
3832
|
+
* @param toolName - the call's wire tool name, when the node carries one.
|
|
3833
|
+
* @returns the family, or `others` (the shipped generic glyph) for an unknown name.
|
|
3834
|
+
*/
|
|
3835
|
+
function leadingGlyph(toolName) {
|
|
3836
|
+
if (typeof toolName !== "string") return "others";
|
|
3837
|
+
return GLYPH_BY_TOOL[toolName] ?? "others";
|
|
3838
|
+
}
|
|
3839
|
+
/**
|
|
3840
|
+
* Whether a run state keeps the row's glyph or replaces it with a state dot.
|
|
3841
|
+
* @param state - the card's run state.
|
|
3842
|
+
* @returns the leading kind; `error` and `warning` are the two dot states.
|
|
3843
|
+
*/
|
|
3844
|
+
function leadingKind(state) {
|
|
3845
|
+
switch (state) {
|
|
3846
|
+
case "error": return "error";
|
|
3847
|
+
case "warning": return "warning";
|
|
3848
|
+
default: return "glyph";
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
//#endregion
|
|
3852
|
+
//#region src/web/client/rows/command-card-facts.ts
|
|
3853
|
+
/**
|
|
3854
|
+
* Collapsed-fact builders for the two command-shaped cards: the command a
|
|
3855
|
+
* `bash` description hides, and the rewrite rule an `ast_edit` preview only
|
|
3856
|
+
* shows once expanded.
|
|
3857
|
+
*
|
|
3858
|
+
* Both cards collapse to one head line. `bash`'s head used to be the model's
|
|
3859
|
+
* own `description` — a sentence about intent, never the command that ran — so
|
|
3860
|
+
* the only way to learn what executed was to expand the card. `ast_edit`'s head
|
|
3861
|
+
* names the file and the replacement count, and its body previews the *result*:
|
|
3862
|
+
* the rule that produced that result lives only in the call's `ops` argument,
|
|
3863
|
+
* which no card showed at all. Each builder recovers one fact from the call's
|
|
3864
|
+
* own arguments, so nothing is inferred from output, and a call head that fell
|
|
3865
|
+
* outside the session window degrades to the facts the card already holds.
|
|
3866
|
+
* @module @xiaoso/dsh-tool-plus/web/client/rows/command-card-facts
|
|
3867
|
+
*/
|
|
3868
|
+
/** Whether `value` is a plain object — the only op shape the engine sends. */
|
|
3869
|
+
function isRecord$1(value) {
|
|
3870
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3871
|
+
}
|
|
3872
|
+
/**
|
|
3873
|
+
* The `bash` card's collapsed summary: what ran, placed after the sentence
|
|
3874
|
+
* saying why.
|
|
3875
|
+
*
|
|
3876
|
+
* The command is the fact the head must carry, so the description can never be
|
|
3877
|
+
* the whole summary — it is a prefix at most. Only its first line is used: a
|
|
3878
|
+
* summary is one row high, and the rest of a multi-line command belongs to the
|
|
3879
|
+
* expanded body, which draws the whole thing.
|
|
3880
|
+
* @param description - the model's one-line intent, or `null` when absent.
|
|
3881
|
+
* @param command - the command actually executed (never empty).
|
|
3882
|
+
* @param outcome - the interruption word (`timedOut` / `cancelled`), or `null`.
|
|
3883
|
+
* @returns the outcome alone when the run was interrupted; otherwise the
|
|
3884
|
+
* description joined to the command's first line, or that line alone.
|
|
3885
|
+
*/
|
|
3886
|
+
function bashCardSummary(description, command, outcome) {
|
|
3887
|
+
if (outcome !== null && outcome !== "") return outcome;
|
|
3888
|
+
const intent = description === null ? "" : description.trim();
|
|
3889
|
+
const line = firstLine(command);
|
|
3890
|
+
return intent === "" ? line : `${intent} · ${line}`;
|
|
3891
|
+
}
|
|
3892
|
+
/**
|
|
3893
|
+
* The `ast_edit` card's rule line: the first usable `ops` entry, written the
|
|
3894
|
+
* way the tool's own `ops` argument writes it (`pat → out`).
|
|
3895
|
+
*
|
|
3896
|
+
* Only the first rule is stated, with the count of the entries behind it: the
|
|
3897
|
+
* card exists to preview one change, and a call may carry many rules whose
|
|
3898
|
+
* patterns are whole AST fragments — listing them would bury the preview. The
|
|
3899
|
+
* remaining count is the array tail, not the number of *usable* entries, so it
|
|
3900
|
+
* always describes the call as it was made.
|
|
3901
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
3902
|
+
* @param t - this plugin's card translate seat.
|
|
3903
|
+
* @returns the rule line, or `null` when the call carries no usable rule.
|
|
3904
|
+
*/
|
|
3905
|
+
function astEditRuleLine(args, t) {
|
|
3906
|
+
const ops = args?.ops;
|
|
3907
|
+
if (!Array.isArray(ops)) return null;
|
|
3908
|
+
for (let index = 0; index < ops.length; index += 1) {
|
|
3909
|
+
const op = ops[index];
|
|
3910
|
+
if (!isRecord$1(op)) continue;
|
|
3911
|
+
const pat = argText(op, "pat");
|
|
3912
|
+
if (pat === null) continue;
|
|
3913
|
+
const replacement = op.out;
|
|
3914
|
+
if (typeof replacement !== "string") continue;
|
|
3915
|
+
const rest = ops.length - 1;
|
|
3916
|
+
const rule = `${t("astEdit.rules")}: ${pat} →${replacement === "" ? "" : ` ${replacement}`}`;
|
|
3917
|
+
return rest === 0 ? rule : `${rule} · ${t("astEdit.rulesRest", { count: rest })}`;
|
|
3918
|
+
}
|
|
3919
|
+
return null;
|
|
3920
|
+
}
|
|
3921
|
+
//#endregion
|
|
3922
|
+
//#region src/web/client/rows/read-card-facts.ts
|
|
3923
|
+
/**
|
|
3924
|
+
* Collapsed-summary facts for the `read` card.
|
|
3925
|
+
*
|
|
3926
|
+
* The Host projection strips the inline selector off the path
|
|
3927
|
+
* (`stripReadSelector` in `web/host/read.ts`) because the card's *link* must
|
|
3928
|
+
* point at a real file, and it keeps only the file's own window. The collapsed
|
|
3929
|
+
* head is then the one place where the question the model actually asked is
|
|
3930
|
+
* still readable — and the projected path cannot answer it: a windowed read of
|
|
3931
|
+
* `package.json:22-25,31-34` and a whole-file read of `package.json` project to
|
|
3932
|
+
* the very same string, so the card hides the difference.
|
|
3933
|
+
*
|
|
3934
|
+
* The summary is therefore built from the call's own argument, selector and
|
|
3935
|
+
* all, with the projected path kept as the fallback for a result whose call
|
|
3936
|
+
* head fell outside the session window (a truncated window carries no
|
|
3937
|
+
* `tool/call` event, so no argument survives). That fallback states a fact the
|
|
3938
|
+
* card already holds; nothing is invented.
|
|
3939
|
+
* @module @xiaoso/dsh-tool-plus/web/client/rows/read-card-facts
|
|
3940
|
+
*/
|
|
3941
|
+
/**
|
|
3942
|
+
* The `read` card's collapsed summary: the path the model asked for, keeping
|
|
3943
|
+
* whatever inline selector it carried (`:22-25`, `:raw`, `:conflicts`, `:28-`,
|
|
3944
|
+
* a sqlite row, an archive member) and shortened against the workspace root.
|
|
3945
|
+
*
|
|
3946
|
+
* The selector is deliberately not validated against the read grammar: the head
|
|
3947
|
+
* reports the call verbatim, and a selector this plugin does not recognize is
|
|
3948
|
+
* still what the model typed — substituting the projection's stripped path
|
|
3949
|
+
* would erase the evidence that two calls differ.
|
|
3950
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
3951
|
+
* @param metaPath - the Host-projected path (already selector-free).
|
|
3952
|
+
* @param cwd - the session workspace root, when known.
|
|
3953
|
+
* @returns the workspace-shortened argument path, or the projected path.
|
|
3954
|
+
*/
|
|
3955
|
+
function readCardSummary(args, metaPath, cwd) {
|
|
3956
|
+
return displayPath(argText(args, "path") ?? metaPath, cwd);
|
|
3957
|
+
}
|
|
3958
|
+
//#endregion
|
|
3959
|
+
//#region src/web/client/rows/fallback-card-facts.ts
|
|
3960
|
+
/**
|
|
3961
|
+
* Collapsed-summary facts for the generic card — every row's degradation path.
|
|
3962
|
+
*
|
|
3963
|
+
* A call lands on the generic shell when its own card cannot be built: it is
|
|
3964
|
+
* still running, its result carried no text, its metadata fell outside the
|
|
3965
|
+
* session window, it is a Code Dispatch child, or the Host projected an
|
|
3966
|
+
* interruption the card has no material for. In every one of those cases the
|
|
3967
|
+
* call's ARGUMENTS are the only material left, and the shipped generic row
|
|
3968
|
+
* answers them with the call's own fact: its variant's summary keys first, then
|
|
3969
|
+
* ANY string the payload carries, then the first line of the raw argument text
|
|
3970
|
+
* (`deriveSummary` in ui-tool's `tool-call-model.ts`). It never answers with an
|
|
3971
|
+
* argument count.
|
|
3972
|
+
*
|
|
3973
|
+
* That is the rule here. The count it replaces told the reader nothing: a
|
|
3974
|
+
* directory read, a `:raw` window, a running `grep` and a child call all
|
|
3975
|
+
* collapsed to "1 个参数" — the one thing a degraded row must not do, since the
|
|
3976
|
+
* head is then the only place the fact exists.
|
|
3977
|
+
*
|
|
3978
|
+
* The key preference follows THIS plugin's own tool schemas rather than the
|
|
3979
|
+
* shipped `SUMMARY_KEYS`: our tools replaced the built-in ones with the OMP
|
|
3980
|
+
* native parameter names (`read`/`write` take `path`, `ast_grep` takes `pat`,
|
|
3981
|
+
* `ast_edit` takes `paths`), and the shipped table's `file_path` is what the
|
|
3982
|
+
* built-in tools used. Both spellings are accepted, ours first, so a transcript
|
|
3983
|
+
* recorded before the takeover still states its path.
|
|
3984
|
+
* @module @xiaoso/dsh-tool-plus/web/client/rows/fallback-card-facts
|
|
3985
|
+
*/
|
|
3986
|
+
/**
|
|
3987
|
+
* First entry of a path-list argument (`ast_edit.paths`), tolerating the
|
|
3988
|
+
* single-string form some calls use.
|
|
3989
|
+
* @param args - the call's parsed arguments, or `null`.
|
|
3990
|
+
* @param name - the argument's name.
|
|
3991
|
+
* @returns the first non-empty path, or `null`.
|
|
3992
|
+
*/
|
|
3993
|
+
function firstPath(args, name) {
|
|
3994
|
+
const value = args?.[name];
|
|
3995
|
+
if (typeof value === "string" && value !== "") return value;
|
|
3996
|
+
if (!Array.isArray(value)) return null;
|
|
3997
|
+
for (const entry of value) if (typeof entry === "string" && entry !== "") return entry;
|
|
3998
|
+
return null;
|
|
3999
|
+
}
|
|
4000
|
+
/**
|
|
4001
|
+
* The first non-empty string the payload carries under one of `keys` — the
|
|
4002
|
+
* shipped row's own key-preference rule, first line only (a payload's own
|
|
4003
|
+
* newlines never reach a one-line head).
|
|
4004
|
+
* @param args - the call's parsed arguments, or `null`.
|
|
4005
|
+
* @param keys - argument names, most significant first.
|
|
4006
|
+
* @returns the value, or `null` when none of them is a non-empty string.
|
|
4007
|
+
*/
|
|
4008
|
+
function pickKeys(args, keys) {
|
|
4009
|
+
for (const key of keys) {
|
|
4010
|
+
const value = argText(args, key);
|
|
4011
|
+
if (value !== null) return firstLine(value);
|
|
4012
|
+
}
|
|
4013
|
+
return null;
|
|
4014
|
+
}
|
|
4015
|
+
/**
|
|
4016
|
+
* The `queries` a search call may carry, joined one query per line — the one
|
|
4017
|
+
* special case the shipped row makes before its key table.
|
|
4018
|
+
* @param args - the call's parsed arguments, or `null`.
|
|
4019
|
+
* @returns the joined subject, or `null` when there is no usable query.
|
|
4020
|
+
*/
|
|
4021
|
+
function queriesSubject(args) {
|
|
4022
|
+
const queries = args?.["queries"];
|
|
4023
|
+
if (!Array.isArray(queries)) return null;
|
|
4024
|
+
const parts = queries.filter((query) => typeof query === "string" && query !== "").map(firstLine);
|
|
4025
|
+
return parts.length === 0 ? null : parts.join(", ");
|
|
4026
|
+
}
|
|
4027
|
+
/**
|
|
4028
|
+
* The fact the call states for a key this plugin renders.
|
|
4029
|
+
* @param toolName - the call's wire tool name.
|
|
4030
|
+
* @param args - the call's parsed arguments, or `null`.
|
|
4031
|
+
* @param cwd - the session workspace root, for shortening an absolute path.
|
|
4032
|
+
* @returns the stated fact, or `null` when this key states nothing usable.
|
|
4033
|
+
*/
|
|
4034
|
+
function statedFact(toolName, args, cwd) {
|
|
4035
|
+
switch (toolName) {
|
|
4036
|
+
case "read": {
|
|
4037
|
+
const asked = pickKeys(args, [
|
|
4038
|
+
"path",
|
|
4039
|
+
"file_path",
|
|
4040
|
+
"url"
|
|
4041
|
+
]);
|
|
4042
|
+
return asked === null ? null : readCardSummary(args, asked, cwd);
|
|
4043
|
+
}
|
|
4044
|
+
case "bash": {
|
|
4045
|
+
const command = argText(args, "command");
|
|
4046
|
+
const description = argText(args, "description");
|
|
4047
|
+
if (command === null) return description;
|
|
4048
|
+
return bashCardSummary(description, command, null);
|
|
4049
|
+
}
|
|
4050
|
+
case "write": {
|
|
4051
|
+
const path = pickKeys(args, ["path", "file_path"]);
|
|
4052
|
+
return path === null ? null : displayPath(path, cwd);
|
|
4053
|
+
}
|
|
4054
|
+
case "edit": {
|
|
4055
|
+
const path = pickKeys(args, ["file_path", "path"]);
|
|
4056
|
+
return path === null ? null : displayPath(path, cwd);
|
|
4057
|
+
}
|
|
4058
|
+
case "ast_edit": {
|
|
4059
|
+
const path = firstPath(args, "paths");
|
|
4060
|
+
return path === null ? null : displayPath(path, cwd);
|
|
4061
|
+
}
|
|
4062
|
+
case "grep":
|
|
4063
|
+
case "glob":
|
|
4064
|
+
case "ast_grep": {
|
|
4065
|
+
const subject = queriesSubject(args) ?? pickKeys(args, [
|
|
4066
|
+
"query",
|
|
4067
|
+
"pattern",
|
|
4068
|
+
"pat",
|
|
4069
|
+
"url",
|
|
4070
|
+
"path"
|
|
4071
|
+
]);
|
|
4072
|
+
return subject === null ? null : displayPath(subject, cwd);
|
|
4073
|
+
}
|
|
4074
|
+
default: return null;
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
/**
|
|
4078
|
+
* The generic card's collapsed summary: the fact the call itself states.
|
|
4079
|
+
* @param toolName - the call's wire tool name (empty when truncated).
|
|
4080
|
+
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
4081
|
+
* @param cwd - the session workspace root, for shortening an absolute path.
|
|
4082
|
+
* @param t - the card translate seat.
|
|
4083
|
+
* @param raw - the call's original argument text, for a payload that is not
|
|
4084
|
+
* valid JSON yet (a call still streaming its arguments) or whose only values
|
|
4085
|
+
* are non-strings.
|
|
4086
|
+
* @returns the collapsed summary text.
|
|
4087
|
+
*/
|
|
4088
|
+
function fallbackCardSummary(toolName, args, cwd, t, raw = null) {
|
|
4089
|
+
const stated = statedFact(toolName, args, cwd);
|
|
4090
|
+
if (stated !== null) return stated;
|
|
4091
|
+
if (args !== null) {
|
|
4092
|
+
for (const value of Object.values(args)) if (typeof value === "string" && value !== "") return firstLine(value);
|
|
4093
|
+
}
|
|
4094
|
+
const text = raw === null || raw === "" ? null : firstLine(raw);
|
|
4095
|
+
if (text !== null && text !== "") return text;
|
|
4096
|
+
return argsSummary(args, t);
|
|
4097
|
+
}
|
|
4098
|
+
/**
|
|
4099
|
+
* Everything from the first colon is a read selector, not part of a filename.
|
|
4100
|
+
* The colon after a Windows drive letter is not one, and a URL is not a session
|
|
4101
|
+
* file at all.
|
|
4102
|
+
* @param value - the path argument, verbatim.
|
|
4103
|
+
* @returns the file the path names, or `null` when it names no openable file.
|
|
4104
|
+
*/
|
|
4105
|
+
function stripSelector(value) {
|
|
4106
|
+
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(value)) return null;
|
|
4107
|
+
const start = /^[a-zA-Z]:[\\/]/.test(value) ? 2 : 0;
|
|
4108
|
+
const colon = value.indexOf(":", start);
|
|
4109
|
+
const base = colon === -1 ? value : value.slice(0, colon);
|
|
4110
|
+
return base === "" ? null : base;
|
|
4111
|
+
}
|
|
4112
|
+
/**
|
|
4113
|
+
* The real file a degraded file-backed row may offer to open — `null` when the
|
|
4114
|
+
* call names no single file.
|
|
4115
|
+
*
|
|
4116
|
+
* The head states the call verbatim (selector included) while the link has to
|
|
4117
|
+
* name a real file: the same split the read card itself makes between its
|
|
4118
|
+
* summary and its link. Whether that file then previews is the side bar's
|
|
4119
|
+
* decision, never this module's.
|
|
4120
|
+
* @param toolName - the call's wire tool name.
|
|
4121
|
+
* @param args - the call's parsed arguments, or `null`.
|
|
4122
|
+
* @returns the file to open, or `null` when there is nothing to open.
|
|
4123
|
+
*/
|
|
4124
|
+
function fallbackCardLink(toolName, args) {
|
|
4125
|
+
switch (toolName) {
|
|
4126
|
+
case "read": {
|
|
4127
|
+
const asked = pickKeys(args, ["path", "file_path"]);
|
|
4128
|
+
return asked === null ? null : stripSelector(asked);
|
|
4129
|
+
}
|
|
4130
|
+
case "write": return pickKeys(args, ["path", "file_path"]);
|
|
4131
|
+
case "edit": return pickKeys(args, ["file_path", "path"]);
|
|
4132
|
+
case "ast_edit": return firstPath(args, "paths");
|
|
4133
|
+
default: return null;
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
//#endregion
|
|
3791
4137
|
//#region src/web/client/ToolCardShell.tsx
|
|
3792
4138
|
/**
|
|
3793
4139
|
* The shared card shell every row renders through.
|
|
3794
4140
|
*
|
|
3795
4141
|
* The shipped `ToolRow` is not reusable from a plugin (ui-tool is not part of
|
|
3796
4142
|
* the client platform baseline), so this is our own: one 24px header line
|
|
3797
|
-
* (
|
|
4143
|
+
* (tool glyph + title + separator + ellipsized summary + optional `+N -M`
|
|
3798
4144
|
* suffix + clickable path), a keyboard-reachable disclosure, and a body that
|
|
3799
|
-
* is either the card a row built or the flattened result text.
|
|
4145
|
+
* is either the card a row built or the flattened result text. The leading
|
|
4146
|
+
* glyph gives way to a state dot on a failed or interrupted call, and the row
|
|
4147
|
+
* sweeps while the call runs, as the shipped row does.
|
|
3800
4148
|
*
|
|
3801
4149
|
* Two properties matter more than the visuals:
|
|
3802
4150
|
* - **Never throw.** Every value is coerced defensively, and {@link CardBoundary}
|
|
@@ -3808,11 +4156,45 @@ window.__ModuleLoader__.load({
|
|
|
3808
4156
|
* both come out right.
|
|
3809
4157
|
* @module @xiaoso/dsh-tool-plus/web/client/ToolCardShell
|
|
3810
4158
|
*/
|
|
4159
|
+
/** Leading glyphs (the shipped table): every glyph renders at 14 inside the 16px leading box. */
|
|
4160
|
+
const LEADING_GLYPHS = {
|
|
4161
|
+
bash: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconApiOutline14, { size: 14 }),
|
|
4162
|
+
read: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }),
|
|
4163
|
+
edit: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutline16, { size: 14 }),
|
|
4164
|
+
search: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSearchOutline16, { size: 14 }),
|
|
4165
|
+
others: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSparkle16, { size: 14 })
|
|
4166
|
+
};
|
|
4167
|
+
/**
|
|
4168
|
+
* The leading mark of one row: the tool's glyph while the call is clean or
|
|
4169
|
+
* running, and the state dot a failure or an interrupt swaps in — the shipped
|
|
4170
|
+
* `leadingFor` rule, so a failed row gives up its identity for the state
|
|
4171
|
+
* exactly where the shipped row does.
|
|
4172
|
+
* @param state - the card's run state.
|
|
4173
|
+
* @param toolName - the call's wire tool name, when the node carries one.
|
|
4174
|
+
* @returns the node the leading box draws.
|
|
4175
|
+
*/
|
|
4176
|
+
function leadingFor(state, toolName) {
|
|
4177
|
+
switch (leadingKind(state)) {
|
|
4178
|
+
case "error": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "error" });
|
|
4179
|
+
case "warning": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.StateDot, { state: "warning" });
|
|
4180
|
+
default: return LEADING_GLYPHS[leadingGlyph(toolName)];
|
|
4181
|
+
}
|
|
4182
|
+
}
|
|
3811
4183
|
/** Chrome CSS, keyed by `data-plugin-css` and injected once per page. */
|
|
3812
4184
|
const CSS = `
|
|
3813
4185
|
.twc-root{display:flex;flex-direction:column;min-width:0}
|
|
3814
|
-
|
|
4186
|
+
/* The sweep's own anchor and clip, repeated from the shipped sheet so the band
|
|
4187
|
+
cannot escape the row if the shared row chrome ever drops them. */
|
|
4188
|
+
.twc-row{position:relative;min-width:0;overflow:hidden}
|
|
3815
4189
|
.twc-leading{flex-shrink:0}
|
|
4190
|
+
/* Running sweep — the shipped row's own treatment (ui-tool/ToolRow.module.css),
|
|
4191
|
+
copied band for band: a 300px glare of the theme background glides from
|
|
4192
|
+
off-left to off-right, washing the glyph and the text toward the background
|
|
4193
|
+
as it passes. Same anchor as the shipped sheet puts it on: DisclosureRow's
|
|
4194
|
+
own row already carries the position:relative and the overflow:hidden that
|
|
4195
|
+
clips the band. */
|
|
4196
|
+
.twc-root[data-state='running'] .twc-row::after{content:'';position:absolute;top:0;bottom:0;left:0;width:300px;background:linear-gradient(90deg,transparent 0%,color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%,transparent 100%);animation:twc-row-sweep 2.6s ease-out infinite;pointer-events:none}
|
|
4197
|
+
@keyframes twc-row-sweep{0%{left:-300px}90%,100%{left:100%}}
|
|
3816
4198
|
.twc-chevron{color:var(--dsw-alias-label-secondary)}
|
|
3817
4199
|
.twc-title{font-weight:400}
|
|
3818
4200
|
.twc-sep{flex:none;width:2px;height:2px;border-radius:1px;margin:0 8px;background:var(--dsw-alias-label-caption)}
|
|
@@ -3878,8 +4260,11 @@ window.__ModuleLoader__.load({
|
|
|
3878
4260
|
.twc-root:hover .twc-inspect,.twc-inspect:focus-visible{opacity:1}
|
|
3879
4261
|
.twc-inspect:hover{background:var(--dsw-alias-interactive-bg-hover-solid);color:var(--dsw-alias-label-primary)}
|
|
3880
4262
|
.twc-visuallyHidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
|
|
3881
|
-
|
|
3882
|
-
|
|
4263
|
+
/* Motion is feedback, never decoration: the running sweep is the one animation
|
|
4264
|
+
this shell borrows from the shipped row, which runs it with no motion query
|
|
4265
|
+
at all. A reduced-motion reader loses only the glide — the state itself is
|
|
4266
|
+
still announced through the visually-hidden status word. */
|
|
4267
|
+
@media (prefers-reduced-motion: reduce){.twc-inspect{transition:none}.twc-root[data-state='running'] .twc-row::after{display:none}}
|
|
3883
4268
|
`;
|
|
3884
4269
|
/** Inject the card stylesheet once per page; the loader drops plugin style tags on unload. */
|
|
3885
4270
|
let cssInjected = false;
|
|
@@ -3906,7 +4291,7 @@ window.__ModuleLoader__.load({
|
|
|
3906
4291
|
*/
|
|
3907
4292
|
function ToolCardShell(props) {
|
|
3908
4293
|
injectCss();
|
|
3909
|
-
const { t, state, title, children, filePath, openFile, inspect } = props;
|
|
4294
|
+
const { t, state, title, children, filePath, openFile, inspect, tool } = props;
|
|
3910
4295
|
const [expanded, setExpanded] = (0, react.useState)(props.defaultOpen === true);
|
|
3911
4296
|
const bodyCard = hasContent(children) ? children : null;
|
|
3912
4297
|
const bodyText = typeof props.text === "string" && props.text !== "" ? props.text : null;
|
|
@@ -3937,7 +4322,7 @@ window.__ModuleLoader__.load({
|
|
|
3937
4322
|
}),
|
|
3938
4323
|
fileLink ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3939
4324
|
type: "button",
|
|
3940
|
-
className: "twc-fileLink",
|
|
4325
|
+
className: errorTone ? "twc-fileLink twc-errorSummary" : "twc-fileLink",
|
|
3941
4326
|
onClick: openPath,
|
|
3942
4327
|
onKeyDown: openPathKeyDown,
|
|
3943
4328
|
children: summaryText
|
|
@@ -3962,7 +4347,7 @@ window.__ModuleLoader__.load({
|
|
|
3962
4347
|
leadingClassName: "twc-leading",
|
|
3963
4348
|
titleClassName: "twc-title",
|
|
3964
4349
|
chevronClassName: "twc-chevron",
|
|
3965
|
-
icon:
|
|
4350
|
+
icon: leadingFor(state, tool),
|
|
3966
4351
|
title,
|
|
3967
4352
|
open,
|
|
3968
4353
|
expandable,
|
|
@@ -3991,20 +4376,24 @@ window.__ModuleLoader__.load({
|
|
|
3991
4376
|
* A card is only drawn once its metadata passed a `narrow*` guard, so anything
|
|
3992
4377
|
* else — a running call, a window-truncated call, a Code Dispatch child, a
|
|
3993
4378
|
* failure, a payload from another plugin version — lands here with the call's
|
|
3994
|
-
* own title, run state,
|
|
4379
|
+
* own title, run state, the fact its arguments state, and result text.
|
|
3995
4380
|
* @param props - see {@link GenericCardProps}.
|
|
3996
4381
|
* @returns the generic shell.
|
|
3997
4382
|
*/
|
|
3998
|
-
function GenericCard({ t, title, block, args, inspect }) {
|
|
4383
|
+
function GenericCard({ t, title, block, args, toolName, cwd, openFile, inspect }) {
|
|
3999
4384
|
const state = cardState(block);
|
|
4000
4385
|
const output = resultText(block);
|
|
4001
4386
|
const failure = state === "error" ? firstLine(output) : "";
|
|
4387
|
+
const link = failure === "" ? fallbackCardLink(toolName, args) : null;
|
|
4002
4388
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCardShell, {
|
|
4003
4389
|
t,
|
|
4004
4390
|
state,
|
|
4005
4391
|
title,
|
|
4006
|
-
|
|
4392
|
+
tool: toolName,
|
|
4393
|
+
summary: failure === "" ? fallbackCardSummary(toolName, args, cwd, t, cardArgsRaw(block)) : failure,
|
|
4007
4394
|
summaryTone: failure === "" ? "default" : "error",
|
|
4395
|
+
filePath: link,
|
|
4396
|
+
openFile: link === null ? void 0 : openFile,
|
|
4008
4397
|
text: output,
|
|
4009
4398
|
inspect
|
|
4010
4399
|
});
|
|
@@ -4054,76 +4443,6 @@ window.__ModuleLoader__.load({
|
|
|
4054
4443
|
}
|
|
4055
4444
|
};
|
|
4056
4445
|
//#endregion
|
|
4057
|
-
//#region src/web/client/rows/command-card-facts.ts
|
|
4058
|
-
/**
|
|
4059
|
-
* Collapsed-fact builders for the two command-shaped cards: the command a
|
|
4060
|
-
* `bash` description hides, and the rewrite rule an `ast_edit` preview only
|
|
4061
|
-
* shows once expanded.
|
|
4062
|
-
*
|
|
4063
|
-
* Both cards collapse to one head line. `bash`'s head used to be the model's
|
|
4064
|
-
* own `description` — a sentence about intent, never the command that ran — so
|
|
4065
|
-
* the only way to learn what executed was to expand the card. `ast_edit`'s head
|
|
4066
|
-
* names the file and the replacement count, and its body previews the *result*:
|
|
4067
|
-
* the rule that produced that result lives only in the call's `ops` argument,
|
|
4068
|
-
* which no card showed at all. Each builder recovers one fact from the call's
|
|
4069
|
-
* own arguments, so nothing is inferred from output, and a call head that fell
|
|
4070
|
-
* outside the session window degrades to the facts the card already holds.
|
|
4071
|
-
* @module @xiaoso/dsh-tool-plus/web/client/rows/command-card-facts
|
|
4072
|
-
*/
|
|
4073
|
-
/** Whether `value` is a plain object — the only op shape the engine sends. */
|
|
4074
|
-
function isRecord$1(value) {
|
|
4075
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4076
|
-
}
|
|
4077
|
-
/**
|
|
4078
|
-
* The `bash` card's collapsed summary: what ran, placed after the sentence
|
|
4079
|
-
* saying why.
|
|
4080
|
-
*
|
|
4081
|
-
* The command is the fact the head must carry, so the description can never be
|
|
4082
|
-
* the whole summary — it is a prefix at most. Only its first line is used: a
|
|
4083
|
-
* summary is one row high, and the rest of a multi-line command belongs to the
|
|
4084
|
-
* expanded body, which draws the whole thing.
|
|
4085
|
-
* @param description - the model's one-line intent, or `null` when absent.
|
|
4086
|
-
* @param command - the command actually executed (never empty).
|
|
4087
|
-
* @param outcome - the interruption word (`timedOut` / `cancelled`), or `null`.
|
|
4088
|
-
* @returns the outcome alone when the run was interrupted; otherwise the
|
|
4089
|
-
* description joined to the command's first line, or that line alone.
|
|
4090
|
-
*/
|
|
4091
|
-
function bashCardSummary(description, command, outcome) {
|
|
4092
|
-
if (outcome !== null && outcome !== "") return outcome;
|
|
4093
|
-
const intent = description === null ? "" : description.trim();
|
|
4094
|
-
const line = firstLine(command);
|
|
4095
|
-
return intent === "" ? line : `${intent} · ${line}`;
|
|
4096
|
-
}
|
|
4097
|
-
/**
|
|
4098
|
-
* The `ast_edit` card's rule line: the first usable `ops` entry, written the
|
|
4099
|
-
* way the tool's own `ops` argument writes it (`pat → out`).
|
|
4100
|
-
*
|
|
4101
|
-
* Only the first rule is stated, with the count of the entries behind it: the
|
|
4102
|
-
* card exists to preview one change, and a call may carry many rules whose
|
|
4103
|
-
* patterns are whole AST fragments — listing them would bury the preview. The
|
|
4104
|
-
* remaining count is the array tail, not the number of *usable* entries, so it
|
|
4105
|
-
* always describes the call as it was made.
|
|
4106
|
-
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
4107
|
-
* @param t - this plugin's card translate seat.
|
|
4108
|
-
* @returns the rule line, or `null` when the call carries no usable rule.
|
|
4109
|
-
*/
|
|
4110
|
-
function astEditRuleLine(args, t) {
|
|
4111
|
-
const ops = args?.ops;
|
|
4112
|
-
if (!Array.isArray(ops)) return null;
|
|
4113
|
-
for (let index = 0; index < ops.length; index += 1) {
|
|
4114
|
-
const op = ops[index];
|
|
4115
|
-
if (!isRecord$1(op)) continue;
|
|
4116
|
-
const pat = argText(op, "pat");
|
|
4117
|
-
if (pat === null) continue;
|
|
4118
|
-
const replacement = op.out;
|
|
4119
|
-
if (typeof replacement !== "string") continue;
|
|
4120
|
-
const rest = ops.length - 1;
|
|
4121
|
-
const rule = `${t("astEdit.rules")}: ${pat} →${replacement === "" ? "" : ` ${replacement}`}`;
|
|
4122
|
-
return rest === 0 ? rule : `${rule} · ${t("astEdit.rulesRest", { count: rest })}`;
|
|
4123
|
-
}
|
|
4124
|
-
return null;
|
|
4125
|
-
}
|
|
4126
|
-
//#endregion
|
|
4127
4446
|
//#region src/web/client/rows/AstEditRow.tsx
|
|
4128
4447
|
/**
|
|
4129
4448
|
* `ast_edit` row: the engine's own change preview.
|
|
@@ -4161,7 +4480,7 @@ window.__ModuleLoader__.load({
|
|
|
4161
4480
|
* @returns the preview card.
|
|
4162
4481
|
*/
|
|
4163
4482
|
function AstEditCard(props) {
|
|
4164
|
-
const { t, block, cwd, inspect, openFile, title } = props;
|
|
4483
|
+
const { t, block, cwd, inspect, openFile, title, toolName } = props;
|
|
4165
4484
|
const args = parseCardArgs(block);
|
|
4166
4485
|
const meta = narrowAstEditCardMeta(cardMeta(block));
|
|
4167
4486
|
if (isSubCall(block) || block.isError === true || meta === null || meta.preview === "") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GenericCard, {
|
|
@@ -4169,6 +4488,9 @@ window.__ModuleLoader__.load({
|
|
|
4169
4488
|
title,
|
|
4170
4489
|
block,
|
|
4171
4490
|
args,
|
|
4491
|
+
toolName,
|
|
4492
|
+
cwd,
|
|
4493
|
+
openFile,
|
|
4172
4494
|
inspect
|
|
4173
4495
|
});
|
|
4174
4496
|
const files = meta.files.length;
|
|
@@ -4181,6 +4503,7 @@ window.__ModuleLoader__.load({
|
|
|
4181
4503
|
t,
|
|
4182
4504
|
state: cardState(block),
|
|
4183
4505
|
title,
|
|
4506
|
+
tool: toolName,
|
|
4184
4507
|
summary,
|
|
4185
4508
|
summarySuffix: `${t("astEdit.replacements", { count: meta.replacements })} · ${t("astEdit.files", { count: files })}`,
|
|
4186
4509
|
filePath: single,
|
|
@@ -4243,7 +4566,7 @@ window.__ModuleLoader__.load({
|
|
|
4243
4566
|
* @returns the diff card.
|
|
4244
4567
|
*/
|
|
4245
4568
|
function FileMutationCard(props) {
|
|
4246
|
-
const { t, block, cwd, inspect, openFile, title } = props;
|
|
4569
|
+
const { t, block, cwd, inspect, openFile, title, toolName } = props;
|
|
4247
4570
|
const args = parseCardArgs(block);
|
|
4248
4571
|
const meta = narrowEditCardMeta(cardMeta(block));
|
|
4249
4572
|
const replaceForm = replaceHunks(args);
|
|
@@ -4252,6 +4575,9 @@ window.__ModuleLoader__.load({
|
|
|
4252
4575
|
title,
|
|
4253
4576
|
block,
|
|
4254
4577
|
args,
|
|
4578
|
+
toolName,
|
|
4579
|
+
cwd,
|
|
4580
|
+
openFile,
|
|
4255
4581
|
inspect
|
|
4256
4582
|
});
|
|
4257
4583
|
const diffs = meta === null ? replaceForm ?? [] : meta.diffs;
|
|
@@ -4263,6 +4589,7 @@ window.__ModuleLoader__.load({
|
|
|
4263
4589
|
t,
|
|
4264
4590
|
state: cardState(block),
|
|
4265
4591
|
title,
|
|
4592
|
+
tool: toolName,
|
|
4266
4593
|
summary,
|
|
4267
4594
|
summarySuffix: `+${totals.added} -${totals.removed}`,
|
|
4268
4595
|
filePath: single,
|
|
@@ -4410,43 +4737,6 @@ window.__ModuleLoader__.load({
|
|
|
4410
4737
|
};
|
|
4411
4738
|
}
|
|
4412
4739
|
//#endregion
|
|
4413
|
-
//#region src/web/client/rows/read-card-facts.ts
|
|
4414
|
-
/**
|
|
4415
|
-
* Collapsed-summary facts for the `read` card.
|
|
4416
|
-
*
|
|
4417
|
-
* The Host projection strips the inline selector off the path
|
|
4418
|
-
* (`stripReadSelector` in `web/host/read.ts`) because the card's *link* must
|
|
4419
|
-
* point at a real file, and it keeps only the file's own window. The collapsed
|
|
4420
|
-
* head is then the one place where the question the model actually asked is
|
|
4421
|
-
* still readable — and the projected path cannot answer it: a windowed read of
|
|
4422
|
-
* `package.json:22-25,31-34` and a whole-file read of `package.json` project to
|
|
4423
|
-
* the very same string, so the card hides the difference.
|
|
4424
|
-
*
|
|
4425
|
-
* The summary is therefore built from the call's own argument, selector and
|
|
4426
|
-
* all, with the projected path kept as the fallback for a result whose call
|
|
4427
|
-
* head fell outside the session window (a truncated window carries no
|
|
4428
|
-
* `tool/call` event, so no argument survives). That fallback states a fact the
|
|
4429
|
-
* card already holds; nothing is invented.
|
|
4430
|
-
* @module @xiaoso/dsh-tool-plus/web/client/rows/read-card-facts
|
|
4431
|
-
*/
|
|
4432
|
-
/**
|
|
4433
|
-
* The `read` card's collapsed summary: the path the model asked for, keeping
|
|
4434
|
-
* whatever inline selector it carried (`:22-25`, `:raw`, `:conflicts`, `:28-`,
|
|
4435
|
-
* a sqlite row, an archive member) and shortened against the workspace root.
|
|
4436
|
-
*
|
|
4437
|
-
* The selector is deliberately not validated against the read grammar: the head
|
|
4438
|
-
* reports the call verbatim, and a selector this plugin does not recognize is
|
|
4439
|
-
* still what the model typed — substituting the projection's stripped path
|
|
4440
|
-
* would erase the evidence that two calls differ.
|
|
4441
|
-
* @param args - the call's parsed arguments, or `null` when its head is gone.
|
|
4442
|
-
* @param metaPath - the Host-projected path (already selector-free).
|
|
4443
|
-
* @param cwd - the session workspace root, when known.
|
|
4444
|
-
* @returns the workspace-shortened argument path, or the projected path.
|
|
4445
|
-
*/
|
|
4446
|
-
function readCardSummary(args, metaPath, cwd) {
|
|
4447
|
-
return displayPath(argText(args, "path") ?? metaPath, cwd);
|
|
4448
|
-
}
|
|
4449
|
-
//#endregion
|
|
4450
4740
|
//#region src/web/client/rows/ReadRow.tsx
|
|
4451
4741
|
/**
|
|
4452
4742
|
* `read` row: a line-numbered window view, or the image the read returned.
|
|
@@ -4491,7 +4781,7 @@ window.__ModuleLoader__.load({
|
|
|
4491
4781
|
* @returns the read card.
|
|
4492
4782
|
*/
|
|
4493
4783
|
function ReadCard(props) {
|
|
4494
|
-
const { t, block, cwd, inspect, openFile, title } = props;
|
|
4784
|
+
const { t, block, cwd, inspect, openFile, title, toolName } = props;
|
|
4495
4785
|
const args = parseCardArgs(block);
|
|
4496
4786
|
const meta = narrowReadCardMeta(cardMeta(block));
|
|
4497
4787
|
const material = (0, react.useMemo)(() => imageCardMaterial(block, cwd), [block, cwd]);
|
|
@@ -4500,6 +4790,9 @@ window.__ModuleLoader__.load({
|
|
|
4500
4790
|
title,
|
|
4501
4791
|
block,
|
|
4502
4792
|
args,
|
|
4793
|
+
toolName,
|
|
4794
|
+
cwd,
|
|
4795
|
+
openFile,
|
|
4503
4796
|
inspect
|
|
4504
4797
|
});
|
|
4505
4798
|
if (meta === null) {
|
|
@@ -4508,6 +4801,9 @@ window.__ModuleLoader__.load({
|
|
|
4508
4801
|
title,
|
|
4509
4802
|
block,
|
|
4510
4803
|
args,
|
|
4804
|
+
toolName,
|
|
4805
|
+
cwd,
|
|
4806
|
+
openFile,
|
|
4511
4807
|
inspect
|
|
4512
4808
|
});
|
|
4513
4809
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ImageReadCard, {
|
|
@@ -4515,6 +4811,7 @@ window.__ModuleLoader__.load({
|
|
|
4515
4811
|
block,
|
|
4516
4812
|
material,
|
|
4517
4813
|
title,
|
|
4814
|
+
toolName,
|
|
4518
4815
|
inspect,
|
|
4519
4816
|
openFile,
|
|
4520
4817
|
loadImage: props.loadImage
|
|
@@ -4524,6 +4821,7 @@ window.__ModuleLoader__.load({
|
|
|
4524
4821
|
t,
|
|
4525
4822
|
state: cardState(block),
|
|
4526
4823
|
title,
|
|
4824
|
+
tool: toolName,
|
|
4527
4825
|
summary: readCardSummary(args, meta.path, cwd),
|
|
4528
4826
|
filePath: meta.path,
|
|
4529
4827
|
filePathLine: meta.offset,
|
|
@@ -4568,13 +4866,14 @@ window.__ModuleLoader__.load({
|
|
|
4568
4866
|
* @param props - see {@link ImageReadCardProps}.
|
|
4569
4867
|
* @returns the image card.
|
|
4570
4868
|
*/
|
|
4571
|
-
function ImageReadCard({ t, block, material, title, inspect, openFile, loadImage }) {
|
|
4869
|
+
function ImageReadCard({ t, block, material, title, toolName, inspect, openFile, loadImage }) {
|
|
4572
4870
|
const images = material.images;
|
|
4573
4871
|
const text = resultText(block);
|
|
4574
4872
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ToolCardShell, {
|
|
4575
4873
|
t,
|
|
4576
4874
|
state: cardState(block),
|
|
4577
4875
|
title,
|
|
4876
|
+
tool: toolName,
|
|
4578
4877
|
summary: material.label,
|
|
4579
4878
|
filePath: material.path,
|
|
4580
4879
|
openFile,
|
|
@@ -4848,7 +5147,7 @@ window.__ModuleLoader__.load({
|
|
|
4848
5147
|
* @returns the search card.
|
|
4849
5148
|
*/
|
|
4850
5149
|
function SearchCard(props) {
|
|
4851
|
-
const { t, block, cwd, inspect, title } = props;
|
|
5150
|
+
const { t, block, cwd, inspect, title, toolName } = props;
|
|
4852
5151
|
const args = parseCardArgs(block);
|
|
4853
5152
|
const meta = narrowSearchCardMeta(cardMeta(block));
|
|
4854
5153
|
if (isSubCall(block) || block.isError === true || meta === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GenericCard, {
|
|
@@ -4856,6 +5155,8 @@ window.__ModuleLoader__.load({
|
|
|
4856
5155
|
title,
|
|
4857
5156
|
block,
|
|
4858
5157
|
args,
|
|
5158
|
+
toolName,
|
|
5159
|
+
cwd,
|
|
4859
5160
|
inspect
|
|
4860
5161
|
});
|
|
4861
5162
|
const subject = argText(args, "pattern") ?? argText(args, "pat") ?? argText(args, "path");
|
|
@@ -4864,6 +5165,7 @@ window.__ModuleLoader__.load({
|
|
|
4864
5165
|
t,
|
|
4865
5166
|
state: cardState(block),
|
|
4866
5167
|
title,
|
|
5168
|
+
tool: toolName,
|
|
4867
5169
|
summary: subject ?? argsSummary(args, t),
|
|
4868
5170
|
summarySuffix: searchCardSuffix(args, meta, cwd, t),
|
|
4869
5171
|
inspect,
|
|
@@ -4922,7 +5224,7 @@ window.__ModuleLoader__.load({
|
|
|
4922
5224
|
* @returns the terminal card or the generic shell.
|
|
4923
5225
|
*/
|
|
4924
5226
|
function TerminalCard(props) {
|
|
4925
|
-
const { t, block, cwd: sessionCwd, home, inspect, title } = props;
|
|
5227
|
+
const { t, block, cwd: sessionCwd, home, inspect, title, toolName } = props;
|
|
4926
5228
|
const args = parseCardArgs(block);
|
|
4927
5229
|
const meta = narrowTerminalCardMeta(cardMeta(block));
|
|
4928
5230
|
const command = argText(args, "command");
|
|
@@ -4932,12 +5234,15 @@ window.__ModuleLoader__.load({
|
|
|
4932
5234
|
title,
|
|
4933
5235
|
block,
|
|
4934
5236
|
args,
|
|
5237
|
+
toolName,
|
|
5238
|
+
cwd: sessionCwd,
|
|
4935
5239
|
inspect
|
|
4936
5240
|
});
|
|
4937
5241
|
if (meta !== null && meta.mode === "background") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ToolCardShell, {
|
|
4938
5242
|
t,
|
|
4939
5243
|
state,
|
|
4940
5244
|
title,
|
|
5245
|
+
tool: toolName,
|
|
4941
5246
|
summary: t("bash.background", { jobId: meta.jobId }),
|
|
4942
5247
|
text: resultText(block),
|
|
4943
5248
|
inspect
|
|
@@ -4955,6 +5260,7 @@ window.__ModuleLoader__.load({
|
|
|
4955
5260
|
t,
|
|
4956
5261
|
state,
|
|
4957
5262
|
title,
|
|
5263
|
+
tool: toolName,
|
|
4958
5264
|
summary,
|
|
4959
5265
|
inspect,
|
|
4960
5266
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.TerminalBlock, {
|
|
@@ -5006,7 +5312,7 @@ window.__ModuleLoader__.load({
|
|
|
5006
5312
|
* @returns the preview card.
|
|
5007
5313
|
*/
|
|
5008
5314
|
function WriteCard(props) {
|
|
5009
|
-
const { t, block, cwd, inspect, openFile, title } = props;
|
|
5315
|
+
const { t, block, cwd, inspect, openFile, title, toolName } = props;
|
|
5010
5316
|
const args = parseCardArgs(block);
|
|
5011
5317
|
const meta = narrowWriteCardMeta(cardMeta(block));
|
|
5012
5318
|
const content = argText(args, "content");
|
|
@@ -5016,6 +5322,9 @@ window.__ModuleLoader__.load({
|
|
|
5016
5322
|
title,
|
|
5017
5323
|
block,
|
|
5018
5324
|
args,
|
|
5325
|
+
toolName,
|
|
5326
|
+
cwd,
|
|
5327
|
+
openFile,
|
|
5019
5328
|
inspect
|
|
5020
5329
|
});
|
|
5021
5330
|
const lines = t("write.lines", { count: lineCount(content) });
|
|
@@ -5025,6 +5334,7 @@ window.__ModuleLoader__.load({
|
|
|
5025
5334
|
t,
|
|
5026
5335
|
state: cardState(block),
|
|
5027
5336
|
title,
|
|
5337
|
+
tool: toolName,
|
|
5028
5338
|
summary: displayPath(path, cwd),
|
|
5029
5339
|
summarySuffix: lines + skip,
|
|
5030
5340
|
filePath: path,
|