@khorsheed/dsh-room 0.2.2 → 0.2.4

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.
Files changed (109) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.en.md +29 -62
  3. package/README.i18n.yaml +2 -2
  4. package/README.md +28 -61
  5. package/lib/client.js +14620 -5705
  6. package/lib/index.js +4069 -730
  7. package/lib/journal-B4FOGO6S.js +482 -0
  8. package/lib/journal-Rid9AAnD.js +485 -0
  9. package/lib/tool.js +112 -166
  10. package/lib/typert.host.js +2031 -374
  11. package/lib/typert.remote-client.d.ts +34 -10
  12. package/lib/typert.remote-client.js +1599 -210
  13. package/lib/types/agent-setup.d.ts +1 -1
  14. package/lib/types/client/InviteAgentAction.js +4 -16
  15. package/lib/types/client/InviteDialog.d.ts +28 -6
  16. package/lib/types/client/InviteDialog.js +76 -12
  17. package/lib/types/client/MemberCard.d.ts +5 -1
  18. package/lib/types/client/MemberCard.js +4 -4
  19. package/lib/types/client/MembersView.d.ts +1 -1
  20. package/lib/types/client/MembersView.js +58 -22
  21. package/lib/types/client/RemoveMemberConfirm.d.ts +40 -0
  22. package/lib/types/client/RemoveMemberConfirm.js +85 -0
  23. package/lib/types/client/RoomApprovalView.d.ts +11 -0
  24. package/lib/types/client/RoomApprovalView.js +28 -0
  25. package/lib/types/client/RoomComposer.d.ts +1 -1
  26. package/lib/types/client/RoomComposer.js +195 -29
  27. package/lib/types/client/RoomDecisionView.d.ts +22 -0
  28. package/lib/types/client/RoomDecisionView.js +35 -0
  29. package/lib/types/client/RoomHelpView.d.ts +17 -0
  30. package/lib/types/client/RoomHelpView.js +56 -0
  31. package/lib/types/client/RoomInboxView.d.ts +10 -0
  32. package/lib/types/client/RoomInboxView.js +14 -0
  33. package/lib/types/client/RoomQuestionView.d.ts +13 -0
  34. package/lib/types/client/RoomQuestionView.js +61 -0
  35. package/lib/types/client/RoomRelayView.js +1 -1
  36. package/lib/types/client/RoomRunView.d.ts +3 -1
  37. package/lib/types/client/RoomRunView.js +12 -5
  38. package/lib/types/client/RoomSettingsView.d.ts +19 -0
  39. package/lib/types/client/RoomSettingsView.js +30 -0
  40. package/lib/types/client/RoomSpeechView.d.ts +22 -8
  41. package/lib/types/client/RoomSpeechView.js +94 -11
  42. package/lib/types/client/RoomStatsLine.d.ts +26 -5
  43. package/lib/types/client/RoomStatsLine.js +53 -7
  44. package/lib/types/client/RoomTaskCapsule.d.ts +19 -0
  45. package/lib/types/client/RoomTaskCapsule.js +37 -0
  46. package/lib/types/client/TasksTab.d.ts +41 -0
  47. package/lib/types/client/TasksTab.js +43 -0
  48. package/lib/types/client/TasksView.d.ts +44 -0
  49. package/lib/types/client/TasksView.js +149 -0
  50. package/lib/types/client/TeamView.d.ts +33 -0
  51. package/lib/types/client/TeamView.js +210 -0
  52. package/lib/types/client/at-menu.d.ts +127 -0
  53. package/lib/types/client/at-menu.js +173 -0
  54. package/lib/types/client/draft-chips.d.ts +67 -0
  55. package/lib/types/client/draft-chips.js +95 -0
  56. package/lib/types/client/executions.d.ts +0 -30
  57. package/lib/types/client/executions.js +4 -34
  58. package/lib/types/client/icons-local.d.ts +25 -0
  59. package/lib/types/client/icons-local.js +10 -0
  60. package/lib/types/client/icons.d.ts +2 -1
  61. package/lib/types/client/icons.js +1 -0
  62. package/lib/types/client/index.js +276 -72
  63. package/lib/types/client/locales.d.ts +482 -52
  64. package/lib/types/client/locales.js +490 -60
  65. package/lib/types/client/nodes.d.ts +112 -17
  66. package/lib/types/client/nodes.js +197 -26
  67. package/lib/types/client/slots.d.ts +117 -47
  68. package/lib/types/client/task-tag.d.ts +32 -0
  69. package/lib/types/client/task-tag.js +33 -0
  70. package/lib/types/client/tasks.d.ts +50 -0
  71. package/lib/types/client/tasks.js +82 -0
  72. package/lib/types/client/team.d.ts +73 -0
  73. package/lib/types/client/team.js +187 -0
  74. package/lib/types/decisions.d.ts +49 -0
  75. package/lib/types/decisions.js +101 -0
  76. package/lib/types/deliveries.d.ts +17 -1
  77. package/lib/types/deliveries.js +82 -6
  78. package/lib/types/dispatch.d.ts +102 -4
  79. package/lib/types/dispatch.js +498 -109
  80. package/lib/types/help.d.ts +137 -0
  81. package/lib/types/help.js +216 -0
  82. package/lib/types/helps.d.ts +26 -0
  83. package/lib/types/helps.js +47 -0
  84. package/lib/types/index.d.ts +292 -52
  85. package/lib/types/index.js +1421 -199
  86. package/lib/types/jobs.d.ts +22 -0
  87. package/lib/types/jobs.js +77 -0
  88. package/lib/types/journal.d.ts +17 -27
  89. package/lib/types/journal.js +120 -43
  90. package/lib/types/messaging.d.ts +65 -0
  91. package/lib/types/messaging.js +160 -0
  92. package/lib/types/plan-service.js +7 -1
  93. package/lib/types/report-gate.d.ts +81 -0
  94. package/lib/types/report-gate.js +89 -0
  95. package/lib/types/tool-definition.d.ts +43 -20
  96. package/lib/types/tool-definition.js +145 -212
  97. package/lib/types/tool.d.ts +2 -2
  98. package/lib/types/tool.js +3 -2
  99. package/lib/types/types.d.ts +755 -78
  100. package/locale/zh.json +1 -1
  101. package/package.json +3 -3
  102. package/lib/types/client/RoomActivityDock.d.ts +0 -19
  103. package/lib/types/client/RoomActivityDock.js +0 -105
  104. package/lib/types/client/RoomDockCapsules.d.ts +0 -64
  105. package/lib/types/client/RoomDockCapsules.js +0 -277
  106. package/lib/types/client/RoomPlanTab.d.ts +0 -15
  107. package/lib/types/client/RoomPlanTab.js +0 -13
  108. package/lib/types/client/RoomTaskLineView.d.ts +0 -13
  109. package/lib/types/client/RoomTaskLineView.js +0 -21
