@wowyuarm/dsh-agent-team 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/README.zh.md +7 -1
- package/package.json +1 -1
- package/packages/agent-team/README.md +3 -3
- package/packages/agent-team/README.zh.md +3 -3
- package/packages/agent-team/lib/index.js +38 -9
- package/packages/agent-team/lib/ledger.js +275 -108
- package/packages/agent-team/lib/spec.js +27 -11
- package/packages/agent-team/lib/typert.host.js +185 -86
- package/packages/agent-team/lib/typert.remote-client.d.ts +3 -1
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +139 -59
- package/packages/agent-team/lib/types/index.d.ts +12 -7
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +16 -2
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types.d.ts +69 -30
- package/packages/agent-team/lib/types/types.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +1 -1
- package/packages/client-agent-team/README.md +2 -1
- package/packages/client-agent-team/README.zh.md +2 -1
- package/packages/client-agent-team/lib/client.js +625 -319
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +34 -12
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +14 -10
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +6 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +4 -4
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +4 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +16 -9
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +2 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +71 -16
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -1
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +1 -0
- package/packages/client-agent-team/lib/types/client/locales.d.ts +24 -0
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +24 -0
- package/packages/client-agent-team/lib/types/client/navigation.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/navigation.js +12 -6
- package/packages/client-agent-team/lib/types/client/slots.d.ts +2 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.js +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +9 -0
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.js +13 -0
- package/packages/tool-agent-team/README.md +7 -7
- package/packages/tool-agent-team/README.zh.md +7 -7
- package/packages/tool-agent-team/lib/index.js +38 -28
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { Button, IconEditOutline16, IconPlayOutline16, IconPlusOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import { Button, IconEditOutline16, IconPlayOutline16, IconPlusOutline16, IconRefreshOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { TeamMemberAvatar } from "./TeamMemberAvatar.js";
|
|
5
5
|
import { SortableRow, useSidebarRowDrag } from "./sidebar-drag.js";
|
|
6
6
|
import { moveSidebarItem, useSidebarOrder } from "./sidebar-order.js";
|
|
@@ -142,21 +142,43 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
|
|
|
142
142
|
function AgentRow({ status, current, channels, loadChannels, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, openMemberSession, onUpdated, t }) {
|
|
143
143
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
144
144
|
const [editing, setEditing] = useState(false);
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
const [rowAlert, setRowAlert] = useState();
|
|
146
|
+
// Both row actions ride the same runtime remote: the Host steers a live
|
|
147
|
+
// session, rebuilds an orphaned composition, or re-runs a failed activation.
|
|
148
|
+
const recover = async () => {
|
|
149
|
+
try {
|
|
150
|
+
const result = await recoverMember({
|
|
151
|
+
requestId: mintRequestId(),
|
|
152
|
+
workspaceId: status.member.workspaceId,
|
|
153
|
+
memberId: status.member.memberId,
|
|
154
|
+
});
|
|
155
|
+
await onUpdated();
|
|
156
|
+
if (!result.ok) {
|
|
157
|
+
setRowAlert(t('restartFailed', { message: result.error.message }));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (result.value.status.availability === 'unavailable') {
|
|
161
|
+
setRowAlert(t('restartStillUnavailable', { diagnostic: result.value.status.diagnostic ?? t('statusUnavailable') }));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
setRowAlert(undefined);
|
|
165
|
+
// A page opened while the Member was down predates the live Session;
|
|
166
|
+
// re-selecting is a no-op when already bound and rebinds when stale.
|
|
167
|
+
if (current === true)
|
|
168
|
+
openMemberSession(status.member.sessionId);
|
|
169
|
+
}
|
|
170
|
+
catch (cause) {
|
|
171
|
+
setRowAlert(t('restartFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
|
|
172
|
+
}
|
|
152
173
|
};
|
|
153
174
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: css.agentRow, "data-menu-open": menuOpen || undefined, children: [_jsxs("button", { type: "button", className: css.agentSelect, "aria-label": t('openAgentSession', { name: status.member.handle }), "aria-current": current ? 'page' : undefined, disabled: status.availability !== 'active', onClick: () => { openMemberSession(status.member.sessionId); }, children: [_jsx(TeamMemberAvatar, { status: status, t: t }), _jsxs("span", { className: css.agentCopy, children: [_jsx("strong", { children: status.member.handle }), _jsx("small", { children: status.member.description })] })] }), _jsx("span", { className: css.rowMenu, children: _jsx(TeamRowMenu, { label: t('actionsAgent', { name: status.member.handle }), items: [
|
|
154
175
|
{ id: 'edit', label: t('editAgent'), icon: _jsx(IconEditOutline16, {}) },
|
|
155
176
|
...(status.presence === 'error' ? [{ id: 'resume', label: t('resumeAgent'), icon: _jsx(IconPlayOutline16, {}) }] : []),
|
|
177
|
+
...(status.availability === 'unavailable' ? [{ id: 'restart', label: t('restartAgent'), icon: _jsx(IconRefreshOutline16, {}) }] : []),
|
|
156
178
|
], onSelect: (id) => {
|
|
157
|
-
if (id === '
|
|
158
|
-
void resume();
|
|
159
|
-
else
|
|
179
|
+
if (id === 'edit')
|
|
160
180
|
setEditing(true);
|
|
161
|
-
|
|
181
|
+
else
|
|
182
|
+
void recover();
|
|
183
|
+
}, onOpenChange: setMenuOpen }) })] }), rowAlert !== undefined && _jsx("div", { className: css.rowAlert, role: "alert", children: rowAlert }), editing && (_jsx(AgentEditorDialog, { status: status, channels: channels, loadChannels: loadChannels, updateMember: updateMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
|
|
162
184
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAc/D,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;IAC1E,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAA;IAChE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAmBD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,oBAAoB,+
|
|
1
|
+
{"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAc/D,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;IAC1E,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAA;IAChE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAmBD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BAiUnR"}
|
|
@@ -39,6 +39,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
39
39
|
const [statusMessage, setStatusMessage] = useState();
|
|
40
40
|
const [loading, setLoading] = useState(true);
|
|
41
41
|
const [pending, setPending] = useState(false);
|
|
42
|
+
const [asTask, setAsTask] = useState(false);
|
|
42
43
|
const [loadingOlder, setLoadingOlder] = useState(false);
|
|
43
44
|
const [managingMembers, setManagingMembers] = useState(false);
|
|
44
45
|
// The composer draft lives in the keyed draft cache: view switches unmount
|
|
@@ -60,9 +61,9 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
60
61
|
return;
|
|
61
62
|
}
|
|
62
63
|
if (ref.startsWith('task:')) {
|
|
63
|
-
const match = view?.items.find(item => item.task
|
|
64
|
+
const match = view?.items.find(item => item.task?.taskRef === ref);
|
|
64
65
|
if (match !== undefined) {
|
|
65
|
-
selectThread(match.
|
|
66
|
+
selectThread(match.thread.threadRef, channelRef, match.task?.taskRef, match.taskNumber);
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
69
|
// Not in the loaded timeline: resolve through the Host and jump to the
|
|
@@ -236,6 +237,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
236
237
|
const request = {
|
|
237
238
|
requestId, workspaceId,
|
|
238
239
|
channelRef, body: draft.trim(), recipients: recipientIds,
|
|
240
|
+
asTask,
|
|
239
241
|
...(attachmentIds.length === 0 ? {} : { attachments: attachmentIds }),
|
|
240
242
|
};
|
|
241
243
|
const result = await sendMessage(request);
|
|
@@ -245,6 +247,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
245
247
|
}
|
|
246
248
|
else if (result.value.kind === 'committed') {
|
|
247
249
|
pendingSendId.current = undefined;
|
|
250
|
+
setAsTask(false);
|
|
248
251
|
await refresh();
|
|
249
252
|
drafts.clear(draftKey);
|
|
250
253
|
setPendingFiles([]);
|
|
@@ -279,12 +282,13 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
279
282
|
const senderStatus = members.find(member => member.member.memberId === item.message.sender);
|
|
280
283
|
const human = item.message.sender === view.humanMemberId;
|
|
281
284
|
const sender = human ? t('human') : senderStatus?.member.handle ?? item.message.sender;
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
285
|
+
const turnGap = isRunGap(index > 0 ? block.items[index - 1].message.occurredAt : undefined, item.message.occurredAt);
|
|
286
|
+
// A turn divider already labels the gapped entry; its row stays chrome-free.
|
|
287
|
+
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: (() => {
|
|
288
|
+
const dot = taskStatusDot(item.task.status);
|
|
289
|
+
return dot === 'ongoing' || dot === 'warning' || dot === 'done'
|
|
290
|
+
? _jsx(StateDot, { size: 8, state: dot })
|
|
291
|
+
: _jsx("span", { className: channelCss.taskDotQuiet, "data-variant": dot });
|
|
292
|
+
})() }), _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);
|
|
293
|
+
}) }, `run-${block.items[0].message.messageRef}`))] }) }), channel !== undefined ? _jsx(TeamComposer, { members: channelMembers, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setStatusMessage(undefined); }, onSubmit: () => { void send(); }, pendingFiles: pendingFiles, onFilesChange: setPendingFiles, asTask: asTask, onAsTaskChange: setAsTask, t: t }, draftKey) : _jsx("div", {})] });
|
|
290
294
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTeamClientMemberStatus, AgentTeamMemberId } from '@wowyuarm/dsh-agent-team/types';
|
|
2
2
|
import type { TeamConversationProps } from './slots.ts';
|
|
3
|
-
export declare function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, pendingFiles, onFilesChange, t }: {
|
|
3
|
+
export declare function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
|
|
4
4
|
readonly members: readonly AgentTeamClientMemberStatus[];
|
|
5
5
|
readonly recipients: ReadonlySet<AgentTeamMemberId>;
|
|
6
6
|
readonly draft: string;
|
|
@@ -10,9 +10,14 @@ export declare function TeamComposer({ members, recipients, draft, pending, conf
|
|
|
10
10
|
readonly onDraftChange: (draft: string) => void;
|
|
11
11
|
readonly onRecipientsChange: (recipients: ReadonlySet<AgentTeamMemberId>) => void;
|
|
12
12
|
readonly onSubmit: () => void;
|
|
13
|
+
/** Conversation-specific prompt; the shared default fits Channel surfaces. */
|
|
14
|
+
readonly placeholder?: string;
|
|
13
15
|
/** Upload-capable surfaces pass this to enable the "+" file picker; the reply path omits it. */
|
|
14
16
|
readonly pendingFiles?: readonly File[];
|
|
15
17
|
readonly onFilesChange?: (files: readonly File[]) => void;
|
|
18
|
+
/** Channel-only: create a real Task with the top-level Message. Default off. */
|
|
19
|
+
readonly asTask?: boolean;
|
|
20
|
+
readonly onAsTaskChange?: (asTask: boolean) => void;
|
|
16
21
|
readonly t: TeamConversationProps['t'];
|
|
17
22
|
}): import("react").JSX.Element;
|
|
18
23
|
//# sourceMappingURL=TeamComposer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAkDvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAkDvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,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,+BA0OA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
-
import { IconPlusOutline16, IconSendOutline16, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import { Button, IconPlusOutline16, IconSendOutline16, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
5
5
|
import css from './composer.module.css';
|
|
6
6
|
import { formatByteSize } from "./attachment-preview.js";
|
|
@@ -42,7 +42,7 @@ function draftPreviewUrl(file) {
|
|
|
42
42
|
}
|
|
43
43
|
return url;
|
|
44
44
|
}
|
|
45
|
-
export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, pendingFiles, onFilesChange, t }) {
|
|
45
|
+
export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
|
|
46
46
|
const inputRef = useRef(null);
|
|
47
47
|
const fileInputRef = useRef(null);
|
|
48
48
|
const rootRef = useRef(null);
|
|
@@ -179,7 +179,7 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
179
179
|
const optionId = `${listId}-${status.member.memberId}`;
|
|
180
180
|
const selected = index === highlight;
|
|
181
181
|
return _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectMention(status); }, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: status.member.description })] }, status.member.memberId);
|
|
182
|
-
}) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeCandidate !== undefined ? `${listId}-${activeCandidate.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: t('messagePlaceholder'), rows: 1, onChange: onChange, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), recipients.size > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: [...recipients].sort().map(memberId => `@${members.find(candidate => candidate.member.memberId === memberId)?.member.handle ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
|
|
182
|
+
}) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeCandidate !== undefined ? `${listId}-${activeCandidate.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: placeholder ?? t('messagePlaceholder'), rows: 1, onChange: onChange, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), recipients.size > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: [...recipients].sort().map(memberId => `@${members.find(candidate => candidate.member.memberId === memberId)?.member.handle ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
|
|
183
183
|
const previewUrl = draftPreviewUrl(file);
|
|
184
184
|
return _jsxs("li", { className: `${css.fileChip} ${previewUrl !== undefined ? css.imageChip : ''}`, children: [previewUrl !== undefined && _jsx("img", { src: previewUrl, alt: "", className: css.imageChipPreview }), _jsxs("span", { className: css.fileChipName, title: file.name, children: [file.name, _jsx("span", { className: css.fileChipSize, children: formatByteSize(file.size) })] }), _jsx("button", { type: "button", className: css.fileChipRemove, "aria-label": t('removeFile', { name: file.name }), disabled: pending, onClick: () => {
|
|
185
185
|
const url = draftPreviewUrls.get(file);
|
|
@@ -187,7 +187,7 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
187
187
|
URL.revokeObjectURL(url);
|
|
188
188
|
onFilesChange(pendingFiles.filter((_, candidate) => candidate !== index));
|
|
189
189
|
}, children: "\u00D7" })] }, `${file.name}-${index}`);
|
|
190
|
-
}) })), _jsxs("div", { className: css.toolbar, children: [onFilesChange !== undefined && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: css.fileInput, "aria-hidden": "true", tabIndex: -1, onChange: event => {
|
|
190
|
+
}) })), _jsxs("div", { className: css.toolbar, children: [onAsTaskChange !== undefined && (_jsx(Button, { size: "sm", variant: "outline", className: asTask === true ? css.asTaskOn : undefined, "aria-pressed": asTask === true, disabled: pending, onClick: () => { onAsTaskChange(asTask !== true); }, children: t('asTask') })), onFilesChange !== undefined && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: css.fileInput, "aria-hidden": "true", tabIndex: -1, onChange: event => {
|
|
191
191
|
const chosen = [...event.target.files ?? []];
|
|
192
192
|
if (chosen.length > 0 && pendingFiles !== undefined)
|
|
193
193
|
onFilesChange([...pendingFiles, ...chosen]);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { TeamConversationProps } from './slots.ts';
|
|
2
|
-
export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
|
|
2
|
+
export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=TeamConversation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAKvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+
|
|
1
|
+
{"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAKvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BAoBzW"}
|
|
@@ -3,12 +3,12 @@ import { useSyncExternalStore } from 'react';
|
|
|
3
3
|
import { TeamChannelPage } from "./TeamChannelPage.js";
|
|
4
4
|
import { TeamThreadPage } from "./TeamThreadPage.js";
|
|
5
5
|
import css from './conversation.module.css';
|
|
6
|
-
export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
|
|
6
|
+
export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
|
|
7
7
|
const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
|
|
8
8
|
const workspaces = useWorkspaces(state => state.items);
|
|
9
9
|
const current = workspaces.find(workspace => workspace.workspaceId === navigationState.workspaceId);
|
|
10
|
-
if (current !== undefined && navigationState.
|
|
11
|
-
return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment,
|
|
10
|
+
if (current !== undefined && navigationState.threadRef !== undefined) {
|
|
11
|
+
return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
|
|
12
12
|
}
|
|
13
13
|
if (current !== undefined && navigationState.channelRef !== undefined) {
|
|
14
14
|
return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, t: t }, navigationState.channelRef);
|
|
@@ -14,6 +14,9 @@ export interface TeamMessageProps {
|
|
|
14
14
|
readonly senderTitle?: string;
|
|
15
15
|
/** Continuation of one same-sender run: suppress repeated identity chrome. */
|
|
16
16
|
readonly grouped?: boolean;
|
|
17
|
+
/** Continuation rows that carry their own footer chip render the time so the
|
|
18
|
+
hairline-separated entry stays self-identifying. */
|
|
19
|
+
readonly showGroupedTime?: boolean;
|
|
17
20
|
readonly attachments?: readonly AgentTeamMessageAttachment[] | undefined;
|
|
18
21
|
/** Cache readback for thumbnails; absent on surfaces without the remotes. */
|
|
19
22
|
readonly loadAttachment?: TeamConversationProps['getAttachment'] | undefined;
|
|
@@ -25,5 +28,5 @@ export interface TeamMessageProps {
|
|
|
25
28
|
readonly children?: ReactNode;
|
|
26
29
|
}
|
|
27
30
|
/** One chat message row with identity chrome and sender-appropriate rendering. */
|
|
28
|
-
export declare function TeamMessage({ senderName, memberId, human, body, occurredAt, mentionNames, senderTitle, grouped, attachments, loadAttachment, t, onOpenRef, onResolveTaskRefs, children }: TeamMessageProps): import("react").JSX.Element;
|
|
31
|
+
export declare function TeamMessage({ senderName, memberId, human, body, occurredAt, mentionNames, senderTitle, grouped, showGroupedTime, attachments, loadAttachment, t, onOpenRef, onResolveTaskRefs, children }: TeamMessageProps): import("react").JSX.Element;
|
|
29
32
|
//# sourceMappingURL=TeamMessage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamMessage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2F,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAE/H,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACrH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAA4E,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAI/H,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC,GAAG,SAAS,CAAA;IAC5E,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,SAAS,CAAA;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACxD,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACzH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+
|
|
1
|
+
{"version":3,"file":"TeamMessage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2F,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAE/H,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACrH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAA4E,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAI/H,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B;2DACuD;IACvD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC,GAAG,SAAS,CAAA;IAC5E,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,SAAS,CAAA;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACxD,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACzH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BA6G3N"}
|
|
@@ -3,10 +3,10 @@ import { Fragment, useCallback, useEffect, useLayoutEffect, useRef, useState } f
|
|
|
3
3
|
import { MarkdownText, MessageText, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { cachedAttachmentDataUrl, formatByteSize, loadAttachmentDataUrl } from "./attachment-preview.js";
|
|
5
5
|
import { cachedResolvedTaskRef, resolveUnknownTaskRefs, useResolvedTaskRefVersion } from "./task-refs.js";
|
|
6
|
-
import { formatMessageTime, isSingleBrandedRef, memberHue, planMessageBody, splitBrandedRefs, splitMentionNames } from "./team-formatters.js";
|
|
6
|
+
import { formatMessageTime, isSingleBrandedRef, memberHue, planMessageBody, shouldClampMessage, splitBrandedRefs, splitMentionNames } from "./team-formatters.js";
|
|
7
7
|
import css from './conversation.module.css';
|
|
8
8
|
/** One chat message row with identity chrome and sender-appropriate rendering. */
|
|
9
|
-
export function TeamMessage({ senderName, memberId, human, body, occurredAt, mentionNames, senderTitle, grouped, attachments, loadAttachment, t, onOpenRef, onResolveTaskRefs, children }) {
|
|
9
|
+
export function TeamMessage({ senderName, memberId, human, body, occurredAt, mentionNames, senderTitle, grouped, showGroupedTime, attachments, loadAttachment, t, onOpenRef, onResolveTaskRefs, children }) {
|
|
10
10
|
const avatarStyle = human ? undefined : { '--team-avatar-hue': memberHue(memberId) };
|
|
11
11
|
// Literal bodies carry mention chips inline — Human input always, and
|
|
12
12
|
// plain-prose Agent bodies where literal rendering loses nothing. Rich
|
|
@@ -29,6 +29,12 @@ export function TeamMessage({ senderName, memberId, human, body, occurredAt, men
|
|
|
29
29
|
void resolveUnknownTaskRefs(bodyTaskRefs, onResolveTaskRefs);
|
|
30
30
|
}, [onResolveTaskRefs, bodyTaskRefKey, refVersion]);
|
|
31
31
|
const taskLabel = useCallback((taskNumber) => t?.('taskLabel', { number: taskNumber }) ?? `Task #${taskNumber}`, [t]);
|
|
32
|
+
// Long bodies start clamped behind the expand control. The default derives
|
|
33
|
+
// from the body alone; the toggle itself is per-mount view state nothing
|
|
34
|
+
// persists.
|
|
35
|
+
const [expanded, setExpanded] = useState(false);
|
|
36
|
+
const clampable = shouldClampMessage(displayBody);
|
|
37
|
+
const clamped = clampable && !expanded;
|
|
32
38
|
const markdownRef = useRef(null);
|
|
33
39
|
useLayoutEffect(() => {
|
|
34
40
|
const root = markdownRef.current;
|
|
@@ -71,13 +77,14 @@ export function TeamMessage({ senderName, memberId, human, body, occurredAt, men
|
|
|
71
77
|
root.addEventListener('click', openTask);
|
|
72
78
|
return () => { root.removeEventListener('click', openTask); };
|
|
73
79
|
}, [richAgentBody, onOpenRef]);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
80
|
+
const bodyNode = plan.render === 'inline' && plan.inline !== undefined
|
|
81
|
+
? _jsx("div", { className: css.messageText, children: plan.inline.segments.map((segment, index) => segment.mention
|
|
82
|
+
? _jsx("span", { className: css.mention, children: segment.text }, index)
|
|
83
|
+
: _jsx(Fragment, { children: renderRefs(segment.text, onOpenRef, taskLabel) }, index)) })
|
|
84
|
+
: plan.render === 'literal'
|
|
85
|
+
? _jsx("div", { className: css.messageText, children: onOpenRef === undefined ? _jsx(MessageText, { text: displayBody }) : renderRefs(displayBody, onOpenRef, taskLabel) })
|
|
86
|
+
: _jsx("div", { ref: markdownRef, className: css.messageMarkdown, children: _jsx(MarkdownText, { text: displayBody }, `${displayBody}:${onOpenRef === undefined ? 'literal' : 'refs'}`) });
|
|
87
|
+
return (_jsxs("article", { className: css.messageRow, "data-human": human || undefined, "data-grouped": grouped || undefined, children: [_jsx("div", { className: css.messageIdentity, style: avatarStyle, "aria-hidden": "true", children: senderName.replace('@', '').slice(0, 1).toUpperCase() }), _jsxs("div", { className: css.messageBody, children: [(!grouped || showGroupedTime === true) && (_jsxs("div", { className: css.nameRow, children: [!grouped && _jsx("strong", { ...(senderTitle === undefined ? {} : { title: senderTitle }), children: senderName }), occurredAt !== undefined && _jsx("span", { className: css.messageTime, children: formatMessageTime(occurredAt) })] })), clampable ? _jsx("div", { className: clamped ? css.messageClamp : undefined, children: bodyNode }) : bodyNode, clampable && (_jsx("button", { type: "button", className: css.messageExpand, "data-message-expand": "true", "aria-expanded": expanded, onClick: () => { setExpanded(value => !value); }, children: expanded ? (t?.('collapseMessage') ?? 'Show less') : (t?.('expandMessage') ?? 'Show more') })), (plan.fallbackNames.length > 0 || plan.fallbackRefs.length > 0) && (_jsxs("div", { className: css.mentionsRow, children: [plan.fallbackNames.map(name => _jsxs("span", { className: css.mention, children: ["@", name] }, name)), plan.fallbackRefs.map(ref => {
|
|
81
88
|
const resolved = cachedResolvedTaskRef(ref);
|
|
82
89
|
const label = resolved !== undefined && ref.startsWith('task:') ? taskLabel(resolved.taskNumber) : ref;
|
|
83
90
|
return _jsx("button", { type: "button", className: css.refLink, title: ref, onClick: () => { onOpenRef(ref); }, children: label }, ref);
|
|
@@ -5,7 +5,7 @@ import type { TeamDraftStore } from './drafts.ts';
|
|
|
5
5
|
interface TeamThreadPageProps {
|
|
6
6
|
readonly workspaceId: WorkspaceId;
|
|
7
7
|
readonly channelRef?: AgentTeamChannelRef;
|
|
8
|
-
readonly taskRef
|
|
8
|
+
readonly taskRef?: AgentTeamTaskRef;
|
|
9
9
|
readonly threadRef: AgentTeamThreadRef;
|
|
10
10
|
readonly taskNumber?: number;
|
|
11
11
|
readonly backToWorkspace: TeamConversationProps['backToWorkspace'];
|
|
@@ -18,6 +18,7 @@ interface TeamThreadPageProps {
|
|
|
18
18
|
readonly getAttachment: TeamConversationProps['getAttachment'];
|
|
19
19
|
readonly reply: TeamConversationProps['reply'];
|
|
20
20
|
readonly changeTask: TeamConversationProps['changeTask'];
|
|
21
|
+
readonly promoteThread: TeamConversationProps['promoteThread'];
|
|
21
22
|
readonly putAttachment: TeamConversationProps['putAttachment'];
|
|
22
23
|
readonly selectChannel: TeamConversationProps['selectChannel'];
|
|
23
24
|
readonly selectThread: TeamConversationProps['selectThread'];
|
|
@@ -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,wCAAwC,CAAA;AAEzE,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,EAAE,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,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,+BA+mBxD"}
|
|
@@ -33,7 +33,8 @@ function readMeta(facts) {
|
|
|
33
33
|
return new Map(facts.map(fact => [factKey(fact.fact), fact]));
|
|
34
34
|
}
|
|
35
35
|
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, t, } = 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 threadRequest = { threadRef, ...(taskRef === undefined ? {} : { taskRef }) };
|
|
37
38
|
const [projection, setProjection] = useState();
|
|
38
39
|
const [channelView, setChannelView] = useState();
|
|
39
40
|
const [members, setMembers] = useState([]);
|
|
@@ -53,13 +54,16 @@ export function TeamThreadPage(props) {
|
|
|
53
54
|
// confirm dialog lists exactly what will be completed with the Task.
|
|
54
55
|
const [confirmingAccept, setConfirmingAccept] = useState(false);
|
|
55
56
|
useEffect(() => {
|
|
56
|
-
if (confirmingAccept && projection?.task
|
|
57
|
+
if (confirmingAccept && projection?.task?.resolution === 'accepted')
|
|
57
58
|
setConfirmingAccept(false);
|
|
58
|
-
}, [confirmingAccept, projection?.task
|
|
59
|
+
}, [confirmingAccept, projection?.task?.resolution]);
|
|
59
60
|
const [replyRequestId, setReplyRequestId] = useState();
|
|
60
61
|
const [confirmation, setConfirmation] = useState();
|
|
61
62
|
const [statusMessage, setStatusMessage] = useState();
|
|
62
63
|
const [pending, setPending] = useState(false);
|
|
64
|
+
// Label source for the promote/accept buttons only: a reply send also raises
|
|
65
|
+
// the shared `pending` gate, and must not retitle those buttons.
|
|
66
|
+
const [mutating, setMutating] = useState();
|
|
63
67
|
const [loading, setLoading] = useState(true);
|
|
64
68
|
const [error, setError] = useState();
|
|
65
69
|
const mountedRef = useRef(false);
|
|
@@ -91,7 +95,7 @@ export function TeamThreadPage(props) {
|
|
|
91
95
|
sequenceRef.current = sequence;
|
|
92
96
|
setLoading(true);
|
|
93
97
|
try {
|
|
94
|
-
const result = await readThread({ requestId: readRequestIdRef.current, workspaceId,
|
|
98
|
+
const result = await readThread({ requestId: readRequestIdRef.current, workspaceId, ...threadRequest });
|
|
95
99
|
if (!mountedRef.current || sequence !== sequenceRef.current)
|
|
96
100
|
return false;
|
|
97
101
|
if (!result.ok) {
|
|
@@ -135,7 +139,7 @@ export function TeamThreadPage(props) {
|
|
|
135
139
|
};
|
|
136
140
|
const refreshPassiveFacts = async () => {
|
|
137
141
|
try {
|
|
138
|
-
const result = await loadThreadHistory({ workspaceId,
|
|
142
|
+
const result = await loadThreadHistory({ workspaceId, ...threadRequest, limit: 100 });
|
|
139
143
|
if (!mountedRef.current || !result.ok)
|
|
140
144
|
return;
|
|
141
145
|
const incoming = result.value.facts;
|
|
@@ -153,7 +157,7 @@ export function TeamThreadPage(props) {
|
|
|
153
157
|
currentFactsRef.current = merged;
|
|
154
158
|
return merged;
|
|
155
159
|
});
|
|
156
|
-
setProjection(current => current === undefined ? current : { ...current, task: result.value.task, thread: result.value.thread, claims: result.value.claims });
|
|
160
|
+
setProjection(current => current === undefined ? current : { ...current, ...(result.value.task === undefined ? {} : { task: result.value.task }), thread: result.value.thread, claims: result.value.claims });
|
|
157
161
|
if (additions.length === 0)
|
|
158
162
|
return;
|
|
159
163
|
// A reader scrolled away from the tail must be offered the explicit
|
|
@@ -200,8 +204,8 @@ export function TeamThreadPage(props) {
|
|
|
200
204
|
// One parallel round covers the whole first paint. The durable read no
|
|
201
205
|
// longer wakes any change scope, so no second wave follows it.
|
|
202
206
|
const [read, history] = await Promise.all([
|
|
203
|
-
readThread({ requestId: readRequestIdRef.current, workspaceId,
|
|
204
|
-
loadThreadHistory({ workspaceId,
|
|
207
|
+
readThread({ requestId: readRequestIdRef.current, workspaceId, ...threadRequest }),
|
|
208
|
+
loadThreadHistory({ workspaceId, ...threadRequest, limit: 20 }).catch(() => undefined),
|
|
205
209
|
]);
|
|
206
210
|
if (!mountedRef.current || sequence !== sequenceRef.current)
|
|
207
211
|
return;
|
|
@@ -262,7 +266,7 @@ export function TeamThreadPage(props) {
|
|
|
262
266
|
return;
|
|
263
267
|
setLoading(true);
|
|
264
268
|
try {
|
|
265
|
-
const result = await loadThreadHistory({ workspaceId,
|
|
269
|
+
const result = await loadThreadHistory({ workspaceId, ...threadRequest, beforeSequence, limit: 20 });
|
|
266
270
|
if (!mountedRef.current)
|
|
267
271
|
return;
|
|
268
272
|
if (!result.ok) {
|
|
@@ -286,6 +290,7 @@ export function TeamThreadPage(props) {
|
|
|
286
290
|
const activeProjection = projection;
|
|
287
291
|
const task = activeProjection?.task;
|
|
288
292
|
const thread = activeProjection?.thread;
|
|
293
|
+
const resolvedTaskNumber = taskNumber ?? channelView?.taskNumbers.find(entry => entry.taskRef === task?.taskRef)?.taskNumber;
|
|
289
294
|
const taskTitle = activeProjection === undefined ? undefined : formatTaskTitle(activeProjection.anchor.body);
|
|
290
295
|
const taskClaims = activeProjection?.claims ?? [];
|
|
291
296
|
const effectiveChannelRef = task?.channelRef ?? channelRef;
|
|
@@ -371,10 +376,59 @@ export function TeamThreadPage(props) {
|
|
|
371
376
|
await readCurrent(true);
|
|
372
377
|
await refreshSupplemental();
|
|
373
378
|
};
|
|
379
|
+
const convertToTask = async () => {
|
|
380
|
+
if (pending || thread === undefined || task !== undefined)
|
|
381
|
+
return;
|
|
382
|
+
setPending(true);
|
|
383
|
+
setMutating('promote');
|
|
384
|
+
setError(undefined);
|
|
385
|
+
const key = 'promote';
|
|
386
|
+
const requestId = mutationRequests.current.get(key) ?? mintRequestId();
|
|
387
|
+
mutationRequests.current.set(key, requestId);
|
|
388
|
+
try {
|
|
389
|
+
const result = await promoteThread({ requestId, workspaceId, threadRef, baseRevision: thread.revision });
|
|
390
|
+
if (!result.ok) {
|
|
391
|
+
setError(result.error.message);
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (result.value.kind === 'committed') {
|
|
395
|
+
mutationRequests.current.delete(key);
|
|
396
|
+
const committed = result.value;
|
|
397
|
+
setProjection(current => current === undefined ? current
|
|
398
|
+
: { ...current, task: committed.task, thread: committed.thread });
|
|
399
|
+
setCurrentFacts(current => {
|
|
400
|
+
const merged = mergeFacts(current, [{ kind: 'activity', sequence: committed.activity.sequence, activity: committed.activity }]);
|
|
401
|
+
currentFactsRef.current = merged;
|
|
402
|
+
return merged;
|
|
403
|
+
});
|
|
404
|
+
await readCurrent(true);
|
|
405
|
+
await refreshSupplemental();
|
|
406
|
+
}
|
|
407
|
+
else if (result.value.kind === 'unread_required') {
|
|
408
|
+
setError(t('unreadRequired', { count: result.value.unreadCount }));
|
|
409
|
+
mutationRequests.current.delete(key);
|
|
410
|
+
await refreshAfterFence();
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
setError(t('staleRevision'));
|
|
414
|
+
mutationRequests.current.delete(key);
|
|
415
|
+
await refreshPassiveFacts();
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
catch (cause) {
|
|
419
|
+
setError(cause instanceof Error ? cause.message : String(cause));
|
|
420
|
+
}
|
|
421
|
+
finally {
|
|
422
|
+
setPending(false);
|
|
423
|
+
setMutating(undefined);
|
|
424
|
+
}
|
|
425
|
+
};
|
|
374
426
|
const mutateTask = async (action) => {
|
|
375
427
|
if (pending || task === undefined || thread === undefined)
|
|
376
428
|
return;
|
|
377
429
|
setPending(true);
|
|
430
|
+
if (action === 'accept')
|
|
431
|
+
setMutating('accept');
|
|
378
432
|
setError(undefined);
|
|
379
433
|
const key = `task:${action}`;
|
|
380
434
|
const requestId = mutationRequests.current.get(key) ?? mintRequestId();
|
|
@@ -413,10 +467,11 @@ export function TeamThreadPage(props) {
|
|
|
413
467
|
}
|
|
414
468
|
finally {
|
|
415
469
|
setPending(false);
|
|
470
|
+
setMutating(undefined);
|
|
416
471
|
}
|
|
417
472
|
};
|
|
418
473
|
const sendReply = async () => {
|
|
419
|
-
if (pending ||
|
|
474
|
+
if (pending || thread === undefined || draft.trim() === '')
|
|
420
475
|
return;
|
|
421
476
|
const id = replyRequestId ?? mintRequestId();
|
|
422
477
|
setReplyRequestId(id);
|
|
@@ -440,7 +495,7 @@ export function TeamThreadPage(props) {
|
|
|
440
495
|
}
|
|
441
496
|
attachmentIds.push(uploaded.value.attachmentId);
|
|
442
497
|
}
|
|
443
|
-
const result = await reply({ requestId: id, workspaceId, taskRef: task.taskRef, body: draft.trim(), baseRevision: thread.revision, recipients: [...recipients].sort(), ...(attachmentIds.length === 0 ? {} : { attachments: attachmentIds }), ...(confirmation === undefined ? {} : { confirmationToken: confirmation }) });
|
|
498
|
+
const result = await reply({ requestId: id, workspaceId, threadRef, ...(task === undefined ? {} : { taskRef: task.taskRef }), body: draft.trim(), baseRevision: thread.revision, recipients: [...recipients].sort(), ...(attachmentIds.length === 0 ? {} : { attachments: attachmentIds }), ...(confirmation === undefined ? {} : { confirmationToken: confirmation }) });
|
|
444
499
|
if (!result.ok) {
|
|
445
500
|
setError(result.error.message);
|
|
446
501
|
return;
|
|
@@ -452,7 +507,7 @@ export function TeamThreadPage(props) {
|
|
|
452
507
|
currentFactsRef.current = merged;
|
|
453
508
|
return merged;
|
|
454
509
|
});
|
|
455
|
-
setProjection(current => current === undefined ? current : { ...current, task: committed.task, thread: committed.thread });
|
|
510
|
+
setProjection(current => current === undefined ? current : { ...current, ...(committed.task === undefined ? {} : { task: committed.task }), thread: committed.thread });
|
|
456
511
|
drafts.clear(draftKey);
|
|
457
512
|
setPendingFiles([]);
|
|
458
513
|
setReplyRequestId(undefined);
|
|
@@ -501,7 +556,7 @@ export function TeamThreadPage(props) {
|
|
|
501
556
|
// Threads are always entered through a Channel page, so a Channel origin
|
|
502
557
|
// returns to its timeline; a Thread restored without one returns further.
|
|
503
558
|
const backLabel = channelRef === undefined ? t('backToWorkspace') : t('backToChannel');
|
|
504
|
-
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('
|
|
559
|
+
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: [(() => {
|
|
505
560
|
const activeClaims = projection?.claims.filter(claim => claim.taskRef === task.taskRef && claim.state === 'active') ?? [];
|
|
506
561
|
const earlyAccept = task.resolution === 'open' && task.status === 'in_progress' && activeClaims.length > 0;
|
|
507
562
|
if (!(task.status === 'in_review' || earlyAccept) || task.resolution !== 'open')
|
|
@@ -518,11 +573,11 @@ export function TeamThreadPage(props) {
|
|
|
518
573
|
// Recomputed here so the confirm list never shows stale rows.
|
|
519
574
|
const activeClaims = projection?.claims.filter(claim => claim.taskRef === task.taskRef && claim.state === 'active') ?? [];
|
|
520
575
|
return _jsxs(Modal, { open: confirmingAccept, onClose: () => { if (!pending)
|
|
521
|
-
setConfirmingAccept(false); }, title: t('acceptEarlyTitle'), closeLabel: t('cancel'), footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: pending, onClick: () => { setConfirmingAccept(false); }, children: t('cancel') }), _jsx(Button, { variant: "primary", disabled: pending, onClick: () => { void mutateTask('accept'); }, children:
|
|
576
|
+
setConfirmingAccept(false); }, title: t('acceptEarlyTitle'), closeLabel: t('cancel'), footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: pending, onClick: () => { setConfirmingAccept(false); }, children: t('cancel') }), _jsx(Button, { variant: "primary", disabled: pending, onClick: () => { void mutateTask('accept'); }, children: mutating === 'accept' ? t('acceptingTask') : t('acceptTask') })] }), children: [_jsx("p", { className: css.confirmBody, children: t('acceptEarlyBody', { count: activeClaims.length }) }), _jsx("ul", { className: css.confirmList, children: activeClaims.map(claim => (_jsxs("li", { children: [memberName(claim.owner), " \u00B7 ", claim.direction] }, claim.claimRef))) })] });
|
|
522
577
|
})(), 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 => {
|
|
523
578
|
const ownerStatus = members.find(status => status.member.memberId === claim.owner);
|
|
524
579
|
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);
|
|
525
|
-
})] }) }) })] }), _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) }), newFactsCount > 0 && _jsxs("div", { className: threadCss.newUpdates, role: "status", children: [_jsx("span", { children: t('readNewUpdates', { count: newFactsCount }) }), _jsx(Button, { size: "sm", variant: "outline", disabled: loading, onClick: () => { timeline.jumpToLatest(); void readCurrent(true); }, children: t('markRead') })] }), remainingUnreadCount > 0 && _jsxs("div", { className: css.timelineAction, role: "status", children: [_jsx("span", { children: t('remainingUnread', { count: remainingUnreadCount }) }), _jsx(Button, { size: "sm", onClick: () => { timeline.jumpToBoundary(); void readCurrent(true); }, disabled: loading, children: t('continueReading') })] })] }) }), projection !== undefined &&
|
|
580
|
+
})] }) }) })] }), _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) }), newFactsCount > 0 && _jsxs("div", { className: threadCss.newUpdates, role: "status", children: [_jsx("span", { children: t('readNewUpdates', { count: newFactsCount }) }), _jsx(Button, { size: "sm", variant: "outline", disabled: loading, onClick: () => { timeline.jumpToLatest(); void readCurrent(true); }, children: t('markRead') })] }), remainingUnreadCount > 0 && _jsxs("div", { className: css.timelineAction, role: "status", children: [_jsx("span", { children: t('remainingUnread', { count: remainingUnreadCount }) }), _jsx(Button, { size: "sm", onClick: () => { timeline.jumpToBoundary(); void readCurrent(true); }, disabled: loading, children: t('continueReading') })] })] }) }), projection !== undefined && thread !== undefined ? (task?.resolution === 'closed'
|
|
526
581
|
? _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') })] })] })
|
|
527
|
-
: _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(); }, pendingFiles: pendingFiles, onFilesChange: setPendingFiles, t: t }, draftKey)) : _jsx("div", {})] });
|
|
582
|
+
: _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", {})] });
|
|
528
583
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AASlD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,+BA6D/T"}
|