@wowyuarm/dsh-agent-team 0.1.0 → 0.1.2
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 +14 -2
- package/README.zh.md +10 -2
- package/package.json +32 -24
- package/packages/agent-team/lib/attachments.js +157 -0
- package/packages/agent-team/lib/index.js +280 -22
- package/packages/agent-team/lib/ledger.js +98 -22
- package/packages/agent-team/lib/recovery.js +87 -0
- package/packages/agent-team/lib/spec.js +27 -5
- package/packages/agent-team/lib/typert.host.js +313 -26
- package/packages/agent-team/lib/typert.remote-client.d.ts +9 -1
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +234 -17
- package/packages/agent-team/lib/types/attachments.d.ts +69 -0
- package/packages/agent-team/lib/types/attachments.d.ts.map +1 -0
- package/packages/agent-team/lib/types/index.d.ts +68 -6
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +17 -1
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/recovery.d.ts +42 -0
- package/packages/agent-team/lib/types/recovery.d.ts.map +1 -0
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types.d.ts +94 -3
- package/packages/agent-team/lib/types/types.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +2 -2
- package/packages/client-agent-team/README.md +4 -3
- package/packages/client-agent-team/README.zh.md +4 -3
- package/packages/client-agent-team/lib/client.js +1791 -322
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +5 -2
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +108 -59
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts +7 -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 +80 -19
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +38 -19
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +4 -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 +61 -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 +12 -2
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +152 -14
- package/packages/client-agent-team/lib/types/client/TeamRunDivider.d.ts +10 -0
- package/packages/client-agent-team/lib/types/client/TeamRunDivider.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamRunDivider.js +12 -0
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +7 -0
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +93 -16
- 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 +6 -5
- package/packages/client-agent-team/lib/types/client/attachment-preview.d.ts +22 -0
- package/packages/client-agent-team/lib/types/client/attachment-preview.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/attachment-preview.js +35 -0
- package/packages/client-agent-team/lib/types/client/drafts.d.ts +41 -0
- package/packages/client-agent-team/lib/types/client/drafts.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/drafts.js +144 -0
- package/packages/client-agent-team/lib/types/client/index.d.ts +2 -0
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +50 -10
- package/packages/client-agent-team/lib/types/client/locales.d.ts +34 -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 +34 -0
- package/packages/client-agent-team/lib/types/client/multi-menu-field.d.ts +33 -0
- package/packages/client-agent-team/lib/types/client/multi-menu-field.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/multi-menu-field.js +30 -0
- package/packages/client-agent-team/lib/types/client/navigation.d.ts +16 -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 +45 -8
- package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts +35 -0
- package/packages/client-agent-team/lib/types/client/sidebar-drag.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/sidebar-drag.js +70 -0
- package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts +27 -0
- package/packages/client-agent-team/lib/types/client/sidebar-order.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/sidebar-order.js +170 -0
- package/packages/client-agent-team/lib/types/client/slots.d.ts +18 -2
- 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 +20 -0
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/task-refs.js +58 -0
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +15 -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 +30 -2
- package/packages/client-agent-team/lib/types/client/team-separators.d.ts +8 -0
- package/packages/client-agent-team/lib/types/client/team-separators.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-separators.js +14 -0
- package/packages/tool-agent-team/lib/index.js +10 -6
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import agentTeamRemote from '../../../agent-team/lib/typert.remote-client.js';
|
|
2
2
|
import { TeamNavigation } from "./navigation.js";
|
|
3
3
|
import { TeamChangeStream } from "./team-changes.js";
|
|
4
|
+
import { TeamDraftStore } from "./drafts.js";
|
|
4
5
|
import { TeamFooterAction } from "./TeamFooterAction.js";
|
|
5
6
|
import { TeamSettings } from "./TeamSettings.js";
|
|
6
7
|
import { TeamConversation } from "./TeamConversation.js";
|
|
@@ -11,16 +12,18 @@ const NS = 'team';
|
|
|
11
12
|
export const inject = [
|
|
12
13
|
'slots', 'workspaces', 'locale', 'remote', 'sessions', 'connection',
|
|
13
14
|
];
|
|
14
|
-
function registerModeShadow(ctx, navigation, changes, name, component, extraInject) {
|
|
15
|
+
function registerModeShadow(ctx, navigation, changes, drafts, name, component, extraInject) {
|
|
15
16
|
// Remote bindings shared by every Team slot; surface-specific entries extend it below.
|
|
16
17
|
const sharedRemotes = {
|
|
17
18
|
loadChannels: (request) => ctx.remote.agentTeam.view(request),
|
|
18
19
|
subscribeChanges: (scope, listener) => changes.subscribe(scope, listener),
|
|
20
|
+
drafts,
|
|
19
21
|
loadMembers: (request) => ctx.remote.agentTeam.members(request),
|
|
20
22
|
joinChannel: (request) => ctx.remote.agentTeam.joinChannel(request),
|
|
21
23
|
removeChannelMember: (request) => ctx.remote.agentTeam.removeChannelMember(request),
|
|
22
24
|
updateChannel: (request) => ctx.remote.agentTeam.updateChannel(request),
|
|
23
25
|
updateMember: (request) => ctx.remote.agentTeam.updateMember(request),
|
|
26
|
+
recoverMember: (request) => ctx.remote.agentTeam.recoverMember(request),
|
|
24
27
|
// The Host-scoped catalog needs no live Member, so suspended ones stay editable too.
|
|
25
28
|
loadModels: async () => {
|
|
26
29
|
const connection = ctx.get('connection');
|
|
@@ -28,16 +31,27 @@ function registerModeShadow(ctx, navigation, changes, name, component, extraInje
|
|
|
28
31
|
return response.result;
|
|
29
32
|
},
|
|
30
33
|
openMemberSession: (sessionId) => {
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
ctx.sessions
|
|
34
|
+
// Stay in Team mode: the conversation shadow stands down for Member
|
|
35
|
+
// Session views (see registerModeShadow), so the shipped conversation
|
|
36
|
+
// root renders the selected Member Session inside the Team shell.
|
|
37
|
+
const sessions = ctx.sessions;
|
|
38
|
+
const snapshot = navigation.getSnapshot();
|
|
39
|
+
const current = sessions.list.getSnapshot().current;
|
|
40
|
+
// The return target is captured on first entry only — switching between
|
|
41
|
+
// Member Sessions must keep pointing at the Human's original session.
|
|
42
|
+
const returnTo = snapshot.memberSessionId === undefined && current !== undefined && current !== sessionId ? current : undefined;
|
|
43
|
+
navigation.actions().enterMemberSession(sessionId, returnTo);
|
|
44
|
+
sessions.open(sessionId);
|
|
35
45
|
},
|
|
36
46
|
};
|
|
37
47
|
ctx.slots.inject(name, () => {
|
|
38
48
|
let dispose;
|
|
39
49
|
const reconcile = () => {
|
|
40
|
-
const
|
|
50
|
+
const snapshot = navigation.getSnapshot();
|
|
51
|
+
// The conversation seat yields to the shipped conversation root while a
|
|
52
|
+
// Member Session view is embedded; both sidebar seats stay shadowed so
|
|
53
|
+
// the Team chrome keeps working around the Member conversation.
|
|
54
|
+
const active = snapshot.mode === 'team' && !(name === 'conversation' && snapshot.memberSessionId !== undefined);
|
|
41
55
|
if (active && dispose === undefined) {
|
|
42
56
|
dispose = ctx.slots.register({
|
|
43
57
|
name,
|
|
@@ -52,8 +66,11 @@ function registerModeShadow(ctx, navigation, changes, name, component, extraInje
|
|
|
52
66
|
readThread: (request) => ctx.remote.agentTeam.readThread(request),
|
|
53
67
|
loadThreadHistory: (request) => ctx.remote.agentTeam.threadHistory(request),
|
|
54
68
|
sendMessage: (request) => ctx.remote.agentTeam.sendMessage(request),
|
|
69
|
+
putAttachment: (request) => ctx.remote.agentTeam.putAttachment(request),
|
|
70
|
+
getAttachment: (request) => ctx.remote.agentTeam.getAttachment(request),
|
|
55
71
|
reply: (request) => ctx.remote.agentTeam.reply(request),
|
|
56
72
|
changeTask: (request) => ctx.remote.agentTeam.changeTask(request),
|
|
73
|
+
resolveTaskRefs: (request) => ctx.remote.agentTeam.resolveTaskRefs(request),
|
|
57
74
|
} : {}),
|
|
58
75
|
...(name === 'sidebar.workspaces' ? {
|
|
59
76
|
addMember: (request) => ctx.remote.agentTeam.addMember(request),
|
|
@@ -80,9 +97,13 @@ function applyUi(ctx) {
|
|
|
80
97
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'agent-team: dictionaries');
|
|
81
98
|
const navigation = new TeamNavigation();
|
|
82
99
|
const disposeNavigation = ctx.reflect.provide('teamNavigation', navigation);
|
|
100
|
+
const drafts = new TeamDraftStore();
|
|
101
|
+
const disposeDrafts = ctx.reflect.provide('teamDrafts', drafts);
|
|
83
102
|
ctx.effect(() => () => {
|
|
84
103
|
navigation.dispose();
|
|
104
|
+
drafts.dispose();
|
|
85
105
|
void disposeNavigation();
|
|
106
|
+
void disposeDrafts();
|
|
86
107
|
}, 'agent-team: navigation service');
|
|
87
108
|
const changes = new TeamChangeStream((request, signal) => ctx.remote.agentTeam.changes(request, signal));
|
|
88
109
|
const loadMemberGroups = async () => {
|
|
@@ -100,11 +121,30 @@ function applyUi(ctx) {
|
|
|
100
121
|
id: 'agent-team',
|
|
101
122
|
order: 100,
|
|
102
123
|
locale: NS,
|
|
103
|
-
inject: () => ({
|
|
124
|
+
inject: () => ({
|
|
125
|
+
navigation,
|
|
126
|
+
...navigation.actions(),
|
|
127
|
+
// The footer is the only surface that leaves Team mode; it also closes
|
|
128
|
+
// an embedded Member Session view, restoring the session the Human came
|
|
129
|
+
// from so the ordinary shell never strands them inside a Member Session.
|
|
130
|
+
leaveTeam: () => {
|
|
131
|
+
const snapshot = navigation.getSnapshot();
|
|
132
|
+
if (snapshot.memberSessionId === undefined) {
|
|
133
|
+
navigation.actions().leaveTeam();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const returnTo = snapshot.returnToSessionId;
|
|
137
|
+
navigation.actions().exitMemberSession();
|
|
138
|
+
if (returnTo !== undefined)
|
|
139
|
+
ctx.sessions.open(returnTo);
|
|
140
|
+
navigation.actions().leaveTeam();
|
|
141
|
+
},
|
|
142
|
+
loadMemberGroups,
|
|
143
|
+
}),
|
|
104
144
|
}, TeamFooterAction));
|
|
105
|
-
registerModeShadow(ctx, navigation, changes, 'sidebar.workspaces', TeamWorkspaceBrowser);
|
|
106
|
-
registerModeShadow(ctx, navigation, changes, 'conversation', TeamConversation);
|
|
107
|
-
registerModeShadow(ctx, navigation, changes, 'sidebar.settings', TeamSettings, () => ({ loadMemberGroups }));
|
|
145
|
+
registerModeShadow(ctx, navigation, changes, drafts, 'sidebar.workspaces', TeamWorkspaceBrowser);
|
|
146
|
+
registerModeShadow(ctx, navigation, changes, drafts, 'conversation', TeamConversation);
|
|
147
|
+
registerModeShadow(ctx, navigation, changes, drafts, 'sidebar.settings', TeamSettings, () => ({ loadMemberGroups }));
|
|
108
148
|
}
|
|
109
149
|
export async function apply(ctx) {
|
|
110
150
|
const disposeRemote = await ctx.remote.$mount(agentTeamRemote);
|
|
@@ -36,6 +36,19 @@ export declare const zh: {
|
|
|
36
36
|
readonly manageMembers: "管理成员";
|
|
37
37
|
readonly editChannel: "编辑频道";
|
|
38
38
|
readonly editAgent: "编辑 Agent";
|
|
39
|
+
readonly resumeAgent: "恢复";
|
|
40
|
+
readonly optionalSuffix: "(可选)";
|
|
41
|
+
readonly agentDescriptionPlaceholder: "留空则暂无描述";
|
|
42
|
+
readonly channelsPickerEmpty: "选择初始频道";
|
|
43
|
+
readonly channelsPickerCount: "已选 {count} 个频道";
|
|
44
|
+
readonly membersPickerEmpty: "选择初始成员";
|
|
45
|
+
readonly membersPickerCount: "已选 {count} 个成员";
|
|
46
|
+
readonly reasoningEffort: "推理强度";
|
|
47
|
+
readonly effortFollowDefault: "跟随模型默认";
|
|
48
|
+
readonly attachFiles: "添加附件";
|
|
49
|
+
readonly removeFile: "移除 {name}";
|
|
50
|
+
readonly attachmentExpired: "文件已过期清理";
|
|
51
|
+
readonly viewImage: "查看大图 {name}";
|
|
39
52
|
readonly actionsChannel: "{name} 的操作";
|
|
40
53
|
readonly actionsAgent: "{name} 的操作";
|
|
41
54
|
readonly editSave: "保存";
|
|
@@ -101,6 +114,10 @@ export declare const zh: {
|
|
|
101
114
|
readonly activityClaimReleased: "{actor} 释放了「{direction}」";
|
|
102
115
|
readonly activityClaimsReleased: "{actor} 因成员权限变化释放了 {count} 个 Claim";
|
|
103
116
|
readonly activityAccepted: "{actor} 验收了此 Task";
|
|
117
|
+
readonly activityAcceptedWithClaims: "{actor} 验收了此 Task,并一并完成了 {count} 个未完成 Claim";
|
|
118
|
+
readonly acceptEarlyTitle: "提前验收任务";
|
|
119
|
+
readonly acceptEarlyBody: "将验收本 Task,并把以下 {count} 个未完成的 Claim 一并标记为完成:";
|
|
120
|
+
readonly acceptingTask: "验收中…";
|
|
104
121
|
readonly activityClosed: "{actor} 关闭了此 Task";
|
|
105
122
|
readonly activityReopened: "{actor} 重新打开了此 Task";
|
|
106
123
|
readonly mentionConfirmation: "提及未关注成员需要确认,再次发送以继续";
|
|
@@ -147,6 +164,19 @@ export declare const en: {
|
|
|
147
164
|
readonly manageMembers: "Manage members";
|
|
148
165
|
readonly editChannel: "Edit Channel";
|
|
149
166
|
readonly editAgent: "Edit Agent";
|
|
167
|
+
readonly resumeAgent: "Resume";
|
|
168
|
+
readonly optionalSuffix: " (optional)";
|
|
169
|
+
readonly agentDescriptionPlaceholder: "Leave empty if not needed";
|
|
170
|
+
readonly channelsPickerEmpty: "Choose initial channels";
|
|
171
|
+
readonly channelsPickerCount: "{count} selected";
|
|
172
|
+
readonly membersPickerEmpty: "Choose initial members";
|
|
173
|
+
readonly membersPickerCount: "{count} selected";
|
|
174
|
+
readonly reasoningEffort: "Reasoning effort";
|
|
175
|
+
readonly effortFollowDefault: "Follow model default";
|
|
176
|
+
readonly attachFiles: "Add attachments";
|
|
177
|
+
readonly removeFile: "Remove {name}";
|
|
178
|
+
readonly attachmentExpired: "File expired and cleaned up";
|
|
179
|
+
readonly viewImage: "View image {name}";
|
|
150
180
|
readonly actionsChannel: "{name} actions";
|
|
151
181
|
readonly actionsAgent: "{name} actions";
|
|
152
182
|
readonly editSave: "Save";
|
|
@@ -212,6 +242,10 @@ export declare const en: {
|
|
|
212
242
|
readonly activityClaimReleased: "{actor} released “{direction}”";
|
|
213
243
|
readonly activityClaimsReleased: "{actor} released {count} Claim(s) after a membership change";
|
|
214
244
|
readonly activityAccepted: "{actor} accepted this Task";
|
|
245
|
+
readonly activityAcceptedWithClaims: "{actor} accepted this Task and completed {count} unfinished Claims with it";
|
|
246
|
+
readonly acceptEarlyTitle: "Accept Task early";
|
|
247
|
+
readonly acceptEarlyBody: "This accepts the Task and marks the following {count} unfinished Claims as done:";
|
|
248
|
+
readonly acceptingTask: "Accepting…";
|
|
215
249
|
readonly activityClosed: "{actor} closed this Task";
|
|
216
250
|
readonly activityReopened: "{actor} reopened this Task";
|
|
217
251
|
readonly mentionConfirmation: "Mentioning an unfollowed member requires confirmation; send again to continue";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+HL,CAAA;AAEV,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+HL,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAA"}
|
|
@@ -36,6 +36,19 @@ export const zh = {
|
|
|
36
36
|
manageMembers: '管理成员',
|
|
37
37
|
editChannel: '编辑频道',
|
|
38
38
|
editAgent: '编辑 Agent',
|
|
39
|
+
resumeAgent: '恢复',
|
|
40
|
+
optionalSuffix: '(可选)',
|
|
41
|
+
agentDescriptionPlaceholder: '留空则暂无描述',
|
|
42
|
+
channelsPickerEmpty: '选择初始频道',
|
|
43
|
+
channelsPickerCount: '已选 {count} 个频道',
|
|
44
|
+
membersPickerEmpty: '选择初始成员',
|
|
45
|
+
membersPickerCount: '已选 {count} 个成员',
|
|
46
|
+
reasoningEffort: '推理强度',
|
|
47
|
+
effortFollowDefault: '跟随模型默认',
|
|
48
|
+
attachFiles: '添加附件',
|
|
49
|
+
removeFile: '移除 {name}',
|
|
50
|
+
attachmentExpired: '文件已过期清理',
|
|
51
|
+
viewImage: '查看大图 {name}',
|
|
39
52
|
actionsChannel: '{name} 的操作',
|
|
40
53
|
actionsAgent: '{name} 的操作',
|
|
41
54
|
editSave: '保存',
|
|
@@ -101,6 +114,10 @@ export const zh = {
|
|
|
101
114
|
activityClaimReleased: '{actor} 释放了「{direction}」',
|
|
102
115
|
activityClaimsReleased: '{actor} 因成员权限变化释放了 {count} 个 Claim',
|
|
103
116
|
activityAccepted: '{actor} 验收了此 Task',
|
|
117
|
+
activityAcceptedWithClaims: '{actor} 验收了此 Task,并一并完成了 {count} 个未完成 Claim',
|
|
118
|
+
acceptEarlyTitle: '提前验收任务',
|
|
119
|
+
acceptEarlyBody: '将验收本 Task,并把以下 {count} 个未完成的 Claim 一并标记为完成:',
|
|
120
|
+
acceptingTask: '验收中…',
|
|
104
121
|
activityClosed: '{actor} 关闭了此 Task',
|
|
105
122
|
activityReopened: '{actor} 重新打开了此 Task',
|
|
106
123
|
mentionConfirmation: '提及未关注成员需要确认,再次发送以继续',
|
|
@@ -147,6 +164,19 @@ export const en = {
|
|
|
147
164
|
manageMembers: 'Manage members',
|
|
148
165
|
editChannel: 'Edit Channel',
|
|
149
166
|
editAgent: 'Edit Agent',
|
|
167
|
+
resumeAgent: 'Resume',
|
|
168
|
+
optionalSuffix: ' (optional)',
|
|
169
|
+
agentDescriptionPlaceholder: 'Leave empty if not needed',
|
|
170
|
+
channelsPickerEmpty: 'Choose initial channels',
|
|
171
|
+
channelsPickerCount: '{count} selected',
|
|
172
|
+
membersPickerEmpty: 'Choose initial members',
|
|
173
|
+
membersPickerCount: '{count} selected',
|
|
174
|
+
reasoningEffort: 'Reasoning effort',
|
|
175
|
+
effortFollowDefault: 'Follow model default',
|
|
176
|
+
attachFiles: 'Add attachments',
|
|
177
|
+
removeFile: 'Remove {name}',
|
|
178
|
+
attachmentExpired: 'File expired and cleaned up',
|
|
179
|
+
viewImage: 'View image {name}',
|
|
150
180
|
actionsChannel: '{name} actions',
|
|
151
181
|
actionsAgent: '{name} actions',
|
|
152
182
|
editSave: 'Save',
|
|
@@ -212,6 +242,10 @@ export const en = {
|
|
|
212
242
|
activityClaimReleased: '{actor} released “{direction}”',
|
|
213
243
|
activityClaimsReleased: '{actor} released {count} Claim(s) after a membership change',
|
|
214
244
|
activityAccepted: '{actor} accepted this Task',
|
|
245
|
+
activityAcceptedWithClaims: '{actor} accepted this Task and completed {count} unfinished Claims with it',
|
|
246
|
+
acceptEarlyTitle: 'Accept Task early',
|
|
247
|
+
acceptEarlyBody: 'This accepts the Task and marks the following {count} unfinished Claims as done:',
|
|
248
|
+
acceptingTask: 'Accepting…',
|
|
215
249
|
activityClosed: '{actor} closed this Task',
|
|
216
250
|
activityReopened: '{actor} reopened this Task',
|
|
217
251
|
mentionConfirmation: 'Mentioning an unfollowed member requires confirmation; send again to continue',
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** One selectable entry; disabled rows render greyed with their reason. */
|
|
3
|
+
export interface MultiMenuOption {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly label: string;
|
|
6
|
+
readonly disabled?: boolean;
|
|
7
|
+
/** Leading marker such as a presence dot. */
|
|
8
|
+
readonly icon?: ReactNode;
|
|
9
|
+
/** Secondary reason text rendered after the label. */
|
|
10
|
+
readonly hint?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Render the labeled multi-select Menu field.
|
|
14
|
+
* @param props.label - field caption; doubles as the trigger's accessible name.
|
|
15
|
+
* @param props.options - selectable entries in display order.
|
|
16
|
+
* @param props.selected - currently checked ids.
|
|
17
|
+
* @param props.onToggle - invoked with an id when its row is clicked.
|
|
18
|
+
* @param props.disabled - disables the trigger while a mutation is in flight.
|
|
19
|
+
* @param props.emptyText - shown instead of the picker when there is nothing to pick.
|
|
20
|
+
* @param props.triggerEmptyLabel - trigger caption when nothing is selected.
|
|
21
|
+
* @param props.formatCount - builds the trigger caption for N selections.
|
|
22
|
+
*/
|
|
23
|
+
export declare function MultiMenuField<I extends string>({ label, options, selected, onToggle, disabled, emptyText, triggerEmptyLabel, formatCount }: {
|
|
24
|
+
readonly label: string;
|
|
25
|
+
readonly options: readonly MultiMenuOption[];
|
|
26
|
+
readonly selected: readonly I[];
|
|
27
|
+
readonly onToggle: (id: I) => void;
|
|
28
|
+
readonly disabled?: boolean;
|
|
29
|
+
readonly emptyText?: string;
|
|
30
|
+
readonly triggerEmptyLabel: string;
|
|
31
|
+
readonly formatCount: (count: number) => string;
|
|
32
|
+
}): import("react").JSX.Element;
|
|
33
|
+
//# sourceMappingURL=multi-menu-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multi-menu-field.d.ts","sourceRoot":"","sources":["../../../src/client/multi-menu-field.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,sDAAsD;IACtD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAgB,EAAE,SAAS,EAAE,iBAAiB,EAAE,WAAW,EAAE,EAAE;IACpJ,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAA;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAA;IAC/B,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,CAAA;IAClC,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAA;IAClC,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;CAChD,+BAsCA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
// MultiMenuField: one labeled multi-select picker shared by every creation
|
|
3
|
+
// form (Agent initial Channels, Channel initial Members). Selection toggles
|
|
4
|
+
// without closing the list so several entries can be picked in one pass; the
|
|
5
|
+
// capped card scrolls internally through long catalogs.
|
|
6
|
+
import { useState } from 'react';
|
|
7
|
+
import { IconChevronDownOutline14, Menu } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
8
|
+
import createCss from './create.module.css';
|
|
9
|
+
import css from './sidebar.module.css';
|
|
10
|
+
/**
|
|
11
|
+
* Render the labeled multi-select Menu field.
|
|
12
|
+
* @param props.label - field caption; doubles as the trigger's accessible name.
|
|
13
|
+
* @param props.options - selectable entries in display order.
|
|
14
|
+
* @param props.selected - currently checked ids.
|
|
15
|
+
* @param props.onToggle - invoked with an id when its row is clicked.
|
|
16
|
+
* @param props.disabled - disables the trigger while a mutation is in flight.
|
|
17
|
+
* @param props.emptyText - shown instead of the picker when there is nothing to pick.
|
|
18
|
+
* @param props.triggerEmptyLabel - trigger caption when nothing is selected.
|
|
19
|
+
* @param props.formatCount - builds the trigger caption for N selections.
|
|
20
|
+
*/
|
|
21
|
+
export function MultiMenuField({ label, options, selected, onToggle, disabled = false, emptyText, triggerEmptyLabel, formatCount }) {
|
|
22
|
+
const [open, setOpen] = useState(false);
|
|
23
|
+
const items = options.map(option => ({
|
|
24
|
+
id: option.id,
|
|
25
|
+
label: option.hint === undefined ? option.label : _jsxs(_Fragment, { children: [option.label, _jsx("small", { className: css.menuHint, children: ` ${option.hint}` })] }),
|
|
26
|
+
...(option.disabled === true ? { disabled: true } : {}),
|
|
27
|
+
...(option.icon === undefined ? {} : { icon: option.icon }),
|
|
28
|
+
}));
|
|
29
|
+
return (_jsxs("div", { className: createCss.field, children: [_jsx("span", { children: label }), options.length === 0 && emptyText !== undefined ? _jsx("small", { className: css.editHint, children: emptyText }) : (_jsx(Menu, { open: open, portal: true, className: createCss.menuCap, items: items, selectedIds: selected, onSelect: id => { onToggle(id); }, onClose: () => { setOpen(false); }, anchor: _jsxs("button", { type: "button", className: createCss.selectTrigger, "aria-label": label, "aria-haspopup": "listbox", "aria-expanded": open, disabled: disabled, onClick: () => { setOpen(value => !value); }, children: [_jsx("span", { className: createCss.selectValue, children: selected.length === 0 ? triggerEmptyLabel : formatCount(selected.length) }), _jsx("span", { className: `${createCss.chevron} ${open ? createCss.chevronOpen : ''}`, "aria-hidden": true, children: _jsx(IconChevronDownOutline14, {}) })] }) }))] }));
|
|
30
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { WorkspaceId } from '@deepseek-ai/dsh-client-runtime/client';
|
|
1
|
+
import type { SessionId, WorkspaceId } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
2
|
import type { AgentTeamChannelRef, AgentTeamTaskRef, AgentTeamThreadRef } from '@wowyuarm/dsh-agent-team/types';
|
|
3
3
|
export type TeamMode = 'conversation' | 'team';
|
|
4
4
|
export interface TeamNavigationSnapshot {
|
|
@@ -8,6 +8,10 @@ export interface TeamNavigationSnapshot {
|
|
|
8
8
|
taskRef?: AgentTeamTaskRef;
|
|
9
9
|
threadRef?: AgentTeamThreadRef;
|
|
10
10
|
taskNumber?: number;
|
|
11
|
+
/** Runtime-only Member Session embedded in the conversation seat; never persisted. */
|
|
12
|
+
memberSessionId?: SessionId;
|
|
13
|
+
/** Runtime-only session to restore when the Member view closes; never persisted. */
|
|
14
|
+
returnToSessionId?: SessionId;
|
|
11
15
|
}
|
|
12
16
|
declare const STORAGE_KEY = "dsh.agent-team.navigation";
|
|
13
17
|
export interface TeamNavigationActions {
|
|
@@ -19,6 +23,14 @@ export interface TeamNavigationActions {
|
|
|
19
23
|
backToWorkspace: () => void;
|
|
20
24
|
/** Leave the selected Channel for the workspace Channel list; keeps mode and Workspace. */
|
|
21
25
|
backToChannels: () => void;
|
|
26
|
+
/**
|
|
27
|
+
* Swap the conversation seat to a Member Session while Team chrome stays
|
|
28
|
+
* mounted. `returnToSessionId` is captured once — switching between Member
|
|
29
|
+
* Sessions keeps the original target.
|
|
30
|
+
*/
|
|
31
|
+
enterMemberSession: (sessionId: SessionId, returnToSessionId?: SessionId) => void;
|
|
32
|
+
/** Close the embedded Member Session view and return to the Team views. */
|
|
33
|
+
exitMemberSession: () => void;
|
|
22
34
|
}
|
|
23
35
|
/** Root-scoped Team mode state. Slot lifetimes subscribe to this source. */
|
|
24
36
|
export declare class TeamNavigation {
|
|
@@ -29,6 +41,9 @@ export declare class TeamNavigation {
|
|
|
29
41
|
actions(): TeamNavigationActions;
|
|
30
42
|
dispose(): void;
|
|
31
43
|
private setMode;
|
|
44
|
+
private setMemberSession;
|
|
45
|
+
/** Any explicit Team navigation or the footer leave closes a Member view. */
|
|
46
|
+
private clearMemberSession;
|
|
32
47
|
private setWorkspace;
|
|
33
48
|
private setChannel;
|
|
34
49
|
private clearChannel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../../src/client/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../../src/client/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AACpF,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AAE/G,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,MAAM,CAAA;AAE9C,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,QAAQ,CAAA;IACd,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,UAAU,CAAC,EAAE,mBAAmB,CAAA;IAChC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IAC1B,SAAS,CAAC,EAAE,kBAAkB,CAAA;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,sFAAsF;IACtF,eAAe,CAAC,EAAE,SAAS,CAAA;IAC3B,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED,QAAA,MAAM,WAAW,8BAA8B,CAAA;AAuC/C,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,eAAe,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,IAAI,CAAA;IACnD,aAAa,EAAE,CAAC,UAAU,EAAE,mBAAmB,KAAK,IAAI,CAAA;IACxD,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,mBAAmB,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACvI,eAAe,EAAE,MAAM,IAAI,CAAA;IAC3B,2FAA2F;IAC3F,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,SAAS,EAAE,SAAS,EAAE,iBAAiB,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;IACjF,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,IAAI,CAAA;CAC9B;AAED,4EAA4E;AAC5E,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAElD,QAAQ,CAAC,WAAW,QAAO,sBAAsB,CAAiB;IAElE,QAAQ,CAAC,SAAS,GAAI,UAAU,MAAM,IAAI,KAAG,CAAC,MAAM,IAAI,CAAC,CAGxD;IAED,OAAO,IAAI,qBAAqB;IAchC,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,gBAAgB;IAaxB,6EAA6E;IAC7E,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,MAAM;CAKf;AAED,OAAO,EAAE,WAAW,EAAE,CAAA"}
|
|
@@ -4,9 +4,16 @@ function readSnapshot() {
|
|
|
4
4
|
return { mode: 'conversation' };
|
|
5
5
|
try {
|
|
6
6
|
const parsed = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '');
|
|
7
|
+
const hasThread = typeof parsed.taskRef === 'string' && typeof parsed.threadRef === 'string';
|
|
7
8
|
return {
|
|
8
9
|
mode: parsed.mode === 'team' ? 'team' : 'conversation',
|
|
9
10
|
...(typeof parsed.workspaceId === 'string' ? { workspaceId: parsed.workspaceId } : {}),
|
|
11
|
+
...(typeof parsed.channelRef === 'string' ? { channelRef: parsed.channelRef } : {}),
|
|
12
|
+
...(hasThread ? {
|
|
13
|
+
taskRef: parsed.taskRef,
|
|
14
|
+
threadRef: parsed.threadRef,
|
|
15
|
+
...(typeof parsed.taskNumber === 'number' && Number.isInteger(parsed.taskNumber) && parsed.taskNumber > 0 ? { taskNumber: parsed.taskNumber } : {}),
|
|
16
|
+
} : {}),
|
|
10
17
|
};
|
|
11
18
|
}
|
|
12
19
|
catch {
|
|
@@ -17,8 +24,15 @@ function persistSnapshot(snapshot) {
|
|
|
17
24
|
if (typeof localStorage === 'undefined')
|
|
18
25
|
return;
|
|
19
26
|
try {
|
|
20
|
-
const { mode, workspaceId } = snapshot;
|
|
21
|
-
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
27
|
+
const { mode, workspaceId, channelRef, taskRef, threadRef, taskNumber } = snapshot;
|
|
28
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify({
|
|
29
|
+
mode,
|
|
30
|
+
...(workspaceId === undefined ? {} : { workspaceId }),
|
|
31
|
+
...(channelRef === undefined ? {} : { channelRef }),
|
|
32
|
+
...(taskRef === undefined ? {} : { taskRef }),
|
|
33
|
+
...(threadRef === undefined ? {} : { threadRef }),
|
|
34
|
+
...(taskNumber === undefined ? {} : { taskNumber }),
|
|
35
|
+
}));
|
|
22
36
|
}
|
|
23
37
|
catch {
|
|
24
38
|
// Local persistence is a convenience; private mode and quota failures do not block navigation.
|
|
@@ -35,13 +49,15 @@ export class TeamNavigation {
|
|
|
35
49
|
};
|
|
36
50
|
actions() {
|
|
37
51
|
return {
|
|
38
|
-
enterTeam: () => { this.setMode('team'); },
|
|
52
|
+
enterTeam: () => { this.clearMemberSession(); this.setMode('team'); },
|
|
39
53
|
leaveTeam: () => { this.setMode('conversation'); },
|
|
40
|
-
selectWorkspace: workspaceId => { this.setWorkspace(workspaceId); },
|
|
41
|
-
selectChannel: channelRef => { this.setChannel(channelRef); },
|
|
42
|
-
selectThread: (taskRef, threadRef, channelRef, taskNumber) => { this.setThread(taskRef, threadRef, channelRef, taskNumber); },
|
|
43
|
-
backToWorkspace: () => { this.setThread(undefined); },
|
|
44
|
-
backToChannels: () => { this.clearChannel(); },
|
|
54
|
+
selectWorkspace: workspaceId => { this.clearMemberSession(); this.setWorkspace(workspaceId); },
|
|
55
|
+
selectChannel: channelRef => { this.clearMemberSession(); this.setChannel(channelRef); },
|
|
56
|
+
selectThread: (taskRef, threadRef, channelRef, taskNumber) => { this.clearMemberSession(); this.setThread(taskRef, threadRef, channelRef, taskNumber); },
|
|
57
|
+
backToWorkspace: () => { this.clearMemberSession(); this.setThread(undefined); },
|
|
58
|
+
backToChannels: () => { this.clearMemberSession(); this.clearChannel(); },
|
|
59
|
+
enterMemberSession: (sessionId, returnToSessionId) => { this.setMemberSession(sessionId, returnToSessionId); },
|
|
60
|
+
exitMemberSession: () => { this.clearMemberSession(); },
|
|
45
61
|
};
|
|
46
62
|
}
|
|
47
63
|
dispose() {
|
|
@@ -53,6 +69,27 @@ export class TeamNavigation {
|
|
|
53
69
|
this.snapshot = { ...this.snapshot, mode };
|
|
54
70
|
this.commit();
|
|
55
71
|
}
|
|
72
|
+
setMemberSession(sessionId, returnToSessionId) {
|
|
73
|
+
// Re-selecting the active Member keeps the original return target.
|
|
74
|
+
if (this.snapshot.memberSessionId === sessionId && this.snapshot.mode === 'team')
|
|
75
|
+
return;
|
|
76
|
+
const { memberSessionId: _memberSessionId, ...base } = this.snapshot;
|
|
77
|
+
this.snapshot = {
|
|
78
|
+
...base,
|
|
79
|
+
mode: 'team',
|
|
80
|
+
memberSessionId: sessionId,
|
|
81
|
+
...(returnToSessionId === undefined ? {} : { returnToSessionId }),
|
|
82
|
+
};
|
|
83
|
+
this.commit();
|
|
84
|
+
}
|
|
85
|
+
/** Any explicit Team navigation or the footer leave closes a Member view. */
|
|
86
|
+
clearMemberSession() {
|
|
87
|
+
if (this.snapshot.memberSessionId === undefined && this.snapshot.returnToSessionId === undefined)
|
|
88
|
+
return;
|
|
89
|
+
const { memberSessionId: _memberSessionId, returnToSessionId: _returnToSessionId, ...base } = this.snapshot;
|
|
90
|
+
this.snapshot = base;
|
|
91
|
+
this.commit();
|
|
92
|
+
}
|
|
56
93
|
setWorkspace(workspaceId) {
|
|
57
94
|
if (this.snapshot.workspaceId === workspaceId)
|
|
58
95
|
return;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/** Insert side of an in-flight drop relative to the hovered row. */
|
|
3
|
+
export type SidebarDropMarker = 'before' | 'after';
|
|
4
|
+
export interface SidebarRowDragHandlers<K extends string> {
|
|
5
|
+
/** Per-row native wiring plus the marker this row currently shows. */
|
|
6
|
+
readonly rowProps: (orderKey: K) => {
|
|
7
|
+
readonly draggable: true;
|
|
8
|
+
readonly className?: string | undefined;
|
|
9
|
+
readonly onDragStart: (event: React.DragEvent<HTMLElement>) => void;
|
|
10
|
+
readonly onDragEnd: () => void;
|
|
11
|
+
readonly onDragOver: (event: React.DragEvent<HTMLElement>) => void;
|
|
12
|
+
readonly onDrop: (event: React.DragEvent<HTMLElement>) => void;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* One drag gesture per panel instance. `onCommit` receives the moved ref,
|
|
17
|
+
* the drop target and the insertion side exactly once per completed gesture;
|
|
18
|
+
* releases outside the list never reach it.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useSidebarRowDrag<K extends string>({ refs, onCommit }: {
|
|
21
|
+
/** Effective current order of the owning list, used for hit validation. */
|
|
22
|
+
readonly refs: readonly K[];
|
|
23
|
+
readonly onCommit: (movedRef: K, targetRef: K, marker: SidebarDropMarker) => void;
|
|
24
|
+
}): SidebarRowDragHandlers<K>;
|
|
25
|
+
/**
|
|
26
|
+
* Transparent wrapper that owns the drop-marker styling for one draggable
|
|
27
|
+
* sidebar row. It adds no layout of its own; the styled row stays inside so
|
|
28
|
+
* hover/focus descendant selectors keep working.
|
|
29
|
+
*/
|
|
30
|
+
export declare function SortableRow<K extends string>({ drag, orderKey, children }: {
|
|
31
|
+
readonly drag: SidebarRowDragHandlers<K>;
|
|
32
|
+
readonly orderKey: K;
|
|
33
|
+
readonly children: ReactNode;
|
|
34
|
+
}): import("react").JSX.Element;
|
|
35
|
+
//# sourceMappingURL=sidebar-drag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidebar-drag.d.ts","sourceRoot":"","sources":["../../../src/client/sidebar-drag.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,oEAAoE;AACpE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,CAAA;AAElD,MAAM,WAAW,sBAAsB,CAAC,CAAC,SAAS,MAAM;IACtD,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK;QAClC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAA;QACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;QACvC,QAAQ,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,IAAI,CAAA;QACnE,QAAQ,CAAC,SAAS,EAAE,MAAM,IAAI,CAAA;QAC9B,QAAQ,CAAC,UAAU,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,IAAI,CAAA;QAClE,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,KAAK,IAAI,CAAA;KAC/D,CAAA;CACF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IACtE,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAClF,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAwC5B;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;IAC1E,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAA;IACxC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAA;IACpB,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC7B,+BAOA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Native whole-row drag for the Team sidebar lists. Unlike the Harness
|
|
4
|
+
* workspace list there is no document-level acceptance here: hovering another
|
|
5
|
+
* row shows the before/after insertion marker and releasing on a row commits
|
|
6
|
+
* exactly once per gesture, while a release outside any row — including over
|
|
7
|
+
* the other sidebar list, whose panel never responds — just cancels without
|
|
8
|
+
* committing.
|
|
9
|
+
*/
|
|
10
|
+
import { useRef, useState } from 'react';
|
|
11
|
+
import css from './sidebar.module.css';
|
|
12
|
+
/**
|
|
13
|
+
* One drag gesture per panel instance. `onCommit` receives the moved ref,
|
|
14
|
+
* the drop target and the insertion side exactly once per completed gesture;
|
|
15
|
+
* releases outside the list never reach it.
|
|
16
|
+
*/
|
|
17
|
+
export function useSidebarRowDrag({ refs, onCommit }) {
|
|
18
|
+
const [state, setState] = useState(null);
|
|
19
|
+
const committed = useRef(false);
|
|
20
|
+
const half = (event) => {
|
|
21
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
22
|
+
return event.clientY - rect.top < rect.height / 2 ? 'before' : 'after';
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
rowProps: orderKey => ({
|
|
26
|
+
draggable: true,
|
|
27
|
+
className: state === null ? undefined
|
|
28
|
+
: state.active === orderKey ? css.sidebarRowDragging
|
|
29
|
+
: state.over === orderKey ? (state.marker === 'before' ? css.sidebarRowDropBefore : css.sidebarRowDropAfter)
|
|
30
|
+
: undefined,
|
|
31
|
+
onDragStart: event => {
|
|
32
|
+
if (event.dataTransfer === null)
|
|
33
|
+
return;
|
|
34
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
35
|
+
event.dataTransfer.setData('text/plain', orderKey);
|
|
36
|
+
committed.current = false;
|
|
37
|
+
setState({ active: orderKey, over: orderKey, marker: 'after' });
|
|
38
|
+
},
|
|
39
|
+
onDragEnd: () => { setState(null); },
|
|
40
|
+
onDragOver: event => {
|
|
41
|
+
if (state === null || !refs.includes(orderKey))
|
|
42
|
+
return;
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
if (event.dataTransfer !== null)
|
|
45
|
+
event.dataTransfer.dropEffect = 'move';
|
|
46
|
+
const marker = half(event);
|
|
47
|
+
if (state.over !== orderKey || state.marker !== marker)
|
|
48
|
+
setState({ ...state, over: orderKey, marker });
|
|
49
|
+
},
|
|
50
|
+
onDrop: event => {
|
|
51
|
+
if (state === null || !refs.includes(orderKey))
|
|
52
|
+
return;
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
if (committed.current)
|
|
55
|
+
return;
|
|
56
|
+
committed.current = true;
|
|
57
|
+
onCommit(state.active, orderKey, half(event));
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Transparent wrapper that owns the drop-marker styling for one draggable
|
|
64
|
+
* sidebar row. It adds no layout of its own; the styled row stays inside so
|
|
65
|
+
* hover/focus descendant selectors keep working.
|
|
66
|
+
*/
|
|
67
|
+
export function SortableRow({ drag, orderKey, children }) {
|
|
68
|
+
const props = drag.rowProps(orderKey);
|
|
69
|
+
return (_jsx("div", { ...props, children: children }));
|
|
70
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { WorkspaceId } from '@deepseek-ai/dsh-client-runtime/client';
|
|
2
|
+
/** Which sidebar list an order belongs to; the two ledgers are independent. */
|
|
3
|
+
export type TeamSidebarListKind = 'channels' | 'agents';
|
|
4
|
+
/** Insert side of a drop relative to the row under the pointer. */
|
|
5
|
+
export type SidebarDropMarker = 'before' | 'after';
|
|
6
|
+
/**
|
|
7
|
+
* Merge the saved personal order into the current Remote order: kept refs
|
|
8
|
+
* stay in the user's relative order with duplicates collapsed, removed refs
|
|
9
|
+
* disappear, and new refs join in their Remote default positions after the
|
|
10
|
+
* known ones.
|
|
11
|
+
*/
|
|
12
|
+
export declare function reconcileSidebarOrder<R extends string>(saved: readonly string[], current: readonly R[]): readonly R[];
|
|
13
|
+
/**
|
|
14
|
+
* Effective row order for one sidebar list: the user's saved order folded
|
|
15
|
+
* into the given Remote default order. Identity-stable across renders while
|
|
16
|
+
* neither the data nor this browser's preference changes.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useSidebarOrder<R extends string>(workspaceId: WorkspaceId | undefined, kind: TeamSidebarListKind, refs: readonly R[]): readonly R[];
|
|
19
|
+
/**
|
|
20
|
+
* The only mutation path: move `movedRef` to the given side of `targetRef`
|
|
21
|
+
* inside the list's current effective order. Returns the new order, or
|
|
22
|
+
* `undefined` when the request cannot change anything (unknown refs, dropping
|
|
23
|
+
* a row onto itself, or an adjacent marker that would put it right back).
|
|
24
|
+
* Persistence and subscriber notification happen here.
|
|
25
|
+
*/
|
|
26
|
+
export declare function moveSidebarItem<R extends string>(workspaceId: WorkspaceId | undefined, kind: TeamSidebarListKind, refs: readonly R[], movedRef: R, targetRef: R, marker: SidebarDropMarker): readonly R[] | undefined;
|
|
27
|
+
//# sourceMappingURL=sidebar-order.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sidebar-order.d.ts","sourceRoot":"","sources":["../../../src/client/sidebar-order.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,+EAA+E;AAC/E,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,QAAQ,CAAA;AAEvD,mEAAmE;AACnE,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,OAAO,CAAA;AA2ElD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAerH;AAqCD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,SAAS,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,SAAS,CAAC,EAAE,CAQnJ;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAC9C,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,IAAI,EAAE,mBAAmB,EACzB,IAAI,EAAE,SAAS,CAAC,EAAE,EAClB,QAAQ,EAAE,CAAC,EACX,SAAS,EAAE,CAAC,EACZ,MAAM,EAAE,iBAAiB,GACxB,SAAS,CAAC,EAAE,GAAG,SAAS,CAe1B"}
|