@wowyuarm/dsh-agent-team 0.1.3 → 0.1.5

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 (79) hide show
  1. package/README.md +7 -1
  2. package/README.zh.md +12 -3
  3. package/package.json +1 -1
  4. package/packages/agent-team/README.md +3 -3
  5. package/packages/agent-team/README.zh.md +3 -3
  6. package/packages/agent-team/lib/auto-compaction.js +39 -1
  7. package/packages/agent-team/lib/index.js +123 -14
  8. package/packages/agent-team/lib/ledger.js +355 -110
  9. package/packages/agent-team/lib/spec.js +39 -11
  10. package/packages/agent-team/lib/typert.host.js +257 -86
  11. package/packages/agent-team/lib/typert.remote-client.d.ts +5 -1
  12. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  13. package/packages/agent-team/lib/typert.remote-client.js +196 -59
  14. package/packages/agent-team/lib/types/auto-compaction.d.ts +9 -0
  15. package/packages/agent-team/lib/types/auto-compaction.d.ts.map +1 -1
  16. package/packages/agent-team/lib/types/index.d.ts +24 -7
  17. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  18. package/packages/agent-team/lib/types/ledger.d.ts +36 -2
  19. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  20. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  21. package/packages/agent-team/lib/types/types.d.ts +109 -30
  22. package/packages/agent-team/lib/types/types.d.ts.map +1 -1
  23. package/packages/agent-team/preset/team-member/agent.cordis.yml +1 -1
  24. package/packages/client-agent-team/README.md +2 -1
  25. package/packages/client-agent-team/README.zh.md +2 -1
  26. package/packages/client-agent-team/lib/client.js +1082 -380
  27. package/packages/client-agent-team/lib/client.js.map +1 -1
  28. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +2 -1
  29. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  30. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +73 -15
  31. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  32. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +34 -10
  33. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  34. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +3 -1
  35. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +6 -1
  36. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  37. package/packages/client-agent-team/lib/types/client/TeamComposer.js +76 -20
  38. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  39. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  40. package/packages/client-agent-team/lib/types/client/TeamConversation.js +3 -3
  41. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +1 -1
  42. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +16 -1
  43. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +4 -1
  44. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  45. package/packages/client-agent-team/lib/types/client/TeamMessage.js +62 -25
  46. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts +3 -2
  47. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -1
  48. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts +5 -2
  49. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts.map +1 -1
  50. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +2 -4
  51. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +2 -1
  52. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  53. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +92 -18
  54. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  55. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  56. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +5 -2
  57. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  58. package/packages/client-agent-team/lib/types/client/index.js +16 -13
  59. package/packages/client-agent-team/lib/types/client/locales.d.ts +40 -0
  60. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  61. package/packages/client-agent-team/lib/types/client/locales.js +40 -0
  62. package/packages/client-agent-team/lib/types/client/navigation.d.ts +1 -1
  63. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
  64. package/packages/client-agent-team/lib/types/client/navigation.js +12 -6
  65. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts +16 -0
  66. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts.map +1 -0
  67. package/packages/client-agent-team/lib/types/client/sidebar-sections.js +102 -0
  68. package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -1
  69. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  70. package/packages/client-agent-team/lib/types/client/task-refs.d.ts +1 -1
  71. package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
  72. package/packages/client-agent-team/lib/types/client/task-refs.js +1 -1
  73. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +9 -0
  74. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  75. package/packages/client-agent-team/lib/types/client/team-formatters.js +22 -2
  76. package/packages/tool-agent-team/README.md +7 -7
  77. package/packages/tool-agent-team/README.zh.md +7 -7
  78. package/packages/tool-agent-team/lib/index.js +38 -28
  79. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
@@ -9,6 +9,7 @@ interface TeamAgentsPanelProps {
9
9
  readonly addMember: TeamSidebarProps['addMember'];
10
10
  readonly updateMember: TeamSidebarProps['updateMember'];
11
11
  readonly recoverMember: TeamSidebarProps['recoverMember'];
12
+ readonly clearMemberContext: TeamSidebarProps['clearMemberContext'];
12
13
  readonly joinChannel: TeamSidebarProps['joinChannel'];
13
14
  readonly removeChannelMember: TeamSidebarProps['removeChannelMember'];
14
15
  readonly loadModels: TeamSidebarProps['loadModels'];
@@ -18,6 +19,6 @@ interface TeamAgentsPanelProps {
18
19
  readonly onCreatingChange: (request: AgentTeamAddMemberRequest, creating: boolean) => void;
19
20
  readonly t: TeamSidebarProps['t'];
20
21
  }
21
- export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }: TeamAgentsPanelProps): import("react").JSX.Element;
22
+ export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, clearMemberContext, joinChannel, removeChannelMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }: TeamAgentsPanelProps): import("react").JSX.Element;
22
23
  export {};
