dsh-better-display 0.3.5 → 0.3.6
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/CHANGELOG.md +4 -0
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/lib/client.js +75 -17
- package/lib/client.js.map +1 -1
- package/lib/types/client/ToolActivity.js +8 -8
- package/lib/types/client/ToolActivity.js.map +1 -1
- package/lib/types/client/tool-activity.d.ts +15 -0
- package/lib/types/client/tool-activity.d.ts.map +1 -1
- package/lib/types/client/tool-activity.js +42 -0
- package/lib/types/client/tool-activity.js.map +1 -1
- package/package.json +1 -1
- package/src/client/ToolActivity.tsx +8 -8
- package/src/client/tool-activity.ts +42 -0
- package/tests/tool-clock.test.ts +45 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.6 — 2026-10-05
|
|
4
|
+
|
|
5
|
+
- Keep a running tool's elapsed clock counting from the call's stamped start instead of the moment the row mounted. Switching sessions and back — or even reloading the page mid-run — now continues the count rather than restarting it at zero; draft-only calls remember their first-seen time per call id until they settle.
|
|
6
|
+
|
|
3
7
|
## 0.3.5 — 2026-10-04
|
|
4
8
|
|
|
5
9
|
- Release finished expand animations so process rows return to their natural height. Content changes after expanding no longer leave blank space or clip steps until a page refresh.
|
package/README.en.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
Open **Settings → Plugins → Add plugin** and enter this in “Package name or address”:
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
github:aa2246740/dsh-better-display#v0.3.
|
|
12
|
+
github:aa2246740/dsh-better-display#v0.3.6
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
The desktop plugin manager owns the Desktop profile and its bundled package manager. This release includes built `lib/`; normal use needs no clone, build, or DSHX installation. Follow the app if it asks you to reload or reopen after installation.
|
|
@@ -17,7 +17,7 @@ The desktop plugin manager owns the Desktop profile and its bundled package mana
|
|
|
17
17
|
### Web CLI
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
dsh plugin --profile web add github:aa2246740/dsh-better-display#v0.3.
|
|
20
|
+
dsh plugin --profile web add github:aa2246740/dsh-better-display#v0.3.6
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
This official CLI command writes only the `web` profile; it cannot modify the Desktop App profile. **pnpm** must be on PATH. For an already-running Web Host, reopen that Host once and reload the page because bundles are read at boot.
|
|
@@ -34,7 +34,7 @@ From a local checkout or tarball (development/local testing):
|
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
36
|
dsh plugin --profile web add ./dsh-better-display
|
|
37
|
-
dsh plugin --profile web add ./dsh-better-display-0.3.
|
|
37
|
+
dsh plugin --profile web add ./dsh-better-display-0.3.6.tgz
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
`dsh.bundle` is captured at Host boot. Do not also insert the same row by hand in the profile `cordis.patch.yml`, or it will mount twice.
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
打开 **设置 → 插件 → 添加插件**,在“包名或地址”中输入:
|
|
10
10
|
|
|
11
11
|
```text
|
|
12
|
-
github:aa2246740/dsh-better-display#v0.3.
|
|
12
|
+
github:aa2246740/dsh-better-display#v0.3.6
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
桌面端的插件管理器负责 Desktop profile 和其内置包管理器。本发布已包含编译好的 `lib/`;普通使用不需要 clone、构建或安装 DSHX。若应用在安装完成后提示刷新或重新打开,请按提示完成。
|
|
@@ -17,7 +17,7 @@ github:aa2246740/dsh-better-display#v0.3.5
|
|
|
17
17
|
### Web CLI
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
|
-
dsh plugin --profile web add github:aa2246740/dsh-better-display#v0.3.
|
|
20
|
+
dsh plugin --profile web add github:aa2246740/dsh-better-display#v0.3.6
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
这条官方 CLI 命令只写入 `web` profile,不能修改 Desktop App 的 profile。本机 PATH 上需要有 **pnpm**。对于已经运行的 Web Host,请重新打开该 Host 一次,再刷新网页;插件 bundle 会在启动时读取。
|
|
@@ -34,7 +34,7 @@ dsh plugin --profile web add github:aa2246740/dsh-better-display#v0.3.5
|
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
36
|
dsh plugin --profile web add ./dsh-better-display
|
|
37
|
-
dsh plugin --profile web add ./dsh-better-display-0.3.
|
|
37
|
+
dsh plugin --profile web add ./dsh-better-display-0.3.6.tgz
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
`dsh.bundle` 是开机捕获的。不要再往 profile 的 `cordis.patch.yml` 手写同一条 insert,会重复挂载。
|
package/lib/client.js
CHANGED
|
@@ -371,6 +371,44 @@ window.__ModuleLoader__.load({
|
|
|
371
371
|
if (facts.exitCode === 0) return "succeeded";
|
|
372
372
|
return "returned";
|
|
373
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* First-seen time for calls the host has not stamped yet. A draft that
|
|
376
|
+
* precedes a landed call carries no timestamp, so without this memory the
|
|
377
|
+
* running clock would restart at zero on every remount — e.g. switching to
|
|
378
|
+
* another conversation and back while the call is still preparing. Keyed by
|
|
379
|
+
* call id, dropped when the call leaves the running phases.
|
|
380
|
+
*/
|
|
381
|
+
const clockFirstSeen = /* @__PURE__ */ new Map();
|
|
382
|
+
const CLOCK_FIRST_SEEN_LIMIT = 512;
|
|
383
|
+
/**
|
|
384
|
+
* Session-stamped start of a call, or null while only a draft exists.
|
|
385
|
+
* A settled result carries `callTime`, the paired tool/call event's time; a
|
|
386
|
+
* still-running call head stamps the same instant in its own `time` field.
|
|
387
|
+
*/
|
|
388
|
+
function callStartTime(entry) {
|
|
389
|
+
const block = entry.block;
|
|
390
|
+
if (!block) return null;
|
|
391
|
+
return "kind" in block ? block.callTime : block.time;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* The instant a still-running call's elapsed clock counts from. The stamped
|
|
395
|
+
* time wins so a remounted view keeps counting from the real start; an
|
|
396
|
+
* un-stamped draft falls back to when this view first saw that call id, which
|
|
397
|
+
* survives remounts for the same reason.
|
|
398
|
+
*/
|
|
399
|
+
function runningClockBase(callId, block, now = Date.now()) {
|
|
400
|
+
const stamped = callStartTime({ block });
|
|
401
|
+
if (stamped != null) return stamped;
|
|
402
|
+
const seen = clockFirstSeen.get(callId);
|
|
403
|
+
if (seen !== void 0) return seen;
|
|
404
|
+
if (clockFirstSeen.size >= CLOCK_FIRST_SEEN_LIMIT) clockFirstSeen.delete(clockFirstSeen.keys().next().value);
|
|
405
|
+
clockFirstSeen.set(callId, now);
|
|
406
|
+
return now;
|
|
407
|
+
}
|
|
408
|
+
/** Forget a call's first-seen time once its clock is no longer live. */
|
|
409
|
+
function forgetCallClock(callId) {
|
|
410
|
+
clockFirstSeen.delete(callId);
|
|
411
|
+
}
|
|
374
412
|
function activitySummary(entry) {
|
|
375
413
|
const { name, raw } = toolIdentity(entry);
|
|
376
414
|
const args = inputFields(raw);
|
|
@@ -10872,7 +10910,11 @@ ${trimmed}
|
|
|
10872
10910
|
const pairs = toPairs(list);
|
|
10873
10911
|
let pairIndex = -1;
|
|
10874
10912
|
while (++pairIndex < pairs.length) visitParents(tree, "text", visitor);
|
|
10875
|
-
/**
|
|
10913
|
+
/**
|
|
10914
|
+
* Visit a text node, and handle it if it is not in an ignored parent.
|
|
10915
|
+
*
|
|
10916
|
+
* @type {BuildVisitor<Root, 'text'>}
|
|
10917
|
+
*/
|
|
10876
10918
|
function visitor(node, parents) {
|
|
10877
10919
|
let index = -1;
|
|
10878
10920
|
/** @type {Parents | undefined} */
|
|
@@ -10890,11 +10932,11 @@ ${trimmed}
|
|
|
10890
10932
|
* Handle a text node which is not in an ignored parent.
|
|
10891
10933
|
*
|
|
10892
10934
|
* @param {Text} node
|
|
10893
|
-
* Text node.
|
|
10935
|
+
* Text node to search in.
|
|
10894
10936
|
* @param {Array<Parents>} parents
|
|
10895
|
-
*
|
|
10937
|
+
* Ancestors of `node`.
|
|
10896
10938
|
* @returns {VisitorResult}
|
|
10897
|
-
*
|
|
10939
|
+
* Where to continue visiting.
|
|
10898
10940
|
*/
|
|
10899
10941
|
function handler(node, parents) {
|
|
10900
10942
|
const parent = parents[parents.length - 1];
|
|
@@ -10922,12 +10964,12 @@ ${trimmed}
|
|
|
10922
10964
|
} : void 0;
|
|
10923
10965
|
if (value === false) find.lastIndex = position + 1;
|
|
10924
10966
|
else {
|
|
10925
|
-
if (start !== position)
|
|
10967
|
+
if (start !== position) add({
|
|
10926
10968
|
type: "text",
|
|
10927
10969
|
value: node.value.slice(start, position)
|
|
10928
10970
|
});
|
|
10929
|
-
if (Array.isArray(value))
|
|
10930
|
-
else if (value)
|
|
10971
|
+
if (Array.isArray(value)) for (const child of value) add(child);
|
|
10972
|
+
else if (value) add(value);
|
|
10931
10973
|
start = position + match[0].length;
|
|
10932
10974
|
change = true;
|
|
10933
10975
|
}
|
|
@@ -10935,13 +10977,26 @@ ${trimmed}
|
|
|
10935
10977
|
match = find.exec(node.value);
|
|
10936
10978
|
}
|
|
10937
10979
|
if (change) {
|
|
10938
|
-
if (start < node.value.length)
|
|
10980
|
+
if (start < node.value.length) add({
|
|
10939
10981
|
type: "text",
|
|
10940
10982
|
value: node.value.slice(start)
|
|
10941
10983
|
});
|
|
10942
10984
|
parent.children.splice(index, 1, ...nodes);
|
|
10943
10985
|
} else nodes = [node];
|
|
10944
10986
|
return index + nodes.length;
|
|
10987
|
+
/**
|
|
10988
|
+
* Add a node.
|
|
10989
|
+
*
|
|
10990
|
+
* @param {PhrasingContent} child
|
|
10991
|
+
* Node to add.
|
|
10992
|
+
* @returns {undefined}
|
|
10993
|
+
* Nothing.
|
|
10994
|
+
*/
|
|
10995
|
+
function add(child) {
|
|
10996
|
+
const previous = nodes[nodes.length - 1];
|
|
10997
|
+
if (previous && previous.type === "text" && child.type === "text") previous.value += child.value;
|
|
10998
|
+
else nodes.push(child);
|
|
10999
|
+
}
|
|
10945
11000
|
}
|
|
10946
11001
|
}
|
|
10947
11002
|
/**
|
|
@@ -10953,9 +11008,9 @@ ${trimmed}
|
|
|
10953
11008
|
* Clean pairs.
|
|
10954
11009
|
*/
|
|
10955
11010
|
function toPairs(tupleOrList) {
|
|
11011
|
+
if (!Array.isArray(tupleOrList)) throw new TypeError("Expected find and replace tuple or list of tuples");
|
|
10956
11012
|
/** @type {Pairs} */
|
|
10957
11013
|
const result = [];
|
|
10958
|
-
if (!Array.isArray(tupleOrList)) throw new TypeError("Expected find and replace tuple or list of tuples");
|
|
10959
11014
|
/** @type {FindAndReplaceList} */
|
|
10960
11015
|
const list = !tupleOrList[0] || Array.isArray(tupleOrList[0]) ? tupleOrList : [tupleOrList];
|
|
10961
11016
|
let index = -1;
|
|
@@ -10969,9 +11024,9 @@ ${trimmed}
|
|
|
10969
11024
|
* Turn a find into an expression.
|
|
10970
11025
|
*
|
|
10971
11026
|
* @param {Find} find
|
|
10972
|
-
*
|
|
11027
|
+
* Pattern to find.
|
|
10973
11028
|
* @returns {RegExp}
|
|
10974
|
-
*
|
|
11029
|
+
* Global expression.
|
|
10975
11030
|
*/
|
|
10976
11031
|
function toExpression(find) {
|
|
10977
11032
|
return typeof find === "string" ? new RegExp(escapeStringRegexp(find), "g") : find;
|
|
@@ -10980,9 +11035,9 @@ ${trimmed}
|
|
|
10980
11035
|
* Turn a replace into a function.
|
|
10981
11036
|
*
|
|
10982
11037
|
* @param {Replace} replace
|
|
10983
|
-
*
|
|
11038
|
+
* Thing to replace with.
|
|
10984
11039
|
* @returns {ReplaceFunction}
|
|
10985
|
-
* Function.
|
|
11040
|
+
* Function that returns that thing.
|
|
10986
11041
|
*/
|
|
10987
11042
|
function toFunction(replace) {
|
|
10988
11043
|
return typeof replace === "function" ? replace : function() {
|
|
@@ -45062,19 +45117,22 @@ ${trimmed}
|
|
|
45062
45117
|
const showState = phase === "preparing" || phase === "running" || phase === "failed" || phase === "interrupted";
|
|
45063
45118
|
const running = phase === "preparing" || phase === "running";
|
|
45064
45119
|
const [liveMs, setLiveMs] = (0, react.useState)(null);
|
|
45065
|
-
const openedAt = (0, react.useRef)(null);
|
|
45066
45120
|
(0, react.useEffect)(() => {
|
|
45067
45121
|
if (!running) {
|
|
45068
|
-
|
|
45122
|
+
forgetCallClock(entry.callId);
|
|
45069
45123
|
setLiveMs(null);
|
|
45070
45124
|
return;
|
|
45071
45125
|
}
|
|
45072
|
-
const base = (
|
|
45126
|
+
const base = runningClockBase(entry.callId, block);
|
|
45073
45127
|
const tick = () => setLiveMs(Math.max(0, Date.now() - base));
|
|
45074
45128
|
tick();
|
|
45075
45129
|
const timer = setInterval(tick, 200);
|
|
45076
45130
|
return () => clearInterval(timer);
|
|
45077
|
-
}, [
|
|
45131
|
+
}, [
|
|
45132
|
+
running,
|
|
45133
|
+
block,
|
|
45134
|
+
entry.callId
|
|
45135
|
+
]);
|
|
45078
45136
|
const elapsed = block && "kind" in block && block.callTime != null ? Math.max(0, block.time - block.callTime) : null;
|
|
45079
45137
|
const shownMs = liveMs ?? (elapsed !== null && elapsed >= SLOW_TOOL_MS ? elapsed : null);
|
|
45080
45138
|
const rawResult = (0, react.useMemo)(() => {
|