package/locale/zh.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "meta": {
3
- "title": "多Agent协同",
3
+ "title": "协作间",
4
4
  "description": "把会话升级为有协调者和成员的共享协作空间。"
5
5
  }
6
6
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@khorsheed/dsh-room",
3
- "description": "Room conversations with native or external coordinators, background delegation, durable goal plans and evidence review, shared member controls, live output and session navigation; the session-granted room model tools (`room_invite` / `room_task` / `room_message`) ship as the package's `./tool` composition entry",
3
+ "description": "Room conversations with native or external coordinators, background delegation, durable goal plans and evidence review, shared member controls, live output and session navigation; the session-granted room model tools (`room_invite` / `room_message` / `room_read` / `room_plan`) ship as the package's `./tool` composition entry",
4
4
  "keywords": [
5
5
  "dsh",
6
6
  "dsh-plugin",
7
7
  "cordis",
8
8
  "multi-agent"
9
9
  ],
10
- "version": "0.2.2",
10
+ "version": "0.2.4",
11
11
  "type": "module",
12
12
  "main": "lib/index.js",
13
13
  "types": "lib/types/index.d.ts",
@@ -85,7 +85,7 @@
85
85
  "@deepseek-ai/dsh-tool-todo": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
86
86
  "@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
87
87
  "@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1",
88
- "@khorsheed/dsh-local-agent": "^0.2.0-rc.2",
88
+ "@khorsheed/dsh-local-agent": "^0.2.1",
89
89
  "react": "^18.2.0",
90
90
  "@deepseek-ai/dsh-client-ui-sidebar-right": "^0.1.0-rc.6 || ^0.2.0-rc.1 || ^0.2.1-rc.1"
91
91
  },