23
24
  //# 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,EAG5B,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,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,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,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,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAA;IACrE,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,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BAiLhQ"}
1
+ {"version":3,"file":"TeamAgentsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,2BAA2B,EAG5B,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAalD,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,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,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,kBAAkB,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,CAAA;IACnE,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAA;IACrE,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,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BAoLpR"}
@@ -1,9 +1,10 @@
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, IconEditOutline16, IconPlayOutline16, IconPlusOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { Button, IconEditOutline16, IconNewChatOutline16, 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";
7
+ import { useSidebarSectionOpen, setSidebarSectionOpen } from "./sidebar-sections.js";
7
8
  import { mintRequestId } from "./requests.js";
8
9
  import { TeamRowMenu } from "./TeamRowMenu.js";
9
10
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
@@ -11,7 +12,7 @@ import { MultiMenuField } from "./multi-menu-field.js";
11
12
  import { AgentEditorDialog, ModelPickerField, sameModel } from "./TeamMemberEditor.js";
12
13
  import createCss from './create.module.css';
13
14
  import css from './sidebar.module.css';
14
- export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
15
+ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, clearMemberContext, joinChannel, removeChannelMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
15
16
  const [members, setMembers] = useState([]);
16
17
  const [channels, setChannels] = useState([]);
17
18
  const [channelRefs, setChannelRefs] = useState([]);
@@ -36,6 +37,7 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
36
37
  void moveSidebarItem(workspaceId, 'agents', orderedAgentRefs, movedRef, targetRef, marker);
37
38
  };
38
39
  const drag = useSidebarRowDrag({ refs: orderedAgentRefs, onCommit: applyMove });
40
+ const sectionOpen = useSidebarSectionOpen(workspaceId, 'agents');
39
41
  const refresh = useCallback(async () => {
40
42
  setLoading(true);
41
43
  const result = await loadMembers({ workspaceId });
@@ -132,31 +134,87 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
132
134
  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 }), _jsx(MultiMenuField, { label: `${t('initialChannels')}${t('optionalSuffix')}`, disabled: creating, options: channels.map(channel => ({ id: channel.channelRef, label: channel.name })), selected: channelRefs, onToggle: ref => {
133
135
  setChannelRefs(current => current.includes(ref) ? current.filter(item => item !== ref) : [...current, ref]);
134
136
  setRetryRequest(undefined);
135
- }, emptyText: t('noChannelsForAgent'), triggerEmptyLabel: t('channelsPickerEmpty'), formatCount: count => t('channelsPickerCount', { count }) }), formOpen && error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('agents'), 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 }), channels: channels, loadChannels: loadChannels, updateMember: updateMember, recoverMember: recoverMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, 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') })] }))] }));
137
+ }, emptyText: t('noChannelsForAgent'), triggerEmptyLabel: t('channelsPickerEmpty'), formatCount: count => t('channelsPickerCount', { count }) }), 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 }), channels: channels, loadChannels: loadChannels, updateMember: updateMember, recoverMember: recoverMember, clearMemberContext: clearMemberContext, joinChannel: joinChannel, removeChannelMember: removeChannelMember, 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') })] }))] }));
136
138
  }
137
139
  /**
138
140
  * One sidebar Agent row: the select button opens the Member's own Session
139
141
  * conversation page, the avatar carries identity plus the presence badge, and
140
142
  * the row menu opens the editor.
141
143
  */
