@khorsheed/dsh-room 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/CHANGELOG.md +53 -0
- package/README.en.md +29 -62
- package/README.i18n.yaml +2 -2
- package/README.md +28 -61
- package/lib/client.js +13097 -4182
- package/lib/index.js +4045 -730
- package/lib/journal-B4FOGO6S.js +482 -0
- package/lib/journal-Rid9AAnD.js +485 -0
- package/lib/tool.js +112 -166
- package/lib/typert.host.js +2031 -374
- package/lib/typert.remote-client.d.ts +34 -10
- package/lib/typert.remote-client.js +1599 -210
- package/lib/types/agent-setup.d.ts +1 -1
- package/lib/types/client/InviteAgentAction.js +4 -16
- package/lib/types/client/InviteDialog.d.ts +28 -6
- package/lib/types/client/InviteDialog.js +76 -12
- package/lib/types/client/MemberCard.d.ts +5 -1
- package/lib/types/client/MemberCard.js +4 -4
- package/lib/types/client/MembersView.d.ts +1 -1
- package/lib/types/client/MembersView.js +58 -22
- package/lib/types/client/RemoveMemberConfirm.d.ts +40 -0
- package/lib/types/client/RemoveMemberConfirm.js +85 -0
- package/lib/types/client/RoomApprovalView.d.ts +11 -0
- package/lib/types/client/RoomApprovalView.js +28 -0
- package/lib/types/client/RoomComposer.d.ts +1 -1
- package/lib/types/client/RoomComposer.js +195 -29
- package/lib/types/client/RoomDecisionView.d.ts +22 -0
- package/lib/types/client/RoomDecisionView.js +35 -0
- package/lib/types/client/RoomHelpView.d.ts +17 -0
- package/lib/types/client/RoomHelpView.js +56 -0
- package/lib/types/client/RoomInboxView.d.ts +10 -0
- package/lib/types/client/RoomInboxView.js +14 -0
- package/lib/types/client/RoomQuestionView.d.ts +13 -0
- package/lib/types/client/RoomQuestionView.js +61 -0
- package/lib/types/client/RoomRelayView.js +1 -1
- package/lib/types/client/RoomRunView.d.ts +3 -1
- package/lib/types/client/RoomRunView.js +12 -5
- package/lib/types/client/RoomSettingsView.d.ts +19 -0
- package/lib/types/client/RoomSettingsView.js +30 -0
- package/lib/types/client/RoomSpeechView.d.ts +22 -8
- package/lib/types/client/RoomSpeechView.js +94 -11
- package/lib/types/client/RoomStatsLine.d.ts +26 -5
- package/lib/types/client/RoomStatsLine.js +53 -7
- package/lib/types/client/RoomTaskCapsule.d.ts +19 -0
- package/lib/types/client/RoomTaskCapsule.js +37 -0
- package/lib/types/client/TasksTab.d.ts +41 -0
- package/lib/types/client/TasksTab.js +43 -0
- package/lib/types/client/TasksView.d.ts +44 -0
- package/lib/types/client/TasksView.js +149 -0
- package/lib/types/client/TeamView.d.ts +33 -0
- package/lib/types/client/TeamView.js +210 -0
- package/lib/types/client/at-menu.d.ts +127 -0
- package/lib/types/client/at-menu.js +173 -0
- package/lib/types/client/draft-chips.d.ts +67 -0
- package/lib/types/client/draft-chips.js +95 -0
- package/lib/types/client/executions.d.ts +0 -30
- package/lib/types/client/executions.js +4 -34
- package/lib/types/client/icons-local.d.ts +25 -0
- package/lib/types/client/icons-local.js +10 -0
- package/lib/types/client/icons.d.ts +2 -1
- package/lib/types/client/icons.js +1 -0
- package/lib/types/client/index.js +276 -72
- package/lib/types/client/locales.d.ts +482 -52
- package/lib/types/client/locales.js +490 -60
- package/lib/types/client/nodes.d.ts +112 -17
- package/lib/types/client/nodes.js +197 -26
- package/lib/types/client/slots.d.ts +117 -47
- package/lib/types/client/task-tag.d.ts +32 -0
- package/lib/types/client/task-tag.js +33 -0
- package/lib/types/client/tasks.d.ts +50 -0
- package/lib/types/client/tasks.js +82 -0
- package/lib/types/client/team.d.ts +73 -0
- package/lib/types/client/team.js +187 -0
- package/lib/types/decisions.d.ts +49 -0
- package/lib/types/decisions.js +101 -0
- package/lib/types/deliveries.d.ts +17 -1
- package/lib/types/deliveries.js +82 -6
- package/lib/types/dispatch.d.ts +92 -4
- package/lib/types/dispatch.js +477 -109
- package/lib/types/help.d.ts +137 -0
- package/lib/types/help.js +216 -0
- package/lib/types/helps.d.ts +26 -0
- package/lib/types/helps.js +47 -0
- package/lib/types/index.d.ts +292 -52
- package/lib/types/index.js +1415 -199
- package/lib/types/jobs.d.ts +22 -0
- package/lib/types/jobs.js +77 -0
- package/lib/types/journal.d.ts +17 -27
- package/lib/types/journal.js +120 -43
- package/lib/types/messaging.d.ts +65 -0
- package/lib/types/messaging.js +160 -0
- package/lib/types/plan-service.js +7 -1
- package/lib/types/report-gate.d.ts +81 -0
- package/lib/types/report-gate.js +89 -0
- package/lib/types/tool-definition.d.ts +43 -20
- package/lib/types/tool-definition.js +145 -212
- package/lib/types/tool.d.ts +2 -2
- package/lib/types/tool.js +3 -2
- package/lib/types/types.d.ts +755 -78
- package/locale/zh.json +1 -1
- package/package.json +3 -3
- package/lib/types/client/RoomActivityDock.d.ts +0 -19
- package/lib/types/client/RoomActivityDock.js +0 -105
- package/lib/types/client/RoomDockCapsules.d.ts +0 -64
- package/lib/types/client/RoomDockCapsules.js +0 -277
- package/lib/types/client/RoomPlanTab.d.ts +0 -15
- package/lib/types/client/RoomPlanTab.js +0 -13
- package/lib/types/client/RoomTaskLineView.d.ts +0 -13
- package/lib/types/client/RoomTaskLineView.js +0 -21
package/locale/zh.json
CHANGED
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` / `
|
|
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.
|
|
10
|
+
"version": "0.2.3",
|
|
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
|
|
88
|
+
"@khorsheed/dsh-local-agent": "^0.2.0",
|
|
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
|
-
}
|