@elinpf/dsh-ops-trace-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +4 -0
- package/README.md +37 -0
- package/README.zh.md +37 -0
- package/cordis.patch.yml +3 -0
- package/lib/client.js +721 -0
- package/lib/index.d.ts +24 -0
- package/lib/index.js +34 -0
- package/lib/invariant.d.ts +17 -0
- package/lib/invariant.js +29 -0
- package/lib/types.d.ts +33 -0
- package/lib/types.js +11 -0
- package/package.json +70 -0
package/README.i18n.yaml
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @elinpf/dsh-ops-trace-ui
|
|
2
|
+
|
|
3
|
+
The host-plane half of the ops trace feature — registers the shared `trace` session projection and ships the web panel client bundle (the investigation-tree panel above the composer).
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
One thin shell, two halves:
|
|
8
|
+
|
|
9
|
+
- **Host half** (`src/index.ts`): registers the shared `trace` projection into `ctx.sessionProjections`, deferred through `ctx.inject` so the loader never orders this row against the registry. The projection definition is imported **verbatim** from `@elinpf/dsh-ops-tool-trace` — reference identity, not a copy — so key/schema/fold/stateVersion can never drift between the tool half and the panel half.
|
|
10
|
+
- **Client half** (`src/client.ts`, esbuild-bundled to `lib/client.js`): registers a `conversation.input.dock` entry (`id: 'ops-tree'`) that renders a collapsible investigation-tree panel, structurally identical to todo_write's TodoPanel. Reads the `trace` projection via `useProjection`; degrades to rendering nothing when the registry is absent.
|
|
11
|
+
|
|
12
|
+
Registers no tools and no prompt sections — the `trace` tool and its methodology live in `@elinpf/dsh-ops-tool-trace`, mounted preset-plane.
|
|
13
|
+
|
|
14
|
+
## Design notes
|
|
15
|
+
|
|
16
|
+
- **Why a separate package**: the trace feature splits by plane. The tool is preset-plane (model-facing); the projection registry and web client carriers are host-plane. This package is the host-plane mount point — a row the ops preset composes so the panel reaches the browser.
|
|
17
|
+
- **Client discovery is runtime**: the web app's ClientModuleRegistry scans the composed host cordis entries, so this package must stay mounted host-plane via its `cordis.patch.yml` row or the panel never reaches the browser.
|
|
18
|
+
- **Shared layout**: sibling ordering, depth, and DFS flattening come from `ops-tool-trace/tree-layout` — the human sees the same layout the model sees.
|
|
19
|
+
- **Per-session UI state**: the dock unmounts on conversation switch, resetting React state; the collapse/selection choices live in a module-level map keyed by session (`DockUiState` in `./types`) so they survive unmount.
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
- id: ops-trace-ui
|
|
25
|
+
name: '@elinpf/dsh-ops-trace-ui'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
No config keys — `Config` is an empty schemastery object.
|
|
29
|
+
|
|
30
|
+
## Testing
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm run build # tsc → lib/, then esbuild → lib/client.js
|
|
34
|
+
npx vitest run
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Unit tests cover both entries' export shape (function-plugin form, no default export), the verbatim projection registration, the fold behavior driven through the registered definition, the dock slot registration, and HMR unload — running every fiber disposer removes both the projection and the dock entry.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @elinpf/dsh-ops-trace-ui
|
|
2
|
+
|
|
3
|
+
ops trace 功能的 host 面薄壳 — 注册共享的 `trace` session projection,并携带 web 面板 client bundle(输入框上方的调查树面板)。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
一个薄壳,两个 half:
|
|
8
|
+
|
|
9
|
+
- **Host 半**(`src/index.ts`):把共享的 `trace` projection 注册进 `ctx.sessionProjections`,通过 `ctx.inject` 延迟执行,loader 不会因此把本行与 registry 排序。projection 定义从 `@elinpf/dsh-ops-tool-trace` **原样引用**(引用同一对象,不是复制),因此 key/schema/fold/stateVersion 在工具半和面板半之间永不漂移。
|
|
10
|
+
- **Client 半**(`src/client.ts`,esbuild 打包为 `lib/client.js`):注册一个 `conversation.input.dock` 条目(`id: 'ops-tree'`),渲染可折叠的调查树面板,结构上与 todo_write 的 TodoPanel 完全一致。通过 `useProjection` 读 `trace` projection;registry 缺席时降级为不渲染。
|
|
11
|
+
|
|
12
|
+
不注册任何工具和 prompt section — `trace` 工具及其方法论在 `@elinpf/dsh-ops-tool-trace`,挂在 preset 面。
|
|
13
|
+
|
|
14
|
+
## 设计要点
|
|
15
|
+
|
|
16
|
+
- **为什么单独成包**:trace 功能按 plane 拆分。工具在 preset 面(面向模型);projection 注册表和 web client 载体在 host 面。本包就是 host 面的挂载点 — ops preset 组合它,面板才能到达浏览器。
|
|
17
|
+
- **client 发现是运行时的**:web app 的 ClientModuleRegistry 扫描组合后的 host cordis 条目,因此本包必须通过 `cordis.patch.yml` 行保持 host 面挂载,否则面板永远到不了浏览器。
|
|
18
|
+
- **共享布局**:兄弟排序、深度、DFS 扁平化来自 `ops-tool-trace/tree-layout` — 人看到的布局与模型看到的一致。
|
|
19
|
+
- **按会话的 UI 状态**:dock 在切换会话时卸载,React state 随之重置;折叠/选择状态放在以会话为键的模块级 map(`./types` 的 `DockUiState`)里,卸载后仍然保留。
|
|
20
|
+
|
|
21
|
+
## 配置
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
- id: ops-trace-ui
|
|
25
|
+
name: '@elinpf/dsh-ops-trace-ui'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
无配置项 — `Config` 是空的 schemastery object。
|
|
29
|
+
|
|
30
|
+
## 测试
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
npm run build # tsc → lib/,然后 esbuild → lib/client.js
|
|
34
|
+
npx vitest run
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
单元测试覆盖:两个入口的导出形态(函数插件形式、无 default 导出)、projection 的原样注册、通过注册定义驱动的 fold 行为、dock slot 注册,以及 HMR 卸载 — 执行全部 fiber disposer 后,projection 和 dock 条目都被移除。
|
package/cordis.patch.yml
ADDED
package/lib/client.js
ADDED
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({ id: "@elinpf/dsh-ops-trace-ui", factory: (require) => {
|
|
2
|
+
var module = { exports: {} }; var exports = module.exports;
|
|
3
|
+
"use strict";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name2 in all)
|
|
10
|
+
__defProp(target, name2, { get: all[name2], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/client.ts
|
|
23
|
+
var client_exports = {};
|
|
24
|
+
__export(client_exports, {
|
|
25
|
+
apply: () => apply,
|
|
26
|
+
inject: () => inject,
|
|
27
|
+
name: () => name
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(client_exports);
|
|
30
|
+
var import_react = require("react");
|
|
31
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
32
|
+
|
|
33
|
+
// ../ops-tool-trace/lib/tree-layout.js
|
|
34
|
+
var STATUS_ORDER = {
|
|
35
|
+
in_progress: 0,
|
|
36
|
+
pending: 1,
|
|
37
|
+
done: 2,
|
|
38
|
+
dead_end: 3,
|
|
39
|
+
goal: 4,
|
|
40
|
+
resolved: 5
|
|
41
|
+
};
|
|
42
|
+
function sortChildren(nodes) {
|
|
43
|
+
return [...nodes].sort((a, b) => {
|
|
44
|
+
const aIsGoal = a.id === "goal";
|
|
45
|
+
const bIsGoal = b.id === "goal";
|
|
46
|
+
if (aIsGoal && !bIsGoal)
|
|
47
|
+
return 1;
|
|
48
|
+
if (!aIsGoal && bIsGoal)
|
|
49
|
+
return -1;
|
|
50
|
+
return (STATUS_ORDER[a.status] ?? 9) - (STATUS_ORDER[b.status] ?? 9);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function buildTreeIndex(nodes) {
|
|
54
|
+
const children = {};
|
|
55
|
+
let root = null;
|
|
56
|
+
for (const n of nodes) {
|
|
57
|
+
if (n.parent === null) {
|
|
58
|
+
root = n;
|
|
59
|
+
} else {
|
|
60
|
+
if (!children[n.parent])
|
|
61
|
+
children[n.parent] = [];
|
|
62
|
+
children[n.parent].push(n);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { children, root };
|
|
66
|
+
}
|
|
67
|
+
function depthOf(nodes, id, cache = {}) {
|
|
68
|
+
if (id in cache)
|
|
69
|
+
return cache[id];
|
|
70
|
+
let depth = 0;
|
|
71
|
+
let current = nodes.find((n) => n.id === id);
|
|
72
|
+
const seen = /* @__PURE__ */ new Set();
|
|
73
|
+
while (current && current.parent !== null && !seen.has(current.id)) {
|
|
74
|
+
seen.add(current.id);
|
|
75
|
+
depth++;
|
|
76
|
+
current = nodes.find((n) => n.id === current.parent);
|
|
77
|
+
}
|
|
78
|
+
cache[id] = depth;
|
|
79
|
+
return depth;
|
|
80
|
+
}
|
|
81
|
+
function flattenTree(nodes) {
|
|
82
|
+
const { children, root } = buildTreeIndex(nodes);
|
|
83
|
+
const result = [];
|
|
84
|
+
const visited = /* @__PURE__ */ new Set();
|
|
85
|
+
function visit(node) {
|
|
86
|
+
if (visited.has(node))
|
|
87
|
+
return;
|
|
88
|
+
visited.add(node);
|
|
89
|
+
result.push(node);
|
|
90
|
+
const kids = children[node.id];
|
|
91
|
+
if (kids)
|
|
92
|
+
for (const k of sortChildren(kids))
|
|
93
|
+
visit(k);
|
|
94
|
+
}
|
|
95
|
+
if (root)
|
|
96
|
+
visit(root);
|
|
97
|
+
for (const n of nodes) {
|
|
98
|
+
if (!visited.has(n))
|
|
99
|
+
result.push(n);
|
|
100
|
+
}
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/client.ts
|
|
105
|
+
var name = "ops-trace-client";
|
|
106
|
+
var inject = ["slots"];
|
|
107
|
+
var CSS = `
|
|
108
|
+
/* Hide trace panel when the trajectory view tab is active (not the chat tab) */
|
|
109
|
+
[data-phase]:has([role="tab"][aria-selected="true"]:not(:first-of-type)) .ops-trace-root {
|
|
110
|
+
display: none;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.ops-trace-root {
|
|
114
|
+
box-sizing: border-box;
|
|
115
|
+
flex: none;
|
|
116
|
+
overflow: hidden;
|
|
117
|
+
margin: 0 auto;
|
|
118
|
+
width: calc(
|
|
119
|
+
100% -
|
|
120
|
+
var(--dsh-composer-side-clearance) -
|
|
121
|
+
var(--dsh-composer-side-clearance) -
|
|
122
|
+
var(--dsh-composer-dock-inset) -
|
|
123
|
+
var(--dsh-composer-dock-inset) -
|
|
124
|
+
var(--dsh-composer-dock-inset) -
|
|
125
|
+
var(--dsh-composer-dock-inset)
|
|
126
|
+
);
|
|
127
|
+
max-width: calc(
|
|
128
|
+
var(--dsh-composer-card-max-width) -
|
|
129
|
+
var(--dsh-composer-dock-inset) -
|
|
130
|
+
var(--dsh-composer-dock-inset) -
|
|
131
|
+
var(--dsh-composer-dock-inset) -
|
|
132
|
+
var(--dsh-composer-dock-inset)
|
|
133
|
+
);
|
|
134
|
+
border: 1px solid var(--dsw-alias-border-l1, #e5e7eb);
|
|
135
|
+
border-radius: 12px;
|
|
136
|
+
background: var(--dsw-specific-tip, #f6f8fa);
|
|
137
|
+
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2, #d0d7de);
|
|
138
|
+
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2, #afb8c1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.ops-trace-body {
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
gap: 8px;
|
|
145
|
+
padding: 6px 12px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.ops-trace-header {
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
gap: 10px;
|
|
152
|
+
width: 100%;
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.ops-trace-title {
|
|
157
|
+
flex: none;
|
|
158
|
+
font-size: 13px;
|
|
159
|
+
line-height: 24px;
|
|
160
|
+
font-weight: 500;
|
|
161
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.ops-trace-switcher {
|
|
165
|
+
display: flex;
|
|
166
|
+
align-items: center;
|
|
167
|
+
gap: 4px;
|
|
168
|
+
flex: none;
|
|
169
|
+
font-size: 13px;
|
|
170
|
+
line-height: 22px;
|
|
171
|
+
font-weight: 500;
|
|
172
|
+
padding: 0 8px;
|
|
173
|
+
border: 1px solid var(--dsw-alias-border-l1, #d0d7de);
|
|
174
|
+
border-radius: 6px;
|
|
175
|
+
background: var(--dsw-alias-bg-primary, #fff);
|
|
176
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
177
|
+
cursor: pointer;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.ops-trace-switcher:hover {
|
|
181
|
+
background: var(--dsw-alias-bg-hover, #e9ecef);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.ops-trace-switcher-arrow {
|
|
185
|
+
display: grid;
|
|
186
|
+
place-items: center;
|
|
187
|
+
color: var(--dsw-alias-label-tertiary, #656d76);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.ops-trace-progress {
|
|
191
|
+
flex: 1 1 auto;
|
|
192
|
+
min-width: 0;
|
|
193
|
+
overflow: hidden;
|
|
194
|
+
font-size: 13px;
|
|
195
|
+
line-height: 20px;
|
|
196
|
+
font-weight: 400;
|
|
197
|
+
color: var(--dsw-alias-label-tertiary, #656d76);
|
|
198
|
+
text-overflow: ellipsis;
|
|
199
|
+
white-space: nowrap;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.ops-trace-chevron-btn {
|
|
203
|
+
display: grid;
|
|
204
|
+
flex: none;
|
|
205
|
+
place-items: center;
|
|
206
|
+
width: 22px;
|
|
207
|
+
height: 22px;
|
|
208
|
+
padding: 0;
|
|
209
|
+
border: none;
|
|
210
|
+
background: transparent;
|
|
211
|
+
color: var(--dsw-alias-label-tertiary, #656d76);
|
|
212
|
+
cursor: pointer;
|
|
213
|
+
border-radius: 4px;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.ops-trace-chevron-btn:hover {
|
|
217
|
+
background: var(--dsw-alias-bg-hover, #e9ecef);
|
|
218
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.ops-trace-chevron {
|
|
222
|
+
display: grid;
|
|
223
|
+
flex: none;
|
|
224
|
+
place-items: center;
|
|
225
|
+
color: var(--dsw-alias-label-tertiary, #656d76);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.ops-trace-selector-list {
|
|
229
|
+
display: flex;
|
|
230
|
+
flex-direction: column;
|
|
231
|
+
gap: 2px;
|
|
232
|
+
margin: 0;
|
|
233
|
+
padding: 0;
|
|
234
|
+
list-style: none;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.ops-trace-selector-item {
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
gap: 8px;
|
|
241
|
+
width: 100%;
|
|
242
|
+
padding: 5px 8px;
|
|
243
|
+
border: none;
|
|
244
|
+
background: transparent;
|
|
245
|
+
cursor: pointer;
|
|
246
|
+
font-size: 13px;
|
|
247
|
+
line-height: 20px;
|
|
248
|
+
color: var(--dsw-alias-label-secondary, #656d76);
|
|
249
|
+
border-radius: 4px;
|
|
250
|
+
text-align: left;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.ops-trace-selector-item:hover {
|
|
254
|
+
background: var(--dsw-alias-bg-hover, #e9ecef);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.ops-trace-selector-item-active {
|
|
258
|
+
background: var(--dsw-alias-bg-selected, #ddf4ff);
|
|
259
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
260
|
+
font-weight: 500;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.ops-trace-selector-dot {
|
|
264
|
+
flex: none;
|
|
265
|
+
width: 8px;
|
|
266
|
+
height: 8px;
|
|
267
|
+
border-radius: 50%;
|
|
268
|
+
border: 1.5px solid var(--dsw-alias-label-tertiary, #848d97);
|
|
269
|
+
background: transparent;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.ops-trace-selector-dot-active {
|
|
273
|
+
border-color: var(--dsw-alias-state-business-primary, #0969da);
|
|
274
|
+
background: var(--dsw-alias-state-business-primary, #0969da);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
.ops-trace-selector-title {
|
|
278
|
+
flex: 1 1 auto;
|
|
279
|
+
min-width: 0;
|
|
280
|
+
overflow: hidden;
|
|
281
|
+
text-overflow: ellipsis;
|
|
282
|
+
white-space: nowrap;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.ops-trace-selector-tag {
|
|
286
|
+
flex: none;
|
|
287
|
+
font-size: 10px;
|
|
288
|
+
padding: 0 5px;
|
|
289
|
+
border-radius: 4px;
|
|
290
|
+
background: rgba(26,127,55,0.12);
|
|
291
|
+
color: #1a7f37;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.ops-trace-lead {
|
|
295
|
+
display: grid;
|
|
296
|
+
flex: none;
|
|
297
|
+
place-items: center;
|
|
298
|
+
color: var(--dsw-alias-label-tertiary, #656d76);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.ops-trace-progress {
|
|
302
|
+
flex: 1 1 auto;
|
|
303
|
+
min-width: 0;
|
|
304
|
+
overflow: hidden;
|
|
305
|
+
font-size: 13px;
|
|
306
|
+
line-height: 20px;
|
|
307
|
+
font-weight: 400;
|
|
308
|
+
color: var(--dsw-alias-label-tertiary, #656d76);
|
|
309
|
+
text-overflow: ellipsis;
|
|
310
|
+
white-space: nowrap;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.ops-trace-list {
|
|
314
|
+
display: flex;
|
|
315
|
+
flex-direction: column;
|
|
316
|
+
gap: 2px;
|
|
317
|
+
margin: 0;
|
|
318
|
+
padding: 0;
|
|
319
|
+
list-style: none;
|
|
320
|
+
max-height: 300px;
|
|
321
|
+
overflow-y: auto;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
.ops-trace-item {
|
|
325
|
+
display: flex;
|
|
326
|
+
flex-direction: column;
|
|
327
|
+
min-width: 0;
|
|
328
|
+
font-size: 13px;
|
|
329
|
+
line-height: 20px;
|
|
330
|
+
color: var(--dsw-alias-label-secondary, #656d76);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.ops-trace-item-row {
|
|
334
|
+
display: flex;
|
|
335
|
+
align-items: flex-start;
|
|
336
|
+
gap: 6px;
|
|
337
|
+
min-width: 0;
|
|
338
|
+
cursor: pointer;
|
|
339
|
+
border-radius: 4px;
|
|
340
|
+
padding: 1px 4px;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.ops-trace-item-row:hover {
|
|
344
|
+
background: var(--dsw-alias-bg-hover, #e9ecef);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.ops-trace-glyph {
|
|
348
|
+
display: grid;
|
|
349
|
+
flex: none;
|
|
350
|
+
place-items: center;
|
|
351
|
+
width: 16px;
|
|
352
|
+
height: 16px;
|
|
353
|
+
margin-top: 2px;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.ops-trace-text {
|
|
357
|
+
flex: 1 1 auto;
|
|
358
|
+
min-width: 0;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.ops-trace-node-title {
|
|
362
|
+
overflow: hidden;
|
|
363
|
+
text-overflow: ellipsis;
|
|
364
|
+
white-space: nowrap;
|
|
365
|
+
color: var(--dsw-alias-label-primary, #1f2328);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.ops-trace-node-title-dead {
|
|
369
|
+
text-decoration: line-through;
|
|
370
|
+
color: var(--dsw-alias-label-tertiary, #848d97);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.ops-trace-node-hint {
|
|
374
|
+
font-size: 11px;
|
|
375
|
+
color: var(--dsw-alias-label-tertiary, #848d97);
|
|
376
|
+
margin-left: 4px;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.ops-trace-node-detail {
|
|
380
|
+
padding: 2px 8px 4px 22px;
|
|
381
|
+
font-size: 12px;
|
|
382
|
+
line-height: 18px;
|
|
383
|
+
color: var(--dsw-alias-label-tertiary, #848d97);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.ops-trace-detail-value {
|
|
387
|
+
min-width: 0;
|
|
388
|
+
word-break: break-word;
|
|
389
|
+
white-space: pre-wrap;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.ops-trace-caused-tag {
|
|
393
|
+
display: inline-block;
|
|
394
|
+
font-size: 11px;
|
|
395
|
+
line-height: 16px;
|
|
396
|
+
padding: 1px 6px;
|
|
397
|
+
margin-right: 4px;
|
|
398
|
+
border-radius: 8px;
|
|
399
|
+
background: rgba(209,36,47,0.10);
|
|
400
|
+
color: #d1242f;
|
|
401
|
+
white-space: nowrap;
|
|
402
|
+
vertical-align: 1px;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.ops-trace-glyph-done { color: var(--dsw-alias-state-success-primary, #1a7f37); }
|
|
406
|
+
.ops-trace-glyph-pending { color: var(--dsw-alias-label-caption, #848d97); }
|
|
407
|
+
.ops-trace-glyph-progress { color: var(--dsw-alias-state-business-primary, #0969da); animation: ops-trace-spin 1s linear infinite; }
|
|
408
|
+
.ops-trace-glyph-deadend { color: #d1242f; }
|
|
409
|
+
.ops-trace-glyph-goal { color: var(--dsw-alias-state-success-primary, #1a7f37); }
|
|
410
|
+
.ops-trace-glyph-resolved { color: var(--dsw-alias-state-success-primary, #1a7f37); }
|
|
411
|
+
|
|
412
|
+
@keyframes ops-trace-spin { to { transform: rotate(360deg); } }
|
|
413
|
+
`.trim();
|
|
414
|
+
var cssInjected = false;
|
|
415
|
+
function injectCSS() {
|
|
416
|
+
if (cssInjected || typeof document === "undefined") return;
|
|
417
|
+
const tag = document.createElement("style");
|
|
418
|
+
tag.dataset.plugin = "ops-trace";
|
|
419
|
+
tag.textContent = CSS;
|
|
420
|
+
document.head.appendChild(tag);
|
|
421
|
+
cssInjected = true;
|
|
422
|
+
}
|
|
423
|
+
function svgBase(children, cls) {
|
|
424
|
+
return (0, import_react.createElement)("svg", {
|
|
425
|
+
width: 14,
|
|
426
|
+
height: 14,
|
|
427
|
+
viewBox: "0 0 14 14",
|
|
428
|
+
fill: "none",
|
|
429
|
+
"aria-hidden": "true",
|
|
430
|
+
className: cls
|
|
431
|
+
}, children);
|
|
432
|
+
}
|
|
433
|
+
function DoneGlyph() {
|
|
434
|
+
return svgBase([
|
|
435
|
+
(0, import_react.createElement)("circle", { cx: 7, cy: 7, r: 6.4, stroke: "currentColor", strokeWidth: 1.2 }),
|
|
436
|
+
(0, import_react.createElement)("path", {
|
|
437
|
+
d: "M10.96 5.71L7.70 8.98C7.48 9.20 7.28 9.40 7.09 9.55C6.90 9.71 6.66 9.85 6.36 9.90C6.20 9.93 6.04 9.93 5.88 9.90C5.59 9.85 5.35 9.71 5.16 9.55C4.97 9.40 4.77 9.20 4.55 8.98L3.04 7.46L3.96 6.54L5.47 8.05C5.72 8.29 5.86 8.43 5.98 8.53C6.09 8.61 6.11 8.61 6.09 8.60C6.11 8.61 6.14 8.61 6.16 8.60C6.14 8.61 6.16 8.61 6.27 8.53C6.39 8.43 6.53 8.29 6.77 8.05L10.04 4.79L10.96 5.71Z",
|
|
438
|
+
fill: "currentColor"
|
|
439
|
+
})
|
|
440
|
+
], "ops-trace-glyph ops-trace-glyph-done");
|
|
441
|
+
}
|
|
442
|
+
function ProgressGlyph(nodeId) {
|
|
443
|
+
const gid = "ops-g-" + nodeId;
|
|
444
|
+
return svgBase([
|
|
445
|
+
(0, import_react.createElement)(
|
|
446
|
+
"defs",
|
|
447
|
+
null,
|
|
448
|
+
(0, import_react.createElement)(
|
|
449
|
+
"linearGradient",
|
|
450
|
+
{
|
|
451
|
+
id: gid,
|
|
452
|
+
x1: 2.5,
|
|
453
|
+
y1: 12,
|
|
454
|
+
x2: 10.5,
|
|
455
|
+
y2: 3.5,
|
|
456
|
+
gradientUnits: "userSpaceOnUse"
|
|
457
|
+
},
|
|
458
|
+
(0, import_react.createElement)("stop", { stopColor: "currentColor" }),
|
|
459
|
+
(0, import_react.createElement)("stop", { offset: 1, stopColor: "currentColor", stopOpacity: 0 })
|
|
460
|
+
)
|
|
461
|
+
),
|
|
462
|
+
(0, import_react.createElement)("circle", { cx: 7, cy: 7, r: 6.4, stroke: `url(#${gid})`, strokeWidth: 1.2 })
|
|
463
|
+
], "ops-trace-glyph ops-trace-glyph-progress");
|
|
464
|
+
}
|
|
465
|
+
function PendingGlyph() {
|
|
466
|
+
return svgBase([
|
|
467
|
+
(0, import_react.createElement)("circle", { cx: 7, cy: 7, r: 6.4, stroke: "currentColor", strokeWidth: 1.2, strokeDasharray: "2.4 2.4" })
|
|
468
|
+
], "ops-trace-glyph ops-trace-glyph-pending");
|
|
469
|
+
}
|
|
470
|
+
function DeadEndGlyph() {
|
|
471
|
+
return svgBase([
|
|
472
|
+
(0, import_react.createElement)("circle", { cx: 7, cy: 7, r: 6.4, stroke: "currentColor", strokeWidth: 1.2 }),
|
|
473
|
+
(0, import_react.createElement)("path", { d: "M4.5 4.5 L9.5 9.5 M9.5 4.5 L4.5 9.5", stroke: "currentColor", strokeWidth: 1.4, strokeLinecap: "round" })
|
|
474
|
+
], "ops-trace-glyph ops-trace-glyph-deadend");
|
|
475
|
+
}
|
|
476
|
+
function GoalGlyph() {
|
|
477
|
+
return svgBase([
|
|
478
|
+
(0, import_react.createElement)("circle", { cx: 7, cy: 7, r: 6.4, stroke: "currentColor", strokeWidth: 1.6, strokeDasharray: "3 2" })
|
|
479
|
+
], "ops-trace-glyph ops-trace-glyph-goal");
|
|
480
|
+
}
|
|
481
|
+
function ResolvedGlyph() {
|
|
482
|
+
return svgBase([
|
|
483
|
+
(0, import_react.createElement)("circle", { cx: 7, cy: 7, r: 6.4, stroke: "currentColor", strokeWidth: 1.4, fill: "currentColor", fillOpacity: 0.15 }),
|
|
484
|
+
(0, import_react.createElement)("path", {
|
|
485
|
+
d: "M10.96 5.71L7.70 8.98C7.48 9.20 7.28 9.40 7.09 9.55C6.90 9.71 6.66 9.85 6.36 9.90C6.20 9.93 6.04 9.93 5.88 9.90C5.59 9.85 5.35 9.71 5.16 9.55C4.97 9.40 4.77 9.20 4.55 8.98L3.04 7.46L3.96 6.54L5.47 8.05C5.72 8.29 5.86 8.43 5.98 8.53C6.09 8.61 6.11 8.61 6.09 8.60C6.11 8.61 6.14 8.61 6.16 8.60C6.14 8.61 6.16 8.61 6.27 8.53C6.39 8.43 6.53 8.29 6.77 8.05L10.04 4.79L10.96 5.71Z",
|
|
486
|
+
fill: "currentColor"
|
|
487
|
+
})
|
|
488
|
+
], "ops-trace-glyph ops-trace-glyph-resolved");
|
|
489
|
+
}
|
|
490
|
+
function StatusGlyph(status, nodeId, hasInfo) {
|
|
491
|
+
switch (status) {
|
|
492
|
+
case "goal":
|
|
493
|
+
return GoalGlyph();
|
|
494
|
+
case "done":
|
|
495
|
+
return hasInfo ? ResolvedGlyph() : DoneGlyph();
|
|
496
|
+
case "in_progress":
|
|
497
|
+
return ProgressGlyph(nodeId);
|
|
498
|
+
case "dead_end":
|
|
499
|
+
return DeadEndGlyph();
|
|
500
|
+
case "resolved":
|
|
501
|
+
return ResolvedGlyph();
|
|
502
|
+
default:
|
|
503
|
+
return PendingGlyph();
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function progressLabel(tree) {
|
|
507
|
+
const counts = {};
|
|
508
|
+
for (const n of tree.nodes) counts[n.status] = (counts[n.status] ?? 0) + 1;
|
|
509
|
+
const parts = [`${tree.nodes.length} nodes`];
|
|
510
|
+
if (counts.done) parts.push(`${counts.done} done`);
|
|
511
|
+
if (counts.in_progress) parts.push(`${counts.in_progress} active`);
|
|
512
|
+
if (counts.pending) parts.push(`${counts.pending} pending`);
|
|
513
|
+
if (counts.dead_end) parts.push(`${counts.dead_end} dead`);
|
|
514
|
+
if (tree.resolved) parts.push("resolved");
|
|
515
|
+
return parts.join("\u2002\xB7\u2002");
|
|
516
|
+
}
|
|
517
|
+
function TreeNodeItem({ node, depth, nodes }) {
|
|
518
|
+
const [expanded, setExpanded] = (0, import_react.useState)(false);
|
|
519
|
+
const titleClass = node.status === "dead_end" ? "ops-trace-node-title ops-trace-node-title-dead" : "ops-trace-node-title";
|
|
520
|
+
const expandable = !!node.summary;
|
|
521
|
+
return (0, import_react.createElement)(
|
|
522
|
+
"li",
|
|
523
|
+
{
|
|
524
|
+
key: node.id,
|
|
525
|
+
className: "ops-trace-item",
|
|
526
|
+
"data-status": node.status,
|
|
527
|
+
style: { paddingLeft: `${depth * 20}px` }
|
|
528
|
+
},
|
|
529
|
+
(0, import_react.createElement)(
|
|
530
|
+
"div",
|
|
531
|
+
{
|
|
532
|
+
className: "ops-trace-item-row",
|
|
533
|
+
role: expandable ? "button" : void 0,
|
|
534
|
+
tabIndex: expandable ? 0 : void 0,
|
|
535
|
+
"aria-expanded": expandable ? expanded : void 0,
|
|
536
|
+
onClick: expandable ? () => setExpanded((v) => !v) : void 0,
|
|
537
|
+
onKeyDown: expandable ? (e) => {
|
|
538
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
539
|
+
e.preventDefault();
|
|
540
|
+
setExpanded((v) => !v);
|
|
541
|
+
}
|
|
542
|
+
} : void 0
|
|
543
|
+
},
|
|
544
|
+
(0, import_react.createElement)(
|
|
545
|
+
"span",
|
|
546
|
+
{ className: "ops-trace-glyph", "aria-hidden": "true" },
|
|
547
|
+
StatusGlyph(node.status, node.id, node.caused_by.length > 0 || !!node.summary)
|
|
548
|
+
),
|
|
549
|
+
(0, import_react.createElement)(
|
|
550
|
+
"div",
|
|
551
|
+
{ className: "ops-trace-text" },
|
|
552
|
+
(0, import_react.createElement)("div", { className: titleClass }, node.title)
|
|
553
|
+
)
|
|
554
|
+
),
|
|
555
|
+
expanded && expandable && (0, import_react.createElement)(
|
|
556
|
+
"div",
|
|
557
|
+
{ className: "ops-trace-node-detail" },
|
|
558
|
+
node.caused_by.length > 0 && (0, import_react.createElement)("span", {
|
|
559
|
+
className: "ops-trace-caused-tag",
|
|
560
|
+
key: "caused"
|
|
561
|
+
}, "\u2190 " + node.caused_by.join(", ")),
|
|
562
|
+
node.summary
|
|
563
|
+
)
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
var dockUiState = /* @__PURE__ */ new Map();
|
|
567
|
+
function uiStateFor(sessionKey) {
|
|
568
|
+
let s = dockUiState.get(sessionKey);
|
|
569
|
+
if (!s) {
|
|
570
|
+
s = { collapsed: false, activeIndex: null };
|
|
571
|
+
dockUiState.set(sessionKey, s);
|
|
572
|
+
}
|
|
573
|
+
return s;
|
|
574
|
+
}
|
|
575
|
+
function activeTreeIndex(forest) {
|
|
576
|
+
for (let i = forest.trees.length - 1; i >= 0; i--) {
|
|
577
|
+
if (!forest.trees[i].resolved) return i;
|
|
578
|
+
}
|
|
579
|
+
return forest.trees.length - 1;
|
|
580
|
+
}
|
|
581
|
+
function TraceDock({ useProjection, session }) {
|
|
582
|
+
let forest = null;
|
|
583
|
+
try {
|
|
584
|
+
if (useProjection) {
|
|
585
|
+
const val = useProjection("trace");
|
|
586
|
+
forest = val ?? null;
|
|
587
|
+
}
|
|
588
|
+
} catch {
|
|
589
|
+
forest = null;
|
|
590
|
+
}
|
|
591
|
+
const sessionKey = session?.sessionId ?? "default";
|
|
592
|
+
const ui = uiStateFor(sessionKey);
|
|
593
|
+
const [activeIndex, setActiveIndexState] = (0, import_react.useState)(ui.activeIndex);
|
|
594
|
+
const [collapsed, setCollapsedState] = (0, import_react.useState)(ui.collapsed);
|
|
595
|
+
const [selectorOpen, setSelectorOpen] = (0, import_react.useState)(false);
|
|
596
|
+
const setActiveIndex = (i) => {
|
|
597
|
+
ui.activeIndex = i;
|
|
598
|
+
setActiveIndexState(i);
|
|
599
|
+
};
|
|
600
|
+
const setCollapsed = (v) => {
|
|
601
|
+
ui.collapsed = v;
|
|
602
|
+
setCollapsedState(v);
|
|
603
|
+
};
|
|
604
|
+
if (!forest || !forest.trees || forest.trees.length === 0) return null;
|
|
605
|
+
const total = forest.trees.length;
|
|
606
|
+
const idx = Math.min(activeIndex ?? activeTreeIndex(forest), total - 1);
|
|
607
|
+
const tree = forest.trees[idx];
|
|
608
|
+
if (!tree || !tree.nodes || tree.nodes.length === 0) return null;
|
|
609
|
+
const hasMultiple = total > 1;
|
|
610
|
+
const headerTitle = hasMultiple ? `Trace ${idx + 1}/${total}` : "Trace";
|
|
611
|
+
return (0, import_react.createElement)(
|
|
612
|
+
"section",
|
|
613
|
+
{
|
|
614
|
+
className: "ops-trace-root",
|
|
615
|
+
"data-testid": "ops-trace-panel",
|
|
616
|
+
"aria-label": "Trace"
|
|
617
|
+
},
|
|
618
|
+
(0, import_react.createElement)(
|
|
619
|
+
"div",
|
|
620
|
+
{ className: "ops-trace-body" },
|
|
621
|
+
// Header row: clicking anywhere (except the switcher) toggles expand/collapse
|
|
622
|
+
(0, import_react.createElement)(
|
|
623
|
+
"div",
|
|
624
|
+
{
|
|
625
|
+
className: "ops-trace-header",
|
|
626
|
+
onClick: (e) => {
|
|
627
|
+
if (e.target.closest(".ops-trace-switcher")) return;
|
|
628
|
+
setCollapsed(!collapsed);
|
|
629
|
+
},
|
|
630
|
+
role: "button",
|
|
631
|
+
tabIndex: 0,
|
|
632
|
+
"aria-expanded": !collapsed,
|
|
633
|
+
onKeyDown: (e) => {
|
|
634
|
+
if (e.target.closest(".ops-trace-switcher")) return;
|
|
635
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
636
|
+
e.preventDefault();
|
|
637
|
+
setCollapsed(!collapsed);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
},
|
|
641
|
+
(0, import_react.createElement)("span", { className: "ops-trace-lead", "aria-hidden": "true" }, (0, import_react.createElement)(import_dsh_client_ui_primitives.IconChecklistOutline14)),
|
|
642
|
+
// "Trace 1/2" — click to toggle the selector list
|
|
643
|
+
hasMultiple ? (0, import_react.createElement)(
|
|
644
|
+
"button",
|
|
645
|
+
{
|
|
646
|
+
type: "button",
|
|
647
|
+
className: "ops-trace-switcher",
|
|
648
|
+
onClick: () => setSelectorOpen((v) => !v),
|
|
649
|
+
"aria-expanded": selectorOpen
|
|
650
|
+
},
|
|
651
|
+
headerTitle,
|
|
652
|
+
(0, import_react.createElement)(
|
|
653
|
+
"span",
|
|
654
|
+
{ className: "ops-trace-switcher-arrow", "aria-hidden": "true" },
|
|
655
|
+
selectorOpen ? (0, import_react.createElement)(import_dsh_client_ui_primitives.IconChevronUpOutline14) : (0, import_react.createElement)(import_dsh_client_ui_primitives.IconChevronDownOutline14)
|
|
656
|
+
)
|
|
657
|
+
) : (0, import_react.createElement)("span", { className: "ops-trace-title" }, headerTitle),
|
|
658
|
+
(0, import_react.createElement)("span", { className: "ops-trace-progress" }, progressLabel(tree)),
|
|
659
|
+
(0, import_react.createElement)(
|
|
660
|
+
"span",
|
|
661
|
+
{ className: "ops-trace-chevron", "aria-hidden": "true" },
|
|
662
|
+
collapsed ? (0, import_react.createElement)(import_dsh_client_ui_primitives.IconChevronUpOutline14) : (0, import_react.createElement)(import_dsh_client_ui_primitives.IconChevronDownOutline14)
|
|
663
|
+
)
|
|
664
|
+
),
|
|
665
|
+
// Selector list — toggled by clicking "Trace N/M"
|
|
666
|
+
hasMultiple && selectorOpen && (0, import_react.createElement)(
|
|
667
|
+
"ul",
|
|
668
|
+
{ className: "ops-trace-selector-list" },
|
|
669
|
+
forest.trees.map((t, i) => {
|
|
670
|
+
const tGoal = t.nodes[0]?.title ?? "";
|
|
671
|
+
const tResolved = t.resolved;
|
|
672
|
+
const isActive = i === idx;
|
|
673
|
+
return (0, import_react.createElement)(
|
|
674
|
+
"li",
|
|
675
|
+
{ key: i },
|
|
676
|
+
(0, import_react.createElement)(
|
|
677
|
+
"button",
|
|
678
|
+
{
|
|
679
|
+
type: "button",
|
|
680
|
+
className: isActive ? "ops-trace-selector-item ops-trace-selector-item-active" : "ops-trace-selector-item",
|
|
681
|
+
onClick: () => {
|
|
682
|
+
setActiveIndex(i);
|
|
683
|
+
setSelectorOpen(false);
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
(0, import_react.createElement)("span", { className: isActive ? "ops-trace-selector-dot ops-trace-selector-dot-active" : "ops-trace-selector-dot", "aria-hidden": "true" }),
|
|
687
|
+
(0, import_react.createElement)("span", { className: "ops-trace-selector-title" }, tGoal),
|
|
688
|
+
tResolved && (0, import_react.createElement)("span", { className: "ops-trace-selector-tag" }, "resolved")
|
|
689
|
+
)
|
|
690
|
+
);
|
|
691
|
+
})
|
|
692
|
+
),
|
|
693
|
+
// Tree content — toggled by chevron
|
|
694
|
+
!collapsed && (() => {
|
|
695
|
+
const cache = {};
|
|
696
|
+
const sorted = flattenTree(tree.nodes);
|
|
697
|
+
return (0, import_react.createElement)(
|
|
698
|
+
"ul",
|
|
699
|
+
{ className: "ops-trace-list" },
|
|
700
|
+
sorted.map((node) => {
|
|
701
|
+
const depth = depthOf(tree.nodes, node.id, cache);
|
|
702
|
+
return (0, import_react.createElement)(TreeNodeItem, { key: node.id, node, depth, nodes: tree.nodes });
|
|
703
|
+
})
|
|
704
|
+
);
|
|
705
|
+
})()
|
|
706
|
+
)
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
function apply(ctx) {
|
|
710
|
+
injectCSS();
|
|
711
|
+
ctx.slots.inject(
|
|
712
|
+
"conversation.input.dock",
|
|
713
|
+
() => ctx.slots.register(
|
|
714
|
+
{ name: "conversation.input.dock", id: "ops-tree", order: 10 },
|
|
715
|
+
TraceDock
|
|
716
|
+
)
|
|
717
|
+
);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
return module.exports; } });
|
|
721
|
+
//# sourceMappingURL=client.js.map
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ops-trace-ui: the host-plane half of the trace feature.
|
|
3
|
+
*
|
|
4
|
+
* Registers the shared `trace` session projection (defined once in
|
|
5
|
+
* @elinpf/dsh-ops-tool-trace) and carries the web client bundle — the
|
|
6
|
+
* investigation-tree panel. Client-half discovery is runtime: the web app's
|
|
7
|
+
* ClientModuleRegistry scans the composed host cordis entries, so this
|
|
8
|
+
* package must stay mounted host-plane via its cordis.patch.yml row or the
|
|
9
|
+
* panel never reaches the browser.
|
|
10
|
+
*
|
|
11
|
+
* Registers no tools and no prompt sections. The trace tool and its
|
|
12
|
+
* methodology live in @elinpf/dsh-ops-tool-trace, mounted preset-plane.
|
|
13
|
+
*
|
|
14
|
+
* @module @elinpf/dsh-ops-trace-ui
|
|
15
|
+
*/
|
|
16
|
+
import z from '@deepseek-ai/schemastery';
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
declare const name = "ops-trace-ui";
|
|
19
|
+
declare const inject: string[];
|
|
20
|
+
declare const Config: z<Schemastery.ObjectS<{}>, Schemastery.ObjectT<{}>>;
|
|
21
|
+
declare function apply(ctx: Context, _config: Record<string, never>): void;
|
|
22
|
+
export { Config, apply, inject, name };
|
|
23
|
+
export type { DockUiState, TraceDockProps } from './types.js';
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ops-trace-ui: the host-plane half of the trace feature.
|
|
3
|
+
*
|
|
4
|
+
* Registers the shared `trace` session projection (defined once in
|
|
5
|
+
* @elinpf/dsh-ops-tool-trace) and carries the web client bundle — the
|
|
6
|
+
* investigation-tree panel. Client-half discovery is runtime: the web app's
|
|
7
|
+
* ClientModuleRegistry scans the composed host cordis entries, so this
|
|
8
|
+
* package must stay mounted host-plane via its cordis.patch.yml row or the
|
|
9
|
+
* panel never reaches the browser.
|
|
10
|
+
*
|
|
11
|
+
* Registers no tools and no prompt sections. The trace tool and its
|
|
12
|
+
* methodology live in @elinpf/dsh-ops-tool-trace, mounted preset-plane.
|
|
13
|
+
*
|
|
14
|
+
* @module @elinpf/dsh-ops-trace-ui
|
|
15
|
+
*/
|
|
16
|
+
import z from '@deepseek-ai/schemastery';
|
|
17
|
+
import { traceProjection } from '@elinpf/dsh-ops-tool-trace';
|
|
18
|
+
// No local module augmentation: the sessionProjections typing lives in
|
|
19
|
+
// ops-tool-trace's index (the shared home) and applies globally via its d.ts.
|
|
20
|
+
// ── Plugin identity ───────────────────────────────────────────────────────────
|
|
21
|
+
const name = 'ops-trace-ui';
|
|
22
|
+
const inject = [];
|
|
23
|
+
// ── Config ───────────────────────────────────────────────────────────────────
|
|
24
|
+
const Config = z.object({});
|
|
25
|
+
function apply(ctx, _config) {
|
|
26
|
+
// sessionProjections is a host-plane service; a static inject is not
|
|
27
|
+
// declared so the loader does not order this row against it — defer with
|
|
28
|
+
// ctx.inject, the same pattern the tool half uses.
|
|
29
|
+
ctx.inject(['sessionProjections'], (pctx) => {
|
|
30
|
+
pctx.sessionProjections.register(traceProjection);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export { Config, apply, inject, name };
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant companion for @elinpf/dsh-ops-trace-ui.
|
|
3
|
+
*
|
|
4
|
+
* @module @elinpf/dsh-ops-trace-ui/invariant
|
|
5
|
+
*/
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
declare const name = "ops-trace-ui-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Context carrying the invariant service.
|
|
13
|
+
* @returns a promise resolving after registration.
|
|
14
|
+
*/
|
|
15
|
+
declare const apply: (ctx: any) => Promise<void>;
|
|
16
|
+
export { apply, inject, name };
|
|
17
|
+
//# sourceMappingURL=invariant.d.ts.map
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant companion for @elinpf/dsh-ops-trace-ui.
|
|
3
|
+
*
|
|
4
|
+
* @module @elinpf/dsh-ops-trace-ui/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = '@elinpf/dsh-ops-trace-ui';
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = 'ops-trace-ui-invariant';
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ['invariants'];
|
|
11
|
+
/**
|
|
12
|
+
* No runtime invariant: this package owns no session event types and no
|
|
13
|
+
* durable shape of its own. It re-registers the shared `trace` projection
|
|
14
|
+
* verbatim from @elinpf/dsh-ops-tool-trace (whose fold is pure and
|
|
15
|
+
* idempotent, validated tool-side before events reach the log) and carries
|
|
16
|
+
* the web panel client bundle — the panel only reads projection snapshots
|
|
17
|
+
* and holds ephemeral UI state.
|
|
18
|
+
*/
|
|
19
|
+
const install = () => { };
|
|
20
|
+
/**
|
|
21
|
+
* Register this package's invariant companion.
|
|
22
|
+
* @param ctx - Context carrying the invariant service.
|
|
23
|
+
* @returns a promise resolving after registration.
|
|
24
|
+
*/
|
|
25
|
+
const apply = async (ctx) => {
|
|
26
|
+
ctx.invariants.register(PACKAGE_NAME, install);
|
|
27
|
+
};
|
|
28
|
+
export { apply, inject, name };
|
|
29
|
+
//# sourceMappingURL=invariant.js.map
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for ops-trace-ui.
|
|
3
|
+
*
|
|
4
|
+
* Types only — no runtime values live here. The client half imports the
|
|
5
|
+
* dock's UI-state shape from here; the host entry re-exports both so the
|
|
6
|
+
* package has one types home (also reachable via the ./types subpath).
|
|
7
|
+
*
|
|
8
|
+
* @module @elinpf/dsh-ops-trace-ui/types
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Per-session panel UI state. The dock unmounts when switching
|
|
12
|
+
* conversations, so React state resets on every switch-back — the two user
|
|
13
|
+
* choices live in a module-level map keyed by session to survive unmount.
|
|
14
|
+
* `activeIndex` null = follow the active tree (auto), set = user-pinned.
|
|
15
|
+
*/
|
|
16
|
+
export interface DockUiState {
|
|
17
|
+
collapsed: boolean;
|
|
18
|
+
activeIndex: number | null;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Props the dock slot runtime hands to the trace panel component. Both are
|
|
22
|
+
* optional: the panel degrades to rendering nothing when the projection
|
|
23
|
+
* registry or the session handle is absent.
|
|
24
|
+
*/
|
|
25
|
+
export interface TraceDockProps {
|
|
26
|
+
/** Projection read face; throws when the projection registry is absent. */
|
|
27
|
+
useProjection?: (key: string) => unknown | undefined;
|
|
28
|
+
/** The session the dock renders for. */
|
|
29
|
+
session?: {
|
|
30
|
+
sessionId?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=types.d.ts.map
|
package/lib/types.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for ops-trace-ui.
|
|
3
|
+
*
|
|
4
|
+
* Types only — no runtime values live here. The client half imports the
|
|
5
|
+
* dock's UI-state shape from here; the host entry re-exports both so the
|
|
6
|
+
* package has one types home (also reachable via the ./types subpath).
|
|
7
|
+
*
|
|
8
|
+
* @module @elinpf/dsh-ops-trace-ui/types
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=types.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@elinpf/dsh-ops-trace-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Host-plane half of the ops trace feature — registers the shared trace session projection and ships the web panel client bundle. The tool itself lives in @elinpf/dsh-ops-tool-trace (preset plane).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"default": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./client": {
|
|
14
|
+
"default": "./lib/client.js"
|
|
15
|
+
},
|
|
16
|
+
"./invariant": {
|
|
17
|
+
"types": "./lib/invariant.d.ts",
|
|
18
|
+
"default": "./lib/invariant.js"
|
|
19
|
+
},
|
|
20
|
+
"./types": {
|
|
21
|
+
"types": "./lib/types.d.ts",
|
|
22
|
+
"default": "./lib/types.js"
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"lib/index.js",
|
|
28
|
+
"lib/client.js",
|
|
29
|
+
"lib/invariant.js",
|
|
30
|
+
"lib/types.js",
|
|
31
|
+
"lib/**/*.d.ts",
|
|
32
|
+
"cordis.patch.yml"
|
|
33
|
+
],
|
|
34
|
+
"dsh": {
|
|
35
|
+
"bundle": {
|
|
36
|
+
"patch": "./cordis.patch.yml"
|
|
37
|
+
},
|
|
38
|
+
"client": {
|
|
39
|
+
"platform": "web"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
44
|
+
"@elinpf/dsh-ops-tool-trace": "^0.1.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
48
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
|
|
49
|
+
"@deepseek-ai/dsh-session-projection": "^0.0.1-rc.1",
|
|
50
|
+
"react": "^18.2.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@deepseek-ai/cordis": "4.0.1",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.0.1-rc.5",
|
|
55
|
+
"@deepseek-ai/dsh-session-projection": "0.0.1-rc.1",
|
|
56
|
+
"esbuild": "^0.24.0",
|
|
57
|
+
"react": "18.3.1",
|
|
58
|
+
"typescript": "^5.4.0",
|
|
59
|
+
"vitest": "^4.1.11"
|
|
60
|
+
},
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"publishConfig": {
|
|
63
|
+
"access": "public"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsc && node esbuild.config.mjs",
|
|
67
|
+
"typecheck": "tsc --noEmit",
|
|
68
|
+
"test": "vitest run"
|
|
69
|
+
}
|
|
70
|
+
}
|