142
- function AgentRow({ status, current, channels, loadChannels, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, openMemberSession, onUpdated, t }) {
144
+ function AgentRow({ status, current, channels, loadChannels, updateMember, recoverMember, clearMemberContext, joinChannel, removeChannelMember, loadModels, openMemberSession, onUpdated, t }) {
143
145
  const [menuOpen, setMenuOpen] = useState(false);
144
146
  const [editing, setEditing] = useState(false);
145
- const resume = async () => {
146
- await recoverMember({
147
- requestId: mintRequestId(),
148
- workspaceId: status.member.workspaceId,
149
- memberId: status.member.memberId,
150
- });
151
- await onUpdated();
147
+ const [clearing, setClearing] = useState(false);
148
+ const [rowAlert, setRowAlert] = useState();
149
+ // Both row actions ride the same runtime remote: the Host steers a live
150
+ // session, rebuilds an orphaned composition, or re-runs a failed activation.
151
+ const recover = async () => {
152
+ try {
153
+ const result = await recoverMember({
154
+ requestId: mintRequestId(),
155
+ workspaceId: status.member.workspaceId,
156
+ memberId: status.member.memberId,
157
+ });
158
+ await onUpdated();
159
+ if (!result.ok) {
160
+ setRowAlert(t('restartFailed', { message: result.error.message }));
161
+ return;
162
+ }
163
+ if (result.value.status.availability === 'unavailable') {
164
+ setRowAlert(t('restartStillUnavailable', { diagnostic: result.value.status.diagnostic ?? t('statusUnavailable') }));
165
+ return;
166
+ }
167
+ setRowAlert(undefined);
168
+ // A page opened while the Member was down predates the live Session;
169
+ // re-selecting is a no-op when already bound and rebinds when stale.
170
+ if (current === true)
171
+ openMemberSession(status.member.sessionId);
172
+ }
173
+ catch (cause) {
174
+ setRowAlert(t('restartFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
175
+ }
152
176
  };
177
+ const clearContext = async () => {
178
+ try {
179
+ const result = await clearMemberContext({
180
+ requestId: mintRequestId(),
181
+ workspaceId: status.member.workspaceId,
182
+ memberId: status.member.memberId,
183
+ });
184
+ await onUpdated();
185
+ if (!result.ok) {
186
+ setRowAlert(t('clearContextFailed', { message: result.error.message }));
187
+ return;
188
+ }
189
+ setRowAlert(undefined);
190
+ // The Host moved the Member onto a fresh Session id (the previous log
191
+ // stays archived on disk). A new id has no resident client instance, so
192
+ // re-entering the member view lazily instantiates it from host truth:
193
+ // blank hero, live updates, and no disposed-generation gray-out.
194
+ if (current === true)
195
+ openMemberSession(result.value.status.member.sessionId);
196
+ }
197
+ catch (cause) {
198
+ setRowAlert(t('clearContextFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
199
+ }
200
+ };
201
+ // Error Members keep a live idle handle, so starting from a new context
202
+ // is available to them too — it doubles as a recovery path (the broken
203
+ // handle's error markers are dropped with it). Only a running turn or a
204
+ // missing handle gates the entry.
205
+ const contextClearable = status.presence === 'available' || status.presence === 'error';
153
206
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: css.agentRow, "data-menu-open": menuOpen || undefined, children: [_jsxs("button", { type: "button", className: css.agentSelect, "aria-label": t('openAgentSession', { name: status.member.handle }), "aria-current": current ? 'page' : undefined, disabled: status.availability !== 'active', onClick: () => { openMemberSession(status.member.sessionId); }, children: [_jsx(TeamMemberAvatar, { status: status, t: t }), _jsxs("span", { className: css.agentCopy, children: [_jsx("strong", { children: status.member.handle }), _jsx("small", { children: status.member.description })] })] }), _jsx("span", { className: css.rowMenu, children: _jsx(TeamRowMenu, { label: t('actionsAgent', { name: status.member.handle }), items: [
154
207
  { id: 'edit', label: t('editAgent'), icon: _jsx(IconEditOutline16, {}) },
155
208
  ...(status.presence === 'error' ? [{ id: 'resume', label: t('resumeAgent'), icon: _jsx(IconPlayOutline16, {}) }] : []),
209
+ ...(status.availability === 'unavailable' ? [{ id: 'restart', label: t('restartAgent'), icon: _jsx(IconRefreshOutline16, {}) }] : []),
210
+ { id: 'clear-context', label: t('clearContextAgent'), icon: _jsx(IconNewChatOutline16, {}), danger: true, disabled: !contextClearable },
211
+ ...(!contextClearable ? [{ type: 'label', id: 'clear-context-reason', text: status.presence === 'working' ? t('clearContextWorkingReason') : t('clearContextUnavailableReason') }] : []),
156
212
  ], onSelect: (id) => {
157
- if (id === 'resume')
158
- void resume();
159
- else
213
+ if (id === 'edit')
160
214
  setEditing(true);
161
- }, onOpenChange: setMenuOpen }) })] }), editing && (_jsx(AgentEditorDialog, { status: status, channels: channels, loadChannels: loadChannels, updateMember: updateMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
215
+ else if (id === 'clear-context')
216
+ setClearing(true);
217
+ else
218
+ void recover();
219
+ }, onOpenChange: setMenuOpen }) })] }), rowAlert !== undefined && _jsx("div", { className: css.rowAlert, role: "alert", children: rowAlert }), clearing && (_jsx(Modal, { open: true, onClose: () => { setClearing(false); }, title: t('clearContextTitle', { name: status.member.handle }), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: () => { setClearing(false); }, children: t('cancel') }), _jsx(Button, { variant: "primary", onClick: () => { setClearing(false); void clearContext(); }, children: t('clearContextConfirm') })] }), children: _jsx("p", { className: createCss.error, children: t('clearContextNotice', { name: status.member.handle }) }) })), editing && (_jsx(AgentEditorDialog, { status: status, channels: channels, loadChannels: loadChannels, updateMember: updateMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
162
220
  }
@@ -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,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAc/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,+BAqTnR"}
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,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAc/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,+BAmVnR"}
@@ -39,6 +39,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
39
39
  const [statusMessage, setStatusMessage] = useState();
40
40
  const [loading, setLoading] = useState(true);
41
41
  const [pending, setPending] = useState(false);
42
+ const [asTask, setAsTask] = useState(false);
42
43
  const [loadingOlder, setLoadingOlder] = useState(false);
43
44
  const [managingMembers, setManagingMembers] = useState(false);
44
45
  // The composer draft lives in the keyed draft cache: view switches unmount
@@ -59,10 +60,30 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
59
60
  selectChannel(ref);
60
61
  return;
61
62
  }
63
+ if (ref.startsWith('thread:')) {
64
+ const match = view?.items.find(item => item.thread.threadRef === ref);
65
+ if (match !== undefined) {
66
+ selectThread(match.thread.threadRef, channelRef, match.task?.taskRef, match.taskNumber);
67
+ return;
68
+ }
69
+ // A ref may point outside the currently loaded Channel window. Ask the
70
+ // Host for the bounded Thread view so it can provide the home Channel.
71
+ void loadChannels({ workspaceId, threadRef: ref, includeActivities: false, limit: 1 }).then(result => {
72
+ if (!result.ok)
73
+ return;
74
+ const target = result.value.items[0];
75
+ if (target !== undefined) {
76
+ const targetChannel = result.value.channels.find(channel => channel.channelRef === target.message.channelRef);
77
+ if (targetChannel !== undefined)
78
+ selectThread(target.thread.threadRef, targetChannel.channelRef, target.task?.taskRef, target.taskNumber);
79
+ }
80
+ });
81
+ return;
82
+ }
62
83
  if (ref.startsWith('task:')) {
63
- const match = view?.items.find(item => item.task.taskRef === ref);
84
+ const match = view?.items.find(item => item.task?.taskRef === ref);
64
85
  if (match !== undefined) {
65
- selectThread(match.task.taskRef, match.thread.threadRef, channelRef, match.taskNumber);
86
+ selectThread(match.thread.threadRef, channelRef, match.task?.taskRef, match.taskNumber);
66
87
  return;
67
88
  }
68
89
  // Not in the loaded timeline: resolve through the Host and jump to the
@@ -236,6 +257,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
236
257
  const request = {
237
258
  requestId, workspaceId,
238
259
  channelRef, body: draft.trim(), recipients: recipientIds,
260
+ asTask,
239
261
  ...(attachmentIds.length === 0 ? {} : { attachments: attachmentIds }),
240
262
  };
241
263
  const result = await sendMessage(request);
@@ -245,6 +267,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
245
267
  }
246
268
  else if (result.value.kind === 'committed') {
247
269
  pendingSendId.current = undefined;
270
+ setAsTask(false);
248
271
  await refresh();
249
272
  drafts.clear(draftKey);
250
273
  setPendingFiles([]);
@@ -279,12 +302,13 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
279
302
  const senderStatus = members.find(member => member.member.memberId === item.message.sender);
280
303
  const human = item.message.sender === view.humanMemberId;
281
304
  const sender = human ? t('human') : senderStatus?.member.handle ?? item.message.sender;
282
- return _jsxs(Fragment, { children: [isRunGap(index > 0 ? block.items[index - 1].message.occurredAt : undefined, item.message.occurredAt)
283
- && _jsx(TeamRunDivider, { occurredAt: item.message.occurredAt }), _jsx(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, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }), children: item.message.topLevel && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openTask', { number: item.taskNumber }), onClick: () => { selectThread(item.task.taskRef, item.thread.threadRef, channelRef, item.taskNumber); }, children: [_jsx("span", { className: channelCss.taskDot, children: (() => {
284
- const dot = taskStatusDot(item.task.status);
285
- return dot === 'ongoing' || dot === 'warning' || dot === 'done'
286
- ? _jsx(StateDot, { size: 8, state: dot })
287
- : _jsx("span", { className: channelCss.taskDotQuiet, "data-variant": dot });
288
- })() }), _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.messageRef);
289
- }) }, `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, t: t }, draftKey) : _jsx("div", {})] });
305
+ const turnGap = isRunGap(index > 0 ? block.items[index - 1].message.occurredAt : undefined, item.message.occurredAt);
306
+ // 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);
313
+ }) }, `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", {})] });
290
314
  }
