@wowyuarm/dsh-agent-team 0.1.11 → 0.1.13
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 +6 -4
- package/README.zh.md +6 -4
- package/cordis.patch.yml +7 -3
- package/package.json +16 -3
- package/packages/agent-team/README.md +4 -4
- package/packages/agent-team/README.zh.md +4 -4
- package/packages/agent-team/lib/context-projection.js +5 -1
- package/packages/agent-team/lib/index.js +162 -113
- package/packages/agent-team/lib/ledger.js +496 -169
- package/packages/agent-team/lib/member-context.js +18 -1
- package/packages/agent-team/lib/member-runtime.js +48 -9
- package/packages/agent-team/lib/mentions.js +80 -0
- package/packages/agent-team/lib/spec.js +20 -0
- package/packages/agent-team/lib/typert.host.js +241 -46
- package/packages/agent-team/lib/typert.remote-client.d.ts +7 -3
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +178 -28
- package/packages/agent-team/lib/types/context-projection.d.ts.map +1 -1
- package/packages/agent-team/lib/types/index.d.ts +33 -14
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +120 -18
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-context.d.ts +6 -0
- package/packages/agent-team/lib/types/member-context.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-runtime.d.ts +2 -1
- package/packages/agent-team/lib/types/member-runtime.d.ts.map +1 -1
- package/packages/agent-team/lib/types/mentions.d.ts +47 -0
- package/packages/agent-team/lib/types/mentions.d.ts.map +1 -0
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/entities.d.ts +3 -20
- package/packages/agent-team/lib/types/types/entities.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/operations.d.ts +37 -1
- package/packages/agent-team/lib/types/types/operations.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types/requests-results.d.ts +107 -25
- package/packages/agent-team/lib/types/types/requests-results.d.ts.map +1 -1
- package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts +87 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/index.d.ts.map +1 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts +59 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/schema.d.ts.map +1 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts +57 -0
- package/packages/agent-team/lib/types/vendor/storage-sqlite/unit.d.ts.map +1 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/index.js +139 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/schema.js +120 -0
- package/packages/agent-team/lib/vendor/storage-sqlite/unit.js +132 -0
- package/packages/agent-team/preset/team-member/agent.cordis.yml +48 -8
- package/packages/client-agent-team/README.md +1 -1
- package/packages/client-agent-team/README.zh.md +1 -1
- package/packages/client-agent-team/lib/client.js +2123 -1406
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts +12 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentImport.js +69 -0
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +31 -19
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts +18 -0
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamAvatarStack.js +22 -0
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +3 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +111 -24
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +10 -3
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +11 -17
- package/packages/client-agent-team/lib/types/client/TeamConversation.js +1 -1
- package/packages/client-agent-team/lib/types/client/TeamCountBadge.d.ts +20 -0
- package/packages/client-agent-team/lib/types/client/TeamCountBadge.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamCountBadge.js +25 -0
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts +9 -5
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPage.js +92 -23
- package/packages/client-agent-team/lib/types/client/TeamMemberRow.d.ts +43 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberRow.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberRow.js +27 -0
- package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts +2 -2
- package/packages/client-agent-team/lib/types/client/TeamMembersAction.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMembersAction.js +2 -2
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamPresenceDot.d.ts.map +1 -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 +21 -31
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +29 -11
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +11 -5
- package/packages/client-agent-team/lib/types/client/locales.d.ts +54 -16
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +54 -16
- package/packages/client-agent-team/lib/types/client/slots.d.ts +3 -2
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-changes.d.ts +15 -25
- package/packages/client-agent-team/lib/types/client/team-changes.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-changes.js +82 -80
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +45 -10
- 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 +71 -30
- package/packages/tool-agent-team/README.md +3 -3
- package/packages/tool-agent-team/README.zh.md +3 -3
- package/packages/tool-agent-team/lib/host-access.js +19 -0
- package/packages/tool-agent-team/lib/index.js +71 -49
- package/packages/tool-agent-team/lib/types/host-access.d.ts +10 -0
- package/packages/tool-agent-team/lib/types/host-access.d.ts.map +1 -1
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/progress-nudge.js +0 -328
- package/packages/agent-team/lib/types/progress-nudge.d.ts +0 -136
- package/packages/agent-team/lib/types/progress-nudge.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts +0 -3
- package/packages/client-agent-team/lib/types/client/TeamSettings.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamSettings.js +0 -5
- package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts +0 -41
- package/packages/client-agent-team/lib/types/client/scope-coverage.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/scope-coverage.js +0 -64
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Button, IconArchiveOutline20, IconEditOutline16, IconPlusOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
5
|
+
import { TeamMemberRow } from "./TeamMemberRow.js";
|
|
5
6
|
import { MultiMenuField } from "./multi-menu-field.js";
|
|
6
7
|
import { SortableRow, useSidebarRowDrag } from "./sidebar-drag.js";
|
|
7
8
|
import { moveSidebarItem, useSidebarOrder } from "./sidebar-order.js";
|
|
@@ -152,7 +153,7 @@ export function TeamChannelsPanel(props) {
|
|
|
152
153
|
return next;
|
|
153
154
|
});
|
|
154
155
|
setPendingCreate(undefined);
|
|
155
|
-
}, triggerEmptyLabel: t('membersPickerEmpty'), formatCount: count => t('membersPickerCount', { count }) }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('channels'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'channels', open); }, actions: (_jsx(Tooltip, { label: t('addChannel'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addChannel'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && view === undefined && _jsx("p", { className: css.emptyState, children: t('loadingChannels') }), !loading &&
|
|
156
|
+
}, triggerEmptyLabel: t('membersPickerEmpty'), formatCount: count => t('membersPickerCount', { count }) }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('channels'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'channels', open); }, actions: (_jsx(Tooltip, { label: t('addChannel'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addChannel'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && view === undefined && _jsx("p", { className: css.emptyState, children: t('loadingChannels') }), !loading && error === undefined && view !== undefined && view.channels.length === 0 && _jsx("p", { className: css.emptyState, children: t('emptyChannels') }), _jsx("div", { className: css.channelList, children: orderedChannels.map(channel => {
|
|
156
157
|
const joined = membership.get(channel.channelRef) ?? new Set();
|
|
157
158
|
return (_jsx(SortableRow, { drag: drag, orderKey: channel.channelRef, children: _jsx(ChannelRow, { channel: channel, members: members, joinedIds: joined, selected: selectedChannelRef === channel.channelRef, updateChannel: updateChannel, archiveChannel: props.archiveChannel, joinChannel: props.joinChannel, removeChannelMember: props.removeChannelMember, onCommitted: () => { void refresh(); }, selectChannel: selectChannel, t: t }) }, channel.channelRef));
|
|
158
159
|
}) })] }), !formOpen && error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] }));
|
|
@@ -226,8 +227,14 @@ function ChannelEditorDialog({ channel, members, joinedIds, updateChannel, joinC
|
|
|
226
227
|
return (_jsx(Modal, { open: true, onClose: onClose, title: t('editChannel'), description: `# ${channel.name}`, closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-channel-edit-form", variant: "primary", disabled: saving || !dirty || name.trim() === '', children: saving ? t('editSaving') : t('editSave') })] }), children: _jsxs("form", { id: "team-channel-edit-form", className: createCss.form, onSubmit: event => { void submit(event); }, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('channelName') }), _jsx(Input, { className: createCss.input, value: name, onChange: event => { setName(event.target.value); pendingRequest.current = undefined; }, disabled: saving, autoFocus: true })] }), _jsxs("label", { className: createCss.field, children: [_jsxs("span", { children: [t('channelDescription'), t('optionalSuffix')] }), _jsx(Input, { className: createCss.input, value: description, placeholder: t('agentDescriptionPlaceholder'), onChange: event => { setDescription(event.target.value); pendingRequest.current = undefined; }, disabled: saving })] }), _jsxs("fieldset", { className: createCss.memberPicker, disabled: saving, children: [_jsx("legend", { children: t('channelMembersSection') }), members.map(status => {
|
|
227
228
|
const joined = joinedIds.has(status.member.memberId);
|
|
228
229
|
const rowPending = membership.pending.has(status.member.memberId);
|
|
229
|
-
|
|
230
|
+
// Same membership law as the Channel page roster: joining needs an
|
|
231
|
+
// active Member, leaving only needs the fact.
|
|
232
|
+
const disabled = rowPending || (!joined && status.availability !== 'active');
|
|
230
233
|
const rowError = membership.errors.get(status.member.memberId);
|
|
231
|
-
return
|
|
234
|
+
return _jsx(TeamMemberRow, { status: status, action: {
|
|
235
|
+
label: rowPending ? t('membershipUpdating') : joined ? t('removeFromChannel') : t('addToChannel'),
|
|
236
|
+
disabled,
|
|
237
|
+
onSelect: () => { void membership.change({ workspaceId: channel.workspaceId, channelRef: channel.channelRef, memberId: status.member.memberId, joined }); },
|
|
238
|
+
}, ...(rowError === undefined ? {} : { error: rowError }), t: t }, status.member.memberId);
|
|
232
239
|
})] }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }));
|
|
233
240
|
}
|
|
@@ -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;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAqD/D,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,EAAE,EAAE;IAClM,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAA;IACxD,oGAAoG;IACpG,QAAQ,CAAC,iBAAiB,CAAC,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAA;IAC/B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,gGAAgG;IAChG,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,IAAI,EAAE,CAAA;IACvC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI,CAAA;IACzD,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;IACnD,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BAiUA"}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
3
3
|
import { IconChecklistOutline14, IconPaperclipOutline16, IconSendOutline16, Tooltip, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
5
|
+
import { allMentionMembers, containsAllMention, containsMention, mentionedMemberIds } from "./team-formatters.js";
|
|
5
6
|
import css from './composer.module.css';
|
|
6
7
|
import { formatByteSize } from "./attachment-preview.js";
|
|
7
8
|
function findMention(draft, caret) {
|
|
@@ -18,12 +19,6 @@ function findMention(draft, caret) {
|
|
|
18
19
|
return undefined;
|
|
19
20
|
return { start: at, end: caret, query: beforeCaret.slice(at + 1) };
|
|
20
21
|
}
|
|
21
|
-
function escapeRegExp(value) {
|
|
22
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
23
|
-
}
|
|
24
|
-
function containsMention(draft, handle) {
|
|
25
|
-
return new RegExp(`(?:^|[^\\p{L}\\p{N}_])@${escapeRegExp(handle)}(?=$|[^\\p{L}\\p{N}_])`, 'u').test(draft);
|
|
26
|
-
}
|
|
27
22
|
function mentionCandidates(members, query) {
|
|
28
23
|
const normalized = query.toLocaleLowerCase();
|
|
29
24
|
return members.filter(status => status.presence !== 'unavailable'
|
|
@@ -37,13 +32,6 @@ function rankMentionCandidates(candidates, followers) {
|
|
|
37
32
|
return candidates;
|
|
38
33
|
return [...candidates].sort((left, right) => Number(followers.has(right.member.memberId)) - Number(followers.has(left.member.memberId)));
|
|
39
34
|
}
|
|
40
|
-
/** Every member a manual handle pick could reach: the @all expansion snapshot. */
|
|
41
|
-
function allMentionMembers(members) {
|
|
42
|
-
return members.filter(status => status.presence !== 'unavailable' && status.member.state !== 'inactive' && status.member.state !== 'archived');
|
|
43
|
-
}
|
|
44
|
-
function containsAllMention(draft) {
|
|
45
|
-
return /(?:^|[^\p{L}\p{N}_])@all(?=$|[^\p{L}\p{N}_])/u.test(draft);
|
|
46
|
-
}
|
|
47
35
|
/** One object URL per draft file; revoked when the draft is removed. */
|
|
48
36
|
const draftPreviewUrls = new WeakMap();
|
|
49
37
|
function draftPreviewUrl(file) {
|
|
@@ -121,15 +109,17 @@ export function TeamComposer({ members, followerMemberIds, drafts, draftKey, pen
|
|
|
121
109
|
return;
|
|
122
110
|
inputRef.current?.focus({ preventScroll: true });
|
|
123
111
|
}, [confirmation, pending]);
|
|
112
|
+
// Handle lookup for both directions of the same judgement: whether a picked
|
|
113
|
+
// recipient is still spelled in the text, and which Members the text names.
|
|
114
|
+
const memberHandles = new Map(members.map(status => [status.member.memberId, status.member.handle]));
|
|
124
115
|
const pruneRecipients = (nextDraft) => {
|
|
125
116
|
// An @all marker stands for its expansion snapshot: the member handles it
|
|
126
117
|
// stands for are not in the text, so text-based pruning must stand down.
|
|
127
118
|
if (containsAllMention(nextDraft))
|
|
128
119
|
return;
|
|
129
|
-
const knownMembers = new Map(members.map(status => [status.member.memberId, status.member]));
|
|
130
120
|
const next = new Set([...recipients].filter(memberId => {
|
|
131
|
-
const
|
|
132
|
-
return
|
|
121
|
+
const handle = memberHandles.get(memberId);
|
|
122
|
+
return handle !== undefined && containsMention(nextDraft, handle);
|
|
133
123
|
}));
|
|
134
124
|
if (next.size !== recipients.size)
|
|
135
125
|
drafts.writeRecipients(draftKey, next);
|
|
@@ -143,6 +133,10 @@ export function TeamComposer({ members, followerMemberIds, drafts, draftKey, pen
|
|
|
143
133
|
return;
|
|
144
134
|
pruneRecipients(draft);
|
|
145
135
|
}, [draft, recipients, members]);
|
|
136
|
+
// The notify row reports what the Host will resolve from this draft, not just
|
|
137
|
+
// what the mention menu picked: an authored `@Handle` delivers exactly like a
|
|
138
|
+
// pick, and `@all` stands for the menu's expansion.
|
|
139
|
+
const notifiedIds = [...new Set([...recipients, ...mentionedMemberIds(draft, members)])].sort();
|
|
146
140
|
const updateMention = (nextDraft, caret) => {
|
|
147
141
|
const match = findMention(nextDraft, caret);
|
|
148
142
|
setMention(match);
|
|
@@ -252,7 +246,7 @@ export function TeamComposer({ members, followerMemberIds, drafts, draftKey, pen
|
|
|
252
246
|
return option.kind === 'all'
|
|
253
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")
|
|
254
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);
|
|
255
|
-
}) }), _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); } })] }),
|
|
249
|
+
}) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeOption !== undefined ? activeOption.kind === 'all' ? `${listId}-all` : `${listId}-${activeOption.status.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: placeholder ?? t('messagePlaceholder'), rows: 1, onChange: onChange, onPaste: onPaste, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), notifiedIds.length > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: notifiedIds.map(memberId => `@${memberHandles.get(memberId) ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
|
|
256
250
|
const previewUrl = draftPreviewUrl(file);
|
|
257
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: () => {
|
|
258
252
|
const url = draftPreviewUrls.get(file);
|
|
@@ -18,7 +18,7 @@ export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAtta
|
|
|
18
18
|
return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, threadObservations: threadObservations, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
|
|
19
19
|
}
|
|
20
20
|
if (current !== undefined && navigationState.channelRef !== undefined) {
|
|
21
|
-
return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, t: t }, navigationState.channelRef);
|
|
21
|
+
return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, loadInbox: loadInbox, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, t: t }, navigationState.channelRef);
|
|
22
22
|
}
|
|
23
23
|
const welcome = current === undefined
|
|
24
24
|
? { eyebrow: t('teamMode'), title: t('team'), body: t('empty') }
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one count capsule. The Human Inbox entry, the Channel feed's Thread
|
|
3
|
+
* entry, and the Inbox queue row all answer the same question — how much is
|
|
4
|
+
* waiting here — so they wear the same box rather than one hand-written copy
|
|
5
|
+
* of the same declarations per surface. Three copies is how the feed's digit
|
|
6
|
+
* ended up on a different line box from the other two.
|
|
7
|
+
*
|
|
8
|
+
* Zero is the absence of a count rather than a capsule reading zero, which is
|
|
9
|
+
* the rule every caller wants and therefore the component's own.
|
|
10
|
+
*/
|
|
11
|
+
export declare function TeamCountBadge({ count, tone, label, className }: {
|
|
12
|
+
readonly count: number;
|
|
13
|
+
/** `hairline` is unread that merely arrived; the solid fill is unread that names this reader. */
|
|
14
|
+
readonly tone?: 'solid' | 'hairline' | undefined;
|
|
15
|
+
/** The accessible name while the count is the only thing saying it; omit it when the surrounding control already carries the number. */
|
|
16
|
+
readonly label?: string | undefined;
|
|
17
|
+
/** Placement belongs to the surface hanging the capsule: the narrow rail pins it to the icon's corner. */
|
|
18
|
+
readonly className?: string | undefined;
|
|
19
|
+
}): import("react").JSX.Element | null;
|
|
20
|
+
//# sourceMappingURL=TeamCountBadge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeamCountBadge.d.ts","sourceRoot":"","sources":["../../../src/client/TeamCountBadge.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,IAAc,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,iGAAiG;IACjG,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,CAAA;IAChD,wIAAwI;IACxI,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,0GAA0G;IAC1G,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACxC,sCASA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import css from './countBadge.module.css';
|
|
3
|
+
/** Every count the Human reads is capped at this, so one wide number never widens the capsule. */
|
|
4
|
+
const COUNT_CAP = 99;
|
|
5
|
+
/**
|
|
6
|
+
* The one count capsule. The Human Inbox entry, the Channel feed's Thread
|
|
7
|
+
* entry, and the Inbox queue row all answer the same question — how much is
|
|
8
|
+
* waiting here — so they wear the same box rather than one hand-written copy
|
|
9
|
+
* of the same declarations per surface. Three copies is how the feed's digit
|
|
10
|
+
* ended up on a different line box from the other two.
|
|
11
|
+
*
|
|
12
|
+
* Zero is the absence of a count rather than a capsule reading zero, which is
|
|
13
|
+
* the rule every caller wants and therefore the component's own.
|
|
14
|
+
*/
|
|
15
|
+
export function TeamCountBadge({ count, tone = 'solid', label, className }) {
|
|
16
|
+
if (count <= 0)
|
|
17
|
+
return null;
|
|
18
|
+
const text = count > COUNT_CAP ? `${COUNT_CAP}+` : String(count);
|
|
19
|
+
const classes = `${css.badge}${tone === 'hairline' ? ` ${css.hairline}` : ''}${className === undefined ? '' : ` ${className}`}`;
|
|
20
|
+
// Tests and the browser journey read the capsule by this attribute rather
|
|
21
|
+
// than by a CSS-module hash, which changes whenever a rule is renamed.
|
|
22
|
+
return label === undefined
|
|
23
|
+
? _jsx("span", { className: classes, "data-team-count-badge": tone, "aria-hidden": "true", children: text })
|
|
24
|
+
: _jsx("span", { className: classes, "data-team-count-badge": tone, role: "img", "aria-label": label, title: label, children: text });
|
|
25
|
+
}
|
|
@@ -9,12 +9,16 @@ interface TeamInboxPageProps {
|
|
|
9
9
|
readonly t: TeamConversationProps['t'];
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* The Human
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* The Human Inbox: one Inbox call per visible Workspace, rendering the Host's
|
|
13
|
+
* two slices — the unread queue (「需要我」, mentions counted inside it rather
|
|
14
|
+
* than alone) and the 「最近活跃」 tail of Threads the reader took part in.
|
|
15
|
+
* Both merge across every Workspace into one list in the Host's own order,
|
|
16
|
+
* mentions first and then newest, rather than by Workspace.
|
|
15
17
|
* Opening the page never acknowledges anything — only a durable Thread read
|
|
16
|
-
* consumes a mention marker, so rows and the badge
|
|
17
|
-
* opened through the existing auto-ack path.
|
|
18
|
+
* advances the watermark and consumes a mention marker, so rows and the badge
|
|
19
|
+
* drop after the Thread is opened through the existing auto-ack path. A Thread
|
|
20
|
+
* holding unread is only ever in the queue: the Host already excludes it from
|
|
21
|
+
* the tail, and this page never re-derives that judgement.
|
|
18
22
|
*/
|
|
19
23
|
export declare function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, selectWorkspace, selectThread, t }: TeamInboxPageProps): import("react").JSX.Element;
|
|
20
24
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamInboxPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamInboxPage.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamInboxPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamInboxPage.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAavD,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC,WAAW,CAAC,CAAA;IACtD,mGAAmG;IACnG,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA0BD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,CAAC,EAAE,EAAE,kBAAkB,+BA0GjI"}
|
|
@@ -1,33 +1,49 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
-
import { formatAbsoluteTime, formatInboxTime } from "./team-formatters.js";
|
|
4
|
+
import { claimersLabel, formatAbsoluteTime, formatInboxTime } from "./team-formatters.js";
|
|
5
|
+
import { TeamAvatarStack } from "./TeamAvatarStack.js";
|
|
6
|
+
import { TeamCountBadge } from "./TeamCountBadge.js";
|
|
5
7
|
import css from './conversation.module.css';
|
|
6
8
|
import inboxCss from './inbox.module.css';
|
|
7
9
|
/**
|
|
8
|
-
*
|
|
9
|
-
* Workspace slice
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* How many 「最近活跃」 rows the merged page may show. The Host bounds each
|
|
11
|
+
* Workspace's own slice; this is the single bound across every Workspace on
|
|
12
|
+
* screen, so the section stays the same size no matter how many are open — and
|
|
13
|
+
* it stays a way back into work rather than a second queue, which is why it is
|
|
14
|
+
* shorter than the queue the reader is actually being asked to work through.
|
|
15
|
+
*/
|
|
16
|
+
const RECENT_ROWS_LIMIT = 5;
|
|
17
|
+
/**
|
|
18
|
+
* Queue order, one total order across Workspaces: the Host's own keys —
|
|
19
|
+
* mentions first, then the ledger sequence, then the Thread ref. Each Workspace
|
|
20
|
+
* slice already arrives in this order, and it is also the Host's truncation
|
|
21
|
+
* policy, so the merge re-applies it instead of inventing a second one: a row
|
|
22
|
+
* that survived the cut on mentions must not sink below a merely newer row
|
|
23
|
+
* after the merge. `newestSequence` is a global ledger position, so it stays
|
|
24
|
+
* comparable across Workspaces.
|
|
13
25
|
*/
|
|
14
26
|
function compareInboxRows(left, right) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return byTime !== 0 ? byTime : right.item.newestSequence - left.item.newestSequence;
|
|
27
|
+
return right.item.directCount - left.item.directCount
|
|
28
|
+
|| right.item.newestSequence - left.item.newestSequence
|
|
29
|
+
|| left.item.thread.threadRef.localeCompare(right.item.thread.threadRef);
|
|
19
30
|
}
|
|
20
31
|
/**
|
|
21
|
-
* The Human
|
|
22
|
-
*
|
|
23
|
-
*
|
|
32
|
+
* The Human Inbox: one Inbox call per visible Workspace, rendering the Host's
|
|
33
|
+
* two slices — the unread queue (「需要我」, mentions counted inside it rather
|
|
34
|
+
* than alone) and the 「最近活跃」 tail of Threads the reader took part in.
|
|
35
|
+
* Both merge across every Workspace into one list in the Host's own order,
|
|
36
|
+
* mentions first and then newest, rather than by Workspace.
|
|
24
37
|
* Opening the page never acknowledges anything — only a durable Thread read
|
|
25
|
-
* consumes a mention marker, so rows and the badge
|
|
26
|
-
* opened through the existing auto-ack path.
|
|
38
|
+
* advances the watermark and consumes a mention marker, so rows and the badge
|
|
39
|
+
* drop after the Thread is opened through the existing auto-ack path. A Thread
|
|
40
|
+
* holding unread is only ever in the queue: the Host already excludes it from
|
|
41
|
+
* the tail, and this page never re-derives that judgement.
|
|
27
42
|
*/
|
|
28
43
|
export function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, selectWorkspace, selectThread, t }) {
|
|
29
44
|
const workspaces = useWorkspaces(state => state.items);
|
|
30
45
|
const [rows, setRows] = useState();
|
|
46
|
+
const [recentRows, setRecentRows] = useState([]);
|
|
31
47
|
const [loading, setLoading] = useState(true);
|
|
32
48
|
const [error, setError] = useState();
|
|
33
49
|
// Only the first refresh owns the loading surface; later wakes refresh the
|
|
@@ -37,15 +53,19 @@ export function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, sele
|
|
|
37
53
|
if (!loadedRef.current)
|
|
38
54
|
setLoading(true);
|
|
39
55
|
const results = await Promise.all(workspaces.map(async (workspace) => {
|
|
40
|
-
const result = await loadInbox({ workspaceId: workspace.workspaceId,
|
|
56
|
+
const result = await loadInbox({ workspaceId: workspace.workspaceId, limit: 100 });
|
|
41
57
|
return result.ok
|
|
42
|
-
? { ok: true, workspaceId: workspace.workspaceId, workspaceTitle: workspace.title, items: result.value.items }
|
|
58
|
+
? { ok: true, workspaceId: workspace.workspaceId, workspaceTitle: workspace.title, items: result.value.items, recent: result.value.recent }
|
|
43
59
|
: { ok: false, message: result.error.message };
|
|
44
60
|
}));
|
|
45
61
|
const failure = results.find(result => !result.ok);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
62
|
+
const asRows = (items, workspaceId, workspaceTitle) => items.map(item => ({ workspaceId, workspaceTitle, item }));
|
|
63
|
+
setRows(results.flatMap(result => result.ok ? asRows(result.items, result.workspaceId, result.workspaceTitle) : []).sort(compareInboxRows));
|
|
64
|
+
// The tail is one global bound rather than one per Workspace: the reader was
|
|
65
|
+
// promised five Threads to step back into, and every Workspace's slice is
|
|
66
|
+
// already capped on its own, so the merged list is trimmed here.
|
|
67
|
+
setRecentRows(results.flatMap(result => result.ok ? asRows(result.recent, result.workspaceId, result.workspaceTitle) : [])
|
|
68
|
+
.sort(compareInboxRows).slice(0, RECENT_ROWS_LIMIT));
|
|
49
69
|
setError(failure?.ok === false ? failure.message : undefined);
|
|
50
70
|
loadedRef.current = true;
|
|
51
71
|
setLoading(false);
|
|
@@ -65,7 +85,56 @@ export function TeamInboxPage({ useWorkspaces, loadInbox, subscribeChanges, sele
|
|
|
65
85
|
// The page rides the shared conversation seat: the same header band, 880px
|
|
66
86
|
// reading column, responsive gutters, and scrollbar gutter as Channel and
|
|
67
87
|
// Thread, so switching surfaces does not shift the content column.
|
|
68
|
-
|
|
88
|
+
const totalUnread = rows?.reduce((sum, row) => sum + row.item.unreadCount, 0) ?? 0;
|
|
89
|
+
const totalMentions = rows?.reduce((sum, row) => sum + row.item.directCount, 0) ?? 0;
|
|
90
|
+
// A row names its Workspace only while the rows on screen span more than one.
|
|
91
|
+
// With a single Workspace in the list the segment is a constant printed down
|
|
92
|
+
// every row, which spends the row's most readable position on something that
|
|
93
|
+
// never varies; the moment a second Workspace reaches the list it comes back,
|
|
94
|
+
// because two rows then have to be tellable apart. This reads the rows rather
|
|
95
|
+
// than the open Workspaces: a Workspace holding nothing to show here is not a
|
|
96
|
+
// reason to keep printing the name of the one that is.
|
|
97
|
+
const shownWorkspaces = new Set();
|
|
98
|
+
for (const row of rows ?? [])
|
|
99
|
+
shownWorkspaces.add(row.workspaceTitle);
|
|
100
|
+
for (const row of recentRows)
|
|
101
|
+
shownWorkspaces.add(row.workspaceTitle);
|
|
102
|
+
const showWorkspace = shownWorkspaces.size > 1;
|
|
103
|
+
return _jsxs("main", { className: css.surface, "data-team-inbox": true, children: [_jsx("div", { className: css.surfaceHeader, children: _jsx("header", { className: css.headerRow, children: _jsxs("div", { className: css.headerCopy, children: [_jsx("h1", { children: t('inboxTitle') }), rows !== undefined && rows.length > 0 && _jsxs("p", { className: inboxCss.headerMeta, children: [_jsx("span", { children: t('inboxHeaderThreads', { count: rows.length }) }), _jsx("span", { className: inboxCss.headerUnread, children: t('inboxHeaderUnread', { count: totalUnread }) }), totalMentions > 0 && _jsx("span", { children: t('inboxHeaderMentions', { count: totalMentions }) })] })] }) }) }), _jsx("div", { className: css.timeline, children: _jsxs("div", { className: css.timelineContent, children: [loading && rows === undefined && error === undefined && _jsx("div", { className: css.emptySurface, children: _jsxs("p", { className: css.loadingState, children: [_jsx("span", { className: css.loadingMark, "aria-hidden": "true" }), t('loadingInbox')] }) }), !loading && rows === undefined && error !== undefined && _jsxs("div", { className: css.errorState, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void refresh(); }, children: t('retry') })] }), rows !== undefined && (rows.length === 0 && recentRows.length === 0
|
|
69
104
|
? _jsx("div", { className: css.emptySurface, children: _jsxs("div", { className: css.emptyState, children: [_jsx("strong", { children: t('inboxEmptyTitle') }), _jsx("span", { children: t('inboxEmptyHint') })] }) })
|
|
70
|
-
:
|
|
105
|
+
: _jsxs(_Fragment, { children: [rows.length > 0 && _jsxs("section", { className: inboxCss.section, children: [_jsxs("h2", { className: inboxCss.sectionTitle, children: [t('inboxSectionNeedsMe'), _jsx("span", { className: inboxCss.sectionCount, children: rows.length })] }), _jsx("div", { className: inboxCss.list, children: rows.map(row => _jsx(InboxQueueRow, { row: row, t: t, showWorkspace: showWorkspace, onOpen: () => { open(row); } }, `${row.workspaceId} ${row.item.thread.threadRef}`)) })] }), recentRows.length > 0 && _jsxs("section", { className: inboxCss.section, children: [_jsxs("h2", { className: inboxCss.sectionTitle, children: [t('inboxSectionRecent'), _jsx("span", { className: inboxCss.sectionCount, children: recentRows.length })] }), _jsx("div", { className: inboxCss.list, children: recentRows.map(row => _jsx(InboxQueueRow, { row: row, t: t, showWorkspace: showWorkspace, onOpen: () => { open(row); } }, `${row.workspaceId} ${row.item.thread.threadRef}`)) })] })] })), rows !== undefined && error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] }) })] });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* One queue row, shaped like the shipped two-line result row: who is on this
|
|
109
|
+
* Thread leads in the gutter, the identity line answers which Thread this is,
|
|
110
|
+
* how much is waiting, and when it last moved, and the gist sits under it on the
|
|
111
|
+
* same column as evidence for that identity rather than as the row's subject.
|
|
112
|
+
*
|
|
113
|
+
* The gutter carries the one thing every row has — who is on the work, or who
|
|
114
|
+
* moved a Thread nobody has claimed — so a Thread that merely arrived and one
|
|
115
|
+
* that named the reader open on the same edge instead of the quieter one opening
|
|
116
|
+
* on a slot reserved for a count it does not hold. It is the grammar the Channel
|
|
117
|
+
* feed's Thread entry row already speaks, where the people on the work lead the
|
|
118
|
+
* row.
|
|
119
|
+
*
|
|
120
|
+
* Before the queue admitted every unread Thread, each row was a mention and the
|
|
121
|
+
* rows were interchangeable; now that named and ambient unread share one list,
|
|
122
|
+
* the row has to carry that difference itself. The count closes the identity
|
|
123
|
+
* line and only its ink changes — the shared capsule fill for a row that names
|
|
124
|
+
* the reader, a hairline for one that merely moved. The two numbers behind that
|
|
125
|
+
* ink (unread, mentions) reach assistive tech through the capsule's own name,
|
|
126
|
+
* because a second visible count beside the first would cost the row the one
|
|
127
|
+
* thing it needs to stay scannable.
|
|
128
|
+
*/
|
|
129
|
+
function InboxQueueRow({ row, t, showWorkspace, onOpen }) {
|
|
130
|
+
const { item } = row;
|
|
131
|
+
const actor = item.newestActor;
|
|
132
|
+
const owners = item.claimOwners;
|
|
133
|
+
const named = item.directCount > 0;
|
|
134
|
+
const countLabel = named
|
|
135
|
+
? t('inboxRowUnreadMentions', { count: item.unreadCount, mentions: item.directCount })
|
|
136
|
+
: t('inboxRowUnread', { count: item.unreadCount });
|
|
137
|
+
return _jsxs("button", { type: "button", className: inboxCss.row, "data-named": named || undefined, onClick: onOpen, children: [_jsx("span", { className: inboxCss.rowActor, children: owners.length > 0
|
|
138
|
+
? _jsx(TeamAvatarStack, { owners: owners, label: claimersLabel(owners, t) })
|
|
139
|
+
: _jsx(TeamAvatarStack, { owners: [actor], label: t('inboxRowActor', { name: `@${actor.name}` }) }) }), _jsxs("span", { className: inboxCss.rowLine, children: [_jsxs("span", { className: inboxCss.rowCrumb, children: [showWorkspace && _jsx("span", { className: inboxCss.rowWorkspace, children: row.workspaceTitle }), showWorkspace && ' / ', _jsxs("span", { className: inboxCss.rowChannel, children: ["#", item.channelName] }), ' ', item.taskNumber !== undefined && _jsx("span", { className: inboxCss.rowTask, children: t('taskLabel', { number: item.taskNumber }) })] }), _jsx(TeamCountBadge, { count: item.unreadCount, tone: named ? 'solid' : 'hairline', label: countLabel }), _jsx("time", { className: inboxCss.rowTime, dateTime: item.newestOccurredAt, title: formatAbsoluteTime(item.newestOccurredAt), children: formatInboxTime(item.newestOccurredAt, t) })] }), _jsx("span", { className: inboxCss.rowPreview, children: item.previewText })] });
|
|
71
140
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { AgentTeamClientMemberStatus } from '@wowyuarm/dsh-agent-team/types';
|
|
2
|
+
import type { TeamSidebarProps } from './slots.ts';
|
|
3
|
+
/** One membership action: what it says and which Member it acts on. */
|
|
4
|
+
export interface TeamMemberAction {
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly onSelect: () => void;
|
|
7
|
+
readonly disabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The one place a Member's identity is drawn: the presence-bearing avatar plus
|
|
11
|
+
* the handle over its description. Read-only rosters, the rosters with a
|
|
12
|
+
* membership action, and the sidebar Agent list all render this, so identity,
|
|
13
|
+
* tone and truncation cannot drift between surfaces that show the same person.
|
|
14
|
+
*
|
|
15
|
+
* A fragment, not a row: each surface owns its own row element, hit target and
|
|
16
|
+
* grid, and places this identity in that grid's first two tracks. The handle
|
|
17
|
+
* spelling is the surface's call — rosters address Members the way the
|
|
18
|
+
* composer does (`@handle`), the sidebar names them as the directory does.
|
|
19
|
+
*/
|
|
20
|
+
export declare function TeamMemberIdentity({ status, name, className, t }: {
|
|
21
|
+
readonly status: AgentTeamClientMemberStatus;
|
|
22
|
+
/** Handle spelling for this surface; defaults to the mention form. */
|
|
23
|
+
readonly name?: string | undefined;
|
|
24
|
+
/** Surface-local copy hook (sidebar line heights, dialog density). */
|
|
25
|
+
readonly className?: string | undefined;
|
|
26
|
+
readonly t: TeamSidebarProps['t'];
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
/**
|
|
29
|
+
* The one roster row: identity, an optional membership action, and the row's
|
|
30
|
+
* own failure line. The action is the row's only chrome, so the eye lands on
|
|
31
|
+
* the handle first and on the action second. A read-only roster omits it and
|
|
32
|
+
* the trailing track collapses, handing its width back to the description.
|
|
33
|
+
*/
|
|
34
|
+
export declare function TeamMemberRow({ status, action, error, className, t }: {
|
|
35
|
+
readonly status: AgentTeamClientMemberStatus;
|
|
36
|
+
readonly action?: TeamMemberAction | undefined;
|
|
37
|
+
/** Transport failure for this row's last mutation, announced in place. */
|
|
38
|
+
readonly error?: string | undefined;
|
|
39
|
+
/** Surface-local hook (dialog, sidebar, footer) for its own row rules. */
|
|
40
|
+
readonly className?: string | undefined;
|
|
41
|
+
readonly t: TeamSidebarProps['t'];
|
|
42
|
+
}): import("react").JSX.Element;
|
|
43
|
+
//# sourceMappingURL=TeamMemberRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeamMemberRow.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberRow.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIlD,uEAAuE;AACvE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE;IACjE,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,sEAAsE;IACtE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,sEAAsE;IACtE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BAQA;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE;IACrE,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAA;IAC9C,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BAMA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import { TeamMemberAvatar } from "./TeamMemberAvatar.js";
|
|
4
|
+
import css from './member-row.module.css';
|
|
5
|
+
/**
|
|
6
|
+
* The one place a Member's identity is drawn: the presence-bearing avatar plus
|
|
7
|
+
* the handle over its description. Read-only rosters, the rosters with a
|
|
8
|
+
* membership action, and the sidebar Agent list all render this, so identity,
|
|
9
|
+
* tone and truncation cannot drift between surfaces that show the same person.
|
|
10
|
+
*
|
|
11
|
+
* A fragment, not a row: each surface owns its own row element, hit target and
|
|
12
|
+
* grid, and places this identity in that grid's first two tracks. The handle
|
|
13
|
+
* spelling is the surface's call — rosters address Members the way the
|
|
14
|
+
* composer does (`@handle`), the sidebar names them as the directory does.
|
|
15
|
+
*/
|
|
16
|
+
export function TeamMemberIdentity({ status, name, className, t }) {
|
|
17
|
+
return _jsxs(_Fragment, { children: [_jsx(TeamMemberAvatar, { status: status, t: t }), _jsxs("span", { className: className === undefined ? css.copy : `${css.copy} ${className}`, children: [_jsx("strong", { children: name ?? `@${status.member.handle.replace(/^@/, '')}` }), _jsx("small", { children: status.member.description })] })] });
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The one roster row: identity, an optional membership action, and the row's
|
|
21
|
+
* own failure line. The action is the row's only chrome, so the eye lands on
|
|
22
|
+
* the handle first and on the action second. A read-only roster omits it and
|
|
23
|
+
* the trailing track collapses, handing its width back to the description.
|
|
24
|
+
*/
|
|
25
|
+
export function TeamMemberRow({ status, action, error, className, t }) {
|
|
26
|
+
return _jsxs("div", { className: className === undefined ? css.row : `${css.row} ${className}`, "data-team-member-row": true, children: [_jsx(TeamMemberIdentity, { status: status, t: t }), action !== undefined && _jsx(Button, { size: "sm", variant: "outline", className: css.action, disabled: action.disabled === true, onClick: action.onSelect, children: action.label }), error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] });
|
|
27
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
type TeamMembersActionProps = Pick<
|
|
1
|
+
import type { TeamSettingsProps } from './slots.ts';
|
|
2
|
+
type TeamMembersActionProps = Pick<TeamSettingsProps, 'wide' | 'loadMemberGroups' | 't'>;
|
|
3
3
|
export declare function TeamMembersAction({ wide, loadMemberGroups, t }: TeamMembersActionProps): import("react").JSX.Element;
|
|
4
4
|
export {};
|
|
5
5
|
//# sourceMappingURL=TeamMembersAction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamMembersAction.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMembersAction.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"TeamMembersAction.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMembersAction.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAKnD,KAAK,sBAAsB,GAAG,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,kBAAkB,GAAG,GAAG,CAAC,CAAA;AAExF,wBAAgB,iBAAiB,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,sBAAsB,+BAkDtF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useRef, useState } from 'react';
|
|
3
3
|
import { IconUserOutline16, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
-
import {
|
|
4
|
+
import { TeamMemberRow } from "./TeamMemberRow.js";
|
|
5
5
|
import membersCss from './members.module.css';
|
|
6
6
|
import css from './team.module.css';
|
|
7
7
|
export function TeamMembersAction({ wide, loadMemberGroups, t }) {
|
|
@@ -28,5 +28,5 @@ export function TeamMembersAction({ wide, loadMemberGroups, t }) {
|
|
|
28
28
|
setPanelOpen(false);
|
|
29
29
|
queueMicrotask(() => { triggerRef.current?.focus(); });
|
|
30
30
|
};
|
|
31
|
-
return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { label: t('members'), delayMs: 500, disabled: wide, children: _jsxs("button", { ref: triggerRef, type: "button", className: wide ? css.settingsAction : `${css.settingsAction} ${css.rail}`, "aria-label": t('members'), "aria-haspopup": "dialog", onClick: openMembers, children: [_jsx(IconUserOutline16, { size: wide ? 16 : 18 }), wide && _jsx("span", { children: t('members') })] }) }), _jsx(Modal, { open: panelOpen, onClose: closeMembers, title: t('members'), closeLabel: t('close'), contentClassName: membersCss.body, children: _jsxs("div", { ref: contentRef, className: membersCss.content, tabIndex: -1, children: [loading && _jsx("p", { className: membersCss.state, role: "status", children: t('loadingAgents') }), !loading && groups.length === 0 && error === undefined && _jsx("p", { className: membersCss.state, children: t('emptyAgents') }), !loading && groups.map(group => (_jsxs("section", { className: membersCss.group, "aria-labelledby": `team-members-${group.workspaceId}`, children: [_jsx("h3", { id: `team-members-${group.workspaceId}`, children: group.workspaceTitle }), group.members.map(status =>
|
|
31
|
+
return (_jsxs(_Fragment, { children: [_jsx(Tooltip, { label: t('members'), delayMs: 500, disabled: wide, children: _jsxs("button", { ref: triggerRef, type: "button", className: wide ? css.settingsAction : `${css.settingsAction} ${css.rail}`, "aria-label": t('members'), "aria-haspopup": "dialog", onClick: openMembers, children: [_jsx(IconUserOutline16, { size: wide ? 16 : 18 }), wide && _jsx("span", { children: t('members') })] }) }), _jsx(Modal, { open: panelOpen, onClose: closeMembers, title: t('members'), closeLabel: t('close'), contentClassName: membersCss.body, children: _jsxs("div", { ref: contentRef, className: membersCss.content, tabIndex: -1, children: [loading && _jsx("p", { className: membersCss.state, role: "status", children: t('loadingAgents') }), !loading && groups.length === 0 && error === undefined && _jsx("p", { className: membersCss.state, children: t('emptyAgents') }), !loading && groups.map(group => (_jsxs("section", { className: membersCss.group, "aria-labelledby": `team-members-${group.workspaceId}`, children: [_jsx("h3", { id: `team-members-${group.workspaceId}`, children: group.workspaceTitle }), group.members.map(status => _jsx(TeamMemberRow, { status: status, className: membersCss.member, t: t }, status.member.memberId))] }, group.workspaceId))), error !== undefined && _jsx("p", { className: membersCss.error, role: "alert", children: error })] }) })] }));
|
|
32
32
|
}
|
|
@@ -3,7 +3,7 @@ import type { TeamSidebarProps } from './slots.ts';
|
|
|
3
3
|
import type { TeamStateDotState } from './TeamStateDot.tsx';
|
|
4
4
|
export declare function presenceLabel(status: AgentTeamClientMemberStatus, t: TeamSidebarProps['t']): string;
|
|
5
5
|
/** One line of human-readable diagnostic text: the reason, plus the refused artifact path when one was reported. */
|
|
6
|
-
export declare function diagnosticText(status: AgentTeamClientMemberStatus): string;
|
|
6
|
+
export declare function diagnosticText(status: Pick<AgentTeamClientMemberStatus, 'diagnostic'>): string;
|
|
7
7
|
/**
|
|
8
8
|
* Whether the restart action can help an unavailable Member: it heals
|
|
9
9
|
* transient and repairable failures, but not a transient rollover window
|
|
@@ -1 +1 @@
|
|
|
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,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,MAAM,EAAE,2BAA2B,GAAG,MAAM,
|
|
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,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,2BAA2B,EAAE,YAAY,CAAC,GAAG,MAAM,CAI9F;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,2BAA2B,GAAG,OAAO,CAK3E;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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,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;
|
|
1
|
+
{"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,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,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;AA+CD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BA2uBxD"}
|