@wowyuarm/dsh-agent-team 0.1.7 → 0.1.8

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 (64) hide show
  1. package/package.json +1 -1
  2. package/packages/agent-team/core-skills/member-skill-manager/SKILL.md +2 -0
  3. package/packages/agent-team/lib/index.js +58 -0
  4. package/packages/agent-team/lib/ledger.js +135 -12
  5. package/packages/agent-team/lib/member-context.js +1 -1
  6. package/packages/agent-team/lib/progress-nudge.js +320 -0
  7. package/packages/agent-team/lib/typert.host.js +27 -26
  8. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  9. package/packages/agent-team/lib/typert.remote-client.js +26 -25
  10. package/packages/agent-team/lib/types/index.d.ts +8 -0
  11. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  12. package/packages/agent-team/lib/types/ledger.d.ts +27 -2
  13. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  14. package/packages/agent-team/lib/types/progress-nudge.d.ts +136 -0
  15. package/packages/agent-team/lib/types/progress-nudge.d.ts.map +1 -0
  16. package/packages/agent-team/lib/types/types/entities.d.ts +18 -0
  17. package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
  18. package/packages/agent-team/lib/types/types/requests-results.d.ts +2 -0
  19. package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
  20. package/packages/agent-team/preset/team-member/agent.cordis.yml +2 -2
  21. package/packages/client-agent-team/lib/client.js +406 -512
  22. package/packages/client-agent-team/lib/client.js.map +1 -1
  23. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  24. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +3 -7
  25. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +3 -1
  26. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  27. package/packages/client-agent-team/lib/types/client/TeamComposer.js +37 -27
  28. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
  29. package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
  30. package/packages/client-agent-team/lib/types/client/TeamConversation.js +2 -2
  31. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.d.ts.map +1 -1
  32. package/packages/client-agent-team/lib/types/client/TeamMemberAvatar.js +3 -2
  33. package/packages/client-agent-team/lib/types/client/TeamMessage.js +5 -0
  34. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +2 -2
  35. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -1
  36. package/packages/client-agent-team/lib/types/client/TeamPresenceDot.js +4 -6
  37. package/packages/client-agent-team/lib/types/client/TeamStateDot.d.ts +14 -0
  38. package/packages/client-agent-team/lib/types/client/TeamStateDot.d.ts.map +1 -0
  39. package/packages/client-agent-team/lib/types/client/TeamStateDot.js +15 -0
  40. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +1 -0
  41. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  42. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +41 -16
  43. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  44. package/packages/client-agent-team/lib/types/client/index.js +2 -67
  45. package/packages/client-agent-team/lib/types/client/locales.d.ts +0 -2
  46. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  47. package/packages/client-agent-team/lib/types/client/locales.js +0 -2
  48. package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts +1 -2
  49. package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts.map +1 -1
  50. package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -1
  51. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  52. package/packages/client-agent-team/lib/types/client/task-refs.d.ts +7 -1
  53. package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
  54. package/packages/client-agent-team/lib/types/client/task-refs.js +25 -3
  55. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +11 -10
  56. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  57. package/packages/client-agent-team/lib/types/client/team-formatters.js +14 -11
  58. package/packages/tool-agent-team/lib/index.js +9 -9
  59. package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts +0 -12
  60. package/packages/client-agent-team/lib/types/client/TeamMemberDock.d.ts.map +0 -1
  61. package/packages/client-agent-team/lib/types/client/TeamMemberDock.js +0 -13
  62. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +0 -20
  63. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +0 -1
  64. package/packages/client-agent-team/lib/types/client/member-session-input.js +0 -110