@@ -1 +1 @@
1
- {"version":3,"file":"TeamChannelsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EAEzB,mBAAmB,EAMpB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAYlD,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,wGAAwG;IACxG,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IAC/D,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAA;IACrE,QAAQ,CAAC,cAAc,EAAE,SAAS,yBAAyB,EAAE,CAAA;IAC7D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,mBAAmB,CAAA;IACjD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BA2L9D"}
1
+ {"version":3,"file":"TeamChannelsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EAEzB,mBAAmB,EAMpB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAalD,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,wGAAwG;IACxG,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IAC/D,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAA;IACrE,QAAQ,CAAC,cAAc,EAAE,SAAS,yBAAyB,EAAE,CAAA;IAC7D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,mBAAmB,CAAA;IACjD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BA8L9D"}
@@ -5,6 +5,7 @@ import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
5
  import { MultiMenuField } from "./multi-menu-field.js";
6
6
  import { SortableRow, useSidebarRowDrag } from "./sidebar-drag.js";
7
7
  import { moveSidebarItem, useSidebarOrder } from "./sidebar-order.js";
8
+ import { useSidebarSectionOpen, setSidebarSectionOpen } from "./sidebar-sections.js";
8
9
  import { mintRequestId } from "./requests.js";
9
10
  import { TeamRowMenu } from "./TeamRowMenu.js";
