@wowyuarm/dsh-agent-team 0.1.10 → 0.1.12

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 (135) hide show
  1. package/README.md +17 -4
  2. package/README.zh.md +17 -4
  3. package/package.json +53 -47
  4. package/packages/agent-team/README.md +6 -4
  5. package/packages/agent-team/README.zh.md +6 -4
  6. package/packages/agent-team/lib/context-management.js +40 -50
  7. package/packages/agent-team/lib/context-projection.js +15 -1
  8. package/packages/agent-team/lib/index.js +244 -154
  9. package/packages/agent-team/lib/invariant.js +38 -4
  10. package/packages/agent-team/lib/ledger.js +775 -383
  11. package/packages/agent-team/lib/member-context.js +6 -2
  12. package/packages/agent-team/lib/member-runtime.js +47 -9
  13. package/packages/agent-team/lib/member-time-context.js +20 -5
  14. package/packages/agent-team/lib/mentions.js +56 -0
  15. package/packages/agent-team/lib/pressure-policy.js +33 -9
  16. package/packages/agent-team/lib/session-event-cursor.js +116 -0
  17. package/packages/agent-team/lib/session-remediation.js +38 -26
  18. package/packages/agent-team/lib/spec.js +47 -34
  19. package/packages/agent-team/lib/stored-session-reader.js +138 -0
  20. package/packages/agent-team/lib/typert.host.js +157 -41
  21. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  22. package/packages/agent-team/lib/typert.remote-client.js +129 -32
  23. package/packages/agent-team/lib/types/context-management.d.ts +7 -0
  24. package/packages/agent-team/lib/types/context-management.d.ts.map +1 -1
  25. package/packages/agent-team/lib/types/context-projection.d.ts +8 -0
  26. package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
  27. package/packages/agent-team/lib/types/index.d.ts +77 -7
  28. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  29. package/packages/agent-team/lib/types/invariant.d.ts.map +1 -1
  30. package/packages/agent-team/lib/types/ledger.d.ts +251 -20
  31. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  32. package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
  33. package/packages/agent-team/lib/types/member-runtime.d.ts +2 -1
  34. package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -1
  35. package/packages/agent-team/lib/types/member-time-context.d.ts.map +1 -1
  36. package/packages/agent-team/lib/types/mentions.d.ts +34 -0
  37. package/packages/agent-team/lib/types/mentions.d.ts.map +1 -0
  38. package/packages/agent-team/lib/types/pressure-policy.d.ts +18 -0
  39. package/packages/agent-team/lib/types/pressure-policy.d.ts.map +1 -1
  40. package/packages/agent-team/lib/types/session-event-cursor.d.ts +119 -0
  41. package/packages/agent-team/lib/types/session-event-cursor.d.ts.map +1 -0
  42. package/packages/agent-team/lib/types/session-remediation.d.ts +23 -0
  43. package/packages/agent-team/lib/types/session-remediation.d.ts.map +1 -1
  44. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  45. package/packages/agent-team/lib/types/stored-session-reader.d.ts +92 -0
  46. package/packages/agent-team/lib/types/stored-session-reader.d.ts.map +1 -0
  47. package/packages/agent-team/lib/types/types/entities.d.ts +28 -20
  48. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  49. package/packages/agent-team/lib/types/types/operations.d.ts +45 -19
  50. package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
  51. package/packages/agent-team/lib/types/types/requests-results.d.ts +87 -4
  52. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  53. package/packages/agent-team/preset/team-member/agent.cordis.yml +49 -7
  54. package/packages/client-agent-team/README.md +1 -1
  55. package/packages/client-agent-team/README.zh.md +1 -1
  56. package/packages/client-agent-team/lib/client.js +2357 -1207
  57. package/packages/client-agent-team/lib/client.js.map +1 -1
  58. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  59. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +27 -7
  60. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts +18 -0
  61. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts.map +1 -0
  62. package/packages/client-agent-team/lib/types/client/TeamAvatarStack.js +22 -0
  63. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +3 -1
  64. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  65. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +167 -35
  66. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  67. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +23 -27
  68. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +11 -5
  69. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  70. package/packages/client-agent-team/lib/types/client/TeamComposer.js +26 -26
  71. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  72. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  73. package/packages/client-agent-team/lib/types/client/TeamConversation.js +9 -2
  74. package/packages/client-agent-team/lib/types/client/TeamCountBadge.d.ts +20 -0
  75. package/packages/client-agent-team/lib/types/client/TeamCountBadge.d.ts.map +1 -0
  76. package/packages/client-agent-team/lib/types/client/TeamCountBadge.js +25 -0
  77. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +25 -0
  78. package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -0
  79. package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +139 -0
  80. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
  81. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +8 -24
  82. package/packages/client-agent-team/lib/types/client/TeamMemberRow.d.ts +43 -0
  83. package/packages/client-agent-team/lib/types/client/TeamMemberRow.d.ts.map +1 -0
  84. package/packages/client-agent-team/lib/types/client/TeamMemberRow.js +27 -0
  85. package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts +2 -2
  86. package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts.map +1 -1
  87. package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
  88. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +9 -2
  89. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  90. package/packages/client-agent-team/lib/types/client/TeamMessage.js +11 -4
  91. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +8 -0
  92. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  93. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +21 -1
  94. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  95. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +108 -30
  96. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  97. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  98. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +68 -7
  99. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  100. package/packages/client-agent-team/lib/types/client/index.js +19 -9
  101. package/packages/client-agent-team/lib/types/client/locales.d.ts +40 -2
  102. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  103. package/packages/client-agent-team/lib/types/client/locales.js +40 -2
  104. package/packages/client-agent-team/lib/types/client/navigation.d.ts +10 -0
  105. package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
  106. package/packages/client-agent-team/lib/types/client/navigation.js +18 -7
  107. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +41 -0
  108. package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +1 -0
  109. package/packages/client-agent-team/lib/types/client/scope-coverage.js +64 -0
  110. package/packages/client-agent-team/lib/types/client/slots.d.ts +7 -2
  111. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  112. package/packages/client-agent-team/lib/types/client/team-changes.d.ts +33 -2
  113. package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
  114. package/packages/client-agent-team/lib/types/client/team-changes.js +107 -25
  115. package/packages/client-agent-team/lib/types/client/team-dialog-save.d.ts +20 -0
  116. package/packages/client-agent-team/lib/types/client/team-dialog-save.d.ts.map +1 -0
  117. package/packages/client-agent-team/lib/types/client/team-dialog-save.js +36 -0
  118. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +43 -1
  119. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  120. package/packages/client-agent-team/lib/types/client/team-formatters.js +81 -6
  121. package/packages/tool-agent-team/README.md +2 -2
  122. package/packages/tool-agent-team/README.zh.md +2 -2
  123. package/packages/tool-agent-team/lib/context-tools.js +18 -14
  124. package/packages/tool-agent-team/lib/host-access.js +20 -0
  125. package/packages/tool-agent-team/lib/index.js +44 -32
  126. package/packages/tool-agent-team/lib/types/context-tools.d.ts.map +1 -1
  127. package/packages/tool-agent-team/lib/types/host-access.d.ts +14 -0
  128. package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -0
  129. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
  130. package/packages/agent-team/lib/progress-nudge.js +0 -328
  131. package/packages/agent-team/lib/types/progress-nudge.d.ts +0 -136
  132. package/packages/agent-team/lib/types/progress-nudge.d.ts.map +0 -1
  133. package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts +0 -3
  134. package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts.map +0 -1
  135. package/packages/client-agent-team/lib/types/client/TeamSettings.js +0 -5
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { Button, IconArchiveOutline20, IconEditOutline16, IconPlusOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
+ import { TeamMemberRow } from "./TeamMemberRow.js";
5
6
  import { MultiMenuField } from "./multi-menu-field.js";
6
7
  import { SortableRow, useSidebarRowDrag } from "./sidebar-drag.js";
7
8
  import { moveSidebarItem, useSidebarOrder } from "./sidebar-order.js";
@@ -10,6 +11,7 @@ import { mintRequestId } from "./requests.js";
10
11
  import { TeamRowMenu } from "./TeamRowMenu.js";
11
12
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
12
13
  import { useChannelMembership } from "./team-membership.js";
14
+ import { useEditDialogSave } from "./team-dialog-save.js";
13
15
  import createCss from './create.module.css';
14
16
  import css from './sidebar.module.css';
15
17
  export function TeamChannelsPanel(props) {
@@ -39,8 +41,12 @@ export function TeamChannelsPanel(props) {
39
41
  };
40
42
  const drag = useSidebarRowDrag({ refs: orderedChannelRefs, onCommit: applyMove });
41
43
  const sectionOpen = useSidebarSectionOpen(workspaceId, 'channels');
44
+ // Only the first refresh owns the loading surface; later wakes (workspace
45
+ // catalog changes, member creation) refresh the rendered rows in place.
46
+ const loadedRef = useRef(false);
42
47
  const refresh = useCallback(async () => {
43
- setLoading(true);
48
+ if (!loadedRef.current)
49
+ setLoading(true);
44
50
  const [channelResult, memberResult] = await Promise.all([
45
51
  loadChannels({ workspaceId, limit: 1 }),
46
52
  loadMembers({ workspaceId }),
@@ -53,6 +59,7 @@ export function TeamChannelsPanel(props) {
53
59
  .map(status => status.member.memberId));
54
60
  setSelected(current => new Set([...current].filter(memberId => selectable.has(memberId))));
55
61
  setError(undefined);
62
+ loadedRef.current = true;
56
63
  }
57
64
  else if (!channelResult.ok) {
58
65
  setError(channelResult.error.message);
@@ -146,7 +153,7 @@ export function TeamChannelsPanel(props) {
146
153
  return next;
147
154
  });
148
155
  setPendingCreate(undefined);
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 => {
156
+ }, 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 && error === undefined && view !== undefined && view.channels.length === 0 && _jsx("p", { className: css.emptyState, children: t('emptyChannels') }), _jsx("div", { className: css.channelList, children: orderedChannels.map(channel => {
150
157
  const joined = membership.get(channel.channelRef) ?? new Set();
151
158
  return (_jsx(SortableRow, { drag: drag, orderKey: channel.channelRef, children: _jsx(ChannelRow, { channel: channel, members: members, joinedIds: joined, selected: selectedChannelRef === channel.channelRef, updateChannel: updateChannel, archiveChannel: props.archiveChannel, joinChannel: props.joinChannel, removeChannelMember: props.removeChannelMember, onCommitted: () => { void refresh(); }, selectChannel: selectChannel, t: t }) }, channel.channelRef));
152
159
  }) })] }), !formOpen && error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] }));
