@wowyuarm/dsh-agent-team 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/README.zh.md +103 -0
  4. package/cordis.patch.yml +36 -0
  5. package/package.json +152 -0
  6. package/packages/agent-team/README.md +58 -0
  7. package/packages/agent-team/README.zh.md +57 -0
  8. package/packages/agent-team/lib/index.js +841 -0
  9. package/packages/agent-team/lib/invariant.js +24 -0
  10. package/packages/agent-team/lib/ledger.js +2161 -0
  11. package/packages/agent-team/lib/member-context.js +69 -0
  12. package/packages/agent-team/lib/preset-roster.js +14 -0
  13. package/packages/agent-team/lib/spec.js +347 -0
  14. package/packages/agent-team/lib/typert.host.d.ts +3 -0
  15. package/packages/agent-team/lib/typert.host.js +2084 -0
  16. package/packages/agent-team/lib/typert.remote-client.d.ts +54 -0
  17. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -0
  18. package/packages/agent-team/lib/typert.remote-client.js +1218 -0
  19. package/packages/agent-team/lib/types/index.d.ts +156 -0
  20. package/packages/agent-team/lib/types/index.d.ts.map +1 -0
  21. package/packages/agent-team/lib/types/invariant.d.ts +13 -0
  22. package/packages/agent-team/lib/types/invariant.d.ts.map +1 -0
  23. package/packages/agent-team/lib/types/ledger.d.ts +271 -0
  24. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -0
  25. package/packages/agent-team/lib/types/member-context.d.ts +7 -0
  26. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -0
  27. package/packages/agent-team/lib/types/preset-roster.d.ts +5 -0
  28. package/packages/agent-team/lib/types/preset-roster.d.ts.map +1 -0
  29. package/packages/agent-team/lib/types/spec.d.ts +13 -0
  30. package/packages/agent-team/lib/types/spec.d.ts.map +1 -0
  31. package/packages/agent-team/lib/types/types.d.ts +818 -0
  32. package/packages/agent-team/lib/types/types.d.ts.map +1 -0
  33. package/packages/agent-team/lib/types.js +1 -0
  34. package/packages/agent-team/preset/team-member/agent.cordis.yml +89 -0
  35. package/packages/client-agent-team/README.md +39 -0
  36. package/packages/client-agent-team/README.zh.md +39 -0
  37. package/packages/client-agent-team/lib/client.js +9633 -0
  38. package/packages/client-agent-team/lib/client.js.map +1 -0
  39. package/packages/client-agent-team/lib/index.js +5 -0
  40. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +20 -0
  41. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -0
  42. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +274 -0
  43. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +19 -0
  44. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -0
  45. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +243 -0
  46. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts +21 -0
  47. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -0
  48. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +196 -0
  49. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +15 -0
  50. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -0
  51. package/packages/client-agent-team/lib/types/client/TeamComposer.js +144 -0
  52. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +3 -0
  53. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -0
  54. package/packages/client-agent-team/lib/types/client/TeamConversation.js +20 -0
  55. package/packages/client-agent-team/lib/types/client/TeamFooterAction.d.ts +3 -0
  56. package/packages/client-agent-team/lib/types/client/TeamFooterAction.d.ts.map +1 -0
  57. package/packages/client-agent-team/lib/types/client/TeamFooterAction.js +19 -0
  58. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts +13 -0
  59. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts.map +1 -0
  60. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.js +90 -0
  61. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.d.ts +12 -0
  62. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.d.ts.map +1 -0
  63. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.js +15 -0
  64. package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts +5 -0
  65. package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts.map +1 -0
  66. package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +32 -0
  67. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +19 -0
  68. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -0
  69. package/packages/client-agent-team/lib/types/client/TeamMessage.js +24 -0
  70. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +11 -0
  71. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -0
  72. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +20 -0
  73. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts +15 -0
  74. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -0
  75. package/packages/client-agent-team/lib/types/client/TeamRowMenu.js +23 -0
  76. package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts +3 -0
  77. package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts.map +1 -0
  78. package/packages/client-agent-team/lib/types/client/TeamSettings.js +5 -0
  79. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts +9 -0
  80. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts.map +1 -0
  81. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +9 -0
  82. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +22 -0
  83. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -0
  84. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +464 -0
  85. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +3 -0
  86. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -0
  87. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +39 -0
  88. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts +11 -0
  89. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.d.ts.map +1 -0
  90. package/packages/client-agent-team/lib/types/client/TeamWorkspaceRow.js +6 -0
  91. package/packages/client-agent-team/lib/types/client/index.d.ts +19 -0
  92. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -0
  93. package/packages/client-agent-team/lib/types/client/index.js +113 -0
  94. package/packages/client-agent-team/lib/types/client/locales.d.ts +224 -0
  95. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -0
  96. package/packages/client-agent-team/lib/types/client/locales.js +222 -0
  97. package/packages/client-agent-team/lib/types/client/navigation.d.ts +39 -0
  98. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -0
  99. package/packages/client-agent-team/lib/types/client/navigation.js +96 -0
  100. package/packages/client-agent-team/lib/types/client/slots.d.ts +74 -0
  101. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -0
  102. package/packages/client-agent-team/lib/types/client/slots.js +1 -0
  103. package/packages/client-agent-team/lib/types/client/team-changes.d.ts +28 -0
  104. package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -0
  105. package/packages/client-agent-team/lib/types/client/team-changes.js +70 -0
  106. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +53 -0
  107. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -0
  108. package/packages/client-agent-team/lib/types/client/team-formatters.js +128 -0
  109. package/packages/client-agent-team/lib/types/client/team-membership.d.ts +25 -0
  110. package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -0
  111. package/packages/client-agent-team/lib/types/client/team-membership.js +39 -0
  112. package/packages/client-agent-team/lib/types/client/team-separators.d.ts +23 -0
  113. package/packages/client-agent-team/lib/types/client/team-separators.d.ts.map +1 -0
  114. package/packages/client-agent-team/lib/types/client/team-separators.js +52 -0
  115. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts +18 -0
  116. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts.map +1 -0
  117. package/packages/client-agent-team/lib/types/client/timeline-scroll.js +54 -0
  118. package/packages/client-agent-team/lib/types/index.d.ts +3 -0
  119. package/packages/client-agent-team/lib/types/index.d.ts.map +1 -0
  120. package/packages/client-agent-team/lib/types/index.js +2 -0
  121. package/packages/tool-agent-team/README.md +23 -0
  122. package/packages/tool-agent-team/README.zh.md +23 -0
  123. package/packages/tool-agent-team/lib/index.js +294 -0
  124. package/packages/tool-agent-team/lib/types/index.d.ts +5 -0
  125. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -0