@@ -1,19 +0,0 @@
1
- import { type ReactNode } from 'react';
2
- import type { RoomState } from '../types.ts';
3
- import type { RoomComposerProps } from './slots.ts';
4
- export interface RoomActivityDockProps {
5
- sessionId?: string;
6
- backgroundSessions?: RoomComposerProps['backgroundSessions'];
7
- activeChildren?: RoomComposerProps['activeChildren'];
8
- stopChild?: RoomComposerProps['stopChild'];
9
- state: RoomState;
10
- openSession?: RoomComposerProps['openPlanSession'];
11
- stopExecution?: RoomComposerProps['stopExecution'];
12
- planCommand?: RoomComposerProps['planCommand'];
13
- openPlan?: RoomComposerProps['openPlan'];
14
- stopMember?: RoomComposerProps['stopMember'];
15
- t: RoomComposerProps['t'];
16
- }
17
- /** Composer-owned capsules: panels grow above their triggers, never into the input. */
18
- export declare function RoomActivityDock({ state, sessionId, backgroundSessions, activeChildren, stopChild, openSession, stopExecution, planCommand, openPlan, stopMember, t }: RoomActivityDockProps): ReactNode;
19
- //# sourceMappingURL=RoomActivityDock.d.ts.map
@@ -1,105 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect, useId, useRef, useState, useSyncExternalStore } from 'react';
3
- import { IconAgentPresetOutlineMedium, IconChecklistOutlineMedium, IconStopFillMedium } from './icons.js';
4
- import { descendantRows, executionRows } from './executions.js';
5
- import { formatDurationMs } from './format.js';
6
- import { RoomPlanView } from './RoomPlanView.js';
7
- import css from './RoomActivityDock.module.css';
8
- const EMPTY_SESSIONS = { byId: {} };
9
- const NO_SESSIONS = { subscribe: () => () => { }, getSnapshot: () => EMPTY_SESSIONS };
10
- /** Composer-owned capsules: panels grow above their triggers, never into the input. */
11
- export function RoomActivityDock({ state, sessionId, backgroundSessions = NO_SESSIONS, activeChildren, stopChild, openSession, stopExecution, planCommand, openPlan, stopMember, t }) {
12
- const [expanded, setExpanded] = useState(null);
13
- const [filter, setFilter] = useState('recent');
14
- const [detail, setDetail] = useState(false);
15
- const [pending, setPending] = useState(null);
16
- const [error, setError] = useState(null);
17
- const [now, setNow] = useState(Date.now);
18
- const [active, setActive] = useState([]);
19
- const root = useRef(null);
20
- const id = useId();
21
- const sessions = useSyncExternalStore(backgroundSessions.subscribe, backgroundSessions.getSnapshot);
22
- const descendants = sessionId === undefined ? [] : descendantRows(sessions.byId, sessionId, state, now, active);
23
- const rows = [...executionRows(state), ...descendants];
24
- const hasChildren = Object.values(sessions.byId).some(child => child.parentId === sessionId && child.origin === 'subagent');
25
- useEffect(() => {
26
- if (!activeChildren || !hasChildren)
27
- return;
28
- let disposed = false;
29
- let pending = false;
30
- const refresh = async () => {
31
- if (pending)
32
- return;
33
- pending = true;
34
- try {
35
- const value = await activeChildren();
36
- if (!disposed && value !== undefined)
37
- setActive(value);
38
- }
39
- catch { /* Unknown is not evidence that a delegation stopped. */ }
40
- finally {
41
- pending = false;
42
- }
43
- };
44
- void refresh();
45
- const timer = setInterval(() => { void refresh(); }, 2000);
46
- return () => { disposed = true; clearInterval(timer); };
47
- }, [activeChildren, hasChildren]);
48
- const running = rows.filter(row => row.status === 'running');
49
- useEffect(() => {
50
- if (expanded === null)
51
- return;
52
- const close = (event) => { if (event.target instanceof Node && !root.current?.contains(event.target))
53
- setExpanded(null); };
54
- const escape = (event) => { if (event.key === 'Escape') {
55
- setExpanded(null);
56
- root.current?.querySelector('[aria-expanded="true"]')?.focus();
57
- } };
58
- document.addEventListener('pointerdown', close);
59
- document.addEventListener('keydown', escape);
60
- return () => { document.removeEventListener('pointerdown', close); document.removeEventListener('keydown', escape); };
61
- }, [expanded]);
62
- useEffect(() => {
63
- if (expanded !== 'agents' || running.length === 0)
64
- return;
65
- setNow(Date.now());
66
- const timer = setInterval(() => setNow(Date.now()), 1000);
67
- return () => clearInterval(timer);
68
- }, [expanded, running.length]);
69
- const stop = async (row) => {
70
- if (pending !== null)
71
- return;
72
- setPending(row.id);
73
- setError(null);
74
- try {
75
- const result = row.sessionTotal && row.childSessionId ? await stopChild?.(row.childSessionId) : row.execution ? await stopExecution?.(row.member, row.execution) : undefined;
76
- if (!result)
77
- return;
78
- if (!result.ok)
79
- setError(result.message);
80
- }
81
- catch {
82
- setError(t('composer.error.generic'));
83
- }
84
- finally {
85
- setPending(null);
86
- }
87
- };
88
- const leaf = state.plan?.tasks.filter(task => task.kind === 'task' && task.status !== 'cancelled') ?? [];
89
- const accepted = leaf.filter(task => task.status === 'accepted').length;
90
- const visible = filter === 'running' ? running : filter === 'attention'
91
- ? rows.filter(row => ['failed', 'uncertain', 'submitted'].includes(row.status)) : filter === 'recent' ? rows.slice(0, 6) : rows;
92
- if (rows.length === 0 && state.plan === undefined)
93
- return null;
94
- return _jsxs("div", { ref: root, className: css.root, "data-testid": "room-activity-dock", children: [expanded === 'agents' && _jsxs("section", { className: css.panel, id: `${id}-agents`, "aria-label": t('activity.agents'), children: [_jsxs("div", { className: css.heading, children: [_jsx("strong", { children: t('activity.agents') }), _jsx("div", { className: css.filters, children: ['recent', 'running', 'attention', 'all'].map(value => _jsx("button", { type: "button", "aria-pressed": filter === value, onClick: () => setFilter(value), children: t(`activity.filter.${value}`) }, value)) })] }), error && _jsx("p", { role: "alert", children: error }), _jsx("div", { className: css.grid, children: visible.map(row => _jsxs("article", { className: css.card, children: [_jsxs("button", { type: "button", className: css.cardMain, disabled: !row.childSessionId || !openSession, onClick: () => row.childSessionId && openSession?.(row.childSessionId), "aria-label": `${t('activity.session')}: ${row.title}`, children: [_jsxs("span", { className: css.meta, children: [_jsx("span", { children: row.member }), _jsx("span", { "data-status": row.status, children: t(`activity.status.${row.status}`) })] }), _jsx("span", { className: css.title, title: row.title, children: row.title }), _jsx("span", { className: css.secondary, children: [row.provider, row.model, row.effort].filter(Boolean).join(' · ') })] }), row.error && _jsx("p", { className: css.error, children: row.error }), _jsxs("div", { className: css.footer, children: [_jsxs("span", { children: [row.sessionTotal ? `${t('activity.sessionTotal')} · ` : '', row.status === 'queued' ? t('activity.notStarted') : row.status === 'running' && row.startedAt !== undefined ? formatDurationMs(Math.max(0, now - row.startedAt)) : row.elapsedMs === undefined ? '—' : formatDurationMs(row.elapsedMs), row.tokens === undefined ? '' : ` · ${Intl.NumberFormat(undefined, { notation: 'compact', maximumFractionDigits: 1 }).format(row.tokens)} tok`] }), row.status === 'running' && ((row.execution && stopExecution) || (row.sessionTotal && stopChild)) ? _jsxs("button", { type: "button", disabled: pending !== null, onClick: () => { void stop(row); }, "aria-label": `${t('activity.stop')}: ${row.title}`, children: [_jsx(IconStopFillMedium, { size: 12 }), t('activity.stop')] })
95
- : row.childSessionId && openSession && _jsx("button", { type: "button", onClick: () => openSession(row.childSessionId), children: t('activity.session') })] })] }, row.id)) }), visible.length === 0 && _jsx("p", { className: css.empty, children: t('activity.empty') })] }), expanded === 'plan' && state.plan && _jsxs("section", { className: css.panel, id: `${id}-plan`, "aria-label": t('activity.plan'), children: [_jsxs("div", { className: css.heading, children: [_jsx("strong", { children: state.plan.objective }), _jsxs("span", { className: css.secondary, children: [t(`plan.status.${state.plan.status}`), " \u00B7 ", t('plan.acceptedCount', { done: accepted, total: leaf.length })] })] }), state.plan.reason && _jsx("p", { className: css.error, children: state.plan.reason }), _jsx("div", { className: css.planList, children: leaf.map(task => {
96
- const member = state.members.find(member => member.id === task.ownerMemberId);
97
- return _jsxs("button", { type: "button", className: css.planRow, onClick: () => { if (openPlan)
98
- openPlan();
99
- else
100
- setDetail(true); }, children: [_jsx("span", { className: css.taskDot, "data-status": task.status, "aria-hidden": true }), _jsx("span", { children: task.title }), _jsxs("span", { className: css.secondary, children: [member?.name, " \u00B7 ", t(`plan.status.${task.status}`)] })] }, task.id);
101
- }) }), leaf.length === 0 && _jsx("p", { className: css.empty, children: t('activity.planning') }), _jsxs("div", { className: css.footer, children: [_jsx("span", {}), _jsx("button", { type: "button", onClick: () => { if (openPlan)
102
- openPlan();
103
- else
104
- setDetail(value => !value); }, children: t('activity.planDetails') })] }), detail && !openPlan && planCommand && _jsx(RoomPlanView, { expanded: true, plan: state.plan, members: state.members, command: planCommand, openSession: openSession, stopMember: stopMember, t: t })] }), _jsxs("div", { className: css.capsules, children: [rows.length > 0 && _jsxs("button", { type: "button", className: css.capsule, "aria-expanded": expanded === 'agents', "aria-controls": `${id}-agents`, title: running.length > 0 ? t('activity.runningHint', { count: new Set(running.map(row => row.member)).size }) : t('activity.agents'), onClick: () => setExpanded(value => value === 'agents' ? null : 'agents'), children: [_jsx("span", { className: css.icon, children: running.length > 0 ? _jsx("span", { className: css.runningDot, "data-testid": "room-running-dot", "aria-hidden": true }) : _jsx(IconAgentPresetOutlineMedium, { size: 16 }) }), t('activity.agents')] }), state.plan && _jsxs("button", { type: "button", className: css.capsule, "aria-expanded": expanded === 'plan', "aria-controls": `${id}-plan`, onClick: () => setExpanded(value => value === 'plan' ? null : 'plan'), children: [_jsx(IconChecklistOutlineMedium, { size: 16 }), t('activity.plan'), " ", _jsxs("span", { className: css.secondary, children: [accepted, "/", leaf.length] })] })] })] });
105
- }
@@ -1,64 +0,0 @@
1
- /**
2
- * The room dock capsules, rendered by the RoomComposer itself above the input
3
- * card — the seat of the old task-board strip (NOT a `conversation.input.dock`
4
- * entry: that slot lives inside the official composer fallback, which the
5
- * room's composer takeover hides). One collapsed row of two capsules plus a
6
- * quick-add button:
7
- *
8
- * ```
9
- * [◔33% 插件API v2上线] [☑ 当前进度 1/3 · ●ada 在做] +
10
- * ```
11
- *
12
- * The row is ANCHORED just above the input card: an expanded panel renders
13
- * above the capsule row (top-to-bottom: panel → capsules → input card) and
14
- * grows upward only, so folding/unfolding never shifts the row under the
15
- * user's pointer.
16
- *
17
- * The GOAL capsule's only visual focus is the progress ring: an SVG circle
18
- * (tertiary track + business-primary progress arc) followed by the percent
19
- * and the truncated goal text; without a goal it renders the 「+ 设定目标」
20
- * guide state (no ring). It expands into the goal card: the full text with an
21
- * inline [编辑], a progress bar with the done/total fraction, and the recent
22
- * advance records (who finished what, relative time).
23
- *
24
- * The TASK capsule leads with the checklist icon (IconChecklistOutlineMedium from
25
- * the official primitives — never a Unicode glyph), then the open-task count
26
- * and the members currently running (color dot + name). It expands into the
27
- * Linear-style task panel: a member filter chip row (color-dot capsules, the
28
- * selected one takes a tinted fill of the member color — no outline) with the
29
- * +添加 trigger at its right end, small-caps tertiary group headers, and task
30
- * rows (colored status icon — business-primary half-ring for in_progress,
31
- * tertiary empty ring for pending, filled check for done, filled error × for
32
- * failed — + title + member chip + right-aligned tertiary status/relative
33
- * time + a row-end [完成]; a failed row's action is [关闭], dismissing to
34
- * cancelled — the run did not finish, so it never closes done).
35
- * Closing a task plays a restrained completion beat (the check pops, the
36
- * title's strike draws in, <300ms, reduced-motion safe) — the row remounts on
37
- * a status change (`key = id:status`) so the CSS animation replays. A blocked
38
- * row greys with a 「等 ada」 tag until its `blockedBy` member has no open
39
- * task left (display only: nothing auto-dispatches). The trailing + opens
40
- * the panel with the add form already expanded (member select + title +
41
- * optional "等谁"). Panels close on a second capsule click or a click
42
- * outside; no overlay.
43
- *
44
- * Visual layer: the capsules ride the official chip register (28px, r24,
45
- * interactive hover fill, 13/20/500 label) with a visible outline (the
46
- * InputBar card's `l2-darkmode-thin` pair, firming to `l3` on hover/expand);
47
- * the expanded cards are the
48
- * official Menu surface (`--dsw-specific-menu`, r12, inverted hairline,
49
- * shadow-lv3); the task capsule sweeps a restrained glare band while tasks
50
- * run (the ToolRow pattern, reduced-motion safe). Every token reference
51
- * carries the light-theme literal as fallback; no divider lines anywhere.
52
- *
53
- * FRESH-ROOM STATE: a room with neither a goal nor any task (membership does
54
- * not count — the main agent is always there) has no use for the guide/task
55
- * pair, which would be pure noise. The collapsed row then renders a single
56
- * 「+ 邀请成员」 capsule (same chip family) that opens the invite dialog
57
- * straight from the dock. The moment a goal is set or a task exists the pair
58
- * returns, and the invite entry retires — the members tab already carries it.
59
- */
60
- import { type ReactNode } from 'react';
61
- import type { RoomDockCapsulesProps } from './slots.ts';
62
- /** The dock capsules (goal + tasks) with their expanded cards. */
63
- export declare function RoomDockCapsules({ sessionId, roomStore, addTask, closeTask, setGoal, formalPlans, roomCwd, invite, listProviders, browseDirectory, modelSurface, renderHarnessModelPicker, t, }: RoomDockCapsulesProps): ReactNode;
64
- //# sourceMappingURL=RoomDockCapsules.d.ts.map
@@ -1,277 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * The room dock capsules, rendered by the RoomComposer itself above the input
4
- * card — the seat of the old task-board strip (NOT a `conversation.input.dock`
5
- * entry: that slot lives inside the official composer fallback, which the
6
- * room's composer takeover hides). One collapsed row of two capsules plus a
7
- * quick-add button:
8
- *
9
- * ```
10
- * [◔33% 插件API v2上线] [☑ 当前进度 1/3 · ●ada 在做] +
11
- * ```
12
- *
13
- * The row is ANCHORED just above the input card: an expanded panel renders
14
- * above the capsule row (top-to-bottom: panel → capsules → input card) and
15
- * grows upward only, so folding/unfolding never shifts the row under the
16
- * user's pointer.
17
- *
18
- * The GOAL capsule's only visual focus is the progress ring: an SVG circle
19
- * (tertiary track + business-primary progress arc) followed by the percent
20
- * and the truncated goal text; without a goal it renders the 「+ 设定目标」
21
- * guide state (no ring). It expands into the goal card: the full text with an
22
- * inline [编辑], a progress bar with the done/total fraction, and the recent
23
- * advance records (who finished what, relative time).
24
- *
25
- * The TASK capsule leads with the checklist icon (IconChecklistOutlineMedium from
26
- * the official primitives — never a Unicode glyph), then the open-task count
27
- * and the members currently running (color dot + name). It expands into the
28
- * Linear-style task panel: a member filter chip row (color-dot capsules, the
29
- * selected one takes a tinted fill of the member color — no outline) with the
30
- * +添加 trigger at its right end, small-caps tertiary group headers, and task
31
- * rows (colored status icon — business-primary half-ring for in_progress,
32
- * tertiary empty ring for pending, filled check for done, filled error × for
33
- * failed — + title + member chip + right-aligned tertiary status/relative
34
- * time + a row-end [完成]; a failed row's action is [关闭], dismissing to
35
- * cancelled — the run did not finish, so it never closes done).
36
- * Closing a task plays a restrained completion beat (the check pops, the
37
- * title's strike draws in, <300ms, reduced-motion safe) — the row remounts on
38
- * a status change (`key = id:status`) so the CSS animation replays. A blocked
39
- * row greys with a 「等 ada」 tag until its `blockedBy` member has no open
40
- * task left (display only: nothing auto-dispatches). The trailing + opens
41
- * the panel with the add form already expanded (member select + title +
42
- * optional "等谁"). Panels close on a second capsule click or a click
43
- * outside; no overlay.
44
- *
45
- * Visual layer: the capsules ride the official chip register (28px, r24,
46
- * interactive hover fill, 13/20/500 label) with a visible outline (the
47
- * InputBar card's `l2-darkmode-thin` pair, firming to `l3` on hover/expand);
48
- * the expanded cards are the
49
- * official Menu surface (`--dsw-specific-menu`, r12, inverted hairline,
50
- * shadow-lv3); the task capsule sweeps a restrained glare band while tasks
51
- * run (the ToolRow pattern, reduced-motion safe). Every token reference
52
- * carries the light-theme literal as fallback; no divider lines anywhere.
53
- *
54
- * FRESH-ROOM STATE: a room with neither a goal nor any task (membership does
55
- * not count — the main agent is always there) has no use for the guide/task
56
- * pair, which would be pure noise. The collapsed row then renders a single
57
- * 「+ 邀请成员」 capsule (same chip family) that opens the invite dialog
58
- * straight from the dock. The moment a goal is set or a task exists the pair
59
- * returns, and the invite entry retires — the members tab already carries it.
60
- */
61
- import { useEffect, useRef, useState, useSyncExternalStore, } from 'react';
62
- import { IconChecklistOutlineMedium } from './icons.js';
63
- import { taskProgress } from '../journal.js';
64
- import { formatRelativeTime } from './format.js';
65
- import { memberColor } from './member-color.js';
66
- import { InviteDialog } from './InviteDialog.js';
67
- import css from './RoomDockCapsules.module.css';
68
- /** Glyph tooltip text per status. */
69
- function statusLabel(status, t) {
70
- switch (status) {
71
- case 'pending': return t('tasks.status.pending');
72
- case 'in_progress': return t('tasks.status.in_progress');
73
- case 'done': return t('tasks.status.done');
74
- case 'cancelled': return t('tasks.status.cancelled');
75
- case 'failed': return t('tasks.status.failed');
76
- }
77
- }
78
- /* The Linear-style status icons: colored, 14px, centered in the 16px cell. */
79
- /** Done: filled success circle, check cut in the card surface tone. */
80
- function DoneGlyph() {
81
- return (_jsxs("svg", { width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", className: css.glyphDone, children: [_jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }), _jsx("path", { d: "M4.2 7.3L6.2 9.3L9.8 5.2", stroke: "var(--dsw-specific-menu, var(--dsw-alias-bg-base, #ffffff))", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" })] }));
82
- }
83
- /** In-progress: business-primary half ring; CSS spins the svg. */
84
- function ProgressGlyph() {
85
- return (_jsx("svg", { width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", className: css.glyphProgress, children: _jsx("circle", { cx: "7", cy: "7", r: "5.5", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeDasharray: `${Math.PI * 5.5} ${2 * Math.PI * 5.5}` }) }));
86
- }
87
- /** Pending: the tertiary empty ring. */
88
- function PendingGlyph() {
89
- return (_jsx("svg", { width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", className: css.glyphPending, children: _jsx("circle", { cx: "7", cy: "7", r: "5.5", stroke: "currentColor", strokeWidth: "1.4" }) }));
90
- }
91
- /** Cancelled: the pending ring in the dimmer caption tone. */
92
- function CancelledGlyph() {
93
- return (_jsx("svg", { width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", className: css.glyphCancelled, children: _jsx("circle", { cx: "7", cy: "7", r: "5.5", stroke: "currentColor", strokeWidth: "1.4" }) }));
94
- }
95
- /** Failed: filled error circle, × cut in the card surface tone (DoneGlyph's cutout idiom). */
96
- function FailedGlyph() {
97
- return (_jsxs("svg", { width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", className: css.glyphFailed, children: [_jsx("circle", { cx: "7", cy: "7", r: "7", fill: "currentColor" }), _jsx("path", { d: "M4.8 4.8L9.2 9.2M9.2 4.8L4.8 9.2", stroke: "var(--dsw-specific-menu, var(--dsw-alias-bg-base, #ffffff))", strokeWidth: "1.4", strokeLinecap: "round" })] }));
98
- }
99
- function StatusGlyph({ status }) {
100
- switch (status) {
101
- case 'done': return _jsx(DoneGlyph, {});
102
- case 'in_progress': return _jsx(ProgressGlyph, {});
103
- case 'pending': return _jsx(PendingGlyph, {});
104
- case 'cancelled': return _jsx(CancelledGlyph, {});
105
- case 'failed': return _jsx(FailedGlyph, {});
106
- }
107
- }
108
- /**
109
- * The goal capsule's progress ring — the collapsed row's single visual focus:
110
- * a tertiary track arc plus the business-primary progress arc, -90° rotated
111
- * so the arc starts at twelve o'clock.
112
- */
113
- function GoalRing({ fraction }) {
114
- const clamped = Math.min(1, Math.max(0, fraction));
115
- const R = 5.5;
116
- const C = 2 * Math.PI * R;
117
- return (_jsxs("svg", { width: 14, height: 14, viewBox: "0 0 14 14", fill: "none", "aria-hidden": "true", className: css.ring, children: [_jsx("circle", { cx: "7", cy: "7", r: R, className: css.ringTrack, strokeWidth: "2" }), _jsx("circle", { cx: "7", cy: "7", r: R, className: css.ringFill, strokeWidth: "2", strokeLinecap: "round", strokeDasharray: `${C * clamped} ${C}`, transform: "rotate(-90 7 7)" })] }));
118
- }
119
- /**
120
- * A task is BLOCKED while the member its `blockedBy` names still has an open
121
- * (pending/in_progress) task — the row greys until the wait is over. The task
122
- * ITSELF never counts as its own blocker (a "等谁" pointing at the owner
123
- * would otherwise grey forever). Display only: nothing auto-dispatches when
124
- * it clears.
125
- */
126
- function isBlocked(task, tasks) {
127
- return task.blockedBy !== undefined
128
- && tasks.some(entry => entry.id !== task.id && entry.member === task.blockedBy
129
- && (entry.status === 'pending' || entry.status === 'in_progress'));
130
- }
131
- /** How many recent advance records the goal card lists. */
132
- const RECENT_ADVANCES = 5;
133
- /** One task row of the task panel. */
134
- function TaskRow({ task, tasks, now, closeTask, t }) {
135
- const blocked = isBlocked(task, tasks);
136
- const open = task.status === 'pending' || task.status === 'in_progress';
137
- return (_jsxs("li", { className: css.item, "data-status": task.status, "data-blocked": blocked || undefined, children: [_jsx("span", { className: css.glyph, title: statusLabel(task.status, t), children: _jsx(StatusGlyph, { status: task.status }) }), _jsx("span", { className: css.content, children: _jsx("span", { className: css.title, children: task.title }) }), _jsxs("span", { className: css.memberChip, children: [_jsx("span", { className: css.memberDot, style: { background: memberColor(task.member) }, "aria-hidden": true }), task.member] }), blocked && _jsx("span", { className: css.blockedTag, children: t('tasks.blocked', { member: task.blockedBy ?? '' }) }), _jsxs("span", { className: css.meta, children: [statusLabel(task.status, t), " \u00B7 ", formatRelativeTime(task.updatedAt, now, t)] }), open && (_jsx("button", { type: "button", className: css.action, onClick: () => { void closeTask(task.id); }, children: t('tasks.close') })), task.status === 'failed' && (_jsx("button", { type: "button", className: css.action, onClick: () => { void closeTask(task.id, 'cancelled'); }, children: t('tasks.dismiss') }))] }));
138
- }
139
- /** The dock capsules (goal + tasks) with their expanded cards. */
140
- export function RoomDockCapsules({ sessionId, roomStore, addTask, closeTask, setGoal, formalPlans = false, roomCwd, invite, listProviders, browseDirectory, modelSurface, renderHarnessModelPicker, t, }) {
141
- const state = useSyncExternalStore(roomStore.subscribe, () => roomStore.getCached(sessionId));
142
- const [open, setOpen] = useState(null);
143
- const [addOpen, setAddOpen] = useState(false);
144
- const [filter, setFilter] = useState('');
145
- const [editingGoal, setEditingGoal] = useState(false);
146
- const [goalDraft, setGoalDraft] = useState('');
147
- const [draftTitle, setDraftTitle] = useState('');
148
- const [draftMember, setDraftMember] = useState('');
149
- const [draftBlockedBy, setDraftBlockedBy] = useState('');
150
- const [error, setError] = useState(null);
151
- const [inviteOpen, setInviteOpen] = useState(false);
152
- const [providers, setProviders] = useState(undefined);
153
- const rootRef = useRef(null);
154
- // Collapse on any press outside the dock (no overlay; a second click on the
155
- // capsule toggles too).
156
- useEffect(() => {
157
- if (open === null)
158
- return;
159
- const onPointerDown = (event) => {
160
- if (rootRef.current !== null && event.target instanceof Node
161
- && !rootRef.current.contains(event.target)) {
162
- setOpen(null);
163
- setAddOpen(false);
164
- setEditingGoal(false);
165
- }
166
- };
167
- document.addEventListener('mousedown', onPointerDown);
168
- return () => { document.removeEventListener('mousedown', onPointerDown); };
169
- }, [open]);
170
- // A cache miss (first pull in flight) or a non-room: the dock vanishes.
171
- if (state === undefined || roomStore.isRoomCached(sessionId) !== true)
172
- return null;
173
- // Fresh room (no goal, no tasks — membership does not count, the main
174
- // agent is always there): the guide/task pair would be noise, so the row
175
- // carries a single 「+ 邀请成员」 capsule that opens the invite dialog
176
- // straight from the dock. The pair returns the moment a goal or a task
177
- // exists; the invite entry then retires (the members tab carries it).
178
- const fresh = state.goal === undefined && state.tasks.length === 0;
179
- if (fresh) {
180
- const openInvite = () => {
181
- setProviders(undefined);
182
- setInviteOpen(true);
183
- void listProviders().then(setProviders);
184
- };
185
- const submitInvite = (values) => invite({
186
- provider: values.provider,
187
- name: values.name,
188
- // Blank instructions are omitted, not sent: the host rejects a
189
- // present-but-blank role, and an absent one simply carries no preset.
190
- ...values.instructions === '' ? {} : { instructions: values.instructions },
191
- ...values.cwd === '' ? {} : { cwd: values.cwd },
192
- ...values.firstTask === '' ? {} : { firstTask: values.firstTask },
193
- ...values.model === '' ? {} : { model: values.model },
194
- });
195
- return (_jsxs("section", { ref: rootRef, className: css.root, "aria-label": t('goal.label'), children: [_jsx("div", { className: css.capsules, children: _jsx("button", { type: "button", className: css.capsule, onClick: openInvite, children: _jsx("span", { className: css.capsuleGuide, children: t('members.invite') }) }) }), inviteOpen && (_jsx(InviteDialog, { mode: "invite", providers: providers?.providers, localAgentAvailable: providers?.localAgentAvailable ?? true, inheritedCwd: roomCwd, existingNames: state.members.map(entry => entry.name), browseDirectory: browseDirectory, modelSurface: modelSurface, renderHarnessModelPicker: renderHarnessModelPicker, onSubmit: submitInvite, onClose: () => { setInviteOpen(false); }, t: t }))] }));
196
- }
197
- const now = Date.now();
198
- const progress = taskProgress(state.tasks);
199
- const runners = [...new Set(state.tasks.filter(task => task.status === 'in_progress').map(task => task.member))];
200
- const members = state.members;
201
- const chosen = members.some(member => member.name === draftMember)
202
- ? draftMember
203
- : (members[0]?.name ?? '');
204
- const advances = state.tasks
205
- .filter(task => task.status === 'done')
206
- .slice()
207
- .sort((a, b) => b.updatedAt - a.updatedAt);
208
- const fraction = progress.total === 0 ? 0 : progress.done / progress.total;
209
- const toggle = (panel) => {
210
- if (open === panel) {
211
- setOpen(null);
212
- setAddOpen(false);
213
- setEditingGoal(false);
214
- return;
215
- }
216
- setOpen(panel);
217
- setAddOpen(false);
218
- setEditingGoal(false);
219
- setError(null);
220
- if (panel === 'goal' && state.goal === undefined) {
221
- setGoalDraft('');
222
- setEditingGoal(true);
223
- }
224
- };
225
- const saveGoal = async () => {
226
- const outcome = await setGoal(goalDraft);
227
- if (!outcome.ok) {
228
- setError(outcome.message);
229
- return;
230
- }
231
- setEditingGoal(false);
232
- setError(null);
233
- };
234
- const add = async () => {
235
- const title = draftTitle.trim();
236
- if (title === '' || chosen === '')
237
- return;
238
- const outcome = await addTask(chosen, title, draftBlockedBy === '' ? undefined : draftBlockedBy);
239
- if (!outcome.ok) {
240
- setError(outcome.message);
241
- return;
242
- }
243
- setDraftTitle('');
244
- setDraftBlockedBy('');
245
- setError(null);
246
- };
247
- const onAddKeyDown = (event) => {
248
- if (event.key === 'Enter') {
249
- event.preventDefault();
250
- void add();
251
- }
252
- };
253
- const visibleTasks = filter === ''
254
- ? undefined
255
- : state.tasks.filter(task => task.member === filter);
256
- return (_jsxs("section", { ref: rootRef, className: css.root, "aria-label": t('goal.label'), children: [!formalPlans && open === 'goal' && (_jsxs("div", { className: css.card, children: [editingGoal ? (_jsxs("div", { className: css.goalEditRow, children: [_jsx("input", { className: css.input, value: goalDraft, placeholder: t('goal.placeholder'), onChange: event => { setGoalDraft(event.target.value); }, onKeyDown: (event) => {
257
- if (event.key === 'Enter') {
258
- event.preventDefault();
259
- void saveGoal();
260
- }
261
- } }), _jsx("button", { type: "button", className: css.action, onClick: () => { void saveGoal(); }, children: t('goal.save') }), state.goal !== undefined && (_jsx("button", { type: "button", className: css.action, onClick: () => { setEditingGoal(false); }, children: t('goal.cancel') }))] })) : (_jsxs("div", { className: css.goalRow, children: [_jsx("span", { className: css.goalText, children: state.goal }), _jsx("button", { type: "button", className: css.action, onClick: () => {
262
- setGoalDraft(state.goal ?? '');
263
- setEditingGoal(true);
264
- }, children: t('goal.edit') })] })), _jsxs("div", { className: css.progressRow, children: [_jsx("div", { className: css.progressTrack, role: "progressbar", "aria-valuemin": 0, "aria-valuemax": progress.total, "aria-valuenow": progress.done, children: _jsx("div", { className: css.progressFill, style: { width: `${fraction * 100}%` } }) }), _jsxs("span", { className: css.progressMeta, children: [progress.done, "/", progress.total] })] }), _jsx("div", { className: css.recentLabel, children: t('goal.recent') }), advances.length === 0 ? (_jsx("div", { className: css.meta, children: t('goal.noAdvances') })) : (_jsx("ul", { className: css.list, children: advances.slice(0, RECENT_ADVANCES).map(task => (_jsxs("li", { className: css.advanceRow, children: [t('advance.line', { member: task.member, title: task.title }), _jsxs("span", { className: css.meta, children: [" \u00B7 ", formatRelativeTime(task.updatedAt, now, t)] })] }, task.id))) })), error !== null && _jsx("div", { className: css.error, role: "alert", children: error })] })), open === 'tasks' && (_jsxs("div", { className: css.card, children: [_jsxs("div", { className: css.panelBar, children: [_jsxs("div", { className: css.chips, role: "group", "aria-label": t(formalPlans ? 'tasks.chat' : 'tasks.capsule'), children: [_jsx("button", { type: "button", className: css.chip, "data-active": filter === '' || undefined, onClick: () => { setFilter(''); }, children: t('tasks.filter.all') }), members.map(member => (_jsxs("button", { type: "button", className: css.chip, "data-active": filter === member.name || undefined, style: { '--room-chip-color': memberColor(member.name) }, onClick: () => { setFilter(member.name); }, children: [_jsx("span", { className: css.memberDot, style: { background: memberColor(member.name) }, "aria-hidden": true }), member.name] }, member.name)))] }), !addOpen && (_jsxs("button", { type: "button", className: css.addTrigger, onClick: () => { setAddOpen(true); }, children: ["\uFF0B ", t('tasks.addTrigger')] }))] }), _jsx("div", { className: css.groups, children: filter === ''
265
- ? members.map((member) => {
266
- const own = state.tasks.filter(task => task.member === member.name);
267
- if (own.length === 0)
268
- return null;
269
- return (_jsxs("div", { className: css.group, children: [_jsx("div", { className: css.head, children: member.name }), _jsx("ul", { className: css.list, children: own.map(task => (_jsx(TaskRow, { task: task, tasks: state.tasks, now: now, closeTask: closeTask, t: t }, `${task.id}:${task.status}`))) })] }, member.name));
270
- })
271
- : (_jsx("ul", { className: css.list, children: (visibleTasks ?? []).map(task => (_jsx(TaskRow, { task: task, tasks: state.tasks, now: now, closeTask: closeTask, t: t }, `${task.id}:${task.status}`))) })) }), addOpen && (_jsxs("div", { className: css.addRow, children: [_jsx("select", { className: css.memberSelect, "aria-label": t('tasks.addMember'), value: chosen, onChange: event => { setDraftMember(event.target.value); }, children: members.map(member => (_jsx("option", { value: member.name, children: member.name }, member.name))) }), _jsx("input", { className: css.input, value: draftTitle, placeholder: t('tasks.addPlaceholder'), onChange: event => { setDraftTitle(event.target.value); }, onKeyDown: onAddKeyDown }), _jsxs("select", { className: css.memberSelect, "aria-label": t('tasks.addBlockedBy'), value: draftBlockedBy, onChange: event => { setDraftBlockedBy(event.target.value); }, children: [_jsx("option", { value: "", children: t('tasks.addBlockedByNone') }), members.map(member => (_jsx("option", { value: member.name, children: member.name }, member.name)))] }), _jsx("button", { type: "button", className: css.action, disabled: draftTitle.trim() === '' || chosen === '', onClick: () => { void add(); }, children: t('tasks.add') })] })), error !== null && _jsx("div", { className: css.error, role: "alert", children: error })] })), _jsxs("div", { className: css.capsules, children: [!formalPlans && _jsx("button", { type: "button", className: css.capsule, "aria-expanded": open === 'goal', "aria-label": t('goal.label'), onClick: () => { toggle('goal'); }, children: state.goal === undefined ? (_jsx("span", { className: css.capsuleGuide, children: t('goal.set') })) : (_jsxs(_Fragment, { children: [_jsx(GoalRing, { fraction: fraction }), _jsxs("span", { className: css.capsulePercent, children: [Math.round(fraction * 100), "%"] }), _jsx("span", { className: css.capsuleText, children: state.goal })] })) }), _jsxs("button", { type: "button", className: css.capsule, "aria-expanded": open === 'tasks', "aria-label": t(formalPlans ? 'tasks.chat' : 'tasks.capsule'), "data-running": runners.length > 0 || undefined, onClick: () => { toggle('tasks'); }, children: [_jsx(IconChecklistOutlineMedium, { size: 14, className: css.checklistIcon }), progress.total === 0 ? (_jsx("span", { className: css.capsuleText, children: t(formalPlans ? 'tasks.chat' : 'tasks.capsule') })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: css.capsuleText, children: t(formalPlans ? 'tasks.chat' : 'tasks.capsule') }), _jsxs("span", { className: css.capsuleCount, children: [progress.done, "/", progress.total] }), runners.length > 0 && (_jsxs("span", { className: css.runners, children: [runners.map(name => (_jsxs("span", { className: css.runner, children: [_jsx("span", { className: css.memberDot, style: { background: memberColor(name) }, "aria-hidden": true }), name] }, name))), _jsx("span", { className: css.runnerSuffix, children: t('tasks.doing') })] }))] }))] }), _jsx("button", { type: "button", className: css.addCapsule, "aria-label": t('tasks.addTrigger'), onClick: () => {
272
- setOpen('tasks');
273
- setAddOpen(true);
274
- setEditingGoal(false);
275
- setError(null);
276
- }, children: "\uFF0B" })] })] }));
277
- }
@@ -1,15 +0,0 @@
1
- import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
- import type { RoomComposerInjected } from './slots.ts';
3
- export declare const ROOM_PLAN_KIND = "room-plan";
4
- export declare const ROOM_PLAN_TAB_ID = "@khorsheed/dsh-room/plan";
5
- declare module '@deepseek-ai/dsh-client-ui-sidebar-right/client' {
6
- interface SidebarRightTabParamsMap {
7
- 'room-plan': Record<string, never>;
8
- }
9
- }
10
- type Injected = Pick<RoomComposerInjected, 'roomStore' | 'planCommand' | 'openPlanSession' | 'stopMember'>;
11
- type Props = PropsRuntime<'sidebar.right.pane.tab'> & InjectFace<Injected> & PropsLocale<'room'>;
12
- /** Uses the host's session-scoped sidebar, with the same review commands as the dock. */
13
- export declare function RoomPlanTab({ sessionId, roomStore, planCommand, openPlanSession, stopMember, t }: Props): import("react").JSX.Element | null;
14
- export {};
15
- //# sourceMappingURL=RoomPlanTab.d.ts.map
@@ -1,13 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useEffect, useSyncExternalStore } from 'react';
3
- import { RoomPlanView } from './RoomPlanView.js';
4
- export const ROOM_PLAN_KIND = 'room-plan';
5
- export const ROOM_PLAN_TAB_ID = '@khorsheed/dsh-room/plan';
6
- /** Uses the host's session-scoped sidebar, with the same review commands as the dock. */
7
- export function RoomPlanTab({ sessionId, roomStore, planCommand, openPlanSession, stopMember, t }) {
8
- const state = useSyncExternalStore(roomStore.subscribe, () => roomStore.getCached(sessionId));
9
- useEffect(() => { void roomStore.ensure(sessionId); }, [roomStore, sessionId]);
10
- if (!state || !planCommand)
11
- return null;
12
- return _jsx(RoomPlanView, { expanded: true, plan: state.plan, members: state.members, command: planCommand, openSession: openPlanSession, stopMember: stopMember, t: t }, sessionId);
13
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Task-advance node ('room-task-line' keyed renderer): one dim line per task
3
- * closed done — `✓ ada 完成了「API 定稿」── 目标进度 2/5`. The same dim
4
- * single-line register as the boundary rows (RoomEventView); the goal
5
- * progress suffix is computed live from the room store (the node's own fold
6
- * only carries this task) and dropped when the store has no state for the
7
- * session (a cold render before the first pull).
8
- */
9
- import { type ReactNode } from 'react';
10
- import type { RoomTaskLineViewProps } from './slots.ts';
11
- /** The task-advance dim line. */
12
- export declare function RoomTaskLineView({ node, sessionId, roomStore, t }: RoomTaskLineViewProps): ReactNode;
13
- //# sourceMappingURL=RoomTaskLineView.d.ts.map
@@ -1,21 +0,0 @@
1
- import { jsxs as _jsxs } from "react/jsx-runtime";
2
- /**
3
- * Task-advance node ('room-task-line' keyed renderer): one dim line per task
4
- * closed done — `✓ ada 完成了「API 定稿」── 目标进度 2/5`. The same dim
5
- * single-line register as the boundary rows (RoomEventView); the goal
6
- * progress suffix is computed live from the room store (the node's own fold
7
- * only carries this task) and dropped when the store has no state for the
8
- * session (a cold render before the first pull).
9
- */
10
- import { useSyncExternalStore } from 'react';
11
- import { taskProgress } from '../journal.js';
12
- import css from './RoomTaskLineView.module.css';
13
- /** The task-advance dim line. */
14
- export function RoomTaskLineView({ node, sessionId, roomStore, t }) {
15
- const state = useSyncExternalStore(roomStore.subscribe, () => roomStore.getCached(sessionId));
16
- const data = node.data;
17
- const progress = state === undefined ? undefined : taskProgress(state.tasks);
18
- return (_jsxs("div", { className: css.row, children: [t('advance.line', { member: data.member, title: data.title }), progress !== undefined && progress.total > 0
19
- ? t('advance.progress', { done: progress.done, total: progress.total })
20
- : ''] }));
21
- }