@@ -193,9 +200,11 @@ function ChannelRow({ channel, members, joinedIds, selected, updateChannel, arch
193
200
  function ChannelEditorDialog({ channel, members, joinedIds, updateChannel, joinChannel, removeChannelMember, onCommitted, onClose, t }) {
194
201
  const [name, setName] = useState(channel.name);
195
202
  const [description, setDescription] = useState(channel.description);
196
- const [saving, setSaving] = useState(false);
197
- const [error, setError] = useState();
198
- const pendingRequest = useRef();
203
+ const { saving, error, pendingRequest, save } = useEditDialogSave({
204
+ save: updateChannel,
205
+ onCommitted,
206
+ onClose,
207
+ });
199
208
  const membership = useChannelMembership({ joinChannel, removeChannelMember }, change => change.memberId, async () => { await onCommitted(); });
200
209
  const dirty = name.trim() !== channel.name || description.trim() !== channel.description;
201
210
  const submit = async (event) => {
@@ -213,32 +222,19 @@ function ChannelEditorDialog({ channel, members, joinedIds, updateChannel, joinC
213
222
  name: normalizedName,
214
223
  description: normalizedDescription,
215
224
  };
216
- pendingRequest.current = request;
217
- setSaving(true);
218
- setError(undefined);
219
- try {
220
- const result = await updateChannel(request);
221
- if (result.ok) {
222
- pendingRequest.current = undefined;
223
- await onCommitted();
224
- onClose();
225
- }
226
- else {
227
- setError(result.error.message);
228
- }
229
- }
230
- catch (cause) {
231
- setError(cause instanceof Error ? cause.message : String(cause));
232
- }
233
- finally {
234
- setSaving(false);
235
- }
225
+ await save(request);
236
226
  };
237
227
  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() === '', 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: [_jsxs("span", { children: [t('channelDescription'), t('optionalSuffix')] }), _jsx(Input, { className: createCss.input, value: description, placeholder: t('agentDescriptionPlaceholder'), 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 => {
238
228
  const joined = joinedIds.has(status.member.memberId);
239
229
  const rowPending = membership.pending.has(status.member.memberId);
240
- const disabled = rowPending || (!joined && status.presence === 'unavailable');
230
+ // Same membership law as the Channel page roster: joining needs an
231
+ // active Member, leaving only needs the fact.
232
+ const disabled = rowPending || (!joined && status.availability !== 'active');
241
233
  const rowError = membership.errors.get(status.member.memberId);
242
- 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);
234
+ return _jsx(TeamMemberRow, { status: status, action: {
235
+ label: rowPending ? t('membershipUpdating') : joined ? t('removeFromChannel') : t('addToChannel'),
236
+ disabled,
237
+ onSelect: () => { void membership.change({ workspaceId: channel.workspaceId, channelRef: channel.channelRef, memberId: status.member.memberId, joined }); },
238
+ }, ...(rowError === undefined ? {} : { error: rowError }), t: t }, status.member.memberId);
243
239
  })] }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }));