@@ -0,0 +1,196 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { Button, IconEditOutline16, IconPlusOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
+ import { TeamRowMenu } from "./TeamRowMenu.js";
6
+ import { TeamSidebarSection } from "./TeamSidebarSection.js";
7
+ import { useChannelMembership } from "./team-membership.js";
8
+ import createCss from './create.module.css';
9
+ import css from './sidebar.module.css';
10
+ export function TeamChannelsPanel(props) {
11
+ const { workspaceId, loadMembers, loadChannels, subscribeChanges, createChannel, updateChannel, creatingAgents, selectedChannelRef, selectChannel, t } = props;
12
+ const [view, setView] = useState();
13
+ const [members, setMembers] = useState([]);
14
+ const [loading, setLoading] = useState(true);
15
+ const [error, setError] = useState();
16
+ const [formOpen, setFormOpen] = useState(false);
17
+ const [name, setName] = useState('');
18
+ const [description, setDescription] = useState('');
19
+ const [selected, setSelected] = useState(new Set());
20
+ const [mutating, setMutating] = useState(false);
21
+ const [pendingCreate, setPendingCreate] = useState();
22
+ const previousCreatingKey = useRef(creatingAgents.map(request => request.requestId).join(','));
23
+ const triggerRef = useRef(null);
24
+ const refresh = useCallback(async () => {
25
+ setLoading(true);
26
+ const [channelResult, memberResult] = await Promise.all([
27
+ loadChannels({ workspaceId, limit: 1 }),
28
+ loadMembers({ workspaceId }),
29
+ ]);
30
+ if (channelResult.ok && memberResult.ok) {
31
+ setView(channelResult.value);
32
+ const visibleMembers = memberResult.value.filter(status => status.member.state !== 'inactive');
33
+ setMembers(visibleMembers);
34
+ const selectable = new Set(visibleMembers.filter(status => status.presence !== 'unavailable')
35
+ .map(status => status.member.memberId));
36
+ setSelected(current => new Set([...current].filter(memberId => selectable.has(memberId))));
37
+ setError(undefined);
38
+ }
39
+ else if (!channelResult.ok) {
40
+ setError(channelResult.error.message);
41
+ }
42
+ else if (!memberResult.ok) {
43
+ setError(memberResult.error.message);
44
+ }
45
+ setLoading(false);
46
+ }, [loadChannels, loadMembers, workspaceId]);
47
+ useEffect(() => { void refresh(); }, [refresh]);
48
+ useEffect(() => subscribeChanges({ kind: 'workspace', workspaceId }, update => {
49
+ if (update.type === 'failed') {
50
+ setError(update.message);
51
+ return;
52
+ }
53
+ void refresh();
54
+ }), [subscribeChanges, refresh, workspaceId]);
55
+ const creatingKey = creatingAgents.map(request => request.requestId).join(',');
56
+ useEffect(() => {
57
+ if (previousCreatingKey.current === creatingKey)
58
+ return;
59
+ previousCreatingKey.current = creatingKey;
60
+ void refresh();
61
+ }, [creatingKey, refresh]);
62
+ const membership = useMemo(() => {
63
+ const byChannel = new Map();
64
+ for (const item of view?.members ?? []) {
65
+ const ids = byChannel.get(item.channelRef) ?? new Set();
66
+ ids.add(item.memberId);
67
+ byChannel.set(item.channelRef, ids);
68
+ }
69
+ return byChannel;
70
+ }, [view]);
71
+ const closeForm = () => {
72
+ if (mutating)
73
+ return;
74
+ setFormOpen(false);
75
+ queueMicrotask(() => { triggerRef.current?.focus(); });
76
+ };
77
+ const submit = async (event) => {
78
+ event.preventDefault();
79
+ if (mutating || name.trim() === '' || description.trim() === '')
80
+ return;
81
+ const payload = { workspaceId, name: name.trim(), description: description.trim(), memberIds: [...selected] };
82
+ const samePending = pendingCreate !== undefined && pendingCreate.workspaceId === payload.workspaceId
83
+ && pendingCreate.name === payload.name && pendingCreate.description === payload.description
84
+ && JSON.stringify(pendingCreate.memberIds) === JSON.stringify(payload.memberIds);
85
+ const request = samePending ? pendingCreate : {
86
+ requestId: crypto.randomUUID(), ...payload,
87
+ };
88
+ setPendingCreate(request);
89
+ setMutating(true);
90
+ setError(undefined);
91
+ try {
92
+ const result = await createChannel(request);
93
+ if (result.ok) {
94
+ setPendingCreate(undefined);
95
+ setName('');
96
+ setDescription('');
97
+ setSelected(new Set());
98
+ setFormOpen(false);
99
+ await refresh();
100
+ queueMicrotask(() => { triggerRef.current?.focus(); });
101
+ }
102
+ else {
103
+ setError(result.error.message);
104
+ }
105
+ }
106
+ catch (cause) {
107
+ setError(cause instanceof Error ? cause.message : String(cause));
108
+ }
109
+ finally {
110
+ setMutating(false);
111
+ }
112
+ };
113
+ return (_jsxs("div", { className: css.panel, children: [_jsx(Modal, { open: formOpen, onClose: closeForm, title: t('addChannel'), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: mutating, onClick: closeForm, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-channel-create-form", variant: "primary", disabled: mutating || name.trim() === '' || description.trim() === '', children: mutating ? t('creatingChannel') : t('createChannel') })] }), children: _jsxs("form", { id: "team-channel-create-form", className: createCss.form, onSubmit: event => { void submit(event); }, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('channelName') }), _jsx(Input, { className: createCss.input, value: name, disabled: mutating, autoFocus: true, onChange: event => { setName(event.target.value); setPendingCreate(undefined); } })] }), _jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('channelDescription') }), _jsx(Input, { className: createCss.input, value: description, disabled: mutating, onChange: event => { setDescription(event.target.value); setPendingCreate(undefined); } })] }), _jsxs("fieldset", { className: createCss.memberPicker, disabled: mutating, children: [_jsx("legend", { children: t('initialMembers') }), creatingAgents.map(request => (_jsxs("label", { className: createCss.memberOption, children: [_jsx("input", { type: "checkbox", disabled: true, "aria-describedby": `creating-member-reason-${request.requestId}` }), _jsx("span", { className: createCss.unavailableDot, "aria-hidden": "true" }), _jsx("span", { children: request.handle }), _jsx("small", { id: `creating-member-reason-${request.requestId}`, children: t('memberCreatingReason') })] }, request.requestId))), members.map(status => {
114
+ const disabled = status.presence === 'unavailable';
115
+ const reasonId = `initial-member-reason-${status.member.memberId}`;
116
+ return (_jsxs("label", { className: createCss.memberOption, children: [_jsx("input", { type: "checkbox", disabled: disabled, "aria-describedby": disabled ? reasonId : undefined, checked: selected.has(status.member.memberId), onChange: event => {
117
+ setSelected(current => {
118
+ const next = new Set(current);
119
+ if (event.target.checked)
120
+ next.add(status.member.memberId);
121
+ else
122
+ next.delete(status.member.memberId);
123
+ return next;
124
+ });
125
+ setPendingCreate(undefined);
126
+ } }), _jsx(TeamPresenceDot, { status: status, t: t }), _jsx("span", { children: status.member.handle }), disabled && _jsx("small", { id: reasonId, children: t('memberUnavailableReason') })] }, status.member.memberId));
127
+ })] }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('channels'), actions: (_jsx(Tooltip, { label: t('addChannel'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addChannel'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && view === undefined && _jsx("p", { className: css.emptyState, children: t('loadingChannels') }), !loading && (view?.channels.length ?? 0) === 0 && _jsx("p", { className: css.emptyState, children: t('emptyChannels') }), _jsx("div", { className: css.channelList, children: view?.channels.map(channel => {
128
+ const joined = membership.get(channel.channelRef) ?? new Set();
129
+ return (_jsx(ChannelRow, { channel: channel, members: members, joinedIds: joined, selected: selectedChannelRef === channel.channelRef, updateChannel: updateChannel, joinChannel: props.joinChannel, removeChannelMember: props.removeChannelMember, onCommitted: () => { void refresh(); }, selectChannel: selectChannel, t: t }, channel.channelRef));
130
+ }) })] }), !formOpen && error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] }));
131
+ }
132
+ /**
133
+ * One sidebar Channel row: the select button keeps the `#` identity while the
134
+ * row menu carries the entry point; editing covers display facts and membership.
135
+ */
136
+ function ChannelRow({ channel, members, joinedIds, selected, updateChannel, joinChannel, removeChannelMember, onCommitted, selectChannel, t }) {
137
+ const [menuOpen, setMenuOpen] = useState(false);
138
+ const [editing, setEditing] = useState(false);
139
+ return (_jsxs(_Fragment, { children: [_jsxs("article", { className: css.channelRow, "data-menu-open": menuOpen || undefined, "aria-current": selected ? 'page' : undefined, children: [_jsx("button", { type: "button", className: css.channelSelect, "aria-label": `# ${channel.name}`, onClick: () => { selectChannel(channel.channelRef); }, children: _jsxs("strong", { className: css.channelName, children: ["# ", channel.name] }) }), _jsx("span", { className: css.rowMenu, children: _jsx(TeamRowMenu, { label: t('actionsChannel', { name: channel.name }), items: [{ id: 'edit', label: t('editChannel'), icon: _jsx(IconEditOutline16, {}) }], onSelect: () => { setEditing(true); }, onOpenChange: setMenuOpen }) })] }), editing && (_jsx(ChannelEditorDialog, { channel: channel, members: members, joinedIds: joinedIds, updateChannel: updateChannel, joinChannel: joinChannel, removeChannelMember: removeChannelMember, onCommitted: onCommitted, onClose: () => { setEditing(false); }, t: t }))] }));
140
+ }
141
+ /**
142
+ * Channel editor: name and description commit through one durable update;
143
+ * Channel membership below keeps its own immediate add/remove flow.
144
+ */
145
+ function ChannelEditorDialog({ channel, members, joinedIds, updateChannel, joinChannel, removeChannelMember, onCommitted, onClose, t }) {
146
+ const [name, setName] = useState(channel.name);
147
+ const [description, setDescription] = useState(channel.description);
148
+ const [saving, setSaving] = useState(false);
149
+ const [error, setError] = useState();
150
+ const pendingRequest = useRef();
151
+ const membership = useChannelMembership({ joinChannel, removeChannelMember }, change => change.memberId, async () => { await onCommitted(); });
152
+ const dirty = name.trim() !== channel.name || description.trim() !== channel.description;
153
+ const submit = async (event) => {
154
+ event.preventDefault();
155
+ const normalizedName = name.trim();
156
+ const normalizedDescription = description.trim();
157
+ if (saving || !dirty || normalizedName === '' || normalizedDescription === '')
158
+ return;
159
+ const samePending = pendingRequest.current !== undefined && pendingRequest.current.channelRef === channel.channelRef
160
+ && pendingRequest.current.name === normalizedName && pendingRequest.current.description === normalizedDescription;
161
+ const request = samePending ? pendingRequest.current : {
162
+ requestId: crypto.randomUUID(),
163
+ workspaceId: channel.workspaceId,
164
+ channelRef: channel.channelRef,
165
+ name: normalizedName,
166
+ description: normalizedDescription,
167
+ };
168
+ pendingRequest.current = request;
169
+ setSaving(true);
170
+ setError(undefined);
171
+ try {
172
+ const result = await updateChannel(request);
173
+ if (result.ok) {
174
+ pendingRequest.current = undefined;
175
+ await onCommitted();
176
+ onClose();
177
+ }
178
+ else {
179
+ setError(result.error.message);
180
+ }
181
+ }
182
+ catch (cause) {
183
+ setError(cause instanceof Error ? cause.message : String(cause));
184
+ }
185
+ finally {
186
+ setSaving(false);
187
+ }
188
+ };
189
+ return (_jsx(Modal, { open: true, onClose: onClose, title: t('editChannel'), description: `# ${channel.name}`, closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-channel-edit-form", variant: "primary", disabled: saving || !dirty || name.trim() === '' || description.trim() === '', children: saving ? t('editSaving') : t('editSave') })] }), children: _jsxs("form", { id: "team-channel-edit-form", className: createCss.form, onSubmit: event => { void submit(event); }, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('channelName') }), _jsx(Input, { className: createCss.input, value: name, onChange: event => { setName(event.target.value); pendingRequest.current = undefined; }, disabled: saving, autoFocus: true })] }), _jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('channelDescription') }), _jsx(Input, { className: createCss.input, value: description, onChange: event => { setDescription(event.target.value); pendingRequest.current = undefined; }, disabled: saving })] }), _jsxs("fieldset", { className: createCss.memberPicker, disabled: saving, children: [_jsx("legend", { children: t('channelMembersSection') }), members.map(status => {
190
+ const joined = joinedIds.has(status.member.memberId);
191
+ const rowPending = membership.pending.has(status.member.memberId);
192
+ const disabled = rowPending || (!joined && status.presence === 'unavailable');
193
+ const rowError = membership.errors.get(status.member.memberId);
194
+ return _jsxs("div", { className: css.editMemberRow, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { className: css.editMemberCopy, children: [_jsxs("strong", { children: ["@", status.member.handle] }), _jsx("small", { children: status.member.description })] }), _jsx(Button, { size: "sm", variant: "outline", disabled: disabled, onClick: () => { void membership.change({ workspaceId: channel.workspaceId, channelRef: channel.channelRef, memberId: status.member.memberId, joined }); }, children: rowPending ? t('membershipUpdating') : joined ? t('removeFromChannel') : t('addToChannel') }), rowError !== undefined && _jsx("p", { className: css.rowError, role: "alert", children: rowError })] }, status.member.memberId);
195
+ })] }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }));
196
+ }
@@ -0,0 +1,15 @@
1
+ import type { AgentTeamClientMemberStatus, AgentTeamMemberId } from '@wowyuarm/dsh-agent-team/types';
2
+ import type { TeamConversationProps } from './slots.ts';
3
+ export declare function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, t }: {
4
+ readonly members: readonly AgentTeamClientMemberStatus[];
5
+ readonly recipients: ReadonlySet<AgentTeamMemberId>;
6
+ readonly draft: string;
7
+ readonly pending: boolean;
8
+ readonly confirmation?: string;
9
+ readonly error?: string;
10
+ readonly onDraftChange: (draft: string) => void;
11
+ readonly onRecipientsChange: (recipients: ReadonlySet<AgentTeamMemberId>) => void;
12
+ readonly onSubmit: () => void;
13
+ readonly t: TeamConversationProps['t'];
14
+ }): import("react").JSX.Element;
15
+ //# sourceMappingURL=TeamComposer.d.ts.map
@@ -0,0 +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;AAoCvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE;IACzI,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAA;IACxD,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,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BAkKA"}
@@ -0,0 +1,144 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useLayoutEffect, useRef, useState } from 'react';
3
+ import { IconSendOutline16, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
+ import css from './composer.module.css';
6
+ function findMention(draft, caret) {
7
+ const beforeCaret = draft.slice(0, caret);
8
+ if (beforeCaret.length === 0 || /\s/u.test(beforeCaret.at(-1) ?? ''))
9
+ return undefined;
10
+ let tokenStart = beforeCaret.length;
11
+ while (tokenStart > 0 && !/\s/u.test(beforeCaret[tokenStart - 1]))
12
+ tokenStart -= 1;
13
+ const at = beforeCaret.lastIndexOf('@');
14
+ if (at < tokenStart)
15
+ return undefined;
16
+ if (at > 0 && /[\p{L}\p{N}_]/u.test(beforeCaret[at - 1]))
17
+ return undefined;
18
+ return { start: at, end: caret, query: beforeCaret.slice(at + 1) };
19
+ }
20
+ function escapeRegExp(value) {
21
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
22
+ }
23
+ function containsMention(draft, handle) {
24
+ return new RegExp(`(?:^|[^\\p{L}\\p{N}_])@${escapeRegExp(handle)}(?=$|[^\\p{L}\\p{N}_])`, 'u').test(draft);
25
+ }
26
+ function mentionCandidates(members, query) {
27
+ const normalized = query.toLocaleLowerCase();
28
+ return members.filter(status => status.presence !== 'unavailable'
29
+ && status.member.state !== 'inactive'
30
+ && status.member.handle.toLocaleLowerCase().startsWith(normalized));
31
+ }
32
+ export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, t }) {
33
+ const inputRef = useRef(null);
34
+ const rootRef = useRef(null);
35
+ const menuRef = useRef(null);
36
+ const composingRef = useRef(false);
37
+ const [mention, setMention] = useState();
38
+ const [highlight, setHighlight] = useState(0);
39
+ const candidates = mention === undefined ? [] : mentionCandidates(members, mention.query);
40
+ const menuOpen = mention !== undefined && candidates.length > 0;
41
+ const activeCandidate = candidates[highlight];
42
+ const listId = 'team-mention-suggestions';
43
+ const menuMaxHeight = useAnchoredMaxHeight(menuRef, 320, menuOpen ? draft : null);
44
+ useDismissOnOutsidePointer(rootRef, menuOpen, open => { if (!open)
45
+ setMention(undefined); });
46
+ useLayoutEffect(() => {
47
+ const input = inputRef.current;
48
+ if (input === null)
49
+ return;
50
+ input.style.height = 'auto';
51
+ input.style.height = `${Math.min(input.scrollHeight, 180)}px`;
52
+ }, [draft]);
53
+ useEffect(() => {
54
+ if (mention === undefined || candidates.length === 0) {
55
+ setHighlight(0);
56
+ return;
57
+ }
58
+ setHighlight(current => Math.min(current, candidates.length - 1));
59
+ }, [mention, candidates.length]);
60
+ const pruneRecipients = (nextDraft) => {
61
+ const knownMembers = new Map(members.map(status => [status.member.memberId, status.member]));
62
+ const next = new Set([...recipients].filter(memberId => {
63
+ const member = knownMembers.get(memberId);
64
+ return member !== undefined && containsMention(nextDraft, member.handle);
65
+ }));
66
+ if (next.size !== recipients.size)
67
+ onRecipientsChange(next);
68
+ };
69
+ const updateMention = (nextDraft, caret) => {
70
+ const match = findMention(nextDraft, caret);
71
+ setMention(match);
72
+ if (match === undefined)
73
+ setHighlight(0);
74
+ };
75
+ const onChange = (event) => {
76
+ const nextDraft = event.target.value;
77
+ onDraftChange(nextDraft);
78
+ pruneRecipients(nextDraft);
79
+ updateMention(nextDraft, event.target.selectionStart ?? nextDraft.length);
80
+ };
81
+ const selectMention = (member) => {
82
+ if (mention === undefined)
83
+ return;
84
+ const inserted = `@${member.member.handle} `;
85
+ const nextDraft = `${draft.slice(0, mention.start)}${inserted}${draft.slice(mention.end)}`;
86
+ const nextCaret = mention.start + inserted.length;
87
+ const nextRecipients = new Set(recipients).add(member.member.memberId);
88
+ onDraftChange(nextDraft);
89
+ onRecipientsChange(nextRecipients);
90
+ setMention(undefined);
91
+ setHighlight(0);
92
+ requestAnimationFrame(() => {
93
+ const input = inputRef.current;
94
+ if (input === null)
95
+ return;
96
+ input.focus({ preventScroll: true });
97
+ input.setSelectionRange(nextCaret, nextCaret);
98
+ });
99
+ };
100
+ const onKeyDown = (event) => {
101
+ const composing = composingRef.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229;
102
+ if (menuOpen && event.key === 'ArrowDown') {
103
+ event.preventDefault();
104
+ setHighlight(current => (current + 1) % candidates.length);
105
+ return;
106
+ }
107
+ if (menuOpen && event.key === 'ArrowUp') {
108
+ event.preventDefault();
109
+ setHighlight(current => (current - 1 + candidates.length) % candidates.length);
110
+ return;
111
+ }
112
+ if (event.key === 'Escape' && menuOpen) {
113
+ event.preventDefault();
114
+ setMention(undefined);
115
+ return;
116
+ }
117
+ // Tab accepts the highlighted candidate; Shift+Tab keeps default focus reversal.
118
+ if (menuOpen && event.key === 'Tab' && !event.shiftKey) {
119
+ event.preventDefault();
120
+ if (activeCandidate !== undefined)
121
+ selectMention(activeCandidate);
122
+ return;
123
+ }
124
+ if (event.key !== 'Enter' || event.shiftKey || composing || event.repeat)
125
+ return;
126
+ if (menuOpen && activeCandidate !== undefined) {
127
+ event.preventDefault();
128
+ selectMention(activeCandidate);
129
+ return;
130
+ }
131
+ event.preventDefault();
132
+ if (!pending && draft.trim() !== '')
133
+ onSubmit();
134
+ };
135
+ return _jsxs("form", { ref: rootRef, className: css.root, onSubmit: event => {
136
+ event.preventDefault();
137
+ if (!pending && draft.trim() !== '')
138
+ onSubmit();
139
+ }, children: [_jsxs("div", { className: css.card, "data-team-composer": true, children: [confirmation !== undefined && _jsx("p", { className: css.confirmation, role: "status", children: confirmation }), _jsxs("div", { className: css.inputArea, children: [menuOpen && _jsx("div", { id: listId, ref: menuRef, className: css.mentionMenu, role: "listbox", "aria-label": t('mentionSuggestions'), style: { maxHeight: menuMaxHeight }, children: candidates.map((status, index) => {
140
+ const optionId = `${listId}-${status.member.memberId}`;
141
+ const selected = index === highlight;
142
+ return _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectMention(status); }, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: status.member.description })] }, status.member.memberId);
143
+ }) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeCandidate !== undefined ? `${listId}-${activeCandidate.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, disabled: pending, placeholder: t('messagePlaceholder'), rows: 1, onChange: onChange, 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(', ') }) }), _jsx("div", { className: css.toolbar, children: _jsx("button", { type: "submit", className: css.sendButton, "aria-label": pending ? t('sendingMessage') : t('sendMessage'), disabled: pending || draft.trim() === '', children: _jsx(IconSendOutline16, { size: 16 }) }) })] }), error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] });
144
+ }
@@ -0,0 +1,3 @@
1
+ import type { TeamConversationProps } from './slots.ts';
2
+ export declare function TeamConversation({ t, useWorkspaces, navigation, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, selectThread, backToWorkspace, backToChannels }: TeamConversationProps): import("react").JSX.Element;
3
+ //# sourceMappingURL=TeamConversation.d.ts.map
@@ -0,0 +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,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,EAAE,qBAAqB,+BAoBpR"}
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useSyncExternalStore } from 'react';
3
+ import { TeamChannelPage } from "./TeamChannelPage.js";
4
+ import { TeamThreadPage } from "./TeamThreadPage.js";
5
+ import css from './conversation.module.css';
6
+ export function TeamConversation({ t, useWorkspaces, navigation, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, selectThread, backToWorkspace, backToChannels }) {
7
+ const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
8
+ const workspaces = useWorkspaces(state => state.items);
9
+ const current = workspaces.find(workspace => workspace.workspaceId === navigationState.workspaceId);
10
+ if (current !== undefined && navigationState.taskRef !== undefined && navigationState.threadRef !== undefined) {
11
+ return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, taskRef: navigationState.taskRef, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, t: t }, navigationState.threadRef);
12
+ }
13
+ if (current !== undefined && navigationState.channelRef !== undefined) {
14
+ return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, backToChannels: backToChannels, t: t }, navigationState.channelRef);
15
+ }
16
+ const welcome = current === undefined
17
+ ? { eyebrow: t('teamMode'), title: t('team'), body: t('empty') }
18
+ : { eyebrow: current.title, title: t('channels'), body: t('selectChannelHint') };
19
+ return _jsx("main", { className: css.welcomeSurface, "data-team-conversation": true, children: _jsxs("div", { className: css.welcome, children: [_jsx("span", { className: css.welcomeEyebrow, children: welcome.eyebrow }), _jsx("h1", { children: welcome.title }), _jsx("p", { children: welcome.body })] }) });
20
+ }
@@ -0,0 +1,3 @@
1
+ import type { TeamFooterProps } from './slots.ts';
2
+ export declare function TeamFooterAction({ wide, navigation, enterTeam, leaveTeam, t }: TeamFooterProps): import("react").JSX.Element;
3
+ //# sourceMappingURL=TeamFooterAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamFooterAction.d.ts","sourceRoot":"","sources":["../../../src/client/TeamFooterAction.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAGjD,wBAAgB,gBAAgB,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,eAAe,+BAgC9F"}
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useSyncExternalStore } from 'react';
3
+ import { IconAgentPresetOutline16, IconChevronLeftOutline14, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import css from './team.module.css';
5
+ export function TeamFooterAction({ wide, navigation, enterTeam, leaveTeam, t }) {
6
+ const state = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
7
+ const inTeam = state.mode === 'team';
8
+ const label = inTeam ? t('backToConversations') : t('team');
9
+ useLayoutEffect(() => {
10
+ if (typeof document === 'undefined')
11
+ return;
12
+ if (inTeam) {
13
+ document.documentElement.dataset.agentTeamMode = 'team';
14
+ return () => { delete document.documentElement.dataset.agentTeamMode; };
15
+ }
16
+ delete document.documentElement.dataset.agentTeamMode;
17
+ }, [inTeam]);
18
+ return (_jsx(_Fragment, { children: _jsx("div", { className: wide ? css.footerStack : `${css.footerStack} ${css.railStack}`, children: _jsx(Tooltip, { label: label, delayMs: 500, disabled: wide, children: _jsxs("button", { type: "button", className: wide ? css.footerAction : `${css.footerAction} ${css.rail}`, "aria-label": label, "data-team-action": inTeam ? 'leave' : 'enter', onClick: inTeam ? leaveTeam : enterTeam, children: [inTeam ? _jsx(IconChevronLeftOutline14, { size: wide ? 16 : 18 }) : _jsx(IconAgentPresetOutline16, { size: wide ? 16 : 18 }), wide && _jsx("span", { children: label })] }) }) }) }));
19
+ }
@@ -0,0 +1,13 @@
1
+ import type { WorkspaceId } from '@deepseek-ai/dsh-client-runtime/client';
2
+ import type { TeamConversationProps } from './slots.ts';
3
+ interface TeamInboxPanelProps {
4
+ readonly workspaceId: WorkspaceId;
5
+ readonly loadInbox: TeamConversationProps['loadInbox'];
6
+ readonly loadChannels: TeamConversationProps['loadChannels'];
7
+ readonly loadChanges: TeamConversationProps['loadChanges'];
8
+ readonly selectThread: TeamConversationProps['selectThread'];
9
+ readonly t: TeamConversationProps['t'];
10
+ }
11
+ export declare function TeamInboxPanel({ workspaceId, loadInbox, loadChannels, loadChanges, selectThread, t }: TeamInboxPanelProps): import("react").JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=TeamInboxPanel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamInboxPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamInboxPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAA;IACtD,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAED,wBAAgB,cAAc,CAAC,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,mBAAmB,+BAkGzH"}
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
+ import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { formatTaskStatus } from "./team-formatters.js";
5
+ import css from './inbox.module.css';
6
+ export function TeamInboxPanel({ workspaceId, loadInbox, loadChannels, loadChanges, selectThread, t }) {
7
+ const [inbox, setInbox] = useState();
8
+ const [view, setView] = useState();
9
+ const [error, setError] = useState();
10
+ const [loading, setLoading] = useState(true);
11
+ const active = useRef(false);
12
+ const refreshSequence = useRef(0);
13
+ const refresh = async (clearError = false) => {
14
+ const sequence = refreshSequence.current + 1;
15
+ refreshSequence.current = sequence;
16
+ if (clearError)
17
+ setError(undefined);
18
+ try {
19
+ const [nextInbox, nextView] = await Promise.all([
20
+ loadInbox({ workspaceId }),
21
+ loadChannels({ workspaceId, topLevelOnly: true, includeActivities: false, limit: 100 }),
22
+ ]);
23
+ if (!active.current || sequence !== refreshSequence.current)
24
+ return;
25
+ if (!nextInbox.ok) {
26
+ setError(nextInbox.error.message);
27
+ return;
28
+ }
29
+ if (!nextView.ok) {
30
+ setError(nextView.error.message);
31
+ return;
32
+ }
33
+ setInbox(nextInbox.value);
34
+ setView(nextView.value);
35
+ setError(undefined);
36
+ }
37
+ catch (cause) {
38
+ if (active.current && sequence === refreshSequence.current)
39
+ setError(cause instanceof Error ? cause.message : String(cause));
40
+ }
41
+ finally {
42
+ if (active.current && sequence === refreshSequence.current)
43
+ setLoading(false);
44
+ }
45
+ };
46
+ useEffect(() => {
47
+ let listening = true;
48
+ active.current = true;
49
+ setInbox(undefined);
50
+ setView(undefined);
51
+ setError(undefined);
52
+ setLoading(true);
53
+ void refresh();
54
+ void (async () => {
55
+ let version = 0;
56
+ while (listening) {
57
+ try {
58
+ const changed = await loadChanges({ afterVersion: version });
59
+ if (!listening)
60
+ return;
61
+ if (!changed.ok) {
62
+ setError(changed.error.message);
63
+ return;
64
+ }
65
+ if (changed.value.version > version) {
66
+ version = changed.value.version;
67
+ await refresh();
68
+ }
69
+ }
70
+ catch (cause) {
71
+ if (listening)
72
+ setError(cause instanceof Error ? cause.message : String(cause));
73
+ return;
74
+ }
75
+ }
76
+ })();
77
+ return () => {
78
+ listening = false;
79
+ active.current = false;
80
+ refreshSequence.current += 1;
81
+ };
82
+ }, [workspaceId]);
83
+ const channels = useMemo(() => new Map(view?.channels.map(channel => [channel.channelRef, channel]) ?? []), [view]);
84
+ const taskNumbers = useMemo(() => new Map(view?.taskNumbers.map(item => [item.taskRef, item.taskNumber]) ?? []), [view]);
85
+ return _jsxs("main", { className: css.surface, "data-team-inbox": true, children: [_jsxs("header", { className: css.header, children: [_jsxs("div", { children: [_jsx("h1", { children: t('inbox') }), inbox !== undefined && _jsx("p", { children: inbox.totalUnreadCount === 0 ? t('inboxEmpty') : t('inboxUnreadCount', { count: inbox.totalUnreadCount }) })] }), inbox !== undefined && inbox.totalDirectCount > 0 && _jsx("span", { className: css.directTotal, children: t('inboxDirectCount', { count: inbox.totalDirectCount }) })] }), _jsxs("section", { className: css.list, "aria-label": t('inbox'), children: [loading && inbox === undefined && error === undefined && _jsx("p", { className: css.state, children: t('loadingInbox') }), !loading && inbox?.items.length === 0 && error === undefined && _jsx("p", { className: css.state, children: t('inboxEmpty') }), error !== undefined && _jsxs("div", { className: css.error, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void refresh(true); }, children: t('retry') })] }), inbox?.items.map(item => {
86
+ const channel = channels.get(item.channelRef);
87
+ const taskNumber = taskNumbers.get(item.task.taskRef);
88
+ return _jsxs("button", { type: "button", className: css.item, onClick: () => { selectThread(item.task.taskRef, item.thread.threadRef, item.channelRef, taskNumber); }, children: [_jsx("span", { className: css.priority, "data-direct": item.directCount > 0, "aria-hidden": "true" }), _jsxs("span", { className: css.copy, children: [_jsx("strong", { children: taskNumber === undefined ? t('task') : `Task #${taskNumber}` }), _jsxs("small", { children: [channel === undefined ? t('channelUnknown') : `# ${channel.name}`, " \u00B7 ", formatTaskStatus(item.task.status, t)] })] }), _jsxs("span", { className: css.count, "aria-label": `${t('inboxUnreadCount', { count: item.unreadCount })}${item.directCount > 0 ? `, ${t('inboxDirectCount', { count: item.directCount })}` : ''}`, children: [t('inboxUnreadCount', { count: item.unreadCount }), item.directCount > 0 && _jsxs(_Fragment, { children: [" \u00B7 ", t('inboxDirectCount', { count: item.directCount })] })] })] }, item.thread.threadRef);
89
+ })] })] });
90
+ }
@@ -0,0 +1,12 @@
1
+ import type { AgentTeamClientMemberStatus } from '@wowyuarm/dsh-agent-team/types';
2
+ import type { TeamSidebarProps } from './slots.ts';
3
+ /**
4
+ * Sidebar Member avatar reusing the conversation identity language: the
5
+ * deterministic member hue and handle initial, with the presence indicator
6
+ * overlaid at the bottom-right so one glyph carries identity and state.
7
+ */
8
+ export declare function TeamMemberAvatar({ status, t }: {
9
+ readonly status: AgentTeamClientMemberStatus;
10
+ readonly t: TeamSidebarProps['t'];
11
+ }): import("react").JSX.Element;
12
+ //# sourceMappingURL=TeamMemberAvatar.d.ts.map
@@ -0,0 +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;AAKlD;;;;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"}
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { StateDot, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { memberHue } from "./team-formatters.js";
4
+ import { presenceDotState, presenceLabel } from "./TeamPresenceDot.js";
5
+ import css from './sidebar.module.css';
6
+ /**
7
+ * Sidebar Member avatar reusing the conversation identity language: the
8
+ * deterministic member hue and handle initial, with the presence indicator
9
+ * overlaid at the bottom-right so one glyph carries identity and state.
10
+ */
11
+ export function TeamMemberAvatar({ status, t }) {
12
+ const label = presenceLabel(status, t);
13
+ 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: state === 'gray' ? _jsx("span", { className: css.unavailableDot }) : _jsx(StateDot, { state: state, size: 8 }) })] }) }));
15
+ }
@@ -0,0 +1,5 @@
1
+ import type { TeamFooterProps } from './slots.ts';
2
+ type TeamMembersActionProps = Pick<TeamFooterProps, 'wide' | 'loadMemberGroups' | 't'>;
3
+ export declare function TeamMembersAction({ wide, loadMemberGroups, t }: TeamMembersActionProps): import("react").JSX.Element;
4
+ export {};
5
+ //# sourceMappingURL=TeamMembersAction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamMembersAction.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMembersAction.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAKjD,KAAK,sBAAsB,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,kBAAkB,GAAG,GAAG,CAAC,CAAA;AAEtF,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,sBAAsB,+BAuDtF"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { IconUserOutline16, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
+ import membersCss from './members.module.css';
6
+ import css from './team.module.css';
7
+ export function TeamMembersAction({ wide, loadMemberGroups, t }) {
8
+ const [panelOpen, setPanelOpen] = useState(false);
9
+ const [groups, setGroups] = useState([]);
10
+ const [loading, setLoading] = useState(false);
11
+ const [error, setError] = useState();
12
+ const triggerRef = useRef(null);
13
+ const contentRef = useRef(null);
14
+ useEffect(() => {
15
+ if (!panelOpen)
16
+ return;
17
+ queueMicrotask(() => { contentRef.current?.focus(); });
18
+ }, [panelOpen]);
19
+ const openMembers = () => {
20
+ setPanelOpen(true);
21
+ setLoading(true);
22
+ setError(undefined);
23
+ void loadMemberGroups().then(setGroups).catch(cause => {
24
+ setError(cause instanceof Error ? cause.message : String(cause));
25
+ }).finally(() => { setLoading(false); });
26
+ };
27
+ const closeMembers = () => {
28
+ setPanelOpen(false);
29
+ queueMicrotask(() => { triggerRef.current?.focus(); });
30
+ };
31
+ return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { label: t('members'), delayMs: 500, disabled: wide, children: _jsxs("button", { ref: triggerRef, type: "button", className: wide ? css.settingsAction : `${css.settingsAction} ${css.rail}`, "aria-label": t('members'), "aria-haspopup": "dialog", onClick: openMembers, children: [_jsx(IconUserOutline16, { size: wide ? 16 : 18 }), wide && _jsx("span", { children: t('members') })] }) }), _jsx(Modal, { open: panelOpen, onClose: closeMembers, title: t('members'), closeLabel: t('close'), contentClassName: membersCss.body, children: _jsxs("div", { ref: contentRef, className: membersCss.content, tabIndex: -1, children: [loading && _jsx("p", { className: membersCss.state, role: "status", children: t('loadingAgents') }), !loading && groups.length === 0 && error === undefined && _jsx("p", { className: membersCss.state, children: t('emptyAgents') }), !loading && groups.map(group => (_jsxs("section", { className: membersCss.group, "aria-labelledby": `team-members-${group.workspaceId}`, children: [_jsx("h3", { id: `team-members-${group.workspaceId}`, children: group.workspaceTitle }), group.members.map(status => (_jsxs("div", { className: membersCss.member, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { className: membersCss.copy, children: [_jsxs("strong", { children: ["@", status.member.handle] }), _jsx("small", { children: status.member.description })] })] }, status.member.memberId)))] }, group.workspaceId))), error !== undefined && _jsx("p", { className: membersCss.error, role: "alert", children: error })] }) })] }));
32
+ }