10
11
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
@@ -37,6 +38,7 @@ export function TeamChannelsPanel(props) {
37
38
  void moveSidebarItem(workspaceId, 'channels', orderedChannelRefs, movedRef, targetRef, marker);
38
39
  };
39
40
  const drag = useSidebarRowDrag({ refs: orderedChannelRefs, onCommit: applyMove });
41
+ const sectionOpen = useSidebarSectionOpen(workspaceId, 'channels');
40
42
  const refresh = useCallback(async () => {
41
43
  setLoading(true);
42
44
  const [channelResult, memberResult] = await Promise.all([
@@ -144,7 +146,7 @@ export function TeamChannelsPanel(props) {
144
146
  return next;
145
147
  });
146
148
  setPendingCreate(undefined);
147
- }, triggerEmptyLabel: t('membersPickerEmpty'), formatCount: count => t('membersPickerCount', { count }) }), 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: orderedChannels.map(channel => {
149
+ }, triggerEmptyLabel: t('membersPickerEmpty'), formatCount: count => t('membersPickerCount', { count }) }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('channels'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'channels', open); }, 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: orderedChannels.map(channel => {
148
150
  const joined = membership.get(channel.channelRef) ?? new Set();
149
151
  return (_jsx(SortableRow, { drag: drag, orderKey: channel.channelRef, children: _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));
150
152
  }) })] }), !formOpen && error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] }));