244
240
  }
@@ -1,16 +1,22 @@
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, followerMemberIds, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
3
+ import type { TeamDraftKey, TeamDraftStore } from './drafts.ts';
4
+ export declare function TeamComposer({ members, followerMemberIds, drafts, draftKey, pending, confirmation, error, onEdit, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
4
5
  readonly members: readonly AgentTeamClientMemberStatus[];
5
6
  /** Current Thread followers; the Thread surface passes them so they rank above other candidates. */
6
7
  readonly followerMemberIds?: ReadonlySet<AgentTeamMemberId>;
7
- readonly recipients: ReadonlySet<AgentTeamMemberId>;
8
- readonly draft: string;
8
+ /**
9
+ * The draft cache this composer subscribes to: owning the subscription here
10
+ * instead of in the hosting page keeps a keystroke from re-rendering the
11
+ * timeline around it.
12
+ */
13
+ readonly drafts: TeamDraftStore;
14
+ readonly draftKey: TeamDraftKey;
9
15
  readonly pending: boolean;
10
16
  readonly confirmation?: string;
11
17
  readonly error?: string;
12
- readonly onDraftChange: (draft: string) => void;
13
- readonly onRecipientsChange: (recipients: ReadonlySet<AgentTeamMemberId>) => void;
18
+ /** The Human edited the draft: the hosting page drops its one-shot send state. */
19
+ readonly onEdit?: () => void;
14
20
  readonly onSubmit: () => void;
15
21
  /** Conversation-specific prompt; the shared default fits Channel surfaces. */
16
22
  readonly placeholder?: string;
@@ -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;AAqEvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,EAAE,EAAE;IAC9N,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAA;IACxD,oGAAoG;IACpG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IAC3D,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,QAAQ,CAAC,kBAAkB,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,gGAAgG;IAChG,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,IAAI,EAAE,CAAA;IACvC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI,CAAA;IACzD,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;IACnD,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BA6SA"}
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;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAqD/D,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,EAAE,EAAE;IAClM,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAA;IACxD,oGAAoG;IACpG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,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,+BAiUA"}
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { useEffect, useLayoutEffect, useRef, useState } from 'react';
3
- import { Button, IconPlusOutline16, IconSendOutline16, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react';
3
+ import { IconChecklistOutline14, IconPaperclipOutline16, IconSendOutline16, Tooltip, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { TeamPresenceDot } from "./TeamPresenceDot.js";
5
+ import { allMentionMembers, containsAllMention, containsMention, mentionedMemberIds } from "./team-formatters.js";
5
6
  import css from './composer.module.css';
6
7
  import { formatByteSize } from "./attachment-preview.js";
7
8
  function findMention(draft, caret) {
@@ -18,12 +19,6 @@ function findMention(draft, caret) {
18
19
  return undefined;
19
20
  return { start: at, end: caret, query: beforeCaret.slice(at + 1) };
20
21
  }
21
- function escapeRegExp(value) {
22
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
23
- }
24
- function containsMention(draft, handle) {
25
- return new RegExp(`(?:^|[^\\p{L}\\p{N}_])@${escapeRegExp(handle)}(?=$|[^\\p{L}\\p{N}_])`, 'u').test(draft);
26
- }
27
22
  function mentionCandidates(members, query) {
28
23
  const normalized = query.toLocaleLowerCase();
29
24
  return members.filter(status => status.presence !== 'unavailable'
@@ -37,13 +32,6 @@ function rankMentionCandidates(candidates, followers) {
37
32
  return candidates;
38
33
  return [...candidates].sort((left, right) => Number(followers.has(right.member.memberId)) - Number(followers.has(left.member.memberId)));
39
34
  }
40
- /** Every member a manual handle pick could reach: the @all expansion snapshot. */
41
- function allMentionMembers(members) {
42
- return members.filter(status => status.presence !== 'unavailable' && status.member.state !== 'inactive' && status.member.state !== 'archived');
43
- }
44
- function containsAllMention(draft) {
45
- return /(?:^|[^\p{L}\p{N}_])@all(?=$|[^\p{L}\p{N}_])/u.test(draft);
46
- }
47
35
  /** One object URL per draft file; revoked when the draft is removed. */
48
36
  const draftPreviewUrls = new WeakMap();
49
37
  function draftPreviewUrl(file) {
@@ -56,13 +44,17 @@ function draftPreviewUrl(file) {
56
44
  }
57
45
  return url;
58
46
  }
59
- export function TeamComposer({ members, followerMemberIds, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
47
+ export function TeamComposer({ members, followerMemberIds, drafts, draftKey, pending, confirmation, error, onEdit, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
60
48
  const inputRef = useRef(null);
61
49
  const fileInputRef = useRef(null);
62
50
  const rootRef = useRef(null);
63
51
  const menuRef = useRef(null);
64
52
  const activeOptionRef = useRef(null);
65
53
  const composingRef = useRef(false);
54
+ // The draft lives in the injected cache so a view switch or refresh does not
55
+ // cost the half-written message. Subscribing here — not in the page that
56
+ // hosts this composer — is what keeps a keystroke off the timeline.
57
+ const { draft, recipients } = useSyncExternalStore(drafts.subscribe, () => drafts.getSnapshot(draftKey));
66
58
  const [mention, setMention] = useState();
67
59
  const [highlight, setHighlight] = useState(0);
68
60
  // @all is a composer-layer expansion of the same eligibility filter a
@@ -117,18 +109,20 @@ export function TeamComposer({ members, followerMemberIds, recipients, draft, pe
117
109
  return;
118
110
  inputRef.current?.focus({ preventScroll: true });
119
111
  }, [confirmation, pending]);
112
+ // Handle lookup for both directions of the same judgement: whether a picked
113
+ // recipient is still spelled in the text, and which Members the text names.
114
+ const memberHandles = new Map(members.map(status => [status.member.memberId, status.member.handle]));
120
115
  const pruneRecipients = (nextDraft) => {
121
116
  // An @all marker stands for its expansion snapshot: the member handles it
122
117
  // stands for are not in the text, so text-based pruning must stand down.
123
118
  if (containsAllMention(nextDraft))
124
119
  return;
125
- const knownMembers = new Map(members.map(status => [status.member.memberId, status.member]));
126
120
  const next = new Set([...recipients].filter(memberId => {
127
- const member = knownMembers.get(memberId);
128
- return member !== undefined && containsMention(nextDraft, member.handle);
121
+ const handle = memberHandles.get(memberId);
122
+ return handle !== undefined && containsMention(nextDraft, handle);
129
123
  }));
130
124
  if (next.size !== recipients.size)
131
- onRecipientsChange(next);
125
+ drafts.writeRecipients(draftKey, next);
132
126
  };
133
127
  // Restored drafts may carry recipients that no longer match the text (or
134
128
  // unknown Members); converge on mount and on every state change so the
@@ -139,6 +133,10 @@ export function TeamComposer({ members, followerMemberIds, recipients, draft, pe
139
133
  return;
140
134
  pruneRecipients(draft);
141
135
  }, [draft, recipients, members]);
136
+ // The notify row reports what the Host will resolve from this draft, not just
137
+ // what the mention menu picked: an authored `@Handle` delivers exactly like a
138
+ // pick, and `@all` stands for the menu's expansion.
139
+ const notifiedIds = [...new Set([...recipients, ...mentionedMemberIds(draft, members)])].sort();
142
140
  const updateMention = (nextDraft, caret) => {
143
141
  const match = findMention(nextDraft, caret);
144
142
  setMention(match);
@@ -147,15 +145,17 @@ export function TeamComposer({ members, followerMemberIds, recipients, draft, pe
147
145
  };
148
146
  const onChange = (event) => {
149
147
  const nextDraft = event.target.value;
150
- onDraftChange(nextDraft);
148
+ drafts.writeDraft(draftKey, nextDraft);
149
+ onEdit?.();
151
150
  pruneRecipients(nextDraft);
152
151
  updateMention(nextDraft, event.target.selectionStart ?? nextDraft.length);
153
152
  };
154
153
  // Both pick kinds share the same commit path: swap the text in, add the
155
154
  // recipients, close the menu, and restore the caret after focus.
156
155
  const commitMention = (nextDraft, nextCaret, nextRecipients) => {
157
- onDraftChange(nextDraft);
158
- onRecipientsChange(nextRecipients);
156
+ drafts.writeDraft(draftKey, nextDraft);
157
+ drafts.writeRecipients(draftKey, nextRecipients);
158
+ onEdit?.();
159
159
  setMention(undefined);
160
160
  setHighlight(0);
161
161
  requestAnimationFrame(() => {
@@ -246,7 +246,7 @@ export function TeamComposer({ members, followerMemberIds, recipients, draft, pe
246
246
  return option.kind === 'all'
247
247
  ? _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, ref: selected ? activeOptionRef : undefined, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx("span", { className: css.mentionAllDot, "aria-hidden": "true" }), _jsx("span", { className: css.mentionName, children: "@all" }), _jsx("span", { className: css.mentionDescription, children: t('mentionAll', { count: allCount }) })] }, "all")
248
248
  : _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, ref: selected ? activeOptionRef : undefined, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx(TeamPresenceDot, { status: option.status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", option.status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: option.status.member.description })] }, option.status.member.memberId);
249
- }) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeOption !== undefined ? activeOption.kind === 'all' ? `${listId}-all` : `${listId}-${activeOption.status.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: placeholder ?? t('messagePlaceholder'), rows: 1, onChange: onChange, onPaste: onPaste, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), recipients.size > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: [...recipients].sort().map(memberId => `@${members.find(candidate => candidate.member.memberId === memberId)?.member.handle ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
249
+ }) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeOption !== undefined ? activeOption.kind === 'all' ? `${listId}-all` : `${listId}-${activeOption.status.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: placeholder ?? t('messagePlaceholder'), rows: 1, onChange: onChange, onPaste: onPaste, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), notifiedIds.length > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: notifiedIds.map(memberId => `@${memberHandles.get(memberId) ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
250
250
  const previewUrl = draftPreviewUrl(file);
251
251
  return _jsxs("li", { className: `${css.fileChip} ${previewUrl !== undefined ? css.imageChip : ''}`, children: [previewUrl !== undefined && _jsx("img", { src: previewUrl, alt: "", className: css.imageChipPreview }), _jsxs("span", { className: css.fileChipName, title: file.name, children: [file.name, _jsx("span", { className: css.fileChipSize, children: formatByteSize(file.size) })] }), _jsx("button", { type: "button", className: css.fileChipRemove, "aria-label": t('removeFile', { name: file.name }), disabled: pending, onClick: () => {
252
252
  const url = draftPreviewUrls.get(file);
@@ -254,12 +254,12 @@ export function TeamComposer({ members, followerMemberIds, recipients, draft, pe
254
254
  URL.revokeObjectURL(url);
255
255
  onFilesChange(pendingFiles.filter((_, candidate) => candidate !== index));
256
256
  }, children: "\u00D7" })] }, `${file.name}-${index}`);
257
- }) })), _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 => {
257
+ }) })), _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 => {
258
258
  const chosen = [...event.target.files ?? []];
259
259
  if (chosen.length > 0 && pendingFiles !== undefined)
260
260
  onFilesChange([...pendingFiles, ...chosen]);
261
261
  event.target.value = '';
262
- } }), _jsx("button", { type: "button", className: css.attachButton, "aria-label": t('attachFiles'), title: t('attachFiles'), disabled: pending, onClick: () => { fileInputRef.current?.click(); }, children: _jsx(IconPlusOutline16, { size: 14 }) })] })), _jsx("button", { type: "submit", className: css.sendButton, "aria-label": pending ? t('sendingMessage') : t('sendMessage'), disabled: pending || draft.trim() === '', onMouseDown: event => {
262
+ } }), _jsx(Tooltip, { label: t('attachFiles'), side: "top", delayMs: 500, children: _jsx("button", { type: "button", className: css.attachButton, "aria-label": t('attachFiles'), disabled: pending, onClick: () => { fileInputRef.current?.click(); }, children: _jsx(IconPaperclipOutline16, { size: 14 }) }) })] })), onAsTaskChange !== undefined && (_jsxs("button", { type: "button", className: asTask === true ? `${css.asTaskPill} ${css.asTaskPillOn}` : css.asTaskPill, "aria-label": t('asTask'), "aria-pressed": asTask === true, title: t('asTask'), disabled: pending, onClick: () => { onAsTaskChange(asTask !== true); }, children: [_jsx(IconChecklistOutline14, { size: 14 }), _jsx("span", { className: css.asTaskLabel, children: t('asTask') })] })), _jsx("button", { type: "submit", className: css.sendButton, "aria-label": pending ? t('sendingMessage') : t('sendMessage'), disabled: pending || draft.trim() === '', onMouseDown: event => {
263
263
  // Keep the composer focused when the send control is clicked.
264
264
  event.preventDefault();
265
265
  inputRef.current?.focus({ preventScroll: true });
@@ -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, threadObservations, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
2
+ export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, loadInbox, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, selectWorkspace, 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,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BAoB7X"}
1
+ {"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAMvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BA0BzZ"}
@@ -1,17 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useSyncExternalStore } from 'react';
3
3
  import { TeamChannelPage } from "./TeamChannelPage.js";
4
+ import { TeamInboxPage } from "./TeamInboxPage.js";
4
5
  import { TeamThreadPage } from "./TeamThreadPage.js";
5
6
  import css from './conversation.module.css';
6
- export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
7
+ export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, loadInbox, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, selectWorkspace, backToWorkspace, backToChannels, resolveTaskRefs }) {
7
8
  const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
8
9
  const workspaces = useWorkspaces(state => state.items);
9
10
  const current = workspaces.find(workspace => workspace.workspaceId === navigationState.workspaceId);
11
+ if (navigationState.inbox === true) {
12
+ // The Inbox page is global: it merges every visible Workspace and needs no
13
+ // selected Workspace. Selecting Inbox clears the Channel/Thread faces, so
14
+ // this face owns the seat while the flag stands.
15
+ return _jsx(TeamInboxPage, { useWorkspaces: useWorkspaces, loadInbox: loadInbox, subscribeChanges: subscribeChanges, selectWorkspace: selectWorkspace, selectThread: selectThread, t: t }, "inbox");
16
+ }
10
17
  if (current !== undefined && navigationState.threadRef !== undefined) {
11
18
  return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, threadObservations: threadObservations, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
12
19
  }
13
20
  if (current !== undefined && navigationState.channelRef !== undefined) {
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);
21
+ return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, loadInbox: loadInbox, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, t: t }, navigationState.channelRef);
15
22
  }