@@ -1 +1 @@
1
- {"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;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"}
1
+ {"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAe/D,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;IAC1E,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAA;IAChE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAmBD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BA8UnR"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Fragment, useEffect, useRef, useState, useSyncExternalStore } from 'react';
3
- import { Button, IconChevronLeftOutline14, IconChevronRightOutline14, Modal, StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { Button, IconChevronLeftOutline14, IconChevronRightOutline14, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { bytesToBase64 } from "./attachment-preview.js";
5
5
  import { mintRequestId } from "./requests.js";
6
6
  import { TeamComposer } from "./TeamComposer.js";
@@ -8,6 +8,7 @@ import { TeamPresenceDot } from "./TeamPresenceDot.js";
8
8
  import { TeamMessage } from "./TeamMessage.js";
9
9
  import { TeamRunDivider } from "./TeamRunDivider.js";
10
10
  import { formatTaskStatus, taskStatusDot, mentionNamesOf } from "./team-formatters.js";
11
+ import { TeamStateDot } from "./TeamStateDot.js";
11
12
  import { useChannelMembership } from "./team-membership.js";
12
13
  import { useTimelineScroll } from "./timeline-scroll.js";
13
14
  import { hostTaskRefLookup, jumpToTaskThread } from "./task-refs.js";
@@ -304,11 +305,6 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
304
305
  const sender = human ? t('human') : senderStatus?.member.handle ?? item.message.sender;
305
306
  const turnGap = isRunGap(index > 0 ? block.items[index - 1].message.occurredAt : undefined, item.message.occurredAt);
306
307
  // A turn divider already labels the gapped entry; its row stays chrome-free.
307
- return _jsxs(Fragment, { children: [turnGap && _jsx(TeamRunDivider, { occurredAt: item.message.occurredAt }), _jsxs(TeamMessage, { senderName: sender, memberId: item.message.sender, human: human, body: item.message.body, attachments: item.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: item.message.occurredAt, mentionNames: mentionNamesOf(item.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: index > 0, showGroupedTime: item.message.topLevel === true && !turnGap, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }), children: [item.message.topLevel && item.task !== undefined && item.taskNumber !== undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openTask', { number: item.taskNumber }), onClick: () => { selectThread(item.thread.threadRef, channelRef, item.task?.taskRef, item.taskNumber); }, children: [_jsx("span", { className: channelCss.taskDot, children: (() => {
308
- const dot = taskStatusDot(item.task.status);
309
- return dot === 'ongoing' || dot === 'warning' || dot === 'done'
310
- ? _jsx(StateDot, { size: 8, state: dot })
311
- : _jsx("span", { className: channelCss.taskDotQuiet, "data-variant": dot });
312
- })() }), _jsx("span", { className: channelCss.taskNumber, children: t('taskLabel', { number: item.taskNumber }) }), _jsx("span", { className: channelCss.taskStatus, children: formatTaskStatus(item.task.status, t) }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] }), item.message.topLevel && item.task === undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openThread'), onClick: () => { selectThread(item.thread.threadRef, channelRef); }, children: [_jsx("span", { className: channelCss.taskNumber, children: t('threadLabel') }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] })] })] }, item.message.messageRef);
308
+ return _jsxs(Fragment, { children: [turnGap && _jsx(TeamRunDivider, { occurredAt: item.message.occurredAt }), _jsxs(TeamMessage, { senderName: sender, memberId: item.message.sender, human: human, body: item.message.body, attachments: item.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: item.message.occurredAt, mentionNames: mentionNamesOf(item.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: index > 0, showGroupedTime: item.message.topLevel === true && !turnGap, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }), children: [item.message.topLevel && item.task !== undefined && item.taskNumber !== undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openTask', { number: item.taskNumber }), onClick: () => { selectThread(item.thread.threadRef, channelRef, item.task?.taskRef, item.taskNumber); }, children: [_jsx("span", { className: channelCss.taskDot, children: _jsx(TeamStateDot, { size: 8, state: taskStatusDot(item.task.status) }) }), _jsx("span", { className: channelCss.taskNumber, children: t('taskLabel', { number: item.taskNumber }) }), _jsx("span", { className: channelCss.taskStatus, children: formatTaskStatus(item.task.status, t) }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] }), item.message.topLevel && item.task === undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openThread'), onClick: () => { selectThread(item.thread.threadRef, channelRef); }, children: [_jsx("span", { className: channelCss.taskNumber, children: t('threadLabel') }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] })] })] }, item.message.messageRef);
313
309
  }) }, `run-${block.items[0].message.messageRef}`))] }) }), channel !== undefined ? _jsx(TeamComposer, { members: channelMembers, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setStatusMessage(undefined); }, onSubmit: () => { void send(); }, pendingFiles: pendingFiles, onFilesChange: setPendingFiles, asTask: asTask, onAsTaskChange: setAsTask, t: t }, draftKey) : _jsx("div", {})] });
314
310
  }
@@ -1,7 +1,9 @@
1
1
  import type { AgentTeamClientMemberStatus, AgentTeamMemberId } from '@wowyuarm/dsh-agent-team/types';
2
2
  import type { TeamConversationProps } from './slots.ts';
3
- export declare function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
3
+ export declare function TeamComposer({ members, followerMemberIds, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
4
4
  readonly members: readonly AgentTeamClientMemberStatus[];
5
+ /** Current Thread followers; the Thread surface passes them so they rank above other candidates. */
6
+ readonly followerMemberIds?: ReadonlySet<AgentTeamMemberId>;
5
7
  readonly recipients: ReadonlySet<AgentTeamMemberId>;
6
8
  readonly draft: string;
7
9
  readonly pending: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AA+DvD,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
+ {"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAqEvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,EAAE,EAAE;IAC9N,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAA;IACxD,oGAAoG;IACpG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IAC3D,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,QAAQ,CAAC,kBAAkB,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,gGAAgG;IAChG,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,IAAI,EAAE,CAAA;IACvC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI,CAAA;IACzD,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;IACnD,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BA6SA"}
@@ -31,6 +31,12 @@ function mentionCandidates(members, query) {
31
31
  && status.member.state !== 'archived'
32
32
  && status.member.handle.toLocaleLowerCase().startsWith(normalized));
33
33
  }
34
+ /** Thread followers rank first: mentioning them delivers directly, while a non-follower enters the two-send invite flow. */
35
+ function rankMentionCandidates(candidates, followers) {
36
+ if (followers === undefined || followers.size === 0)
37
+ return candidates;
38
+ return [...candidates].sort((left, right) => Number(followers.has(right.member.memberId)) - Number(followers.has(left.member.memberId)));
39
+ }
34
40
  /** Every member a manual handle pick could reach: the @all expansion snapshot. */
35
41
  function allMentionMembers(members) {
36
42
  return members.filter(status => status.presence !== 'unavailable' && status.member.state !== 'inactive' && status.member.state !== 'archived');
@@ -50,22 +56,24 @@ function draftPreviewUrl(file) {
50
56
  }
51
57
  return url;
52
58
  }
53
- export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
59
+ export function TeamComposer({ members, followerMemberIds, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
54
60
  const inputRef = useRef(null);
55
61
  const fileInputRef = useRef(null);
56
62
  const rootRef = useRef(null);
57
63
  const menuRef = useRef(null);
64
+ const activeOptionRef = useRef(null);
58
65
  const composingRef = useRef(false);
59
66
  const [mention, setMention] = useState();
60
67
  const [highlight, setHighlight] = useState(0);
61
68
  // @all is a composer-layer expansion of the same eligibility filter a
62
69
  // handle pick uses; the fixed row stays on top of the matching members.
63
- const memberCandidates = mention === undefined ? [] : mentionCandidates(members, mention.query);
70
+ const memberCandidates = mention === undefined ? [] : rankMentionCandidates(mentionCandidates(members, mention.query), followerMemberIds);
64
71
  const options = mention !== undefined && 'all'.startsWith(mention.query.toLocaleLowerCase())
65
72
  ? [{ kind: 'all' }, ...memberCandidates.map(status => ({ kind: 'member', status }))]
66
73
  : memberCandidates.map(status => ({ kind: 'member', status }));
67
74
  const menuOpen = mention !== undefined && options.length > 0;
68
75
  const activeOption = options[highlight];
76
+ const activeOptionKey = activeOption === undefined ? undefined : activeOption.kind === 'all' ? 'all' : activeOption.status.member.memberId;
69
77
  const allCount = allMentionMembers(members).length;
70
78
  const listId = 'team-mention-suggestions';
71
79
  const menuMaxHeight = useAnchoredMaxHeight(menuRef, 320, menuOpen ? draft : null);
@@ -85,6 +93,14 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
85
93
  }
86
94
  setHighlight(current => Math.min(current, options.length - 1));
87
95
  }, [mention, options.length]);