@@ -1,6 +1,6 @@
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, pendingFiles, onFilesChange, t }: {
3
+ export declare function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
4
4
  readonly members: readonly AgentTeamClientMemberStatus[];
5
5
  readonly recipients: ReadonlySet<AgentTeamMemberId>;
6
6
  readonly draft: string;
@@ -10,9 +10,14 @@ export declare function TeamComposer({ members, recipients, draft, pending, conf
10
10
  readonly onDraftChange: (draft: string) => void;
11
11
  readonly onRecipientsChange: (recipients: ReadonlySet<AgentTeamMemberId>) => void;
12
12
  readonly onSubmit: () => void;
13
+ /** Conversation-specific prompt; the shared default fits Channel surfaces. */
14
+ readonly placeholder?: string;
13
15
  /** Upload-capable surfaces pass this to enable the "+" file picker; the reply path omits it. */
14
16
  readonly pendingFiles?: readonly File[];
15
17
  readonly onFilesChange?: (files: readonly File[]) => void;
18
+ /** Channel-only: create a real Task with the top-level Message. Default off. */
19
+ readonly asTask?: boolean;
20
+ readonly onAsTaskChange?: (asTask: boolean) => void;
16
21
  readonly t: TeamConversationProps['t'];
17
22
  }): import("react").JSX.Element;
18
23
  //# sourceMappingURL=TeamComposer.d.ts.map
@@ -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;AAkDvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE;IACtK,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,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,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BAgOA"}
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;AA8DvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,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;IAC3M,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,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,+BAqSA"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useLayoutEffect, useRef, useState } from 'react';
3
- import { IconPlusOutline16, IconSendOutline16, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { Button, IconPlusOutline16, IconSendOutline16, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
5
  import css from './composer.module.css';
6
6
  import { formatByteSize } from "./attachment-preview.js";
@@ -30,6 +30,13 @@ function mentionCandidates(members, query) {
30
30
  && status.member.state !== 'inactive'
31
31
  && status.member.handle.toLocaleLowerCase().startsWith(normalized));
32
32
  }
33
+ /** Every member a manual handle pick could reach: the @all expansion snapshot. */
34
+ function allMentionMembers(members) {
35
+ return members.filter(status => status.presence !== 'unavailable' && status.member.state !== 'inactive');
36
+ }
37
+ function containsAllMention(draft) {
38
+ return /(?:^|[^\p{L}\p{N}_])@all(?=$|[^\p{L}\p{N}_])/u.test(draft);
39
+ }
33
40
  /** One object URL per draft file; revoked when the draft is removed. */
34
41
  const draftPreviewUrls = new WeakMap();
35
42
  function draftPreviewUrl(file) {
@@ -42,7 +49,7 @@ function draftPreviewUrl(file) {
42
49
  }
43
50
  return url;
44
51
  }
45
- export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, pendingFiles, onFilesChange, t }) {
52
+ export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
46
53
  const inputRef = useRef(null);
47
54
  const fileInputRef = useRef(null);
48
55
  const rootRef = useRef(null);
@@ -50,9 +57,15 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
50
57
  const composingRef = useRef(false);
51
58
  const [mention, setMention] = useState();
52
59
  const [highlight, setHighlight] = useState(0);
53
- const candidates = mention === undefined ? [] : mentionCandidates(members, mention.query);
54
- const menuOpen = mention !== undefined && candidates.length > 0;
55
- const activeCandidate = candidates[highlight];
60
+ // @all is a composer-layer expansion of the same eligibility filter a
61
+ // handle pick uses; the fixed row stays on top of the matching members.
62
+ const memberCandidates = mention === undefined ? [] : mentionCandidates(members, mention.query);
63
+ const options = mention !== undefined && 'all'.startsWith(mention.query.toLocaleLowerCase())
64
+ ? [{ kind: 'all' }, ...memberCandidates.map(status => ({ kind: 'member', status }))]
65
+ : memberCandidates.map(status => ({ kind: 'member', status }));
66
+ const menuOpen = mention !== undefined && options.length > 0;
67
+ const activeOption = options[highlight];
68
+ const allCount = allMentionMembers(members).length;
56
69
  const listId = 'team-mention-suggestions';
57
70
  const menuMaxHeight = useAnchoredMaxHeight(menuRef, 320, menuOpen ? draft : null);
58
71
  useDismissOnOutsidePointer(rootRef, menuOpen, open => { if (!open)
@@ -65,12 +78,12 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
65
78
  input.style.height = `${Math.min(input.scrollHeight, 180)}px`;
66
79
  }, [draft]);
67
80
  useEffect(() => {
68
- if (mention === undefined || candidates.length === 0) {
81
+ if (mention === undefined || options.length === 0) {
69
82
  setHighlight(0);
70
83
  return;
71
84
  }
72
- setHighlight(current => Math.min(current, candidates.length - 1));
73
- }, [mention, candidates.length]);
85
+ setHighlight(current => Math.min(current, options.length - 1));
86
+ }, [mention, options.length]);
74
87
  // Match the resident DSH composer without stealing a later user choice: Team
