@wowyuarm/dsh-agent-team 0.1.7 → 0.1.9
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/package.json +2 -1
- package/packages/agent-team/README.md +5 -3
- package/packages/agent-team/README.zh.md +5 -3
- package/packages/agent-team/core-skills/member-skill-manager/SKILL.md +2 -0
- package/packages/agent-team/lib/attachments.js +14 -4
- package/packages/agent-team/lib/context-management.js +379 -0
- package/packages/agent-team/lib/context-projection.js +574 -0
- package/packages/agent-team/lib/context-source.js +72 -0
- package/packages/agent-team/lib/index.js +1258 -54
- package/packages/agent-team/lib/ledger.js +357 -26
- package/packages/agent-team/lib/member-context.js +7 -3
- package/packages/agent-team/lib/member-runtime.js +159 -4
- package/packages/agent-team/lib/pressure-policy.js +200 -0
- package/packages/agent-team/lib/progress-nudge.js +320 -0
- package/packages/agent-team/lib/spec.js +18 -0
- package/packages/agent-team/lib/typert.host.js +124 -42
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +39 -25
- package/packages/agent-team/lib/types/attachments.d.ts +1 -1
- package/packages/agent-team/lib/types/attachments.d.ts.map +1 -1
- package/packages/agent-team/lib/types/context-management.d.ts +142 -0
- package/packages/agent-team/lib/types/context-management.d.ts.map +1 -0
- package/packages/agent-team/lib/types/context-projection.d.ts +219 -0
- package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -0
- package/packages/agent-team/lib/types/context-source.d.ts +83 -0
- package/packages/agent-team/lib/types/context-source.d.ts.map +1 -0
- package/packages/agent-team/lib/types/index.d.ts +283 -2
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +120 -7
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-runtime.d.ts +30 -2
- package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -1
- package/packages/agent-team/lib/types/pressure-policy.d.ts +106 -0
- package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -0
- package/packages/agent-team/lib/types/progress-nudge.d.ts +136 -0
- package/packages/agent-team/lib/types/progress-nudge.d.ts.map +1 -0
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +32 -1
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/operations.d.ts +38 -3
- package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts +54 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +10 -2
- package/packages/client-agent-team/lib/client.js +283 -398
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +1 -2
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +61 -41
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +3 -7
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +37 -27
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.js +3 -2
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +5 -0
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +2 -2
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +4 -6
- package/packages/client-agent-team/lib/types/client/TeamStateDot.d.ts +14 -0
- package/packages/client-agent-team/lib/types/client/TeamStateDot.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamStateDot.js +15 -0
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +1 -0
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +41 -16
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -2
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +36 -78
- package/packages/client-agent-team/lib/types/client/locales.d.ts +0 -16
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +0 -16
- package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts +1 -2
- package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts +8 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts +7 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.js +25 -3
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +11 -10
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.js +14 -11
- package/packages/tool-agent-team/README.md +8 -5
- package/packages/tool-agent-team/README.zh.md +8 -5
- package/packages/tool-agent-team/lib/context-tools.js +179 -0
- package/packages/tool-agent-team/lib/index.js +144 -25
- package/packages/tool-agent-team/lib/types/context-tools.d.ts +16 -0
- package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -0
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/auto-compaction.js +0 -201
- package/packages/agent-team/lib/types/auto-compaction.d.ts +0 -47
- package/packages/agent-team/lib/types/auto-compaction.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts +0 -12
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberDock.js +0 -13
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +0 -20
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/member-session-input.js +0 -110
|
@@ -8,7 +8,6 @@ interface TeamAgentsPanelProps {
|
|
|
8
8
|
readonly addMember: TeamSidebarProps['addMember'];
|
|
9
9
|
readonly updateMember: TeamSidebarProps['updateMember'];
|
|
10
10
|
readonly recoverMember: TeamSidebarProps['recoverMember'];
|
|
11
|
-
readonly clearMemberContext: TeamSidebarProps['clearMemberContext'];
|
|
12
11
|
readonly archiveMember: TeamSidebarProps['archiveMember'];
|
|
13
12
|
readonly loadModels: TeamSidebarProps['loadModels'];
|
|
14
13
|
/** The Member Session currently embedded in the conversation seat, if any. */
|
|
@@ -17,6 +16,6 @@ interface TeamAgentsPanelProps {
|
|
|
17
16
|
readonly onCreatingChange: (request: AgentTeamAddMemberRequest, creating: boolean) => void;
|
|
18
17
|
readonly t: TeamSidebarProps['t'];
|
|
19
18
|
}
|
|
20
|
-
export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember,
|
|
19
|
+
export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, archiveMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }: TeamAgentsPanelProps): import("react").JSX.Element;
|
|
21
20
|
export {};
|
|
22
21
|
//# sourceMappingURL=TeamAgentsPanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamAgentsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,2BAA2B,EAE5B,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAYlD,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IAC/D,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACjD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"TeamAgentsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,2BAA2B,EAE5B,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAYlD,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IAC/D,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACjD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,8EAA8E;IAC9E,QAAQ,CAAC,eAAe,CAAC,EAAE,2BAA2B,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAA;IAC7E,QAAQ,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IACjE,QAAQ,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,yBAAyB,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC1F,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC;AAED,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BA2N/N"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { Button, IconArchiveOutline20, IconEditOutline16,
|
|
3
|
+
import { Button, IconArchiveOutline20, IconEditOutline16, IconPlayOutline16, IconPlusOutline16, IconRefreshOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { TeamMemberAvatar } from "./TeamMemberAvatar.js";
|
|
5
5
|
import { SortableRow, useSidebarRowDrag } from "./sidebar-drag.js";
|
|
6
6
|
import { moveSidebarItem, useSidebarOrder } from "./sidebar-order.js";
|
|
@@ -11,7 +11,7 @@ import { TeamSidebarSection } from "./TeamSidebarSection.js";
|
|
|
11
11
|
import { AgentEditorDialog, ModelPickerField, sameModel } from "./TeamMemberEditor.js";
|
|
12
12
|
import createCss from './create.module.css';
|
|
13
13
|
import css from './sidebar.module.css';
|
|
14
|
-
export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember,
|
|
14
|
+
export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, archiveMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
|
|
15
15
|
const [members, setMembers] = useState([]);
|
|
16
16
|
const [loading, setLoading] = useState(true);
|
|
17
17
|
const [error, setError] = useState();
|
|
@@ -35,20 +35,74 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, ad
|
|
|
35
35
|
};
|
|
36
36
|
const drag = useSidebarRowDrag({ refs: orderedAgentRefs, onCommit: applyMove });
|
|
37
37
|
const sectionOpen = useSidebarSectionOpen(workspaceId, 'agents');
|
|
38
|
+
// Observed old→new Session binding per Member while this panel is
|
|
39
|
+
// mounted. A model-initiated rollover moves the binding without any
|
|
40
|
+
// Client action; when the conversation seat is showing the Member's
|
|
41
|
+
// previous live Session, the panel follows to the new one exactly once —
|
|
42
|
+
// but only after the refreshed status is actually active, so the commit
|
|
43
|
+
// window (binding moved, new Session not yet live) never redirects to a
|
|
44
|
+
// Session that does not exist. The follow requires the seat to still show
|
|
45
|
+
// the previous Session at completion as well: a rollover observed while
|
|
46
|
+
// unavailable stays pending, and navigating away before it completes
|
|
47
|
+
// cancels the follow instead of pulling the pane back.
|
|
48
|
+
const observedSessionIds = useRef(new Map());
|
|
49
|
+
const pendingFollows = useRef(new Map());
|
|
50
|
+
// The seat's embedded Session id rides a ref: the change subscription may
|
|
51
|
+
// deliver an invalidation through a refresh closure captured before the
|
|
52
|
+
// seat rebinds, and the follow decision must always read the live value.
|
|
53
|
+
const seatSessionIdRef = useRef(memberSessionId);
|
|
54
|
+
seatSessionIdRef.current = memberSessionId;
|
|
55
|
+
const openMemberSessionRef = useRef(openMemberSession);
|
|
56
|
+
openMemberSessionRef.current = openMemberSession;
|
|
57
|
+
const followRollover = useCallback((statuses) => {
|
|
58
|
+
for (const status of statuses) {
|
|
59
|
+
const memberId = status.member.memberId;
|
|
60
|
+
const observed = observedSessionIds.current.get(memberId);
|
|
61
|
+
if (observed === undefined) {
|
|
62
|
+
observedSessionIds.current.set(memberId, status.member.sessionId);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (observed !== status.member.sessionId) {
|
|
66
|
+
observedSessionIds.current.set(memberId, status.member.sessionId);
|
|
67
|
+
// Only the exact previous live page follows; browsing an older
|
|
68
|
+
// archive or another Member stays untouched. A binding change seen
|
|
69
|
+
// before any page is embedded is still recorded so the observation
|
|
70
|
+
// baseline stays current.
|
|
71
|
+
const seatSessionId = seatSessionIdRef.current;
|
|
72
|
+
if (seatSessionId !== undefined && observed === seatSessionId)
|
|
73
|
+
pendingFollows.current.set(memberId, { previousSessionId: observed, nextSessionId: status.member.sessionId });
|
|
74
|
+
else
|
|
75
|
+
pendingFollows.current.delete(memberId);
|
|
76
|
+
}
|
|
77
|
+
const pending = pendingFollows.current.get(memberId);
|
|
78
|
+
if (pending !== undefined && pending.nextSessionId === status.member.sessionId && status.availability === 'active') {
|
|
79
|
+
// The seat must still show the followed Member's previous Session at
|
|
80
|
+
// completion too: the commit window can outlive the user's patience,
|
|
81
|
+
// and navigating away (or onto another Member/archive view) cancels
|
|
82
|
+
// the follow instead of yanking the pane back onto the new Session.
|
|
83
|
+
const seatSessionId = seatSessionIdRef.current;
|
|
84
|
+
pendingFollows.current.delete(memberId);
|
|
85
|
+
if (seatSessionId !== undefined && seatSessionId === pending.previousSessionId)
|
|
86
|
+
openMemberSessionRef.current(pending.nextSessionId);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}, []);
|
|
38
90
|
const refresh = useCallback(async () => {
|
|
39
91
|
setLoading(true);
|
|
40
92
|
const result = await loadMembers({ workspaceId });
|
|
41
93
|
if (result.ok) {
|
|
42
94
|
// Archived Members are hidden from every surface; the row disappears
|
|
43
95
|
// the moment the workspace-scope wake delivers the archived state.
|
|
44
|
-
|
|
96
|
+
const next = result.value.filter(status => status.member.state !== 'inactive' && status.member.state !== 'archived');
|
|
97
|
+
setMembers(next);
|
|
98
|
+
followRollover(next);
|
|
45
99
|
setError(undefined);
|
|
46
100
|
}
|
|
47
101
|
else {
|
|
48
102
|
setError(result.error.message);
|
|
49
103
|
}
|
|
50
104
|
setLoading(false);
|
|
51
|
-
}, [loadMembers, workspaceId]);
|
|
105
|
+
}, [loadMembers, workspaceId, followRollover]);
|
|
52
106
|
useEffect(() => { void refresh(); }, [refresh]);
|
|
53
107
|
useEffect(() => subscribeChanges({ kind: 'workspace', workspaceId }, update => {
|
|
54
108
|
if (update.type === 'failed') {
|
|
@@ -126,17 +180,16 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, ad
|
|
|
126
180
|
...(model === undefined ? {} : { model }),
|
|
127
181
|
});
|
|
128
182
|
};
|
|
129
|
-
return (_jsxs("div", { className: css.panel, children: [_jsx(Modal, { open: formOpen, onClose: closeForm, title: t('addAgent'), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: creating, onClick: closeForm, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-create-form", variant: "primary", disabled: creating || handle.trim().length === 0, children: creating ? t('creatingAgent') : t('createAgent') })] }), children: _jsxs("form", { id: "team-agent-create-form", className: createCss.form, onSubmit: submit, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('agentName') }), _jsx(Input, { className: createCss.input, value: handle, onChange: event => { setHandle(event.target.value); setRetryRequest(undefined); }, disabled: creating, autoFocus: true })] }), _jsxs("label", { className: createCss.field, children: [_jsxs("span", { children: [t('agentDescription'), t('optionalSuffix')] }), _jsx(Input, { className: createCss.input, value: description, placeholder: t('agentDescriptionPlaceholder'), onChange: event => { setDescription(event.target.value); setRetryRequest(undefined); }, disabled: creating })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { setModel(choice); setRetryRequest(undefined); }, loadModels: loadModels, disabled: creating, t: t }), formOpen && error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('agents'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'agents', open); }, actions: (_jsx(Tooltip, { label: t('addAgent'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addAgent'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('loadingAgents') }), !loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('emptyAgents') }), _jsx("div", { className: css.agentList, children: orderedMembers.map(status => (_jsx(SortableRow, { drag: drag, orderKey: status.member.memberId, children: _jsx(AgentRow, { status: status, ...(memberSessionId === undefined ? {} : { current: status.member.sessionId === memberSessionId }), updateMember: updateMember, recoverMember: recoverMember,
|
|
183
|
+
return (_jsxs("div", { className: css.panel, children: [_jsx(Modal, { open: formOpen, onClose: closeForm, title: t('addAgent'), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: creating, onClick: closeForm, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-create-form", variant: "primary", disabled: creating || handle.trim().length === 0, children: creating ? t('creatingAgent') : t('createAgent') })] }), children: _jsxs("form", { id: "team-agent-create-form", className: createCss.form, onSubmit: submit, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('agentName') }), _jsx(Input, { className: createCss.input, value: handle, onChange: event => { setHandle(event.target.value); setRetryRequest(undefined); }, disabled: creating, autoFocus: true })] }), _jsxs("label", { className: createCss.field, children: [_jsxs("span", { children: [t('agentDescription'), t('optionalSuffix')] }), _jsx(Input, { className: createCss.input, value: description, placeholder: t('agentDescriptionPlaceholder'), onChange: event => { setDescription(event.target.value); setRetryRequest(undefined); }, disabled: creating })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { setModel(choice); setRetryRequest(undefined); }, loadModels: loadModels, disabled: creating, t: t }), formOpen && error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('agents'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'agents', open); }, actions: (_jsx(Tooltip, { label: t('addAgent'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addAgent'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('loadingAgents') }), !loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('emptyAgents') }), _jsx("div", { className: css.agentList, children: orderedMembers.map(status => (_jsx(SortableRow, { drag: drag, orderKey: status.member.memberId, children: _jsx(AgentRow, { status: status, ...(memberSessionId === undefined ? {} : { current: status.member.sessionId === memberSessionId }), updateMember: updateMember, recoverMember: recoverMember, archiveMember: archiveMember, loadModels: loadModels, openMemberSession: openMemberSession, onUpdated: () => { void refresh(); }, t: t }) }, status.member.memberId))) })] }), !formOpen && error !== undefined && (_jsxs("div", { className: css.retryError, role: "alert", children: [_jsx("span", { children: error }), retryRequest !== undefined && _jsx("button", { type: "button", className: css.textButton, disabled: creating, onClick: () => { setFormOpen(true); }, children: t('retry') })] }))] }));
|
|
130
184
|
}
|
|
131
185
|
/**
|
|
132
186
|
* One sidebar Agent row: the select button opens the Member's own Session
|
|
133
187
|
* conversation page, the avatar carries identity plus the presence badge, and
|
|
134
188
|
* the row menu opens the editor.
|
|
135
189
|
*/
|
|
136
|
-
function AgentRow({ status, current, updateMember, recoverMember,
|
|
190
|
+
function AgentRow({ status, current, updateMember, recoverMember, archiveMember, loadModels, openMemberSession, onUpdated, t }) {
|
|
137
191
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
138
192
|
const [editing, setEditing] = useState(false);
|
|
139
|
-
const [clearing, setClearing] = useState(false);
|
|
140
193
|
const [archiving, setArchiving] = useState(false);
|
|
141
194
|
const [rowAlert, setRowAlert] = useState();
|
|
142
195
|
// Both row actions ride the same runtime remote: the Host steers a live
|
|
@@ -167,35 +220,6 @@ function AgentRow({ status, current, updateMember, recoverMember, clearMemberCon
|
|
|
167
220
|
setRowAlert(t('restartFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
|
|
168
221
|
}
|
|
169
222
|
};
|
|
170
|
-
const clearContext = async () => {
|
|
171
|
-
try {
|
|
172
|
-
const result = await clearMemberContext({
|
|
173
|
-
requestId: mintRequestId(),
|
|
174
|
-
workspaceId: status.member.workspaceId,
|
|
175
|
-
memberId: status.member.memberId,
|
|
176
|
-
});
|
|
177
|
-
await onUpdated();
|
|
178
|
-
if (!result.ok) {
|
|
179
|
-
setRowAlert(t('clearContextFailed', { message: result.error.message }));
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
setRowAlert(undefined);
|
|
183
|
-
// The Host moved the Member onto a fresh Session id (the previous log
|
|
184
|
-
// stays archived on disk). A new id has no resident client instance, so
|
|
185
|
-
// re-entering the member view lazily instantiates it from host truth:
|
|
186
|
-
// blank hero, live updates, and no disposed-generation gray-out.
|
|
187
|
-
if (current === true)
|
|
188
|
-
openMemberSession(result.value.status.member.sessionId);
|
|
189
|
-
}
|
|
190
|
-
catch (cause) {
|
|
191
|
-
setRowAlert(t('clearContextFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
// Error Members keep a live idle handle, so starting from a new context
|
|
195
|
-
// is available to them too — it doubles as a recovery path (the broken
|
|
196
|
-
// handle's error markers are dropped with it). Only a running turn or a
|
|
197
|
-
// missing handle gates the entry.
|
|
198
|
-
const contextClearable = status.presence === 'available' || status.presence === 'error';
|
|
199
223
|
const archive = async () => {
|
|
200
224
|
try {
|
|
201
225
|
const result = await archiveMember({
|
|
@@ -215,17 +239,13 @@ function AgentRow({ status, current, updateMember, recoverMember, clearMemberCon
|
|
|
215
239
|
{ id: 'edit', label: t('editAgent'), icon: _jsx(IconEditOutline16, {}) },
|
|
216
240
|
...(status.presence === 'error' ? [{ id: 'resume', label: t('resumeAgent'), icon: _jsx(IconPlayOutline16, {}) }] : []),
|
|
217
241
|
...(status.availability === 'unavailable' ? [{ id: 'restart', label: t('restartAgent'), icon: _jsx(IconRefreshOutline16, {}) }] : []),
|
|
218
|
-
{ id: 'clear-context', label: t('clearContextAgent'), icon: _jsx(IconNewChatOutline16, {}), danger: true, disabled: !contextClearable },
|
|
219
|
-
...(!contextClearable ? [{ type: 'label', id: 'clear-context-reason', text: status.presence === 'working' ? t('clearContextWorkingReason') : t('clearContextUnavailableReason') }] : []),
|
|
220
242
|
{ id: 'archive', label: t('archiveAgent'), icon: _jsx(IconArchiveOutline20, { size: 16 }), danger: true },
|
|
221
243
|
], onSelect: (id) => {
|
|
222
244
|
if (id === 'edit')
|
|
223
245
|
setEditing(true);
|
|
224
|
-
else if (id === 'clear-context')
|
|
225
|
-
setClearing(true);
|
|
226
246
|
else if (id === 'archive')
|
|
227
247
|
setArchiving(true);
|
|
228
248
|
else
|
|
229
249
|
void recover();
|
|
230
|
-
}, onOpenChange: setMenuOpen }) })] }), rowAlert !== undefined && _jsx("div", { className: css.rowAlert, role: "alert", children: rowAlert }),
|
|
250
|
+
}, onOpenChange: setMenuOpen }) })] }), rowAlert !== undefined && _jsx("div", { className: css.rowAlert, role: "alert", children: rowAlert }), archiving && (_jsx(Modal, { open: true, onClose: () => { setArchiving(false); }, title: t('archiveAgentTitle', { name: status.member.handle }), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: () => { setArchiving(false); }, children: t('cancel') }), _jsx(Button, { variant: "primary", onClick: () => { setArchiving(false); void archive(); }, children: t('archiveAgentConfirm') })] }), children: _jsx("p", { className: createCss.error, children: t('archiveAgentNotice', { name: status.member.handle }) }) })), editing && (_jsx(AgentEditorDialog, { status: status, updateMember: updateMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
|
|
231
251
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAe/D,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;IAC1E,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAA;IAChE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAmBD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BA8UnR"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Fragment, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
3
|
-
import { Button, IconChevronLeftOutline14, IconChevronRightOutline14, Modal
|
|
3
|
+
import { Button, IconChevronLeftOutline14, IconChevronRightOutline14, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { bytesToBase64 } from "./attachment-preview.js";
|
|
5
5
|
import { mintRequestId } from "./requests.js";
|
|
6
6
|
import { TeamComposer } from "./TeamComposer.js";
|
|
@@ -8,6 +8,7 @@ import { TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
|
8
8
|
import { TeamMessage } from "./TeamMessage.js";
|
|
9
9
|
import { TeamRunDivider } from "./TeamRunDivider.js";
|
|
10
10
|
import { formatTaskStatus, taskStatusDot, mentionNamesOf } from "./team-formatters.js";
|
|
11
|
+
import { TeamStateDot } from "./TeamStateDot.js";
|
|
11
12
|
import { useChannelMembership } from "./team-membership.js";
|
|
12
13
|
import { useTimelineScroll } from "./timeline-scroll.js";
|
|
13
14
|
import { hostTaskRefLookup, jumpToTaskThread } from "./task-refs.js";
|
|
@@ -304,11 +305,6 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
304
305
|
const sender = human ? t('human') : senderStatus?.member.handle ?? item.message.sender;
|
|
305
306
|
const turnGap = isRunGap(index > 0 ? block.items[index - 1].message.occurredAt : undefined, item.message.occurredAt);
|
|
306
307
|
// A turn divider already labels the gapped entry; its row stays chrome-free.
|
|
307
|
-
return _jsxs(Fragment, { children: [turnGap && _jsx(TeamRunDivider, { occurredAt: item.message.occurredAt }), _jsxs(TeamMessage, { senderName: sender, memberId: item.message.sender, human: human, body: item.message.body, attachments: item.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: item.message.occurredAt, mentionNames: mentionNamesOf(item.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: index > 0, showGroupedTime: item.message.topLevel === true && !turnGap, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }), children: [item.message.topLevel && item.task !== undefined && item.taskNumber !== undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openTask', { number: item.taskNumber }), onClick: () => { selectThread(item.thread.threadRef, channelRef, item.task?.taskRef, item.taskNumber); }, children: [_jsx("span", { className: channelCss.taskDot, children: (() => {
|
|
308
|
-
const dot = taskStatusDot(item.task.status);
|
|
309
|
-
return dot === 'ongoing' || dot === 'warning' || dot === 'done'
|
|
310
|
-
? _jsx(StateDot, { size: 8, state: dot })
|
|
311
|
-
: _jsx("span", { className: channelCss.taskDotQuiet, "data-variant": dot });
|
|
312
|
-
})() }), _jsx("span", { className: channelCss.taskNumber, children: t('taskLabel', { number: item.taskNumber }) }), _jsx("span", { className: channelCss.taskStatus, children: formatTaskStatus(item.task.status, t) }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] }), item.message.topLevel && item.task === undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openThread'), onClick: () => { selectThread(item.thread.threadRef, channelRef); }, children: [_jsx("span", { className: channelCss.taskNumber, children: t('threadLabel') }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] })] })] }, item.message.messageRef);
|
|
308
|
+
return _jsxs(Fragment, { children: [turnGap && _jsx(TeamRunDivider, { occurredAt: item.message.occurredAt }), _jsxs(TeamMessage, { senderName: sender, memberId: item.message.sender, human: human, body: item.message.body, attachments: item.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: item.message.occurredAt, mentionNames: mentionNamesOf(item.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: index > 0, showGroupedTime: item.message.topLevel === true && !turnGap, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }), children: [item.message.topLevel && item.task !== undefined && item.taskNumber !== undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openTask', { number: item.taskNumber }), onClick: () => { selectThread(item.thread.threadRef, channelRef, item.task?.taskRef, item.taskNumber); }, children: [_jsx("span", { className: channelCss.taskDot, children: _jsx(TeamStateDot, { size: 8, state: taskStatusDot(item.task.status) }) }), _jsx("span", { className: channelCss.taskNumber, children: t('taskLabel', { number: item.taskNumber }) }), _jsx("span", { className: channelCss.taskStatus, children: formatTaskStatus(item.task.status, t) }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] }), item.message.topLevel && item.task === undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openThread'), onClick: () => { selectThread(item.thread.threadRef, channelRef); }, children: [_jsx("span", { className: channelCss.taskNumber, children: t('threadLabel') }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] })] })] }, item.message.messageRef);
|
|
313
309
|
}) }, `run-${block.items[0].message.messageRef}`))] }) }), channel !== undefined ? _jsx(TeamComposer, { members: channelMembers, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setStatusMessage(undefined); }, onSubmit: () => { void send(); }, pendingFiles: pendingFiles, onFilesChange: setPendingFiles, asTask: asTask, onAsTaskChange: setAsTask, t: t }, draftKey) : _jsx("div", {})] });
|
|
314
310
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { AgentTeamClientMemberStatus, AgentTeamMemberId } from '@wowyuarm/dsh-agent-team/types';
|
|
2
2
|
import type { TeamConversationProps } from './slots.ts';
|
|
3
|
-
export declare function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
|
|
3
|
+
export declare function TeamComposer({ members, followerMemberIds, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
|
|
4
4
|
readonly members: readonly AgentTeamClientMemberStatus[];
|
|
5
|
+
/** Current Thread followers; the Thread surface passes them so they rank above other candidates. */
|
|
6
|
+
readonly followerMemberIds?: ReadonlySet<AgentTeamMemberId>;
|
|
5
7
|
readonly recipients: ReadonlySet<AgentTeamMemberId>;
|
|
6
8
|
readonly draft: string;
|
|
7
9
|
readonly pending: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAqEvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,EAAE,EAAE;IAC9N,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAA;IACxD,oGAAoG;IACpG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IAC3D,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,QAAQ,CAAC,kBAAkB,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,gGAAgG;IAChG,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,IAAI,EAAE,CAAA;IACvC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI,CAAA;IACzD,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;IACnD,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BA6SA"}
|
|
@@ -31,6 +31,12 @@ function mentionCandidates(members, query) {
|
|
|
31
31
|
&& status.member.state !== 'archived'
|
|
32
32
|
&& status.member.handle.toLocaleLowerCase().startsWith(normalized));
|
|
33
33
|
}
|
|
34
|
+
/** Thread followers rank first: mentioning them delivers directly, while a non-follower enters the two-send invite flow. */
|
|
35
|
+
function rankMentionCandidates(candidates, followers) {
|
|
36
|
+
if (followers === undefined || followers.size === 0)
|
|
37
|
+
return candidates;
|
|
38
|
+
return [...candidates].sort((left, right) => Number(followers.has(right.member.memberId)) - Number(followers.has(left.member.memberId)));
|
|
39
|
+
}
|
|
34
40
|
/** Every member a manual handle pick could reach: the @all expansion snapshot. */
|
|
35
41
|
function allMentionMembers(members) {
|
|
36
42
|
return members.filter(status => status.presence !== 'unavailable' && status.member.state !== 'inactive' && status.member.state !== 'archived');
|
|
@@ -50,22 +56,24 @@ function draftPreviewUrl(file) {
|
|
|
50
56
|
}
|
|
51
57
|
return url;
|
|
52
58
|
}
|
|
53
|
-
export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
|
|
59
|
+
export function TeamComposer({ members, followerMemberIds, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
|
|
54
60
|
const inputRef = useRef(null);
|
|
55
61
|
const fileInputRef = useRef(null);
|
|
56
62
|
const rootRef = useRef(null);
|
|
57
63
|
const menuRef = useRef(null);
|
|
64
|
+
const activeOptionRef = useRef(null);
|
|
58
65
|
const composingRef = useRef(false);
|
|
59
66
|
const [mention, setMention] = useState();
|
|
60
67
|
const [highlight, setHighlight] = useState(0);
|
|
61
68
|
// @all is a composer-layer expansion of the same eligibility filter a
|
|
62
69
|
// handle pick uses; the fixed row stays on top of the matching members.
|
|
63
|
-
const memberCandidates = mention === undefined ? [] : mentionCandidates(members, mention.query);
|
|
70
|
+
const memberCandidates = mention === undefined ? [] : rankMentionCandidates(mentionCandidates(members, mention.query), followerMemberIds);
|
|
64
71
|
const options = mention !== undefined && 'all'.startsWith(mention.query.toLocaleLowerCase())
|
|
65
72
|
? [{ kind: 'all' }, ...memberCandidates.map(status => ({ kind: 'member', status }))]
|
|
66
73
|
: memberCandidates.map(status => ({ kind: 'member', status }));
|
|
67
74
|
const menuOpen = mention !== undefined && options.length > 0;
|
|
68
75
|
const activeOption = options[highlight];
|
|
76
|
+
const activeOptionKey = activeOption === undefined ? undefined : activeOption.kind === 'all' ? 'all' : activeOption.status.member.memberId;
|
|
69
77
|
const allCount = allMentionMembers(members).length;
|
|
70
78
|
const listId = 'team-mention-suggestions';
|
|
71
79
|
const menuMaxHeight = useAnchoredMaxHeight(menuRef, 320, menuOpen ? draft : null);
|
|
@@ -85,6 +93,14 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
85
93
|
}
|
|
86
94
|
setHighlight(current => Math.min(current, options.length - 1));
|
|
87
95
|
}, [mention, options.length]);
|
|
96
|
+
// The anchored height cap keeps long rosters scrollable; without this the
|
|
97
|
+
// keyboard-highlighted row can stay hidden below the fold.
|
|
98
|
+
useLayoutEffect(() => {
|
|
99
|
+
if (!menuOpen)
|
|
100
|
+
return;
|
|
101
|
+
// Optional call: jsdom renders the menu without a layout engine.
|
|
102
|
+
activeOptionRef.current?.scrollIntoView?.({ block: 'nearest' });
|
|
103
|
+
}, [menuOpen, highlight, activeOptionKey]);
|
|
88
104
|
// Match the resident DSH composer without stealing a later user choice: Team
|
|
89
105
|
// data can load after navigation, so a dialog or another control may already
|
|
90
106
|
// own focus by the time this composer appears.
|
|
@@ -135,6 +151,21 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
135
151
|
pruneRecipients(nextDraft);
|
|
136
152
|
updateMention(nextDraft, event.target.selectionStart ?? nextDraft.length);
|
|
137
153
|
};
|
|
154
|
+
// Both pick kinds share the same commit path: swap the text in, add the
|
|
155
|
+
// recipients, close the menu, and restore the caret after focus.
|
|
156
|
+
const commitMention = (nextDraft, nextCaret, nextRecipients) => {
|
|
157
|
+
onDraftChange(nextDraft);
|
|
158
|
+
onRecipientsChange(nextRecipients);
|
|
159
|
+
setMention(undefined);
|
|
160
|
+
setHighlight(0);
|
|
161
|
+
requestAnimationFrame(() => {
|
|
162
|
+
const input = inputRef.current;
|
|
163
|
+
if (input === null)
|
|
164
|
+
return;
|
|
165
|
+
input.focus({ preventScroll: true });
|
|
166
|
+
input.setSelectionRange(nextCaret, nextCaret);
|
|
167
|
+
});
|
|
168
|
+
};
|
|
138
169
|
const selectOption = (option) => {
|
|
139
170
|
if (mention === undefined)
|
|
140
171
|
return;
|
|
@@ -146,35 +177,14 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
146
177
|
const nextRecipients = new Set(recipients);
|
|
147
178
|
for (const status of allMentionMembers(members))
|
|
148
179
|
nextRecipients.add(status.member.memberId);
|
|
149
|
-
|
|
150
|
-
onRecipientsChange(nextRecipients);
|
|
151
|
-
setMention(undefined);
|
|
152
|
-
setHighlight(0);
|
|
153
|
-
requestAnimationFrame(() => {
|
|
154
|
-
const input = inputRef.current;
|
|
155
|
-
if (input === null)
|
|
156
|
-
return;
|
|
157
|
-
input.focus({ preventScroll: true });
|
|
158
|
-
input.setSelectionRange(nextCaret, nextCaret);
|
|
159
|
-
});
|
|
180
|
+
commitMention(nextDraft, nextCaret, nextRecipients);
|
|
160
181
|
return;
|
|
161
182
|
}
|
|
162
183
|
const member = option.status;
|
|
163
184
|
const inserted = `@${member.member.handle} `;
|
|
164
185
|
const nextDraft = `${draft.slice(0, mention.start)}${inserted}${draft.slice(mention.end)}`;
|
|
165
186
|
const nextCaret = mention.start + inserted.length;
|
|
166
|
-
|
|
167
|
-
onDraftChange(nextDraft);
|
|
168
|
-
onRecipientsChange(nextRecipients);
|
|
169
|
-
setMention(undefined);
|
|
170
|
-
setHighlight(0);
|
|
171
|
-
requestAnimationFrame(() => {
|
|
172
|
-
const input = inputRef.current;
|
|
173
|
-
if (input === null)
|
|
174
|
-
return;
|
|
175
|
-
input.focus({ preventScroll: true });
|
|
176
|
-
input.setSelectionRange(nextCaret, nextCaret);
|
|
177
|
-
});
|
|
187
|
+
commitMention(nextDraft, nextCaret, new Set(recipients).add(member.member.memberId));
|
|
178
188
|
};
|
|
179
189
|
// Pasted files (screenshots, copies) join the same chips the "+" picker
|
|
180
190
|
// fills; only a paste that carries files is intercepted, so plain text
|
|
@@ -234,8 +244,8 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
234
244
|
const optionId = option.kind === 'all' ? `${listId}-all` : `${listId}-${option.status.member.memberId}`;
|
|
235
245
|
const selected = index === highlight;
|
|
236
246
|
return option.kind === 'all'
|
|
237
|
-
? _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx("span", { className: css.mentionAllDot, "aria-hidden": "true" }), _jsx("span", { className: css.mentionName, children: "@all" }), _jsx("span", { className: css.mentionDescription, children: t('mentionAll', { count: allCount }) })] }, "all")
|
|
238
|
-
: _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx(TeamPresenceDot, { status: option.status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", option.status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: option.status.member.description })] }, option.status.member.memberId);
|
|
247
|
+
? _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, ref: selected ? activeOptionRef : undefined, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx("span", { className: css.mentionAllDot, "aria-hidden": "true" }), _jsx("span", { className: css.mentionName, children: "@all" }), _jsx("span", { className: css.mentionDescription, children: t('mentionAll', { count: allCount }) })] }, "all")
|
|
248
|
+
: _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, ref: selected ? activeOptionRef : undefined, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx(TeamPresenceDot, { status: option.status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", option.status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: option.status.member.description })] }, option.status.member.memberId);
|
|
239
249
|
}) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeOption !== undefined ? activeOption.kind === 'all' ? `${listId}-all` : `${listId}-${activeOption.status.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: placeholder ?? t('messagePlaceholder'), rows: 1, onChange: onChange, onPaste: onPaste, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), recipients.size > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: [...recipients].sort().map(memberId => `@${members.find(candidate => candidate.member.memberId === memberId)?.member.handle ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
|
|
240
250
|
const previewUrl = draftPreviewUrl(file);
|
|
241
251
|
return _jsxs("li", { className: `${css.fileChip} ${previewUrl !== undefined ? css.imageChip : ''}`, children: [previewUrl !== undefined && _jsx("img", { src: previewUrl, alt: "", className: css.imageChipPreview }), _jsxs("span", { className: css.fileChipName, title: file.name, children: [file.name, _jsx("span", { className: css.fileChipSize, children: formatByteSize(file.size) })] }), _jsx("button", { type: "button", className: css.fileChipRemove, "aria-label": t('removeFile', { name: file.name }), disabled: pending, onClick: () => {
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { TeamConversationProps } from './slots.ts';
|
|
2
|
-
export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
|
|
2
|
+
export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=TeamConversation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAKvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+
|
|
1
|
+
{"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAKvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BAoB7X"}
|
|
@@ -3,12 +3,12 @@ import { useSyncExternalStore } from 'react';
|
|
|
3
3
|
import { TeamChannelPage } from "./TeamChannelPage.js";
|
|
4
4
|
import { TeamThreadPage } from "./TeamThreadPage.js";
|
|
5
5
|
import css from './conversation.module.css';
|
|
6
|
-
export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
|
|
6
|
+
export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
|
|
7
7
|
const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
|
|
8
8
|
const workspaces = useWorkspaces(state => state.items);
|
|
9
9
|
const current = workspaces.find(workspace => workspace.workspaceId === navigationState.workspaceId);
|
|
10
10
|
if (current !== undefined && navigationState.threadRef !== undefined) {
|
|
11
|
-
return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
|
|
11
|
+
return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, threadObservations: threadObservations, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
|
|
12
12
|
}
|
|
13
13
|
if (current !== undefined && navigationState.channelRef !== undefined) {
|
|
14
14
|
return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, t: t }, navigationState.channelRef);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamMemberAvatar.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberAvatar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamMemberAvatar.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberAvatar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAMlD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC9C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BAkBA"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
3
|
import { memberHue } from "./team-formatters.js";
|
|
4
4
|
import { presenceDotState, presenceLabel } from "./TeamPresenceDot.js";
|
|
5
|
+
import { TeamStateDot } from "./TeamStateDot.js";
|
|
5
6
|
import css from './sidebar.module.css';
|
|
6
7
|
/**
|
|
7
8
|
* Sidebar Member avatar reusing the conversation identity language: the
|
|
@@ -11,5 +12,5 @@ import css from './sidebar.module.css';
|
|
|
11
12
|
export function TeamMemberAvatar({ status, t }) {
|
|
12
13
|
const label = presenceLabel(status, t);
|
|
13
14
|
const state = presenceDotState(status.presence);
|
|
14
|
-
return (_jsx(Tooltip, { label: label, delayMs: 300, children: _jsxs("span", { className: css.agentAvatar, style: { '--team-avatar-hue': memberHue(status.member.memberId) }, role: "img", "aria-label": label, children: [status.member.handle.replace('@', '').slice(0, 1).toUpperCase(), _jsx("span", { className: css.agentAvatarBadge, "aria-hidden": "true", children:
|
|
15
|
+
return (_jsx(Tooltip, { label: label, delayMs: 300, children: _jsxs("span", { className: css.agentAvatar, style: { '--team-avatar-hue': memberHue(status.member.memberId) }, role: "img", "aria-label": label, children: [status.member.handle.replace('@', '').slice(0, 1).toUpperCase(), _jsx("span", { className: css.agentAvatarBadge, "aria-hidden": "true", children: _jsx(TeamStateDot, { state: state }) })] }) }));
|
|
15
16
|
}
|
|
@@ -218,6 +218,11 @@ function renderRefs(text, onOpenRef, taskLabel, refLabel) {
|
|
|
218
218
|
if (segment.ref === undefined)
|
|
219
219
|
return _jsx(Fragment, { children: segment.text }, index);
|
|
220
220
|
const resolved = cachedResolvedTaskRef(segment.ref);
|
|
221
|
+
// Task refs link only once the Host confirms them; abbreviated or unknown
|
|
222
|
+
// refs stay plain text so prose never misfires as a link. Channel and
|
|
223
|
+
// Thread refs have no resolver and link at the authored position.
|
|
224
|
+
if (segment.ref.startsWith('task:') && resolved === undefined)
|
|
225
|
+
return _jsx(Fragment, { children: segment.text }, index);
|
|
221
226
|
const label = resolved !== undefined && segment.ref.startsWith('task:') ? taskLabel(resolved.taskNumber) : refLabel(segment.ref);
|
|
222
227
|
return _jsx("button", { type: "button", className: css.refLink, title: segment.ref, onClick: () => { onOpenRef(segment.ref); }, children: label }, index);
|
|
223
228
|
});
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { StateDotState } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
2
1
|
import type { AgentTeamClientMemberStatus } from '@wowyuarm/dsh-agent-team/types';
|
|
3
2
|
import type { TeamSidebarProps } from './slots.ts';
|
|
3
|
+
import type { TeamStateDotState } from './TeamStateDot.tsx';
|
|
4
4
|
export declare function presenceLabel(status: AgentTeamClientMemberStatus, t: TeamSidebarProps['t']): string;
|
|
5
5
|
/** Shared presence → indicator mapping for dots and avatar badges. */
|
|
6
|
-
export declare function presenceDotState(presence: AgentTeamClientMemberStatus['presence']):
|
|
6
|
+
export declare function presenceDotState(presence: AgentTeamClientMemberStatus['presence']): TeamStateDotState;
|
|
7
7
|
export declare function TeamPresenceDot({ status, t }: {
|
|
8
8
|
readonly status: AgentTeamClientMemberStatus;
|
|
9
9
|
readonly t: TeamSidebarProps['t'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamPresenceDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamPresenceDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"TeamPresenceDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamPresenceDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,2BAA2B,EAAE,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,MAAM,CAKnG;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAAG,iBAAiB,CAErG;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC7C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BASA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import { TeamStateDot } from "./TeamStateDot.js";
|
|
3
4
|
import css from './presence.module.css';
|
|
4
5
|
export function presenceLabel(status, t) {
|
|
5
6
|
const label = status.presence === 'available' ? t('statusAvailable')
|
|
@@ -9,12 +10,9 @@ export function presenceLabel(status, t) {
|
|
|
9
10
|
}
|
|
10
11
|
/** Shared presence → indicator mapping for dots and avatar badges. */
|
|
11
12
|
export function presenceDotState(presence) {
|
|
12
|
-
return presence === 'available' ? 'done' : presence === 'working' ? 'ongoing' : presence === 'error' ? 'error' : '
|
|
13
|
+
return presence === 'available' ? 'done' : presence === 'working' ? 'ongoing' : presence === 'error' ? 'error' : 'quiet';
|
|
13
14
|
}
|
|
14
15
|
export function TeamPresenceDot({ status, t }) {
|
|
15
16
|
const label = presenceLabel(status, t);
|
|
16
|
-
|
|
17
|
-
return (_jsx(Tooltip, { label: label, delayMs: 300, children: _jsx("span", { className: css.target, role: "img", "aria-label": label, children: state === 'gray'
|
|
18
|
-
? _jsx("span", { className: css.unavailableDot, "aria-hidden": "true" })
|
|
19
|
-
: _jsx(StateDot, { state: state }) }) }));
|
|
17
|
+
return (_jsx(Tooltip, { label: label, delayMs: 300, children: _jsx("span", { className: css.target, role: "img", "aria-label": label, children: _jsx(TeamStateDot, { state: presenceDotState(status.presence) }) }) }));
|
|
20
18
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { StateDotState } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
2
|
+
/** The Team status dot language: the DSH four-color states plus the two quiet statuses StateDot has no slot for. */
|
|
3
|
+
export type TeamStateDotState = StateDotState | 'todo' | 'quiet';
|
|
4
|
+
/**
|
|
5
|
+
* Render the one shared Team status indicator. Native StateDot states pass
|
|
6
|
+
* through; the quiet statuses mirror StateDot geometry locally so every
|
|
7
|
+
* surface renders the same shape — todo a hollow ring (not started), quiet a
|
|
8
|
+
* solid tertiary dot with the same 10% halo (closed, unavailable).
|
|
9
|
+
*/
|
|
10
|
+
export declare function TeamStateDot({ state, size }: {
|
|
11
|
+
readonly state: TeamStateDotState;
|
|
12
|
+
readonly size?: number | undefined;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
//# sourceMappingURL=TeamStateDot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeamStateDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamStateDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAA;AAG1E,oHAAoH;AACpH,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,MAAM,GAAG,OAAO,CAAA;AAEhE;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;IACjD,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAA;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC,+BAKA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import css from './state-dot.module.css';
|
|
4
|
+
/**
|
|
5
|
+
* Render the one shared Team status indicator. Native StateDot states pass
|
|
6
|
+
* through; the quiet statuses mirror StateDot geometry locally so every
|
|
7
|
+
* surface renders the same shape — todo a hollow ring (not started), quiet a
|
|
8
|
+
* solid tertiary dot with the same 10% halo (closed, unavailable).
|
|
9
|
+
*/
|
|
10
|
+
export function TeamStateDot({ state, size = 10 }) {
|
|
11
|
+
if (state === 'todo' || state === 'quiet') {
|
|
12
|
+
return _jsx("span", { className: css.quiet, "data-variant": state, style: { width: size, height: size }, "aria-hidden": "true" });
|
|
13
|
+
}
|
|
14
|
+
return _jsx(StateDot, { state: state, size: size });
|
|
15
|
+
}
|
|
@@ -12,6 +12,7 @@ interface TeamThreadPageProps {
|
|
|
12
12
|
readonly loadChannels: TeamConversationProps['loadChannels'];
|
|
13
13
|
readonly readThread: TeamConversationProps['readThread'];
|
|
14
14
|
readonly loadThreadHistory: TeamConversationProps['loadThreadHistory'];
|
|
15
|
+
readonly threadObservations: TeamConversationProps['threadObservations'];
|
|
15
16
|
readonly subscribeChanges: TeamConversationProps['subscribeChanges'];
|
|
16
17
|
readonly loadMembers: TeamConversationProps['loadMembers'];
|
|
17
18
|
readonly drafts: TeamDraftStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAe/D,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,QAAQ,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,oBAAoB,CAAC,CAAA;IACxE,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC9C,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA8BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAgsBxD"}
|