96
+ // The anchored height cap keeps long rosters scrollable; without this the
97
+ // keyboard-highlighted row can stay hidden below the fold.
98
+ useLayoutEffect(() => {
99
+ if (!menuOpen)
100
+ return;
101
+ // Optional call: jsdom renders the menu without a layout engine.
102
+ activeOptionRef.current?.scrollIntoView?.({ block: 'nearest' });
103
+ }, [menuOpen, highlight, activeOptionKey]);
88
104
  // Match the resident DSH composer without stealing a later user choice: Team
89
105
  // data can load after navigation, so a dialog or another control may already
90
106
  // own focus by the time this composer appears.
@@ -135,6 +151,21 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
135
151
  pruneRecipients(nextDraft);
136
152
  updateMention(nextDraft, event.target.selectionStart ?? nextDraft.length);
137
153
  };
154
+ // Both pick kinds share the same commit path: swap the text in, add the
155
+ // recipients, close the menu, and restore the caret after focus.
156
+ const commitMention = (nextDraft, nextCaret, nextRecipients) => {
157
+ onDraftChange(nextDraft);
158
+ onRecipientsChange(nextRecipients);
159
+ setMention(undefined);
160
+ setHighlight(0);
161
+ requestAnimationFrame(() => {
162
+ const input = inputRef.current;
163
+ if (input === null)
164
+ return;
165
+ input.focus({ preventScroll: true });
166
+ input.setSelectionRange(nextCaret, nextCaret);
167
+ });
168
+ };
138
169
  const selectOption = (option) => {
139
170
  if (mention === undefined)
140
171
  return;
@@ -146,35 +177,14 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
146
177
  const nextRecipients = new Set(recipients);
147
178
  for (const status of allMentionMembers(members))
148
179
  nextRecipients.add(status.member.memberId);
149
- onDraftChange(nextDraft);
150
- onRecipientsChange(nextRecipients);
151
- setMention(undefined);
152
- setHighlight(0);
153
- requestAnimationFrame(() => {
154
- const input = inputRef.current;
155
- if (input === null)
156
- return;
157
- input.focus({ preventScroll: true });
158
- input.setSelectionRange(nextCaret, nextCaret);
159
- });
180
+ commitMention(nextDraft, nextCaret, nextRecipients);
160
181
  return;
161
182
  }
162
183
  const member = option.status;
163
184
  const inserted = `@${member.member.handle} `;
164
185
  const nextDraft = `${draft.slice(0, mention.start)}${inserted}${draft.slice(mention.end)}`;
165
186
  const nextCaret = mention.start + inserted.length;
166
- const nextRecipients = new Set(recipients).add(member.member.memberId);
167
- onDraftChange(nextDraft);
168
- onRecipientsChange(nextRecipients);
169
- setMention(undefined);
170
- setHighlight(0);
171
- requestAnimationFrame(() => {
172
- const input = inputRef.current;
173
- if (input === null)
174
- return;
175
- input.focus({ preventScroll: true });
176
- input.setSelectionRange(nextCaret, nextCaret);
177
- });
187
+ commitMention(nextDraft, nextCaret, new Set(recipients).add(member.member.memberId));
178
188
  };
179
189
  // Pasted files (screenshots, copies) join the same chips the "+" picker
180
190
  // fills; only a paste that carries files is intercepted, so plain text
@@ -234,8 +244,8 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
234
244
  const optionId = option.kind === 'all' ? `${listId}-all` : `${listId}-${option.status.member.memberId}`;
235
245
  const selected = index === highlight;
236
246
  return option.kind === 'all'