75
88
  // data can load after navigation, so a dialog or another control may already
76
89
  // own focus by the time this composer appears.
@@ -88,6 +101,10 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
88
101
  inputRef.current?.focus({ preventScroll: true });
89
102
  }, [confirmation, pending]);
90
103
  const pruneRecipients = (nextDraft) => {
104
+ // An @all marker stands for its expansion snapshot: the member handles it
105
+ // stands for are not in the text, so text-based pruning must stand down.
106
+ if (containsAllMention(nextDraft))
107
+ return;
91
108
  const knownMembers = new Map(members.map(status => [status.member.memberId, status.member]));
92
109
  const next = new Set([...recipients].filter(memberId => {
93
110
  const member = knownMembers.get(memberId);
@@ -117,9 +134,31 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
117
134
  pruneRecipients(nextDraft);
118
135
  updateMention(nextDraft, event.target.selectionStart ?? nextDraft.length);
119
136
  };
120
- const selectMention = (member) => {
137
+ const selectOption = (option) => {
121
138
  if (mention === undefined)
122
139
  return;
140
+ if (option.kind === 'all') {
141
+ // Expand at pick time: the recipients snapshot is every eligible member
142
+ // a handle pick could reach at this moment.
143
+ const nextDraft = `${draft.slice(0, mention.start)}@all ${draft.slice(mention.end)}`;
144
+ const nextCaret = mention.start + '@all '.length;
145
+ const nextRecipients = new Set(recipients);
146
+ for (const status of allMentionMembers(members))
147
+ nextRecipients.add(status.member.memberId);
148
+ onDraftChange(nextDraft);
149
+ onRecipientsChange(nextRecipients);
150
+ setMention(undefined);
151
+ setHighlight(0);
152
+ requestAnimationFrame(() => {
153
+ const input = inputRef.current;
154
+ if (input === null)
155
+ return;
156
+ input.focus({ preventScroll: true });
157
+ input.setSelectionRange(nextCaret, nextCaret);
158
+ });
159
+ return;
160
+ }
161
+ const member = option.status;
123
162
  const inserted = `@${member.member.handle} `;
124
163
  const nextDraft = `${draft.slice(0, mention.start)}${inserted}${draft.slice(mention.end)}`;
125
164
  const nextCaret = mention.start + inserted.length;
@@ -136,16 +175,31 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
136
175
  input.setSelectionRange(nextCaret, nextCaret);
137
176
  });
138
177
  };
178
+ // Pasted files (screenshots, copies) join the same chips the "+" picker
179
+ // fills; only a paste that carries files is intercepted, so plain text
180
+ // keeps the browser's native insertion.
181
+ const onPaste = (event) => {
182
+ if (pending || onFilesChange === undefined || pendingFiles === undefined)
183
+ return;
184
+ const files = Array.from(event.clipboardData.items)
185
+ .filter(item => item.kind === 'file')
186
+ .map(item => item.getAsFile())
187
+ .filter((file) => file !== null);
188
+ if (files.length === 0)
189
+ return;
190
+ event.preventDefault();
191
+ onFilesChange([...pendingFiles, ...files]);
192
+ };
139
193
  const onKeyDown = (event) => {
140
194
  const composing = composingRef.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229;
141
195
  if (menuOpen && event.key === 'ArrowDown') {
142
196
  event.preventDefault();
143
- setHighlight(current => (current + 1) % candidates.length);
197
+ setHighlight(current => (current + 1) % options.length);
144
198
  return;
145
199
  }
146
200
  if (menuOpen && event.key === 'ArrowUp') {
147
201
  event.preventDefault();
148
- setHighlight(current => (current - 1 + candidates.length) % candidates.length);
202
+ setHighlight(current => (current - 1 + options.length) % options.length);
149
203
  return;
150
204
  }
151
205
  if (event.key === 'Escape' && menuOpen) {
@@ -156,15 +210,15 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
156
210
  // Tab accepts the highlighted candidate; Shift+Tab keeps default focus reversal.
157
211
  if (menuOpen && event.key === 'Tab' && !event.shiftKey) {
158
212
  event.preventDefault();
159
- if (activeCandidate !== undefined)
160
- selectMention(activeCandidate);
213
+ if (activeOption !== undefined)
214
+ selectOption(activeOption);
161
215
  return;
162
216
  }
163
217
  if (event.key !== 'Enter' || event.shiftKey || composing || event.repeat)
164
218
  return;
165
- if (menuOpen && activeCandidate !== undefined) {
219
+ if (menuOpen && activeOption !== undefined) {
166
220
  event.preventDefault();
167
- selectMention(activeCandidate);
221
+ selectOption(activeOption);
168
222
  return;
169
223
  }
170
224
  event.preventDefault();
@@ -175,11 +229,13 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
175
229
  event.preventDefault();
176
230
  if (!pending && draft.trim() !== '')
177
231
  onSubmit();
178
- }, 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) => {
179
- const optionId = `${listId}-${status.member.memberId}`;
232
+ }, 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: options.map((option, index) => {
233
+ const optionId = option.kind === 'all' ? `${listId}-all` : `${listId}-${option.status.member.memberId}`;
180
234
  const selected = index === highlight;
181
- 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);
182
- }) }), _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, readOnly: 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(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
235
+ return option.kind === 'all'
236
+ ? _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")
237
+ : _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);
238
+ }) }), _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) => {
183
239
  const previewUrl = draftPreviewUrl(file);
184
240
  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: () => {
185
241
  const url = draftPreviewUrls.get(file);
@@ -187,7 +243,7 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
187
243
  URL.revokeObjectURL(url);
188
244
  onFilesChange(pendingFiles.filter((_, candidate) => candidate !== index));
189
245
  }, children: "\u00D7" })] }, `${file.name}-${index}`);
