@try-works/dsh-recursive-mode 0.2.2 → 0.2.3
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/lib/client/board.d.ts +7 -1
- package/lib/client/index.d.ts +1 -0
- package/lib/client/slots.d.ts +10 -0
- package/lib/client.js +62 -7
- package/package.json +1 -1
- package/src/client/board.tsx +14 -7
- package/src/client/index.ts +1 -0
- package/src/client/slots.ts +43 -0
- package/src/client/styles.ts +11 -0
package/lib/client/board.d.ts
CHANGED
|
@@ -9,8 +9,14 @@ export interface BoardProps {
|
|
|
9
9
|
workspacePath?: string;
|
|
10
10
|
onOpenInspector?: (selection: BoardSelection) => void;
|
|
11
11
|
onClose?: () => void;
|
|
12
|
+
/**
|
|
13
|
+
* 'overlay' (default) renders the board as a full-viewport fixed overlay
|
|
14
|
+
* (shell.overlay / launcher). 'view' renders it inline, filling its parent
|
|
15
|
+
* (a conversation.view tab), with no close button and no fixed positioning.
|
|
16
|
+
*/
|
|
17
|
+
variant?: 'overlay' | 'view';
|
|
12
18
|
}
|
|
13
|
-
export declare function Board({ snapshot, workspacePath, onOpenInspector, onClose }: BoardProps): import("react").DetailedReactHTMLElement<{
|
|
19
|
+
export declare function Board({ snapshot, workspacePath, onOpenInspector, onClose, variant }: BoardProps): import("react").DetailedReactHTMLElement<{
|
|
14
20
|
className: string;
|
|
15
21
|
'data-theme': import("./theme.ts").BoardTheme;
|
|
16
22
|
}, HTMLElement> | null;
|
package/lib/client/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import type { ClientContext } from './contract.ts';
|
|
15
15
|
export { Board, listRuns } from './board.tsx';
|
|
16
16
|
export { Inspector } from './inspector.tsx';
|
|
17
|
+
export { RecursiveView } from './slots.ts';
|
|
17
18
|
export { RecursiveSettings } from './settings.tsx';
|
|
18
19
|
export { useLiveProjection } from './use-live.ts';
|
|
19
20
|
export type { LiveProjectionSnapshot } from './use-live.ts';
|
package/lib/client/slots.d.ts
CHANGED
|
@@ -41,6 +41,16 @@ export declare function useRecursiveSessions(useSessions: SnapshotSelectorHook<S
|
|
|
41
41
|
export declare function RecursiveLauncherGate({ useSessions }: {
|
|
42
42
|
useSessions: SnapshotSelectorHook<SessionListStateLike>;
|
|
43
43
|
}): ReactNode;
|
|
44
|
+
/**
|
|
45
|
+
* RecursiveView: the conversation.view tab body. Renders the run board INLINE
|
|
46
|
+
* (fills the view area) keyed on the CURRENT workspace, and swaps to the
|
|
47
|
+
* inspector modal when a run is opened. Read-only (R9): uses the live host
|
|
48
|
+
* route. No preset gate — the tab is discoverable in any session.
|
|
49
|
+
*/
|
|
50
|
+
export declare function RecursiveView({ useSessions, useWorkspaces }: {
|
|
51
|
+
useSessions: SnapshotSelectorHook<SessionListStateLike>;
|
|
52
|
+
useWorkspaces: SnapshotSelectorHook<WorkspaceListStateLike>;
|
|
53
|
+
}): ReactNode;
|
|
44
54
|
export declare function registerSlots(ctx: ClientContext): () => void;
|
|
45
55
|
/**
|
|
46
56
|
* Board overlay: subscribes to the shared board store, gates on open + recursive
|
package/lib/client.js
CHANGED
|
@@ -335,8 +335,8 @@ window.__ModuleLoader__.load({
|
|
|
335
335
|
for (const worktreeRoot of Object.keys(projection)) for (const runId of Object.keys(projection[worktreeRoot])) runs.push(projection[worktreeRoot][runId]);
|
|
336
336
|
return runs;
|
|
337
337
|
}
|
|
338
|
-
function closeButton$1(onClose) {
|
|
339
|
-
if (onClose === void 0) return null;
|
|
338
|
+
function closeButton$1(onClose, variant) {
|
|
339
|
+
if (onClose === void 0 || variant === "view") return null;
|
|
340
340
|
return (0, react.createElement)("button", {
|
|
341
341
|
type: "button",
|
|
342
342
|
className: "rec-close",
|
|
@@ -356,7 +356,7 @@ window.__ModuleLoader__.load({
|
|
|
356
356
|
"data-pill": kind
|
|
357
357
|
}, PILL_LABELS[kind]);
|
|
358
358
|
}
|
|
359
|
-
function Board({ snapshot, workspacePath, onOpenInspector, onClose }) {
|
|
359
|
+
function Board({ snapshot, workspacePath, onOpenInspector, onClose, variant = "overlay" }) {
|
|
360
360
|
const { theme, toggle } = useBoardTheme();
|
|
361
361
|
if (snapshot === null || snapshot.root === null) return null;
|
|
362
362
|
const runs = workspacePath !== void 0 && workspacePath !== "" && !sameWorkspacePath(workspacePath, snapshot.root) ? [] : listRuns(snapshot.projection);
|
|
@@ -365,21 +365,22 @@ window.__ModuleLoader__.load({
|
|
|
365
365
|
worktreeRoot: run.worktreeRoot,
|
|
366
366
|
runId: run.runId
|
|
367
367
|
});
|
|
368
|
+
const rootCls = variant === "view" ? "rec-board rec-board-view" : "rec-board";
|
|
368
369
|
if (runs.length === 0) return (0, react.createElement)("div", {
|
|
369
|
-
className:
|
|
370
|
+
className: rootCls,
|
|
370
371
|
"data-theme": theme,
|
|
371
372
|
"data-empty": true
|
|
372
373
|
}, (0, react.createElement)("header", { className: "rec-board-header" }, (0, react.createElement)("h2", { className: "rec-board-title" }, "Recursive runs"), (0, react.createElement)("span", { className: "rec-board-path" }, headerPath), (0, react.createElement)("span", { className: "rec-board-count" }, "0 runs"), (0, react.createElement)(ThemeToggle, {
|
|
373
374
|
theme,
|
|
374
375
|
toggle
|
|
375
|
-
}), closeButton$1(onClose)), (0, react.createElement)("p", { className: "rec-board-empty" }, "No recursive runs in this workspace yet."));
|
|
376
|
+
}), closeButton$1(onClose, variant)), (0, react.createElement)("p", { className: "rec-board-empty" }, "No recursive runs in this workspace yet."));
|
|
376
377
|
return (0, react.createElement)("div", {
|
|
377
|
-
className:
|
|
378
|
+
className: rootCls,
|
|
378
379
|
"data-theme": theme
|
|
379
380
|
}, (0, react.createElement)("header", { className: "rec-board-header" }, (0, react.createElement)("h2", { className: "rec-board-title" }, "Recursive runs"), (0, react.createElement)("span", { className: "rec-board-path" }, headerPath), (0, react.createElement)("span", { className: "rec-board-count" }, runs.length + " runs"), (0, react.createElement)(ThemeToggle, {
|
|
380
381
|
theme,
|
|
381
382
|
toggle
|
|
382
|
-
}), closeButton$1(onClose)), (0, react.createElement)("div", { className: "rec-columns" }, KANBAN_LANES.map((lane) => {
|
|
383
|
+
}), closeButton$1(onClose, variant)), (0, react.createElement)("div", { className: "rec-columns" }, KANBAN_LANES.map((lane) => {
|
|
383
384
|
const laneRuns = runs.filter((r) => columnForRun(r) === lane.id);
|
|
384
385
|
return (0, react.createElement)("section", {
|
|
385
386
|
key: lane.id,
|
|
@@ -781,6 +782,17 @@ window.__ModuleLoader__.load({
|
|
|
781
782
|
overflow: hidden;
|
|
782
783
|
}
|
|
783
784
|
|
|
785
|
+
/* Inline board (conversation.view tab): fills its parent view area, not fixed. */
|
|
786
|
+
.rec-board-view {
|
|
787
|
+
position: relative;
|
|
788
|
+
inset: auto;
|
|
789
|
+
z-index: auto;
|
|
790
|
+
width: 100%;
|
|
791
|
+
height: 100%;
|
|
792
|
+
min-height: 0;
|
|
793
|
+
flex: 1;
|
|
794
|
+
}
|
|
795
|
+
|
|
784
796
|
.rec-board-header {
|
|
785
797
|
display: flex;
|
|
786
798
|
align-items: center;
|
|
@@ -1257,9 +1269,51 @@ window.__ModuleLoader__.load({
|
|
|
1257
1269
|
onClick: () => boardState.openBoard()
|
|
1258
1270
|
}, "⧉");
|
|
1259
1271
|
}
|
|
1272
|
+
/**
|
|
1273
|
+
* RecursiveView: the conversation.view tab body. Renders the run board INLINE
|
|
1274
|
+
* (fills the view area) keyed on the CURRENT workspace, and swaps to the
|
|
1275
|
+
* inspector modal when a run is opened. Read-only (R9): uses the live host
|
|
1276
|
+
* route. No preset gate — the tab is discoverable in any session.
|
|
1277
|
+
*/
|
|
1278
|
+
function RecursiveView({ useSessions, useWorkspaces }) {
|
|
1279
|
+
const board = useBoardState();
|
|
1280
|
+
const sessions = useRecursiveSessions(useSessions);
|
|
1281
|
+
const wsPath = currentWorkspacePath(useWorkspaces((s) => s) ?? {
|
|
1282
|
+
items: [],
|
|
1283
|
+
recentWorkspaceId: void 0
|
|
1284
|
+
}, sessions);
|
|
1285
|
+
const snapshot = useLiveProjection({ cwd: wsPath });
|
|
1286
|
+
if (board.selection !== null) return (0, react.createElement)(Inspector, {
|
|
1287
|
+
runId: board.selection.runId,
|
|
1288
|
+
worktreeRoot: board.selection.worktreeRoot,
|
|
1289
|
+
snapshot,
|
|
1290
|
+
onBackToToolDetails: () => boardState.backToBoard(),
|
|
1291
|
+
onClose: () => boardState.close()
|
|
1292
|
+
});
|
|
1293
|
+
return (0, react.createElement)(Board, {
|
|
1294
|
+
snapshot,
|
|
1295
|
+
workspacePath: wsPath,
|
|
1296
|
+
variant: "view",
|
|
1297
|
+
onOpenInspector: (sel) => boardState.openInspector(sel)
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1260
1300
|
function registerSlots(ctx) {
|
|
1261
1301
|
const disposers = [];
|
|
1262
1302
|
disposers.push(injectBoardStyles());
|
|
1303
|
+
disposers.push(ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
1304
|
+
name: "conversation.view",
|
|
1305
|
+
id: "recursive",
|
|
1306
|
+
order: 20,
|
|
1307
|
+
label: "Recursive"
|
|
1308
|
+
}, (props) => {
|
|
1309
|
+
const useSessions = props?.useSessions;
|
|
1310
|
+
const useWorkspaces = props?.useWorkspaces;
|
|
1311
|
+
if (useSessions === void 0 || useWorkspaces === void 0) return null;
|
|
1312
|
+
return (0, react.createElement)(RecursiveView, {
|
|
1313
|
+
useSessions,
|
|
1314
|
+
useWorkspaces
|
|
1315
|
+
});
|
|
1316
|
+
})));
|
|
1263
1317
|
disposers.push(ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
|
|
1264
1318
|
name: "sidebar.footer.action",
|
|
1265
1319
|
id: "recursive",
|
|
@@ -1355,6 +1409,7 @@ window.__ModuleLoader__.load({
|
|
|
1355
1409
|
exports.Board = Board;
|
|
1356
1410
|
exports.Inspector = Inspector;
|
|
1357
1411
|
exports.RecursiveSettings = RecursiveSettings;
|
|
1412
|
+
exports.RecursiveView = RecursiveView;
|
|
1358
1413
|
exports.apply = apply;
|
|
1359
1414
|
exports.currentSessionCwd = currentSessionCwd;
|
|
1360
1415
|
exports.currentWorkspacePath = currentWorkspacePath;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@try-works/dsh-recursive-mode",
|
|
3
3
|
"description": "recursive-mode workflow as a DeepSeek Harness bundle: RecursiveRuntime service + recursive_status tool",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"types": "lib/index.d.ts",
|
package/src/client/board.tsx
CHANGED
|
@@ -32,10 +32,16 @@ export interface BoardProps {
|
|
|
32
32
|
workspacePath?: string
|
|
33
33
|
onOpenInspector?: (selection: BoardSelection) => void
|
|
34
34
|
onClose?: () => void
|
|
35
|
+
/**
|
|
36
|
+
* 'overlay' (default) renders the board as a full-viewport fixed overlay
|
|
37
|
+
* (shell.overlay / launcher). 'view' renders it inline, filling its parent
|
|
38
|
+
* (a conversation.view tab), with no close button and no fixed positioning.
|
|
39
|
+
*/
|
|
40
|
+
variant?: 'overlay' | 'view'
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
function closeButton(onClose: (() => void) | undefined) {
|
|
38
|
-
if (onClose === undefined) return null
|
|
43
|
+
function closeButton(onClose: (() => void) | undefined, variant: 'overlay' | 'view') {
|
|
44
|
+
if (onClose === undefined || variant === 'view') return null
|
|
39
45
|
return createElement('button', { type: 'button', className: 'rec-close', onClick: onClose, title: 'Close', 'aria-label': 'Close' }, '×')
|
|
40
46
|
}
|
|
41
47
|
|
|
@@ -49,7 +55,7 @@ function solidPill(kind: ReturnType<typeof cardPill>) {
|
|
|
49
55
|
return createElement('span', { className: 'rec-pill', 'data-pill': kind }, PILL_LABELS[kind])
|
|
50
56
|
}
|
|
51
57
|
|
|
52
|
-
export function Board({ snapshot, workspacePath, onOpenInspector, onClose }: BoardProps) {
|
|
58
|
+
export function Board({ snapshot, workspacePath, onOpenInspector, onClose, variant = 'overlay' }: BoardProps) {
|
|
53
59
|
const { theme, toggle } = useBoardTheme()
|
|
54
60
|
if (snapshot === null || snapshot.root === null) return null
|
|
55
61
|
// Run 16: when the synchronous workspace path differs from the async snapshot
|
|
@@ -59,25 +65,26 @@ export function Board({ snapshot, workspacePath, onOpenInspector, onClose }: Boa
|
|
|
59
65
|
const runs = stale ? [] : listRuns(snapshot.projection)
|
|
60
66
|
const headerPath = workspacePath ?? snapshot.root
|
|
61
67
|
const open = (run: RecursiveRunCard) => () => onOpenInspector?.({ worktreeRoot: run.worktreeRoot, runId: run.runId })
|
|
68
|
+
const rootCls = variant === 'view' ? 'rec-board rec-board-view' : 'rec-board'
|
|
62
69
|
if (runs.length === 0) {
|
|
63
|
-
return createElement('div', { className:
|
|
70
|
+
return createElement('div', { className: rootCls, 'data-theme': theme, 'data-empty': true },
|
|
64
71
|
createElement('header', { className: 'rec-board-header' },
|
|
65
72
|
createElement('h2', { className: 'rec-board-title' }, 'Recursive runs'),
|
|
66
73
|
createElement('span', { className: 'rec-board-path' }, headerPath),
|
|
67
74
|
createElement('span', { className: 'rec-board-count' }, '0 runs'),
|
|
68
75
|
createElement(ThemeToggle, { theme, toggle }),
|
|
69
|
-
closeButton(onClose),
|
|
76
|
+
closeButton(onClose, variant),
|
|
70
77
|
),
|
|
71
78
|
createElement('p', { className: 'rec-board-empty' }, 'No recursive runs in this workspace yet.'),
|
|
72
79
|
)
|
|
73
80
|
}
|
|
74
|
-
return createElement('div', { className:
|
|
81
|
+
return createElement('div', { className: rootCls, 'data-theme': theme },
|
|
75
82
|
createElement('header', { className: 'rec-board-header' },
|
|
76
83
|
createElement('h2', { className: 'rec-board-title' }, 'Recursive runs'),
|
|
77
84
|
createElement('span', { className: 'rec-board-path' }, headerPath),
|
|
78
85
|
createElement('span', { className: 'rec-board-count' }, runs.length + ' runs'),
|
|
79
86
|
createElement(ThemeToggle, { theme, toggle }),
|
|
80
|
-
closeButton(onClose),
|
|
87
|
+
closeButton(onClose, variant),
|
|
81
88
|
),
|
|
82
89
|
createElement('div', { className: 'rec-columns' },
|
|
83
90
|
KANBAN_LANES.map((lane) => {
|
package/src/client/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { claimClientApply, releaseClientApply } from './apply-guard.ts'
|
|
|
17
17
|
|
|
18
18
|
export { Board, listRuns } from './board.tsx'
|
|
19
19
|
export { Inspector } from './inspector.tsx'
|
|
20
|
+
export { RecursiveView } from './slots.ts'
|
|
20
21
|
export { RecursiveSettings } from './settings.tsx'
|
|
21
22
|
export { useLiveProjection } from './use-live.ts'
|
|
22
23
|
export type { LiveProjectionSnapshot } from './use-live.ts'
|
package/src/client/slots.ts
CHANGED
|
@@ -64,11 +64,54 @@ export function RecursiveLauncherGate({ useSessions }: { useSessions: SnapshotSe
|
|
|
64
64
|
return createElement('button', { className: 'rec-launcher', title: 'Recursive runs', onClick: () => boardState.openBoard() }, '⧉')
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* RecursiveView: the conversation.view tab body. Renders the run board INLINE
|
|
69
|
+
* (fills the view area) keyed on the CURRENT workspace, and swaps to the
|
|
70
|
+
* inspector modal when a run is opened. Read-only (R9): uses the live host
|
|
71
|
+
* route. No preset gate — the tab is discoverable in any session.
|
|
72
|
+
*/
|
|
73
|
+
export function RecursiveView({ useSessions, useWorkspaces }: { useSessions: SnapshotSelectorHook<SessionListStateLike>; useWorkspaces: SnapshotSelectorHook<WorkspaceListStateLike> }): ReactNode {
|
|
74
|
+
const board = useBoardState()
|
|
75
|
+
const sessions = useRecursiveSessions(useSessions)
|
|
76
|
+
const workspaces: WorkspaceListStateLike = useWorkspaces((s) => s) ?? { items: [], recentWorkspaceId: undefined }
|
|
77
|
+
const wsPath = currentWorkspacePath(workspaces, sessions)
|
|
78
|
+
const scope = { cwd: wsPath }
|
|
79
|
+
const snapshot = useLiveProjection(scope)
|
|
80
|
+
// Inspector drill-down (modal over the inline board) when a run is selected.
|
|
81
|
+
if (board.selection !== null) {
|
|
82
|
+
return createElement(Inspector, {
|
|
83
|
+
runId: board.selection.runId,
|
|
84
|
+
worktreeRoot: board.selection.worktreeRoot,
|
|
85
|
+
snapshot,
|
|
86
|
+
onBackToToolDetails: () => boardState.backToBoard(),
|
|
87
|
+
onClose: () => boardState.close(),
|
|
88
|
+
})
|
|
89
|
+
}
|
|
90
|
+
return createElement(Board, { snapshot, workspacePath: wsPath, variant: 'view', onOpenInspector: (sel) => boardState.openInspector(sel) })
|
|
91
|
+
}
|
|
92
|
+
|
|
67
93
|
export function registerSlots(ctx: ClientContext): () => void {
|
|
68
94
|
const disposers: (() => void)[] = []
|
|
69
95
|
// Run 15: inject the one-shot theme-token stylesheet once per document (idempotent).
|
|
70
96
|
disposers.push(injectBoardStyles())
|
|
71
97
|
|
|
98
|
+
// Conversation view tab (Chat | Trajectory | Recursive): the recursive run
|
|
99
|
+
// board as a first-class tab in the conversation header, rendered INLINE in
|
|
100
|
+
// the view area (not a fixed overlay). Order 20 places it to the RIGHT of
|
|
101
|
+
// Trajectory (order 10). Always present — no recursive-preset gate, so the
|
|
102
|
+
// entry point is discoverable in any session.
|
|
103
|
+
disposers.push(ctx.slots.inject('conversation.view', () => ctx.slots.register({
|
|
104
|
+
name: 'conversation.view',
|
|
105
|
+
id: 'recursive',
|
|
106
|
+
order: 20,
|
|
107
|
+
label: 'Recursive',
|
|
108
|
+
}, (props: RootSlotProps) => {
|
|
109
|
+
const useSessions = props?.useSessions
|
|
110
|
+
const useWorkspaces = props?.useWorkspaces
|
|
111
|
+
if (useSessions === undefined || useWorkspaces === undefined) return null
|
|
112
|
+
return createElement(RecursiveView, { useSessions, useWorkspaces })
|
|
113
|
+
})))
|
|
114
|
+
|
|
72
115
|
// Board launcher in the sidebar footer action list — OPENS the shared board store (run 08 R1).
|
|
73
116
|
// Run 11 (UX gate): the seat is root-scoped (visible in every session), but the board it
|
|
74
117
|
// opens is recursive-preset-gated. In a code/other session the icon was a dead click — a
|
package/src/client/styles.ts
CHANGED
|
@@ -136,6 +136,17 @@ const BOARD_CSS = `
|
|
|
136
136
|
overflow: hidden;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
+
/* Inline board (conversation.view tab): fills its parent view area, not fixed. */
|
|
140
|
+
.rec-board-view {
|
|
141
|
+
position: relative;
|
|
142
|
+
inset: auto;
|
|
143
|
+
z-index: auto;
|
|
144
|
+
width: 100%;
|
|
145
|
+
height: 100%;
|
|
146
|
+
min-height: 0;
|
|
147
|
+
flex: 1;
|
|
148
|
+
}
|
|
149
|
+
|
|
139
150
|
.rec-board-header {
|
|
140
151
|
display: flex;
|
|
141
152
|
align-items: center;
|