237
- ? _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx("span", { className: css.mentionAllDot, "aria-hidden": "true" }), _jsx("span", { className: css.mentionName, children: "@all" }), _jsx("span", { className: css.mentionDescription, children: t('mentionAll', { count: allCount }) })] }, "all")
238
- : _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx(TeamPresenceDot, { status: option.status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", option.status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: option.status.member.description })] }, option.status.member.memberId);
247
+ ? _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, ref: selected ? activeOptionRef : undefined, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx("span", { className: css.mentionAllDot, "aria-hidden": "true" }), _jsx("span", { className: css.mentionName, children: "@all" }), _jsx("span", { className: css.mentionDescription, children: t('mentionAll', { count: allCount }) })] }, "all")
248
+ : _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, ref: selected ? activeOptionRef : undefined, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx(TeamPresenceDot, { status: option.status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", option.status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: option.status.member.description })] }, option.status.member.memberId);
239
249
  }) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeOption !== undefined ? activeOption.kind === 'all' ? `${listId}-all` : `${listId}-${activeOption.status.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: placeholder ?? t('messagePlaceholder'), rows: 1, onChange: onChange, onPaste: onPaste, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), recipients.size > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: [...recipients].sort().map(memberId => `@${members.find(candidate => candidate.member.memberId === memberId)?.member.handle ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
240
250
  const previewUrl = draftPreviewUrl(file);
241
251
  return _jsxs("li", { className: `${css.fileChip} ${previewUrl !== undefined ? css.imageChip : ''}`, children: [previewUrl !== undefined && _jsx("img", { src: previewUrl, alt: "", className: css.imageChipPreview }), _jsxs("span", { className: css.fileChipName, title: file.name, children: [file.name, _jsx("span", { className: css.fileChipSize, children: formatByteSize(file.size) })] }), _jsx("button", { type: "button", className: css.fileChipRemove, "aria-label": t('removeFile', { name: file.name }), disabled: pending, onClick: () => {
@@ -1,3 +1,3 @@
1
1
  import type { TeamConversationProps } from './slots.ts';
2
- export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
2
+ export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
3
3
  //# sourceMappingURL=TeamConversation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAKvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BAoBzW"}
1
+ {"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAKvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BAoB7X"}
@@ -3,12 +3,12 @@ import { useSyncExternalStore } from 'react';
3
3
  import { TeamChannelPage } from "./TeamChannelPage.js";
4
4
  import { TeamThreadPage } from "./TeamThreadPage.js";
5
5
  import css from './conversation.module.css';
6
- export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
6
+ export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
7
7
  const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
8
8
  const workspaces = useWorkspaces(state => state.items);
9
9
  const current = workspaces.find(workspace => workspace.workspaceId === navigationState.workspaceId);
10
10
  if (current !== undefined && navigationState.threadRef !== undefined) {
11
- return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
11
+ return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, threadObservations: threadObservations, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
12
12
  }
13
13
  if (current !== undefined && navigationState.channelRef !== undefined) {
14
14
  return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, t: t }, navigationState.channelRef);
@@ -1 +1 @@
1
- {"version":3,"file":"TeamMemberAvatar.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberAvatar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAKlD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC9C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BAkBA"}
1
+ {"version":3,"file":"TeamMemberAvatar.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberAvatar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAMlD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC9C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BAkBA"}
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { StateDot, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ import { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
3
3
  import { memberHue } from "./team-formatters.js";
4
4
  import { presenceDotState, presenceLabel } from "./TeamPresenceDot.js";
5
+ import { TeamStateDot } from "./TeamStateDot.js";
5
6
  import css from './sidebar.module.css';
6
7
  /**
7
8
  * Sidebar Member avatar reusing the conversation identity language: the
@@ -11,5 +12,5 @@ import css from './sidebar.module.css';
11
12
  export function TeamMemberAvatar({ status, t }) {
12
13
  const label = presenceLabel(status, t);
13
14
  const state = presenceDotState(status.presence);
14
- return (_jsx(Tooltip, { label: label, delayMs: 300, children: _jsxs("span", { className: css.agentAvatar, style: { '--team-avatar-hue': memberHue(status.member.memberId) }, role: "img", "aria-label": label, children: [status.member.handle.replace('@', '').slice(0, 1).toUpperCase(), _jsx("span", { className: css.agentAvatarBadge, "aria-hidden": "true", children: state === 'gray' ? _jsx("span", { className: css.unavailableDot }) : _jsx(StateDot, { state: state, size: 8 }) })] }) }));
15
+ return (_jsx(Tooltip, { label: label, delayMs: 300, children: _jsxs("span", { className: css.agentAvatar, style: { '--team-avatar-hue': memberHue(status.member.memberId) }, role: "img", "aria-label": label, children: [status.member.handle.replace('@', '').slice(0, 1).toUpperCase(), _jsx("span", { className: css.agentAvatarBadge, "aria-hidden": "true", children: _jsx(TeamStateDot, { state: state }) })] }) }));
15
16
  }
@@ -218,6 +218,11 @@ function renderRefs(text, onOpenRef, taskLabel, refLabel) {
218
218
  if (segment.ref === undefined)
219
219
  return _jsx(Fragment, { children: segment.text }, index);
220
220
  const resolved = cachedResolvedTaskRef(segment.ref);
221
+ // Task refs link only once the Host confirms them; abbreviated or unknown
222
+ // refs stay plain text so prose never misfires as a link. Channel and
223
+ // Thread refs have no resolver and link at the authored position.
224
+ if (segment.ref.startsWith('task:') && resolved === undefined)
225
+ return _jsx(Fragment, { children: segment.text }, index);
221
226
  const label = resolved !== undefined && segment.ref.startsWith('task:') ? taskLabel(resolved.taskNumber) : refLabel(segment.ref);
222
227
  return _jsx("button", { type: "button", className: css.refLink, title: segment.ref, onClick: () => { onOpenRef(segment.ref); }, children: label }, index);
223
228
  });
@@ -1,9 +1,9 @@
1
- import type { StateDotState } from '@deepseek-ai/dsh-client-ui-primitives';
2
1
  import type { AgentTeamClientMemberStatus } from '@wowyuarm/dsh-agent-team/types';
3
2
  import type { TeamSidebarProps } from './slots.ts';
3
+ import type { TeamStateDotState } from './TeamStateDot.tsx';
4
4
  export declare function presenceLabel(status: AgentTeamClientMemberStatus, t: TeamSidebarProps['t']): string;
5
5
  /** Shared presence → indicator mapping for dots and avatar badges. */
6
- export declare function presenceDotState(presence: AgentTeamClientMemberStatus['presence']): StateDotState | 'gray';
6
+ export declare function presenceDotState(presence: AgentTeamClientMemberStatus['presence']): TeamStateDotState;
7
7
  export declare function TeamPresenceDot({ status, t }: {
8
8
  readonly status: AgentTeamClientMemberStatus;
9
9
  readonly t: TeamSidebarProps['t'];
@@ -1 +1 @@
1
- {"version":3,"file":"TeamPresenceDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamPresenceDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAA;AAC1E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAGlD,wBAAgB,aAAa,CAAC,MAAM,EAAE,2BAA2B,EAAE,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,MAAM,CAKnG;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAAG,aAAa,GAAG,MAAM,CAE1G;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC7C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BAYA"}
1
+ {"version":3,"file":"TeamPresenceDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamPresenceDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAG3D,wBAAgB,aAAa,CAAC,MAAM,EAAE,2BAA2B,EAAE,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,GAAG,MAAM,CAKnG;AAED,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,2BAA2B,CAAC,UAAU,CAAC,GAAG,iBAAiB,CAErG;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE;IAC7C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BASA"}
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { StateDot, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ import { Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { TeamStateDot } from "./TeamStateDot.js";
3
4
  import css from './presence.module.css';
4
5
  export function presenceLabel(status, t) {
5
6
  const label = status.presence === 'available' ? t('statusAvailable')
@@ -9,12 +10,9 @@ export function presenceLabel(status, t) {
9
10
  }
10
11
  /** Shared presence → indicator mapping for dots and avatar badges. */
11
12
  export function presenceDotState(presence) {
12
- return presence === 'available' ? 'done' : presence === 'working' ? 'ongoing' : presence === 'error' ? 'error' : 'gray';
13
+ return presence === 'available' ? 'done' : presence === 'working' ? 'ongoing' : presence === 'error' ? 'error' : 'quiet';
13
14
  }
14
15
  export function TeamPresenceDot({ status, t }) {
15
16
  const label = presenceLabel(status, t);
16
- const state = presenceDotState(status.presence);
17
- return (_jsx(Tooltip, { label: label, delayMs: 300, children: _jsx("span", { className: css.target, role: "img", "aria-label": label, children: state === 'gray'
18
- ? _jsx("span", { className: css.unavailableDot, "aria-hidden": "true" })
19
- : _jsx(StateDot, { state: state }) }) }));
17
+ return (_jsx(Tooltip, { label: label, delayMs: 300, children: _jsx("span", { className: css.target, role: "img", "aria-label": label, children: _jsx(TeamStateDot, { state: presenceDotState(status.presence) }) }) }));
20
18
  }
@@ -0,0 +1,14 @@
1
+ import type { StateDotState } from '@deepseek-ai/dsh-client-ui-primitives';
2
+ /** The Team status dot language: the DSH four-color states plus the two quiet statuses StateDot has no slot for. */
3
+ export type TeamStateDotState = StateDotState | 'todo' | 'quiet';
4
+ /**
5
+ * Render the one shared Team status indicator. Native StateDot states pass
6
+ * through; the quiet statuses mirror StateDot geometry locally so every
7
+ * surface renders the same shape — todo a hollow ring (not started), quiet a
8
+ * solid tertiary dot with the same 10% halo (closed, unavailable).
9
+ */
10
+ export declare function TeamStateDot({ state, size }: {
11
+ readonly state: TeamStateDotState;
12
+ readonly size?: number | undefined;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=TeamStateDot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamStateDot.d.ts","sourceRoot":"","sources":["../../../src/client/TeamStateDot.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAA;AAG1E,oHAAoH;AACpH,MAAM,MAAM,iBAAiB,GAAG,aAAa,GAAG,MAAM,GAAG,OAAO,CAAA;AAEhE;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;IACjD,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAA;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC,+BAKA"}
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import css from './state-dot.module.css';
4
+ /**
5
+ * Render the one shared Team status indicator. Native StateDot states pass
6
+ * through; the quiet statuses mirror StateDot geometry locally so every
7
+ * surface renders the same shape — todo a hollow ring (not started), quiet a
8
+ * solid tertiary dot with the same 10% halo (closed, unavailable).
9
+ */
10
+ export function TeamStateDot({ state, size = 10 }) {
11
+ if (state === 'todo' || state === 'quiet') {
12
+ return _jsx("span", { className: css.quiet, "data-variant": state, style: { width: size, height: size }, "aria-hidden": "true" });
13
+ }
14
+ return _jsx(StateDot, { state: state, size: size });
15
+ }
@@ -12,6 +12,7 @@ interface TeamThreadPageProps {
12
12
  readonly loadChannels: TeamConversationProps['loadChannels'];
13
13
  readonly readThread: TeamConversationProps['readThread'];
14
14
  readonly loadThreadHistory: TeamConversationProps['loadThreadHistory'];
15
+ readonly threadObservations: TeamConversationProps['threadObservations'];
15
16
  readonly subscribeChanges: TeamConversationProps['subscribeChanges'];
16
17
  readonly loadMembers: TeamConversationProps['loadMembers'];
17
18
  readonly drafts: TeamDraftStore;
@@ -1 +1 @@
1
- {"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAc/D,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC9C,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA8BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAuqBxD"}
1
+ {"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kDAAkD,CAAA;AAEnF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAe/D,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,QAAQ,CAAC,kBAAkB,EAAE,qBAAqB,CAAC,oBAAoB,CAAC,CAAA;IACxE,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC9C,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA8BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAgsBxD"}
@@ -5,7 +5,8 @@ import { TeamComposer } from "./TeamComposer.js";
5
5
  import { TeamPresenceDot } from "./TeamPresenceDot.js";
6
6
  import { TeamMessage } from "./TeamMessage.js";
7
7
  import { TeamRunDivider } from "./TeamRunDivider.js";
8
- import { formatActivity, formatClaimState, formatTaskStatus, formatTaskTitle, mentionNamesOf } from "./team-formatters.js";
8
+ import { formatActivity, formatClaimState, formatTaskStatus, formatTaskTitle, mentionNamesOf, taskStatusDot } from "./team-formatters.js";
9
+ import { TeamStateDot } from "./TeamStateDot.js";
9
10
  import { mintRequestId } from "./requests.js";
10
11
  import { daySeparatorLabel, isRunGap, timelineDayKey } from "./team-separators.js";
11
12
  import { useTimelineScroll } from "./timeline-scroll.js";
@@ -33,11 +34,14 @@ function readMeta(facts) {
33
34
  return new Map(facts.map(fact => [factKey(fact.fact), fact]));
34
35
  }
35
36
  export function TeamThreadPage(props) {
36
- const { workspaceId, channelRef, taskRef, threadRef, taskNumber, backToWorkspace, selectChannel, selectThread, resolveTaskRefs, putAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, drafts, getAttachment, reply, changeTask, promoteThread, t, } = props;
37
+ const { workspaceId, channelRef, taskRef, threadRef, taskNumber, backToWorkspace, selectChannel, selectThread, resolveTaskRefs, putAttachment, loadChannels, readThread, loadThreadHistory, threadObservations, subscribeChanges, loadMembers, drafts, getAttachment, reply, changeTask, promoteThread, t, } = props;
37
38
  const threadRequest = { threadRef, ...(taskRef === undefined ? {} : { taskRef }) };
38
39
  const [projection, setProjection] = useState();
39
40
  const [channelView, setChannelView] = useState();
40
41
  const [members, setMembers] = useState([]);
42
+ // Current Thread followers; the composer ranks them first because a mention
43
+ // to a follower delivers directly instead of entering the invite detour.
44
+ const [followerIds, setFollowerIds] = useState(() => new Set());
41
45
  const [currentFacts, setCurrentFacts] = useState([]);
42
46
  const [olderFacts, setOlderFacts] = useState([]);
43
47
  const [readFacts, setReadFacts] = useState([]);
@@ -196,29 +200,37 @@ export function TeamThreadPage(props) {
196
200
  // Every arrival while the Thread is open is acknowledged durably: the
197
201
  // timeline renders it either way, and the Human has no manual read
198
202
  // action anymore. The count only feeds the pure jump hint for a reader
199
- // away from the bottom; returning to the bottom clears it.
203
+ // away from the tail; a bottom-pinned reader already sees the arrivals.
200
204
  if (!timeline.isPinned())
201
205
  setNewFactsCount(current => current + additions.length);
202
- const priorSequence = sequenceRef.current;
203
- if (await readCurrent(true))
204
- return;
205
- // The durable acknowledgment did not happen (it failed, a newer read
206
- // superseded it, or the page unmounted); the count keeps the jump hint
207
- // visible unless that newer read now owns the tail.
208
- if (!mountedRef.current || sequenceRef.current !== priorSequence + 1)
209
- return;
210
- setNewFactsCount(current => current + additions.length);
206
+ // The acknowledgment outcome does not change what is on screen: a
207
+ // failed read surfaces through the error surface, not through the count.
208
+ await readCurrent(true);
211
209
  }
212
210
  catch {
213
211
  // A passive refresh is an invalidation convenience; the next explicit action rereads Host state.
214
212
  }
215
213
  };
214
+ // Follower ranking is an enhancement, not a page fact: a failed observation
215
+ // read leaves the roster order in place instead of surfacing an error.
216
+ const refreshFollowers = async () => {
217
+ try {
218
+ const result = await threadObservations({ workspaceId, ...threadRequest });
219
+ if (!mountedRef.current || !result.ok)
220
+ return;
221
+ setFollowerIds(new Set(result.value.followers));
222
+ }
223
+ catch {
224
+ // The next thread-scope wake retries; ranking falls back to roster order.
225
+ }
226
+ };
216
227
  useEffect(() => {
217
228
  mountedRef.current = true;
218
229
  projectionRef.current = undefined;
219
230
  setProjection(undefined);
220
231
  setChannelView(undefined);
221
232
  setMembers([]);
233
+ setFollowerIds(new Set());
222
234
  setCurrentFacts([]);
223
235
  currentFactsRef.current = [];
224
236
  setOlderFacts([]);
@@ -235,9 +247,10 @@ export function TeamThreadPage(props) {
235
247
  void (async () => {
236
248
  // One parallel round covers the whole first paint. The durable read no
237
249
  // longer wakes any change scope, so no second wave follows it.
238
- const [read, history] = await Promise.all([
250
+ const [read, history, observations] = await Promise.all([
239
251
  readThread({ requestId: readRequestIdRef.current, workspaceId, ...threadRequest }),
240
252
  loadThreadHistory({ workspaceId, ...threadRequest, limit: 20 }).catch(() => undefined),
253
+ threadObservations({ workspaceId, ...threadRequest }).catch(() => undefined),
241
254
  ]);
242
255
  if (!mountedRef.current || sequence !== sequenceRef.current)
243
256
  return;
@@ -259,6 +272,8 @@ export function TeamThreadPage(props) {
259
272
  setHistoryCursor(history.value.cursor);
260
273
  setHistoryHasMore(history.value.hasMore);
261
274
  }
275
+ if (observations !== undefined && observations.ok)
276
+ setFollowerIds(new Set(observations.value.followers));
262
277
  setError(undefined);
263
278
  setLoading(false);
264
279
  await drainUnread();
@@ -273,6 +288,8 @@ export function TeamThreadPage(props) {
273
288
  return;
274
289
  }
275
290
  void refreshPassiveFacts();
291
+ // Attention changes wake this scope too; keep the mention ranking current.
292
+ void refreshFollowers();
276
293
  }),
277
294
  subscribeChanges({ kind: 'workspace', workspaceId }, update => {
278
295
  if (!mountedRef.current)
@@ -298,6 +315,14 @@ export function TeamThreadPage(props) {
298
315
  if (newFactsCount > 0 && timeline.isPinned())
299
316
  setNewFactsCount(0);
300
317
  }, [newFactsCount, currentFacts, olderFacts, timeline]);
318
+ // The hint answers "where is the tail?"; the moment the reader is back
319
+ // within the follow margin — by the jump click or their own scroll — it
320
+ // must go away even when no render follows that position change.
321
+ const handleTimelineScroll = () => {
322
+ timeline.onScroll();
323
+ if (timeline.isPinned())
324
+ setNewFactsCount(0);
325
+ };
301
326
  const loadOlder = async () => {
302
327
  if (historyHasMore === false && historyCursor === undefined)
303
328
  return;
@@ -614,7 +639,7 @@ export function TeamThreadPage(props) {
614
639
  // Threads are always entered through a Channel page, so a Channel origin
615
640
  // returns to its timeline; a Thread restored without one returns further.
616
641
  const backLabel = channelRef === undefined ? t('backToWorkspace') : t('backToChannel');
617
- return _jsxs("main", { className: css.surface, "data-team-thread": threadRef, children: [_jsxs("div", { className: css.surfaceHeader, children: [_jsx("div", { className: css.backRow, children: _jsx(Button, { size: "sm", icon: _jsx(IconChevronLeftOutline14, {}), onClick: backToWorkspace, children: backLabel }) }), _jsxs("header", { className: css.headerRow, children: [_jsxs("div", { className: css.headerCopy, children: [_jsxs("div", { className: threadCss.titleLine, children: [_jsx("h1", { children: task === undefined ? t('threadLabel') : t('taskLabel', { number: resolvedTaskNumber ?? '…' }) }), task !== undefined && _jsx(Pill, { children: formatTaskStatus(task.status, t) })] }), taskTitle !== undefined && taskTitle !== '' && _jsx("p", { className: threadCss.taskTitle, title: taskTitle, children: taskTitle })] }), task === undefined && thread !== undefined && _jsx("div", { className: css.headerActions, children: _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void convertToTask(); }, children: mutating === 'promote' ? t('promotingTask') : t('promoteToTask') }) }), task !== undefined && thread !== undefined && task.resolution !== 'closed' && _jsxs("div", { className: css.headerActions, children: [(() => {
642
+ return _jsxs("main", { className: css.surface, "data-team-thread": threadRef, children: [_jsxs("div", { className: css.surfaceHeader, children: [_jsx("div", { className: css.backRow, children: _jsx(Button, { size: "sm", icon: _jsx(IconChevronLeftOutline14, {}), onClick: backToWorkspace, children: backLabel }) }), _jsxs("header", { className: css.headerRow, children: [_jsxs("div", { className: css.headerCopy, children: [_jsxs("div", { className: threadCss.titleLine, children: [_jsx("h1", { children: task === undefined ? t('threadLabel') : t('taskLabel', { number: resolvedTaskNumber ?? '…' }) }), task !== undefined && _jsxs(Pill, { children: [_jsx(TeamStateDot, { size: 8, state: taskStatusDot(task.status) }), formatTaskStatus(task.status, t)] })] }), taskTitle !== undefined && taskTitle !== '' && _jsx("p", { className: threadCss.taskTitle, title: taskTitle, children: taskTitle })] }), task === undefined && thread !== undefined && _jsx("div", { className: css.headerActions, children: _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void convertToTask(); }, children: mutating === 'promote' ? t('promotingTask') : t('promoteToTask') }) }), task !== undefined && thread !== undefined && task.resolution !== 'closed' && _jsxs("div", { className: css.headerActions, children: [(() => {
618
643
  const activeClaims = projection?.claims.filter(claim => claim.taskRef === task.taskRef && claim.state === 'active') ?? [];
619
644
  const earlyAccept = task.resolution === 'open' && task.status === 'in_progress' && activeClaims.length > 0;
620
645
  // todo Tasks accept directly: the work finished outside the
@@ -637,7 +662,7 @@ export function TeamThreadPage(props) {
637
662
  })(), task !== undefined && thread !== undefined && _jsx("section", { className: threadCss.workSection, "aria-label": t('claims'), children: _jsx(DisclosureRow, { expandOnRowClick: true, expandable: true, open: claimsOpen, onToggle: () => { setClaimsOpen(current => !current); }, icon: _jsx(IconChecklistOutline14, { size: 14 }), title: `${t('claims')} · ${taskClaims.length}`, children: _jsxs("div", { className: threadCss.claimList, children: [taskClaims.length === 0 && _jsx("p", { className: threadCss.emptyClaims, children: t('noClaims') }), taskClaims.map(claim => {
638
663
  const ownerStatus = members.find(status => status.member.memberId === claim.owner);
639
664
  return _jsxs("article", { className: threadCss.claimRow, children: [ownerStatus === undefined ? _jsx("span", {}) : _jsx(TeamPresenceDot, { status: ownerStatus, t: t }), _jsx("strong", { className: threadCss.claimOwner, children: memberName(claim.owner) }), _jsx("span", { className: threadCss.claimDirection, children: claim.direction }), _jsx("small", { className: threadCss.claimState, children: formatClaimState(claim.state, t) })] }, claim.claimRef);
640
- })] }) }) })] }), _jsx("section", { ref: timeline.ref, onScroll: timeline.onScroll, className: css.timeline, "aria-label": t('timelineLabel'), children: _jsxs("div", { className: css.timelineContent, children: [loading && projection === undefined && error === undefined && _jsx("div", { className: css.emptySurface, children: _jsxs("p", { className: css.loadingState, children: [_jsx("span", { className: css.loadingMark, "aria-hidden": "true" }), t('loadingThread')] }) }), projection === undefined && error !== undefined && _jsxs("div", { className: css.errorState, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void readCurrent(); }, children: t('retry') })] }), olderFacts.length > 0 && _jsxs("section", { className: threadCss.historySection, "aria-label": t('olderHistory'), children: [_jsx("h2", { children: t('olderHistory') }), renderFactBlocks(olderFacts, undefined)] }), historyHasMore && _jsx("div", { className: css.timelineAction, children: _jsx(Button, { size: "sm", onClick: () => { void loadOlder(); }, children: t('loadOlder') }) }), currentFactsWithAnchor.length > 0 && _jsx("section", { className: threadCss.publicSection, children: renderFactBlocks(currentFactsWithAnchor, unreadBoundary) }), autoReadExhausted && _jsxs("div", { className: css.timelineAction, role: "alert", children: [_jsx("span", { children: t('autoReadIncomplete') }), _jsx(Button, { size: "sm", onClick: () => { setAutoReadExhausted(false); void drainUnread(); }, disabled: loading, children: t('retry') })] }), newFactsCount > 0 && _jsx("div", { className: threadCss.newUpdates, role: "status", children: _jsx("button", { type: "button", className: threadCss.newUpdatesJump, onClick: timeline.scrollToBottom, children: t('newUpdatesJump', { count: newFactsCount }) }) })] }) }), projection !== undefined && thread !== undefined ? (task?.resolution === 'closed'
665
+ })] }) }) })] }), _jsx("section", { ref: timeline.ref, onScroll: handleTimelineScroll, className: css.timeline, "aria-label": t('timelineLabel'), children: _jsxs("div", { className: css.timelineContent, children: [loading && projection === undefined && error === undefined && _jsx("div", { className: css.emptySurface, children: _jsxs("p", { className: css.loadingState, children: [_jsx("span", { className: css.loadingMark, "aria-hidden": "true" }), t('loadingThread')] }) }), projection === undefined && error !== undefined && _jsxs("div", { className: css.errorState, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void readCurrent(); }, children: t('retry') })] }), olderFacts.length > 0 && _jsxs("section", { className: threadCss.historySection, "aria-label": t('olderHistory'), children: [_jsx("h2", { children: t('olderHistory') }), renderFactBlocks(olderFacts, undefined)] }), historyHasMore && _jsx("div", { className: css.timelineAction, children: _jsx(Button, { size: "sm", onClick: () => { void loadOlder(); }, children: t('loadOlder') }) }), currentFactsWithAnchor.length > 0 && _jsx("section", { className: threadCss.publicSection, children: renderFactBlocks(currentFactsWithAnchor, unreadBoundary) }), autoReadExhausted && _jsxs("div", { className: css.timelineAction, role: "alert", children: [_jsx("span", { children: t('autoReadIncomplete') }), _jsx(Button, { size: "sm", onClick: () => { setAutoReadExhausted(false); void drainUnread(); }, disabled: loading, children: t('retry') })] }), newFactsCount > 0 && _jsx("div", { className: threadCss.newUpdates, role: "status", children: _jsx("button", { type: "button", className: threadCss.newUpdatesJump, onClick: () => { setNewFactsCount(0); timeline.scrollToBottom(); }, children: t('newUpdatesJump', { count: newFactsCount }) }) })] }) }), projection !== undefined && thread !== undefined ? (task?.resolution === 'closed'
641
666
  ? _jsxs("div", { className: threadCss.closedBar, "data-team-closed": true, children: [error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error }), _jsxs("div", { className: threadCss.closedNotice, children: [_jsx("span", { children: t('taskClosedNotice') }), _jsx(Button, { size: "sm", variant: "outline", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] })
642
- : _jsx(TeamComposer, { members: channelMembers, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onSubmit: () => { void sendReply(); }, placeholder: t('replyPlaceholder'), pendingFiles: pendingFiles, onFilesChange: setPendingFiles, t: t }, draftKey)) : _jsx("div", {})] });
667
+ : _jsx(TeamComposer, { members: channelMembers, followerMemberIds: followerIds, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onSubmit: () => { void sendReply(); }, placeholder: t('replyPlaceholder'), pendingFiles: pendingFiles, onFilesChange: setPendingFiles, t: t }, draftKey)) : _jsx("div", {})] });
643
668
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAoCnE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAO5C,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAEnD,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,eAAO,MAAM,MAAM,UAElB,CAAA;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,IAAI,EAAE,OAAO,CAAA;KACd;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,cAAc,EAAE,cAAc,CAAA;QAC9B,UAAU,EAAE,cAAc,CAAA;KAC3B;CACF;AAuND,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAoCnE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAK5C,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAEnD,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,eAAO,MAAM,MAAM,UAElB,CAAA;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,IAAI,EAAE,OAAO,CAAA;KACd;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,cAAc,EAAE,cAAc,CAAA;QAC9B,UAAU,EAAE,cAAc,CAAA;KAC3B;CACF;AAyJD,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}