190
- }) })), _jsxs("div", { className: css.toolbar, children: [onFilesChange !== undefined && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: css.fileInput, "aria-hidden": "true", tabIndex: -1, onChange: event => {
246
+ }) })), _jsxs("div", { className: css.toolbar, children: [onAsTaskChange !== undefined && (_jsx(Button, { size: "sm", variant: "outline", className: asTask === true ? css.asTaskOn : undefined, "aria-pressed": asTask === true, disabled: pending, onClick: () => { onAsTaskChange(asTask !== true); }, children: t('asTask') })), onFilesChange !== undefined && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: css.fileInput, "aria-hidden": "true", tabIndex: -1, onChange: event => {
191
247
  const chosen = [...event.target.files ?? []];
192
248
  if (chosen.length > 0 && pendingFiles !== undefined)
193
249
  onFilesChange([...pendingFiles, ...chosen]);
@@ -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, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
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;
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,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BAoB1V"}
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,+BAoBzW"}
@@ -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, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
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 }) {
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
- if (current !== undefined && navigationState.taskRef !== undefined && navigationState.threadRef !== undefined) {
11
- return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, taskRef: navigationState.taskRef, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, t: t }, navigationState.threadRef);
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);
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":"TeamMemberSessionComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberSessionComposer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,sBAAsB,EAAc,MAAM,iDAAiD,CAAA;AAIzG,+EAA+E;AAC/E,MAAM,MAAM,8BAA8B,GAAG,YAAY,CAAC,2BAA2B,CAAC,GAAG;IACvF,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAA;IAC3C,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAA;IACxC,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,KAAK,IAAI,CAAA;CACxD,CAAA;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,8BAA8B,+BA6GvL"}
1
+ {"version":3,"file":"TeamMemberSessionComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberSessionComposer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,sBAAsB,EAAc,MAAM,iDAAiD,CAAA;AAIzG,+EAA+E;AAC/E,MAAM,MAAM,8BAA8B,GAAG,YAAY,CAAC,2BAA2B,CAAC,GAAG;IACvF,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAA;IAC3C,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAA;IACxC,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,KAAK,IAAI,CAAA;CACxD,CAAA;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,8BAA8B,+BA4HvL"}