16
23
  const welcome = current === undefined
17
24
  ? { eyebrow: t('teamMode'), title: t('team'), body: t('empty') }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The one count capsule. The Human Inbox entry, the Channel feed's Thread
3
+ * entry, and the Inbox queue row all answer the same question — how much is
4
+ * waiting here — so they wear the same box rather than one hand-written copy
5
+ * of the same declarations per surface. Three copies is how the feed's digit
6
+ * ended up on a different line box from the other two.
7
+ *
8
+ * Zero is the absence of a count rather than a capsule reading zero, which is
9
+ * the rule every caller wants and therefore the component's own.
10
+ */
11
+ export declare function TeamCountBadge({ count, tone, label, className }: {
12
+ readonly count: number;
13
+ /** `hairline` is unread that merely arrived; the solid fill is unread that names this reader. */
14
+ readonly tone?: 'solid' | 'hairline' | undefined;
15
+ /** The accessible name while the count is the only thing saying it; omit it when the surrounding control already carries the number. */
16
+ readonly label?: string | undefined;
17
+ /** Placement belongs to the surface hanging the capsule: the narrow rail pins it to the icon's corner. */
18
+ readonly className?: string | undefined;
19
+ }): import("react").JSX.Element | null;
20
+ //# sourceMappingURL=TeamCountBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamCountBadge.d.ts","sourceRoot":"","sources":["../../../src/client/TeamCountBadge.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,IAAc,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iGAAiG;IACjG,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAA;IAChD,wIAAwI;IACxI,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,0GAA0G;IAC1G,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC,sCASA"}
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import css from './countBadge.module.css';
3
+ /** Every count the Human reads is capped at this, so one wide number never widens the capsule. */
4
+ const COUNT_CAP = 99;
5
+ /**
6
+ * The one count capsule. The Human Inbox entry, the Channel feed's Thread
7
+ * entry, and the Inbox queue row all answer the same question — how much is
8
+ * waiting here — so they wear the same box rather than one hand-written copy
9
+ * of the same declarations per surface. Three copies is how the feed's digit
10
+ * ended up on a different line box from the other two.
11
+ *
12
+ * Zero is the absence of a count rather than a capsule reading zero, which is
13
+ * the rule every caller wants and therefore the component's own.
14
+ */
15
+ export function TeamCountBadge({ count, tone = 'solid', label, className }) {
16
+ if (count <= 0)
17
+ return null;
18
+ const text = count > COUNT_CAP ? `${COUNT_CAP}+` : String(count);
19
+ const classes = `${css.badge}${tone === 'hairline' ? ` ${css.hairline}` : ''}${className === undefined ? '' : ` ${className}`}`;
20
+ // Tests and the browser journey read the capsule by this attribute rather
21
+ // than by a CSS-module hash, which changes whenever a rule is renamed.
22
+ return label === undefined
23
+ ? _jsx("span", { className: classes, "data-team-count-badge": tone, "aria-hidden": "true", children: text })
24
+ : _jsx("span", { className: classes, "data-team-count-badge": tone, role: "img", "aria-label": label, title: label, children: text });
25
+ }
@@ -0,0 +1,25 @@
1
+ import type { TeamConversationProps } from './slots.ts';
2
+ interface TeamInboxPageProps {
3
+ readonly useWorkspaces: TeamConversationProps['useWorkspaces'];
4
+ readonly loadInbox: TeamConversationProps['loadInbox'];
5
+ /** Wake source while the page is open: one scope-less subscription, shared with the badge poll. */
6
+ readonly subscribeChanges: TeamConversationProps['subscribeChanges'];
7
+ readonly selectWorkspace: TeamConversationProps['selectWorkspace'];
8
+ readonly selectThread: TeamConversationProps['selectThread'];
9
+ readonly t: TeamConversationProps['t'];
10
+ }
11
+ /**
12
+ * The Human Inbox: one Inbox call per visible Workspace, rendering the Host's
13
+ * two slices — the unread queue (「需要我」, mentions counted inside it rather
14
+ * than alone) and the 「最近活跃」 tail of Threads the reader took part in.
15
+ * Both merge across every Workspace into one recency-ordered list, newest
16
+ * first, since a queue is read by recency rather than by Workspace.
17
+ * Opening the page never acknowledges anything — only a durable Thread read
18
+ * advances the watermark and consumes a mention marker, so rows and the badge
19
+ * drop after the Thread is opened through the existing auto-ack path. A Thread
20
+ * holding unread is only ever in the queue: the Host already excludes it from
21
+ * the tail, and this page never re-derives that judgement.
22
+ */
23
+ export declare function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, selectWorkspace, selectThread, t }: TeamInboxPageProps): import("react").JSX.Element;
24
+ export {};
25
+ //# sourceMappingURL=TeamInboxPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamInboxPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamInboxPage.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAavD,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAA;IACtD,mGAAmG;IACnG,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAyBD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,kBAAkB,+BA0GjI"}
@@ -0,0 +1,139 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useRef, useState } from 'react';
3
+ import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { formatAbsoluteTime, formatInboxTime } from "./team-formatters.js";
5
+ import { TeamAvatarStack } from "./TeamAvatarStack.js";
6
+ import { TeamCountBadge } from "./TeamCountBadge.js";
7
+ import css from './conversation.module.css';
8
+ import inboxCss from './inbox.module.css';
9
+ /**
10
+ * How many 「最近活跃」 rows the merged page may show. The Host bounds each
11
+ * Workspace's own slice; this is the single bound across every Workspace on
12
+ * screen, so the section stays the same size no matter how many are open — and
13
+ * it stays a way back into work rather than a second queue, which is why it is
14
+ * shorter than the queue the reader is actually being asked to work through.
15
+ */
16
+ const RECENT_ROWS_LIMIT = 5;
17
+ /**
18
+ * Queue order: newest unread first, one total order across Workspaces. Each
19
+ * Workspace slice arrives in the Host's own order (that order is the Host's
20
+ * truncation policy); the merged display order is decided here, where the
21
+ * merge happens, and the ledger sequence breaks ties so the queue never
22
+ * reshuffles two rows that share an instant.
23
+ */
24
+ function compareInboxRows(left, right) {
25
+ const leftAt = Date.parse(left.item.newestOccurredAt);
26
+ const rightAt = Date.parse(right.item.newestOccurredAt);
27
+ const byTime = (Number.isNaN(rightAt) ? 0 : rightAt) - (Number.isNaN(leftAt) ? 0 : leftAt);
28
+ return byTime !== 0 ? byTime : right.item.newestSequence - left.item.newestSequence;
29
+ }
30
+ /**
31
+ * The Human Inbox: one Inbox call per visible Workspace, rendering the Host's
32
+ * two slices — the unread queue (「需要我」, mentions counted inside it rather
33
+ * than alone) and the 「最近活跃」 tail of Threads the reader took part in.
34
+ * Both merge across every Workspace into one recency-ordered list, newest
35
+ * first, since a queue is read by recency rather than by Workspace.
36
+ * Opening the page never acknowledges anything — only a durable Thread read
37
+ * advances the watermark and consumes a mention marker, so rows and the badge
38
+ * drop after the Thread is opened through the existing auto-ack path. A Thread
39
+ * holding unread is only ever in the queue: the Host already excludes it from
40
+ * the tail, and this page never re-derives that judgement.
41
+ */
42
+ export function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, selectWorkspace, selectThread, t }) {
43
+ const workspaces = useWorkspaces(state => state.items);
44
+ const [rows, setRows] = useState();
45
+ const [recentRows, setRecentRows] = useState([]);
46
+ const [loading, setLoading] = useState(true);
47
+ const [error, setError] = useState();
48
+ // Only the first refresh owns the loading surface; later wakes refresh the
49
+ // rendered rows in place instead of flashing them back to skeleton.
50
+ const loadedRef = useRef(false);
51
+ const refresh = useCallback(async () => {
52
+ if (!loadedRef.current)
53
+ setLoading(true);
54
+ const results = await Promise.all(workspaces.map(async (workspace) => {
55
+ const result = await loadInbox({ workspaceId: workspace.workspaceId, limit: 100 });
56
+ return result.ok
57
+ ? { ok: true, workspaceId: workspace.workspaceId, workspaceTitle: workspace.title, items: result.value.items, recent: result.value.recent }
58
+ : { ok: false, message: result.error.message };
59
+ }));
60
+ const failure = results.find(result => !result.ok);
61
+ const asRows = (items, workspaceId, workspaceTitle) => items.map(item => ({ workspaceId, workspaceTitle, item }));
62
+ setRows(results.flatMap(result => result.ok ? asRows(result.items, result.workspaceId, result.workspaceTitle) : []).sort(compareInboxRows));
63
+ // The tail is one global bound rather than one per Workspace: the reader was
64
+ // promised ten Threads to step back into, and every Workspace's slice is
65
+ // already capped on its own, so the merged list is trimmed here.
66
+ setRecentRows(results.flatMap(result => result.ok ? asRows(result.recent, result.workspaceId, result.workspaceTitle) : [])
67
+ .sort(compareInboxRows).slice(0, RECENT_ROWS_LIMIT));
68
+ setError(failure?.ok === false ? failure.message : undefined);
69
+ loadedRef.current = true;
70
+ setLoading(false);
71
+ }, [loadInbox, workspaces]);
72
+ useEffect(() => { void refresh(); }, [refresh]);
73
+ useEffect(() => subscribeChanges(undefined, update => {
74
+ if (update.type === 'failed') {
75
+ setError(update.message);
76
+ return;
77
+ }
78
+ void refresh();
79
+ }), [subscribeChanges, refresh]);
80
+ const open = (row) => {
81
+ selectWorkspace(row.workspaceId);
82
+ selectThread(row.item.thread.threadRef, row.item.channelRef, row.item.task?.taskRef, row.item.taskNumber);
83
+ };
84
+ // The page rides the shared conversation seat: the same header band, 880px
85
+ // reading column, responsive gutters, and scrollbar gutter as Channel and
86
+ // Thread, so switching surfaces does not shift the content column.
87
+ const totalUnread = rows?.reduce((sum, row) => sum + row.item.unreadCount, 0) ?? 0;
88
+ const totalMentions = rows?.reduce((sum, row) => sum + row.item.directCount, 0) ?? 0;
89
+ // A row names its Workspace only while the rows on screen span more than one.
90
+ // With a single Workspace in the list the segment is a constant printed down
91
+ // every row, which spends the row's most readable position on something that
92
+ // never varies; the moment a second Workspace reaches the list it comes back,
93
+ // because two rows then have to be tellable apart. This reads the rows rather
94
+ // than the open Workspaces: a Workspace holding nothing to show here is not a
95
+ // reason to keep printing the name of the one that is.
96
+ const shownWorkspaces = new Set();
97
+ for (const row of rows ?? [])
98
+ shownWorkspaces.add(row.workspaceTitle);
99
+ for (const row of recentRows)
100
+ shownWorkspaces.add(row.workspaceTitle);
101
+ const showWorkspace = shownWorkspaces.size > 1;
102
+ return _jsxs("main", { className: css.surface, "data-team-inbox": true, children: [_jsx("div", { className: css.surfaceHeader, children: _jsx("header", { className: css.headerRow, children: _jsxs("div", { className: css.headerCopy, children: [_jsx("h1", { children: t('inboxTitle') }), rows !== undefined && rows.length > 0 && _jsxs("p", { className: inboxCss.headerMeta, children: [_jsx("span", { children: t('inboxHeaderThreads', { count: rows.length }) }), _jsx("span", { className: inboxCss.headerUnread, children: t('inboxHeaderUnread', { count: totalUnread }) }), totalMentions > 0 && _jsx("span", { children: t('inboxHeaderMentions', { count: totalMentions }) })] })] }) }) }), _jsx("div", { className: css.timeline, children: _jsxs("div", { className: css.timelineContent, children: [loading && rows === undefined && error === undefined && _jsx("div", { className: css.emptySurface, children: _jsxs("p", { className: css.loadingState, children: [_jsx("span", { className: css.loadingMark, "aria-hidden": "true" }), t('loadingInbox')] }) }), !loading && rows === undefined && error !== undefined && _jsxs("div", { className: css.errorState, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void refresh(); }, children: t('retry') })] }), rows !== undefined && (rows.length === 0 && recentRows.length === 0
103
+ ? _jsx("div", { className: css.emptySurface, children: _jsxs("div", { className: css.emptyState, children: [_jsx("strong", { children: t('inboxEmptyTitle') }), _jsx("span", { children: t('inboxEmptyHint') })] }) })
104
+ : _jsxs(_Fragment, { children: [rows.length > 0 && _jsxs("section", { className: inboxCss.section, children: [_jsxs("h2", { className: inboxCss.sectionTitle, children: [t('inboxSectionNeedsMe'), _jsx("span", { className: inboxCss.sectionCount, children: rows.length })] }), _jsx("div", { className: inboxCss.list, children: rows.map(row => _jsx(InboxQueueRow, { row: row, t: t, showWorkspace: showWorkspace, onOpen: () => { open(row); } }, `${row.workspaceId} ${row.item.thread.threadRef}`)) })] }), recentRows.length > 0 && _jsxs("section", { className: inboxCss.section, children: [_jsxs("h2", { className: inboxCss.sectionTitle, children: [t('inboxSectionRecent'), _jsx("span", { className: inboxCss.sectionCount, children: recentRows.length })] }), _jsx("div", { className: inboxCss.list, children: recentRows.map(row => _jsx(InboxQueueRow, { row: row, t: t, showWorkspace: showWorkspace, onOpen: () => { open(row); } }, `${row.workspaceId} ${row.item.thread.threadRef}`)) })] })] })), rows !== undefined && error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] }) })] });
105
+ }
106
+ /**
107
+ * One queue row, shaped like the shipped two-line result row: who is on this
108
+ * Thread leads in the gutter, the identity line answers which Thread this is,
109
+ * how much is waiting, and when it last moved, and the gist sits under it on the
110
+ * same column as evidence for that identity rather than as the row's subject.
111
+ *
112
+ * The gutter carries the one thing every row has — who is on the work, or who
113
+ * moved a Thread nobody has claimed — so a Thread that merely arrived and one
114
+ * that named the reader open on the same edge instead of the quieter one opening
115
+ * on a slot reserved for a count it does not hold. It is the grammar the Channel
116
+ * feed's Thread entry row already speaks, where the people on the work lead the
117
+ * row.
118
+ *
119
+ * Before the queue admitted every unread Thread, each row was a mention and the
120
+ * rows were interchangeable; now that named and ambient unread share one list,
121
+ * the row has to carry that difference itself. The count closes the identity
122
+ * line and only its ink changes — the shared capsule fill for a row that names
123
+ * the reader, a hairline for one that merely moved. The two numbers behind that
124
+ * ink (unread, mentions) reach assistive tech through the capsule's own name,
125
+ * because a second visible count beside the first would cost the row the one
126
+ * thing it needs to stay scannable.
127
+ */
128
+ function InboxQueueRow({ row, t, showWorkspace, onOpen }) {
129
+ const { item } = row;
130
+ const actor = item.newestActor;
131
+ const owners = item.claimOwners;
132
+ const named = item.directCount > 0;
133
+ const countLabel = named
134
+ ? t('inboxRowUnreadMentions', { count: item.unreadCount, mentions: item.directCount })
135
+ : t('inboxRowUnread', { count: item.unreadCount });
136
+ return _jsxs("button", { type: "button", className: inboxCss.row, "data-named": named || undefined, onClick: onOpen, children: [_jsx("span", { className: inboxCss.rowActor, children: owners.length > 0
137
+ ? _jsx(TeamAvatarStack, { owners: owners, label: t('claimers', { names: owners.map(owner => `@${owner.name}`).join(', ') }) })
138
+ : _jsx(TeamAvatarStack, { owners: [actor], label: t('inboxRowActor', { name: `@${actor.name}` }) }) }), _jsxs("span", { className: inboxCss.rowLine, children: [_jsxs("span", { className: inboxCss.rowCrumb, children: [showWorkspace && _jsx("span", { className: inboxCss.rowWorkspace, children: row.workspaceTitle }), showWorkspace && ' / ', _jsxs("span", { className: inboxCss.rowChannel, children: ["#", item.channelName] }), ' ', item.taskNumber !== undefined && _jsx("span", { className: inboxCss.rowTask, children: t('taskLabel', { number: item.taskNumber }) })] }), _jsx(TeamCountBadge, { count: item.unreadCount, tone: named ? 'solid' : 'hairline', label: countLabel }), _jsx("time", { className: inboxCss.rowTime, dateTime: item.newestOccurredAt, title: formatAbsoluteTime(item.newestOccurredAt), children: formatInboxTime(item.newestOccurredAt, t) })] }), _jsx("span", { className: inboxCss.rowPreview, children: item.previewText })] });
139
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"TeamMemberEditor.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberEditor.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,uBAAuB,EAAgC,MAAM,gCAAgC,CAAA;AACxI,OAAO,KAAK,EAAiD,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAYjG;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE;IAClF,QAAQ,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAAA;IACnD,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,uBAAuB,GAAG,SAAS,KAAK,IAAI,CAAA;IAC7E,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BA0GA;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE;IAC/F,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;IAC5B,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BA0EA;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE,KAAK,EAAE,uBAAuB,GAAG,SAAS,GAAG,OAAO,CAIxH"}
1
+ {"version":3,"file":"TeamMemberEditor.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberEditor.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,uBAAuB,EAAgC,MAAM,gCAAgC,CAAA;AACxI,OAAO,KAAK,EAAiD,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAajG;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE;IAClF,QAAQ,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAAA;IACnD,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,uBAAuB,GAAG,SAAS,KAAK,IAAI,CAAA;IAC7E,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BA0GA;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE;IAC/F,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;IAC5B,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BA4DA;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE,KAAK,EAAE,uBAAuB,GAAG,SAAS,GAAG,OAAO,CAIxH"}