dsh-collab-workbench 0.0.0-stage → 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.

Potentially problematic release.


This version of dsh-collab-workbench might be problematic. Click here for more details.

package/client.js ADDED
@@ -0,0 +1,2136 @@
1
+ // Generated from src/client by scripts/build-client.mjs. Do not edit by hand.
2
+ window.__ModuleLoader__.load({
3
+ id: 'dsh-collab-workbench/client',
4
+ factory: (require) => {
5
+ const module = { exports: {} }
6
+ var __create = Object.create;
7
+ var __defProp = Object.defineProperty;
8
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __getProtoOf = Object.getPrototypeOf;
11
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
12
+ var __export = (target, all) => {
13
+ for (var name2 in all)
14
+ __defProp(target, name2, { get: all[name2], enumerable: true });
15
+ };
16
+ var __copyProps = (to, from, except, desc) => {
17
+ if (from && typeof from === "object" || typeof from === "function") {
18
+ for (let key of __getOwnPropNames(from))
19
+ if (!__hasOwnProp.call(to, key) && key !== except)
20
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
+ }
22
+ return to;
23
+ };
24
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
+ // If the importer is in node compatibility mode or this is not an ESM
26
+ // file that has been converted to a CommonJS file using a Babel-
27
+ // compatible transform (i.e. "__esModule" has not been set), then set
28
+ // "default" to the CommonJS "module.exports" for node compatibility.
29
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
+ mod
31
+ ));
32
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
+
34
+ // BEGIN WORKBENCH APPROVAL UI
35
+ // This source is embedded into the existing Client factory by scripts/build-approvals.mjs.
36
+ // Never serialize a pending interaction or expose its answer as a Host endpoint/tool.
37
+ function matchApproval(item, pending) {
38
+ return !!item && item.outcome === 'pending' && !!item.callId &&
39
+ pending?.kind === 'approval' && pending.sessionId === item.sessionId &&
40
+ pending.callId === item.callId && pending.toolName === item.toolName &&
41
+ (pending.reason ?? '') === (item.reason ?? '') && pending.answerable === true &&
42
+ typeof pending.answer === 'function'
43
+ }
44
+
45
+ // Outcome/decidedAt never participate in position or identity. Stable sort keeps
46
+ // source order for equal timestamps, including messages from the same millisecond.
47
+ function approvalTimeline(lines = [], items = []) {
48
+ return [...lines.map(line=>({kind:'message',time:line.time,line})),
49
+ ...items.map(item=>({kind:'approval',time:item.askedAt,item}))]
50
+ .sort((a,b)=>(Number(a.time)||0)-(Number(b.time)||0))
51
+ }
52
+
53
+ function createApprovalUI(React, ctx, call) {
54
+ const h = React.createElement
55
+ const NS = 'dwbApprovals'
56
+ const zh = {title:'等待权限审批',allow:'允许一次',reject:'拒绝',open:'打开原会话',reason:'申请原因',command:'操作内容',permission:'申请权限',pending:'等待审批',sending:'已提交,等待宿主确认',expired:'已取消或过期',allowed:'已允许一次',rejected:'已拒绝',unavailable:'审批不可用',connecting:'正在连接原生审批…',history:'审批记录',error:'审批状态读取失败',unknown:'尚未找到可操作的原生请求,请打开原会话查看',task:'任务',scope:'仅授权本次操作,不修改长期权限',details:'权限审批详情'}
57
+ const en = {title:'Permission approval required',allow:'Allow once',reject:'Reject',open:'Open original session',reason:'Reason',command:'Operation',permission:'Requested access',pending:'Waiting for approval',sending:'Submitted; awaiting Host confirmation',expired:'Cancelled or expired',allowed:'Allowed once',rejected:'Rejected',unavailable:'Approval unavailable',connecting:'Connecting to native approval…',history:'Approval history',error:'Failed to read approval state',unknown:'No actionable native request; open its original session',task:'Task',scope:'Authorizes this operation only; does not change policy',details:'Permission approval details'}
58
+ ctx.effect(()=>ctx.locale.register(NS,{zh,en}),'workbench: approval locales')
59
+ const t = ctx.locale.bind(NS)
60
+ const locks = new WeakSet()
61
+ // Retain native requester scopes for the plugin lifetime, not the visible chat's
62
+ // lifetime: switching groups/tabs must not withdraw an unanswered approval.
63
+ const retained = new Map(), watched = new Map()
64
+ let active = true, checking = false
65
+ const updateRetentions = (groupId, ids) => {
66
+ if(!active)return
67
+ watched.set(groupId,new Set(ids ?? []))
68
+ const keep=new Set([...watched.values()].flatMap(ids=>[...ids]))
69
+ for(const [id,ref] of retained)if(!keep.has(id)){ref.release();retained.delete(id)}
70
+ for(const id of keep)if(!retained.has(id)){
71
+ const ref=ctx.sessions.retain(id,{source:'controllerOperation'})
72
+ retained.set(id,ref);ref.ready.catch(()=>{})
73
+ }
74
+ }
75
+ ctx.effect(()=>{
76
+ const check=async()=>{
77
+ if(!active||checking||!watched.size)return
78
+ checking=true
79
+ try {
80
+ const listing=await call('groups.list',{})
81
+ if(!active)return
82
+ const present=new Set((listing.groups??[]).filter(g=>g.status==='active').map(g=>g.id))
83
+ for(const id of [...watched.keys()]){
84
+ if(!present.has(id)){updateRetentions(id,[]);watched.delete(id);continue}
85
+ try {const data=await call('approvals.list',{groupId:id});updateRetentions(id,data.sessions)}catch{/* transport owns cancellation; never auto-answer */}
86
+ }
87
+ }catch{/* keep scopes through transient transport failures */}finally{checking=false}
88
+ }
89
+ const timer=setInterval(check,2000);timer.unref?.()
90
+ return()=>{active=false;clearInterval(timer);for(const ref of retained.values())ref.release();retained.clear();watched.clear()}
91
+ },'workbench: approval requester lifetimes')
92
+ const css = `.dwb-approvals{padding:12px 16px;display:flex;flex-direction:column;gap:10px;min-width:0}.dwb-approval{border:1px solid var(--dsw-alias-state-warn-secondary);border-radius:12px;background:var(--dsw-specific-input-major);overflow:hidden;color:var(--dsw-alias-label-primary)}.dwb-approval-head{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-primary);padding:10px 16px;font-size:13px;line-height:18px;display:flex;gap:8px;flex-wrap:wrap}.dwb-approval-body{padding:12px 16px 0;font-size:13px;line-height:20px}.dwb-approval-body p{margin:0 0 8px}.dwb-approval-command{white-space:pre-wrap;overflow-wrap:anywhere;max-height:200px;overflow:auto;font-family:var(--ds-font-family-code);font-size:12px;color:var(--dsw-alias-label-secondary)}.dwb-approval-note{color:var(--dsw-alias-label-tertiary)}.dwb-approval-actions{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:8px;padding:12px 16px}.dwb-approval button{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:7px 12px;font-size:13px;cursor:pointer;min-height:34px}.dwb-approval button:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2)}.dwb-approval button:focus-visible{outline:2px solid var(--dsw-alias-state-info-primary);outline-offset:2px}.dwb-approval button:disabled{opacity:.5;cursor:default}.dwb-approval .dwb-approval-allow{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-primary);border-color:var(--dsw-alias-state-warn-secondary);font-weight:600}.dwb-approval .dwb-approval-reject:hover:not(:disabled){color:var(--dsw-alias-state-error-primary)}.dwb-approval-error{color:var(--dsw-alias-state-error-primary);padding:8px 16px;font-size:13px}`
93
+ function Card({item, pending, refresh}) {
94
+ const [sending,setSending]=React.useState(false),[error,setError]=React.useState(null)
95
+ const available=matchApproval(item,pending)
96
+ const answer=async outcome=>{
97
+ if(!matchApproval(item,pending)||locks.has(pending))return
98
+ locks.add(pending);setSending(true);setError(null)
99
+ try {await pending.answer(outcome);refresh()}
100
+ catch(e){setError(String(e?.message??e));if(pending.answerable){locks.delete(pending);setSending(false)}}
101
+ }
102
+ const status=item.outcome==='pending'?(sending?t('sending'):available?t('pending'):item.live?t('connecting'):t('expired')):
103
+ t({'allowed-once':'allowed',rejected:'rejected',cancelled:'expired',expired:'expired',unavailable:'unavailable'}[item.outcome]??'expired')
104
+ return h('section',{className:'dwb-approval','aria-label':t('details'),'data-approval-id':item.id},
105
+ h('div',{className:'dwb-approval-head'},h('strong',null,item.outcome==='pending'?t('title'):t('history')),h('span',null,item.roleName),item.taskId?h('span',null,`${t('task')} ${item.taskId}`):null),
106
+ h('div',{className:'dwb-approval-body'},item.taskTitle?h('p',null,item.taskTitle):null,
107
+ h('p',null,`${item.toolName} · ${status}`),item.reason?h('p',null,`${t('reason')}:${item.reason}`):null,
108
+ item.permission?h('p',null,`${t('permission')}:${item.permission}`):null,
109
+ item.command?h('pre',{className:'dwb-approval-command'},item.command):null,
110
+ h('p',{className:'dwb-approval-note'},item.outcome==='pending'?t('scope'):status),
111
+ item.outcome==='pending'&&!available?h('p',{className:'dwb-approval-note'},t('unknown')):null),
112
+ error?h('div',{className:'dwb-approval-error',role:'alert'},error):null,
113
+ h('div',{className:'dwb-approval-actions'},
114
+ h('button',{type:'button',onClick:()=>ctx.uiWorkspace.openSession(item.sessionId)},t('open')),
115
+ item.outcome==='pending'?h(React.Fragment,null,
116
+ h('button',{type:'button',className:'dwb-approval-reject',disabled:!available||sending,onClick:()=>answer('rejected')},t('reject')),
117
+ h('button',{type:'button',className:'dwb-approval-allow',disabled:!available||sending,onClick:()=>answer('allowed-once')},t('allow'))):null))
118
+ }
119
+ return function GroupApprovals({groupId,useSessionStatus,lines=[],renderMessage}) {
120
+ const [snapshot,setSnapshot]=React.useState({groupId,items:[],sessions:[],errors:[]}),[error,setError]=React.useState(null)
121
+ const refs=React.useRef(retained),generation=React.useRef(0),refreshRef=React.useRef(()=>{})
122
+ const statuses=useSessionStatus(map=>map)
123
+ const [,localeTick]=React.useState(0)
124
+ React.useEffect(()=>ctx.locale.subscribe(()=>localeTick(n=>n+1)),[])
125
+ React.useEffect(()=>{
126
+ const gen=++generation.current;let dead=false,busy=false
127
+ const refresh=async()=>{
128
+ if(dead||busy)return;busy=true
129
+ try{
130
+ const data=await call('approvals.list',{groupId})
131
+ if(dead||generation.current!==gen)return
132
+ setSnapshot({...data,groupId});setError(data.errors?.length?t('error'):null)
133
+ updateRetentions(groupId,data.sessions)
134
+ }catch(e){if(!dead)setError(String(e?.message??e))}finally{busy=false}
135
+ }
136
+ refreshRef.current=refresh;refresh();const timer=setInterval(refresh,1200)
137
+ return()=>{dead=true;++generation.current;clearInterval(timer)}
138
+ },[groupId])
139
+ // Never show or answer a previous group's snapshot while its replacement loads.
140
+ const items=snapshot.groupId===groupId?snapshot.items:[]
141
+ const visibleError=snapshot.groupId===groupId?error:null
142
+ if(!items.length&&!visibleError&&!renderMessage)return null
143
+ const timeline=approvalTimeline(lines,items)
144
+ return h(React.Fragment,null,h('style',null,css),visibleError?h('div',{className:'dwb-approval-error',role:'alert'},visibleError):null,
145
+ ...timeline.map((entry,index)=>{
146
+ if(entry.kind==='message')return renderMessage?.(entry.line,timeline[index-1]?.kind==='message'?timeline[index-1].line:null)
147
+ const item=entry.item
148
+ return h('div',{className:'dwb-approvals',key:`approval:${item.sessionId}:${item.id}`},h(Card,{item,pending:items.filter(a=>a.outcome==='pending'&&a.sessionId===item.sessionId&&a.callId===item.callId).length===1?statuses?.get(item.sessionId)?.pendingInteraction:null,refresh:()=>refreshRef.current()}))
149
+ }))
150
+ }
151
+ }
152
+
153
+ // END WORKBENCH APPROVAL UI
154
+
155
+ // BEGIN WORKBENCH UPDATE UI
156
+ // Update controls for the existing Workbench panel; embedded into client.js at build time.
157
+ // All styles live in the components that render them, so no other page owns their CSS.
158
+ function createUpdateUI(React, ctx, call) {
159
+ const h = React.createElement
160
+ const NS = 'dwbUpdates'
161
+ const zh = {
162
+ about:'关于', cloud:'有新版本', title:'协作工作台', current:'当前版本', latest:'最新版本', check:'检查更新', checking:'正在检查…', update:'更新', updating:'正在安装…', restart:'更新已安装,请重启 DSH 后使用', ready:'发现新版本', currentState:'已是最新版本', local:'当前从本地文件安装,请从原安装来源更新。', other:'当前安装来源不支持在线更新。', pending:'尚未检查更新', failed:'检查或安装失败', confirm:'确认安装协作工作台 {version}?安装后需要重启 DSH。', release:'版本信息', inaccessible:'此版本暂不提供更新检查。', error:'无法检查更新,请检查网络后重试。'
163
+ }
164
+ const en = {
165
+ about:'About', cloud:'Update available', title:'Collaboration Workbench', current:'Installed version', latest:'Latest version', check:'Check for updates', checking:'Checking…', update:'Update', updating:'Installing…', restart:'Update installed. Restart DSH to use it.', ready:'New version available', currentState:'Up to date', local:'Installed from a local file; update it from the original source.', other:'Updates are not available for this install source.', pending:'Not checked yet', failed:'Update check or install failed', confirm:'Install Collaboration Workbench {version}? Restart DSH afterwards.', release:'Release information', inaccessible:'Update checks are unavailable in this version.', error:'Could not check for updates. Check your network and try again.'
166
+ }
167
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'collaboration workbench: update locales')
168
+ const t = ctx.locale.bind(NS)
169
+ const css = `.dwb-update-cloud{width:52px;min-height:42px;padding:5px 0;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-brand-primary);cursor:pointer;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:2px}.dwb-update-cloud:hover{background:var(--dsw-alias-bg-layer-2)}.dwb-update-cloud:focus-visible,.dwb-update-about button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.dwb-update-cloud span{font-size:10px}.dwb-update-about{flex:1;min-width:0;overflow:auto;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);padding:56px 24px 24px}.dwb-update-card{max-width:640px;padding:20px;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1)}.dwb-update-about h2{font-size:18px;margin:0 0 18px}.dwb-update-about dl{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:10px 20px;margin:0 0 18px}.dwb-update-about dt{color:var(--dsw-alias-label-secondary)}.dwb-update-about dd{margin:0;overflow-wrap:anywhere}.dwb-update-state{font-size:13px;line-height:1.5;color:var(--dsw-alias-label-secondary);margin:0 0 16px}.dwb-update-error{color:var(--dsw-alias-state-error-primary)}.dwb-update-actions{display:flex;gap:10px;flex-wrap:wrap}.dwb-update-about button{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);border-radius:8px;padding:7px 12px;min-height:34px;cursor:pointer}.dwb-update-about button:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2)}.dwb-update-about button:disabled{opacity:.5;cursor:default}.dwb-update-about button.dwb-update-primary{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-brand-primary);border-color:var(--dsw-alias-brand-primary)}.dwb-update-about button.dwb-update-primary:hover:not(:disabled){filter:brightness(1.06)}`
170
+ function useUpdates() {
171
+ const [status, setStatus] = React.useState({ phase:'idle', source:'unknown', current:null, latest:null, available:false })
172
+ const [error, setError] = React.useState(null)
173
+ const [busy, setBusy] = React.useState(false)
174
+ const refresh = React.useCallback(async (fresh = false) => {
175
+ setBusy(true)
176
+ setError(null)
177
+ try {
178
+ const value = await call('updates.check', { fresh })
179
+ setStatus(value)
180
+ return value
181
+ } catch (failure) {
182
+ setError(String(failure?.message ?? failure))
183
+ return null
184
+ } finally { setBusy(false) }
185
+ }, [])
186
+ React.useEffect(() => {
187
+ let active = true
188
+ const check = async () => {
189
+ try { const value = await call('updates.check', { fresh:false }); if (active) setStatus(value) }
190
+ catch (failure) { if (active) setError(String(failure?.message ?? failure)) }
191
+ }
192
+ void check()
193
+ const timer = setInterval(check, status.phase === 'installing' ? 2_000 : 60_000)
194
+ return () => { active = false; clearInterval(timer) }
195
+ }, [status.phase])
196
+ const install = React.useCallback(async () => {
197
+ if (!status.available || busy || !window.confirm(t('confirm').replace('{version}', status.latest))) return
198
+ setBusy(true)
199
+ setError(null)
200
+ try { setStatus(await call('updates.install')) }
201
+ catch (failure) { setError(String(failure?.message ?? failure)) }
202
+ finally { setBusy(false) }
203
+ }, [status.available, status.latest, busy])
204
+ return {status, error, busy, refresh, install}
205
+ }
206
+ function UpdateCloud({ onClick }) {
207
+ return h(React.Fragment, null, h('style', null, css),
208
+ h('button', { type:'button', className:'dwb-update-cloud', title:t('cloud'), 'aria-label':t('cloud'), onClick },
209
+ h('svg', {width:18,height:18,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:1.8,'aria-hidden':true},
210
+ h('path',{d:'M7 18h11a4 4 0 0 0 .2-8A6.5 6.5 0 0 0 5.6 9 4.5 4.5 0 0 0 7 18Z'}),
211
+ h('path',{d:'m12 15 0-6m-2.5 2.5L12 9l2.5 2.5'})),
212
+ h('span',null,t('update'))))
213
+ }
214
+ function UpdateAbout({ status, error, busy, refresh, install }) {
215
+ const state = status.phase === 'installed' ? t('restart') : status.phase === 'installing' ? t('updating') : error || status.phase === 'failed' ? t('failed') : status.source === 'local' ? t('local') : status.source !== 'registry' ? t('other') : status.available ? t('ready') : status.checkedAt ? status.latest ? t('currentState') : t('error') : t('pending')
216
+ const canUpdate = status.available && status.phase !== 'installing' && status.phase !== 'installed'
217
+ return h('section', {className:'dwb-update-about', 'aria-label':t('title')},
218
+ h('style',null,css),
219
+ h('div',{className:'dwb-update-card'},
220
+ h('h2',null,t('title')),
221
+ h('dl',null,h('dt',null,t('current')),h('dd',null,status.current ?? '—'),h('dt',null,t('latest')),h('dd',null,status.latest ?? '—')),
222
+ h('p',{className:`dwb-update-state ${error || status.phase === 'failed' ? 'dwb-update-error' : ''}`,role:error || status.phase === 'failed' ? 'alert':undefined}, state, error ? `: ${error}` : status.phase === 'failed' && status.error ? `: ${status.error}` : ''),
223
+ h('div',{className:'dwb-update-actions'},
224
+ h('button',{type:'button',onClick:()=>refresh(true),disabled:busy || status.phase === 'installing'},busy?t('checking'):t('check')),
225
+ canUpdate ? h('button',{type:'button',className:'dwb-update-primary',onClick:install,disabled:busy},t('update')) : null)))
226
+ }
227
+ return {useUpdates, UpdateCloud, UpdateAbout, t}
228
+ }
229
+
230
+ // END WORKBENCH UPDATE UI
231
+
232
+ // src/client/main.jsx
233
+ var main_exports = {};
234
+ __export(main_exports, {
235
+ apply: () => apply,
236
+ inject: () => inject,
237
+ name: () => name
238
+ });
239
+ module.exports = __toCommonJS(main_exports);
240
+ var import_react8 = __toESM(require("react"), 1);
241
+
242
+ // src/client/app.jsx
243
+ var import_react7 = __toESM(require("react"), 1);
244
+ var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
245
+
246
+ // src/client/styles.js
247
+ var CSS = `
248
+ .dwb-app{
249
+ --dwb-bg:var(--dsw-alias-bg-base);
250
+ --dwb-bg-rail:var(--dsw-specific-sidebar-fill,var(--dsw-alias-bg-layer-1));
251
+ --dwb-bg-list:var(--dsw-specific-sidebar-fill,var(--dsw-alias-bg-layer-1));
252
+ --dwb-bg-chat:var(--dsw-alias-bg-base);
253
+ --dwb-bg-panel:var(--dsw-alias-bg-layer-1);
254
+ --dwb-bg-overlay:var(--dsw-alias-bg-overlay,var(--dsw-alias-bg-layer-1));
255
+ --dwb-border:var(--dsw-alias-border-l2);
256
+ --dwb-border-soft:var(--dsw-alias-border-l1);
257
+ --dwb-text:var(--dsw-alias-label-primary);
258
+ --dwb-text-2:var(--dsw-alias-label-secondary);
259
+ --dwb-text-3:var(--dsw-alias-label-secondary);
260
+ --dwb-accent:var(--dsw-alias-brand-primary);
261
+ --dwb-fill:var(--dsw-alias-state-business-primary,var(--dsw-static-deepseek-500,#4176e6));
262
+ --dwb-link:var(--dsw-alias-link,var(--dwb-fill));
263
+ --dwb-accent-soft:color-mix(in srgb,var(--dsw-alias-brand-primary) 14%,transparent);
264
+ --dwb-hover:color-mix(in srgb,var(--dsw-alias-label-primary) 7%,transparent);
265
+ --dwb-bubble:var(--dsw-alias-bg-layer-2);
266
+ --dwb-sys:var(--dsw-alias-bg-layer-1);
267
+ --dwb-red:var(--dsw-alias-state-error-primary);
268
+ --dwb-green:var(--dsw-alias-state-success-primary);
269
+ --dwb-amber:var(--dsw-alias-state-warn-primary);
270
+ display:flex; height:100%; width:100%;
271
+ font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
272
+ color:var(--dwb-text); background:var(--dwb-bg-chat); font-size:14px; overflow:hidden;
273
+ }
274
+ .dwb-app *{box-sizing:border-box}
275
+ .dwb-app ::-webkit-scrollbar{width:6px;height:6px}
276
+ .dwb-app ::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--dsw-alias-label-primary) 20%,transparent);border-radius:3px}
277
+ .dwb-app ::-webkit-scrollbar-track{background:transparent}
278
+ .dwb-app button{font-family:inherit}
279
+
280
+ /* ============ \u4E3B\u9898\u6D3E\u751F\u5934\u50CF\uFF088 \u8272\u5FAA\u73AF\uFF0C\u6765\u81EA brand/success/warn/error \u53CA\u5176\u6DF7\u5408\uFF09 ============ */
281
+ .dwb-av{color:#fff}
282
+ .dwb-av0{background:var(--dwb-fill)}
283
+ .dwb-av1{background:var(--dsw-alias-state-success-primary)}
284
+ .dwb-av2{background:var(--dsw-alias-state-warn-primary)}
285
+ .dwb-av3{background:var(--dsw-alias-state-error-primary)}
286
+ .dwb-av4{background:color-mix(in srgb,var(--dwb-fill) 55%,var(--dsw-alias-state-success-primary))}
287
+ .dwb-av5{background:color-mix(in srgb,var(--dwb-fill) 55%,var(--dsw-alias-state-error-primary))}
288
+ .dwb-av6{background:color-mix(in srgb,var(--dsw-alias-state-success-primary) 55%,var(--dsw-alias-state-warn-primary))}
289
+ .dwb-av7{background:color-mix(in srgb,var(--dwb-fill) 45%,var(--dsw-alias-state-warn-primary))}
290
+ .dwb-av-self{background:var(--dwb-fill)}
291
+
292
+ /* ============ \u5DE6\u4FA7\u7A84\u5BFC\u822A\u5217\uFF08\u9489\u9489\u5F0F\uFF09 ============ */
293
+ .dwb-rail{
294
+ width:64px; flex:none; background:var(--dwb-bg-rail);
295
+ display:flex; flex-direction:column; align-items:center; gap:4px;
296
+ padding:50px 0 10px; /* 40px \u5BF9\u9F50 DSH \u4FA7\u680F\u54C1\u724C\u884C + 10px \u539F\u95F4\u8DDD */
297
+ }
298
+ .dwb-rail-logo{font-size:22px; padding:6px 0 10px; filter:grayscale(.15)}
299
+ .dwb-rail-item{
300
+ width:52px; padding:7px 0 5px; border:none; border-radius:10px; background:transparent;
301
+ color:var(--dwb-text-3); cursor:pointer; display:flex; flex-direction:column; align-items:center; gap:2px;
302
+ transition:background .15s,color .15s;
303
+ }
304
+ .dwb-rail-item:hover{background:var(--dwb-hover);color:var(--dwb-text-2)}
305
+ .dwb-rail-item.on{background:var(--dwb-accent-soft);color:var(--dwb-accent)}
306
+ .dwb-rail-icon{font-size:17px;line-height:1}
307
+ .dwb-rail-label{font-size:10px;line-height:1}
308
+
309
+ /* ============ \u5217\u8868\u5217\uFF08\u7FA4\u7EC4/\u4EBA\u5458/\u4EFB\u52A1\u7B5B\u9009\u5171\u7528\uFF09 ============ */
310
+ .dwb-col-list{
311
+ width:264px; flex:none; background:var(--dwb-bg-list);
312
+ border-right:1px solid var(--dwb-border-soft); display:flex; flex-direction:column; min-height:0;
313
+ }
314
+ .dwb-list-head{padding:52px 12px 8px; border-bottom:1px solid var(--dwb-border-soft); flex:none; /* \u9876\u90E8 40px \u5BF9\u9F50 DSH \u54C1\u724C\u884C */}
315
+ .dwb-list-title-row{display:flex; align-items:center; margin-bottom:8px}
316
+ .dwb-list-title{font-size:15px; font-weight:600}
317
+ .dwb-search{
318
+ width:100%; background:var(--dsw-specific-input-major,transparent);
319
+ border:1px solid var(--dwb-border-soft); color:var(--dwb-text-2);
320
+ border-radius:8px; padding:7px 10px; font-size:12px; outline:none;
321
+ }
322
+ .dwb-search:focus{border-color:var(--dwb-accent); color:var(--dwb-text)}
323
+ .dwb-search::placeholder{color:var(--dwb-text-3)}
324
+ .dwb-list-scroll{flex:1; overflow-y:auto; padding:6px}
325
+ .dwb-list-empty{color:var(--dwb-text-3); font-size:12px; text-align:center; padding:28px 10px; line-height:1.9; white-space:pre-line}
326
+
327
+ .dwb-icon-btn{
328
+ width:28px; height:28px; border:none; border-radius:7px; background:transparent;
329
+ color:var(--dwb-text-2); cursor:pointer; font-size:14px; line-height:1; flex:none;
330
+ display:inline-flex; align-items:center; justify-content:center;
331
+ }
332
+ .dwb-icon-btn:hover{background:var(--dwb-hover); color:var(--dwb-text)}
333
+ .dwb-icon-btn.on{background:var(--dwb-accent-soft); color:var(--dwb-accent)}
334
+
335
+ /* \u7FA4\u7EC4\u9879\uFF08\u5FAE\u4FE1\u5F0F\uFF09 */
336
+ .dwb-gitem{
337
+ display:flex; align-items:center; gap:10px; width:100%; padding:9px 10px; margin-bottom:2px;
338
+ border:none; border-radius:10px; background:transparent; color:var(--dwb-text);
339
+ cursor:pointer; text-align:left; transition:background .12s;
340
+ }
341
+ .dwb-gitem:hover{background:var(--dwb-hover)}
342
+ .dwb-gitem.on{background:var(--dwb-accent-soft); box-shadow:inset 2px 0 0 var(--dwb-accent)}
343
+ .dwb-gitem.sm{padding:6px 8px; gap:8px}
344
+ .dwb-gmid{flex:1; min-width:0; display:flex; flex-direction:column; gap:2px}
345
+ .dwb-gname{font-size:13.5px; font-weight:550; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
346
+ .dwb-gpreview{font-size:12px; color:var(--dwb-text-3); white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
347
+ .dwb-gright{display:flex; flex-direction:column; align-items:flex-end; gap:5px; flex:none}
348
+ .dwb-gtime{font-size:11px; color:var(--dwb-text-3)}
349
+ .dwb-unread{
350
+ min-width:17px; height:17px; padding:0 5px; border-radius:9px; background:var(--dwb-red);
351
+ color:#fff; font-size:10.5px; font-weight:600; display:inline-flex; align-items:center; justify-content:center;
352
+ }
353
+
354
+ /* \u4EBA\u5458/\u4EFB\u52A1\u5DE6\u4FA7\u5206\u7EC4 */
355
+ .dwb-org-sec{margin-bottom:8px}
356
+ .dwb-org-sec-title{font-size:11px; color:var(--dwb-text-3); letter-spacing:1px; padding:8px 10px 4px}
357
+ .dwb-filters{display:flex; flex-wrap:wrap; gap:4px; padding:0 8px}
358
+ .dwb-filters.col{flex-direction:column; align-items:stretch}
359
+ .dwb-filter{
360
+ border:none; border-radius:7px; background:transparent; color:var(--dwb-text-2);
361
+ padding:5px 10px; font-size:12px; cursor:pointer; text-align:left;
362
+ white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
363
+ }
364
+ .dwb-filter:hover{background:var(--dwb-hover)}
365
+ .dwb-filter.on{background:var(--dwb-accent-soft); color:var(--dwb-accent); font-weight:600}
366
+
367
+ /* ============ \u5BF9\u8BDD\u533A ============ */
368
+ .dwb-col-chat{flex:1; min-width:0; display:flex; flex-direction:column; background:var(--dwb-bg-chat); position:relative}
369
+ .dwb-chat-head{
370
+ display:flex; align-items:center; gap:10px; padding:10px 16px;
371
+ border-bottom:1px solid var(--dwb-border-soft); background:var(--dwb-bg-panel); flex:none;
372
+ }
373
+ .dwb-chat-headmid{min-width:0; display:flex; flex-direction:column; gap:1px}
374
+ .dwb-chat-name{font-size:14.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
375
+ .dwb-chat-sub{font-size:11.5px; color:var(--dwb-text-3)}
376
+ .dwb-avatar-stack{display:flex; align-items:center; margin-left:8px}
377
+ .dwb-avatar-stack>span{margin-left:-6px; box-shadow:0 0 0 2px var(--dwb-bg-panel)}
378
+ .dwb-avatar-stack>span:first-child{margin-left:0}
379
+ .dwb-stack-more{font-size:11px; color:var(--dwb-text-3); margin-left:4px}
380
+
381
+ .dwb-chat-body{flex:1; display:flex; min-height:0}
382
+
383
+ /* \u6D88\u606F\u6D41 */
384
+ .dwb-msgs{flex:1; overflow-y:auto; padding:16px 18px; display:flex; flex-direction:column; gap:2px}
385
+ .dwb-time-sep{
386
+ align-self:center; margin:10px 0 8px; padding:2px 10px; border-radius:10px;
387
+ background:var(--dwb-sys); color:var(--dwb-text-3); font-size:11px; flex:none;
388
+ }
389
+ .dwb-sysmsg{
390
+ align-self:center; max-width:76%; margin:6px 0; padding:8px 12px; border-radius:10px;
391
+ background:var(--dwb-sys); color:var(--dwb-text-2); font-size:12px; line-height:1.7;
392
+ cursor:default; white-space:pre-wrap; word-break:break-word;
393
+ }
394
+ .dwb-sysmsg.open{cursor:pointer}
395
+ .dwb-sysmsg-head{display:flex; align-items:center; gap:6px; flex-wrap:wrap}
396
+ .dwb-sysmsg-body{margin-top:6px; color:var(--dwb-text-3)}
397
+ .dwb-sys-toggle{color:var(--dwb-link); font-size:11.5px; cursor:pointer; flex:none}
398
+ .dwb-sysmsg.task-notice{max-width:60%; padding:4px 12px; opacity:.7}
399
+ .dwb-sysmsg.task-notice .dwb-sysmsg-head{display:flex; align-items:center; gap:6px; font-size:11.5px}
400
+ .dwb-sysmsg-badge{font-size:12px; flex:none}
401
+ .dwb-sysmsg-text{overflow:hidden; text-overflow:ellipsis; white-space:nowrap; flex:1; min-width:0}
402
+
403
+ .dwb-mrow{display:flex; gap:9px; margin-top:10px; align-items:flex-start}
404
+ .dwb-mrow.grouped{margin-top:3px}
405
+ .dwb-mrow.self{flex-direction:row-reverse}
406
+ .dwb-mavatar{width:32px; flex:none; display:flex; justify-content:center}
407
+ .dwb-mrow:not(.grouped) .dwb-mavatar{padding-top:17px}
408
+ .dwb-mrow.grouped .dwb-mavatar{visibility:hidden; height:0; overflow:hidden}
409
+ .dwb-mbody{display:flex; flex-direction:column; gap:3px; max-width:min(72%,640px); min-width:0}
410
+ .dwb-mrow.self .dwb-mbody{align-items:flex-end}
411
+ .dwb-mname{font-size:11.5px; color:var(--dwb-text-3); padding:0 4px}
412
+ .dwb-bubble{
413
+ padding:8px 12px; border-radius:4px 12px 12px 12px;
414
+ background:var(--dwb-bubble); font-size:13.5px; line-height:1.7;
415
+ white-space:pre-wrap; word-break:break-word;
416
+ }
417
+ .dwb-mrow.grouped .dwb-bubble{border-radius:4px 12px 12px 4px}
418
+ .dwb-mrow.self .dwb-bubble{border-radius:12px 4px 4px 12px}
419
+ .dwb-mrow.self.grouped .dwb-bubble{border-radius:12px 4px 4px 4px}
420
+ .dwb-at{color:var(--dwb-link); font-weight:550}
421
+
422
+ /* \u8F93\u5165\u533A\uFF08\u89C2\u611F\u5BF9\u9F50 DSH \u539F\u751F InputBar\uFF1A\u8BBE\u8BA1\u503C\u53D6\u81EA dsh-client-ui-conversation \u7684 InputBar.module.css\uFF0C
423
+ \u5706\u89D2/\u914D\u8272/\u5C3A\u5BF8\u7528 --dsw-* \u4EE4\u724C\uFF0C\u7F3A\u5931\u65F6\u56DE\u9000\u5230\u672C\u63D2\u4EF6\u6D3E\u751F\u503C\uFF09 */
424
+ .dwb-composer{
425
+ --dsh-composer-side-clearance:16px;
426
+ --dsh-composer-card-max-width:min(calc(100% - 16px),952px);
427
+ --dsh-composer-text-max-height:336px;
428
+ flex:none; position:relative;
429
+ display:flex; flex-direction:column; align-items:center;
430
+ padding:0 var(--dsh-composer-side-clearance) 4px;
431
+ }
432
+ .dwb-comp-notice{
433
+ width:100%; max-width:var(--dsh-composer-card-max-width);
434
+ border-radius:var(--dsw-radius-md,12px);
435
+ background:var(--dsw-alias-interactive-bg-hover,var(--dwb-hover));
436
+ color:var(--dsw-alias-state-error-primary,var(--dwb-red));
437
+ margin-bottom:6px; padding:4px 8px; font-size:12px; line-height:18px;
438
+ }
439
+ .dwb-comp-card{
440
+ box-sizing:border-box; width:100%; max-width:var(--dsh-composer-card-max-width);
441
+ --dsw-elevation-stroke-color:var(--dsw-alias-border-l2,var(--dwb-border));
442
+ border-radius:var(--dsw-radius-panel,28px);
443
+ background:var(--dsw-specific-input-major,var(--dsw-alias-bg-layer-1));
444
+ box-shadow:var(--dsw-elevation-soft,0 0 0 .5px var(--dwb-border-soft));
445
+ border:0;
446
+ display:flex; flex-direction:column; gap:12px; padding:8px 0 0;
447
+ font-size:var(--dsh-content-font-size,14px);
448
+ line-height:calc(24px + var(--dsh-content-font-delta,0px));
449
+ position:relative;
450
+ }
451
+ .dwb-ta{
452
+ box-sizing:border-box; width:100%; min-height:36px;
453
+ max-height:var(--dsh-composer-text-max-height);
454
+ margin-right:4px; overflow-y:auto; resize:none; border:0; outline:none;
455
+ background:transparent; color:var(--dwb-text);
456
+ font-family:inherit; font-size:inherit; line-height:inherit;
457
+ caret-color:var(--dwb-fill);
458
+ padding:4px 8px 0 14px;
459
+ white-space:pre-wrap; word-break:break-word; overflow-wrap:anywhere;
460
+ scrollbar-color:var(--dsw-alias-scrollbar-bg-l2,color-mix(in srgb,var(--dsw-alias-label-primary) 20%,transparent)) transparent;
461
+ }
462
+ .dwb-ta::placeholder{color:var(--dsw-alias-label-caption,var(--dwb-text-3))}
463
+ /* \uFF0B \u53F7\u4E0A\u4F20\u7684\u9644\u4EF6 chips\uFF08\u53D1\u9001\u524D\uFF09 */
464
+ .dwb-comp-files{display:flex; flex-wrap:wrap; gap:4px; padding:2px 8px 0 14px}
465
+ .dwb-comp-file{
466
+ display:inline-flex; align-items:center; gap:6px; min-width:0; max-width:100%;
467
+ border:.5px solid var(--dsw-alias-border-l1,var(--dwb-border-soft));
468
+ border-radius:var(--dsw-radius-sm,8px);
469
+ background:var(--dsw-alias-bg-base,var(--dwb-bg));
470
+ padding:3px 4px 3px 8px; font-size:12px; line-height:18px; color:var(--dwb-text-2);
471
+ }
472
+ .dwb-comp-file.pending{opacity:.6}
473
+ .dwb-comp-file.error{color:var(--dwb-red)}
474
+ .dwb-comp-file-name{max-width:180px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
475
+ .dwb-comp-file-size{color:var(--dwb-text-3); flex:none; font-size:11px}
476
+ .dwb-comp-file-x{
477
+ border:none; background:transparent; color:var(--dwb-text-3); cursor:pointer;
478
+ display:inline-flex; align-items:center; justify-content:center;
479
+ width:18px; height:18px; padding:0; border-radius:999px; flex:none;
480
+ }
481
+ .dwb-comp-file-x:hover{background:var(--dwb-hover); color:var(--dwb-text)}
482
+ /* \u6D88\u606F\u6D41\u4E2D\u7684\u6587\u4EF6\u5361\u7247\uFF08\u53D1\u9001\u540E\uFF09 */
483
+ .dwb-mfiles{display:flex; flex-wrap:wrap; gap:6px; margin-top:4px; max-width:380px}
484
+ .dwb-mfile{
485
+ display:inline-flex; align-items:center; gap:8px; min-width:0;
486
+ border:1px solid var(--dwb-border-soft); border-radius:10px;
487
+ background:var(--dwb-bubble); padding:7px 12px;
488
+ }
489
+ .dwb-mfile-info{display:flex; flex-direction:column; min-width:0}
490
+ .dwb-mfile-name{font-size:12.5px; color:var(--dwb-text); max-width:220px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
491
+ .dwb-mfile-size{font-size:11px; color:var(--dwb-text-3); max-width:220px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
492
+ .dwb-comp-row{
493
+ display:flex; flex-wrap:wrap; justify-content:space-between; align-items:center;
494
+ gap:12px; min-width:0; padding:2px 8px 6px;
495
+ }
496
+ .dwb-comp-tools,.dwb-comp-trailing{display:flex; align-items:center; min-width:0; gap:12px}
497
+ .dwb-comp-trailing{flex:none; margin-left:auto; gap:8px}
498
+ .dwb-comp-add{
499
+ corner-shape:round;
500
+ background:var(--dsw-specific-selector,color-mix(in srgb,var(--dsw-alias-label-primary) 8%,transparent));
501
+ width:28px; height:28px; display:grid; place-items:center; flex:none;
502
+ color:var(--dwb-text); cursor:pointer; border:none; border-radius:999px;
503
+ transition:background-color .1s;
504
+ }
505
+ .dwb-comp-add:hover,.dwb-comp-add.on{background:var(--dsw-alias-interactive-bg-hover-solid,var(--dwb-hover))}
506
+ .dwb-comp-hint{font-size:12px; line-height:18px; color:var(--dsw-alias-label-caption,var(--dwb-text-3))}
507
+ .dwb-comp-send{
508
+ corner-shape:round;
509
+ background:var(--dsw-alias-button-info-fill,var(--dwb-fill)); color:#fff;
510
+ width:34px; height:34px; display:grid; place-items:center; flex:none;
511
+ cursor:pointer; border:none; border-radius:999px;
512
+ transition:background-color .1s; transform:translateY(-2px);
513
+ }
514
+ .dwb-comp-send:hover:not(:disabled){background:var(--dsw-alias-button-info-hover,color-mix(in srgb,var(--dwb-fill) 85%,#fff))}
515
+ .dwb-comp-send:disabled{opacity:.4; cursor:default}
516
+ .dwb-comp-send:focus-visible,.dwb-comp-add:focus-visible{
517
+ outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dwb-fill));
518
+ outline-offset:1px;
519
+ }
520
+
521
+ /* \u7FA4\u4FE1\u606F\u4FA7\u680F */
522
+ .dwb-ctx{
523
+ width:236px; flex:none; border-left:1px solid var(--dwb-border-soft); background:var(--dwb-bg-list);
524
+ overflow-y:auto; display:flex; flex-direction:column;
525
+ }
526
+ .dwb-ctx-sec{padding:12px 14px; border-bottom:1px solid var(--dwb-border-soft)}
527
+ .dwb-ctx-title{font-size:11px; color:var(--dwb-text-3); letter-spacing:1px; margin-bottom:8px}
528
+ .dwb-member-row{display:flex; align-items:center; gap:8px; padding:4px 0}
529
+ .dwb-member-name{font-size:13px; flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
530
+ .dwb-dot{width:8px; height:8px; border-radius:50%; background:var(--dsw-alias-state-idle-primary); flex:none}
531
+ .dwb-dot.on{background:var(--dwb-green)}
532
+ .dwb-ctx-empty{font-size:12px; color:var(--dwb-text-3); padding:4px 0}
533
+ .dwb-ctx-task{display:flex; align-items:center; gap:6px; padding:4px 0; font-size:12.5px}
534
+ .dwb-ctx-task-title{flex:1; min-width:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
535
+ .dwb-ctx-kv{display:flex; align-items:baseline; gap:8px; font-size:12px; padding:3px 0}
536
+ .dwb-ctx-kv span{color:var(--dwb-text-3); flex:none}
537
+ .dwb-ctx-kv b{font-weight:500; word-break:break-all}
538
+ .dwb-ctx-kv.col{flex-direction:column; gap:2px}
539
+ .dwb-ctx-dir{font-size:11px; color:var(--dwb-text-2)}
540
+
541
+ /* ============ \u7BA1\u7406\u533A\uFF08\u4EBA\u5458/\u4EFB\u52A1\u53F3\u4FA7\uFF09 ============ */
542
+ .dwb-col-manage{flex:1; min-width:0; display:flex; flex-direction:column; overflow-y:auto; background:var(--dwb-bg-chat)}
543
+ .dwb-manage-head{display:flex; align-items:baseline; gap:12px; padding:16px 20px 4px}
544
+ .dwb-manage-title{font-size:16px; font-weight:600}
545
+ .dwb-manage-sub{font-size:12px; color:var(--dwb-text-3)}
546
+ .dwb-form-card{margin:12px 20px; padding:14px 16px; background:var(--dwb-bg-panel); border:1px solid var(--dwb-border-soft); border-radius:12px}
547
+ .dwb-form-title{font-size:13px; font-weight:600; margin-bottom:10px}
548
+ .dwb-form-row{display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:8px}
549
+ .dwb-form-row:last-child{margin-bottom:0}
550
+ .dwb-assign-label{font-size:12px; color:var(--dwb-text-3)}
551
+ .dwb-manage-empty{flex:1; display:flex; align-items:center; justify-content:center; min-height:300px}
552
+ .dwb-cards{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:12px; padding:4px 20px 20px}
553
+
554
+ .dwb-card{background:var(--dwb-bg-panel); border:1px solid var(--dwb-border-soft); border-radius:12px; padding:13px 15px; display:flex; flex-direction:column; gap:8px}
555
+ .dwb-card.off{opacity:.6}
556
+ .dwb-card.tall{min-height:108px; padding:18px 20px; gap:12px}
557
+ .dwb-card.tall .dwb-card-desc{max-height:110px}
558
+ .dwb-card-hl>.dwb-card{border-color:var(--dwb-accent); box-shadow:0 0 0 1px var(--dwb-accent)}
559
+ .dwb-card-head{display:flex; align-items:center; gap:10px; min-width:0}
560
+ .dwb-card-mid{flex:1; min-width:0; display:flex; flex-direction:column; gap:1px}
561
+ .dwb-card-title{font-size:13.5px; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
562
+ .dwb-card-sub{font-size:11.5px; color:var(--dwb-text-3)}
563
+ .dwb-card-desc{font-size:12px; color:var(--dwb-text-2); line-height:1.7; max-height:66px; overflow-y:auto; white-space:pre-wrap}
564
+ .dwb-card-actions{display:flex; gap:6px; margin-top:auto}
565
+ .dwb-card-foot{display:flex; align-items:center; gap:8px; flex-wrap:wrap}
566
+ .dwb-card-assignees{display:flex; align-items:center; gap:3px; min-width:0}
567
+
568
+ /* \u5FBD\u7AE0\uFF08\u72B6\u6001\u8272 + \u900F\u660E\u6DF7\u5408\u5E95\uFF0C\u8DDF\u968F\u4E3B\u9898\uFF09 */
569
+ .dwb-chip{
570
+ display:inline-flex; align-items:center; padding:1px 8px; border-radius:9px;
571
+ font-size:11px; font-weight:550; flex:none; line-height:1.6;
572
+ }
573
+ .dwb-chip.p-high{background:color-mix(in srgb,var(--dwb-red) 14%,transparent); color:var(--dwb-red)}
574
+ .dwb-chip.p-mid{background:color-mix(in srgb,var(--dwb-amber) 16%,transparent); color:var(--dwb-amber)}
575
+ .dwb-chip.p-low{background:color-mix(in srgb,var(--dwb-green) 14%,transparent); color:var(--dwb-green)}
576
+ .dwb-chip.st-done{background:color-mix(in srgb,var(--dwb-green) 14%,transparent); color:var(--dwb-green)}
577
+ .dwb-chip.st-doing{background:color-mix(in srgb,var(--dwb-amber) 16%,transparent); color:var(--dwb-amber)}
578
+ .dwb-chip.st-todo{background:color-mix(in srgb,var(--dsw-alias-label-primary) 10%,transparent); color:var(--dwb-text-2)}
579
+
580
+ /* \u5F39\u7A97 */
581
+ .dwb-modal-mask{position:fixed; inset:0; background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.35)); z-index:1000; display:flex; align-items:center; justify-content:center}
582
+ .dwb-modal{width:600px; max-width:94%; max-height:88%; overflow-y:auto; background:var(--dsw-alias-bg-layer-1); border:1px solid var(--dwb-border-soft); border-radius:16px; padding:24px 28px; box-shadow:var(--dsw-shadow-lv3,0 12px 32px rgba(0,0,0,.14))}
583
+ .dwb-modal-title{font-size:16px; font-weight:600; margin-bottom:16px}
584
+ .dwb-field{margin-bottom:16px}
585
+ .dwb-field>label{display:block; font-size:12px; color:var(--dwb-text-2); margin-bottom:6px}
586
+ .dwb-field-hint{font-size:11px; color:var(--dwb-text-3); margin-top:5px; line-height:1.6}
587
+ .dwb-member-pick{display:flex; flex-wrap:wrap; gap:8px}
588
+ .dwb-pick{
589
+ display:inline-flex; align-items:center; gap:6px; padding:5px 10px; border-radius:9px;
590
+ border:1px solid var(--dwb-border-soft); background:color-mix(in srgb,var(--dsw-alias-label-primary) 4%,transparent);
591
+ cursor:pointer; font-size:12.5px; color:var(--dwb-text);
592
+ }
593
+ .dwb-pick:hover{border-color:var(--dwb-border)}
594
+ .dwb-pick.on{border-color:var(--dwb-accent); background:var(--dwb-accent-soft)}
595
+ .dwb-pick.sm{padding:3px 8px; font-size:12px}
596
+ .dwb-pick input{margin:0}
597
+ .dwb-pick-type{font-size:10.5px; color:var(--dwb-text-3)}
598
+ .dwb-modal-actions{display:flex; justify-content:flex-end; gap:10px; margin-top:12px}
599
+ /* \u5F39\u7A97\u5185\u591A\u884C\u6587\u672C\u57DF\uFF08\u804C\u8D23\u63CF\u8FF0\u7B49\uFF09 */
600
+ .dwb-textarea{
601
+ width:100%; min-height:128px; max-height:280px; resize:vertical;
602
+ background:var(--dsw-specific-input-major,transparent);
603
+ border:1px solid var(--dwb-border-soft); color:var(--dwb-text);
604
+ border-radius:8px; padding:8px 11px; font-size:13px; line-height:1.7;
605
+ outline:none; font-family:inherit;
606
+ }
607
+ .dwb-textarea:focus{border-color:var(--dwb-accent)}
608
+ .dwb-textarea::placeholder{color:var(--dwb-text-3)}
609
+
610
+ /* \u901A\u7528\u63A7\u4EF6 */
611
+ .dwb-btn{
612
+ border:none; border-radius:8px; background:color-mix(in srgb,var(--dsw-alias-label-primary) 10%,transparent);
613
+ color:var(--dwb-text); padding:8px 14px; font-size:13px; cursor:pointer; font-family:inherit;
614
+ }
615
+ .dwb-btn:hover{background:color-mix(in srgb,var(--dsw-alias-label-primary) 16%,transparent)}
616
+ .dwb-btn.primary{background:var(--dwb-fill); color:#fff}
617
+ .dwb-btn.primary:hover{background:color-mix(in srgb,var(--dwb-fill) 85%,#fff)}
618
+ .dwb-btn.danger{background:color-mix(in srgb,var(--dwb-red) 14%,transparent); color:var(--dwb-red)}
619
+ .dwb-btn.danger:hover{background:color-mix(in srgb,var(--dwb-red) 22%,transparent)}
620
+ .dwb-btn.small{padding:4px 10px; font-size:12px}
621
+ .dwb-btn:disabled{background:color-mix(in srgb,var(--dsw-alias-label-primary) 8%,transparent); color:var(--dwb-text-3); cursor:default}
622
+ .dwb-input,.dwb-select{
623
+ background:var(--dsw-specific-input-major,transparent); border:1px solid var(--dwb-border-soft);
624
+ color:var(--dwb-text); border-radius:8px; padding:7px 11px; font-size:13px; outline:none; font-family:inherit;
625
+ }
626
+ .dwb-input:focus,.dwb-select:focus{border-color:var(--dwb-accent)}
627
+ .dwb-input::placeholder{color:var(--dwb-text-3)}
628
+ /* \u5DE5\u4F5C\u76EE\u5F55\u8F93\u5165\u884C\uFF1A\u8F93\u5165\u6846\u6491\u6EE1\u5269\u4F59\u5BBD\u5EA6\uFF08flex item \u9ED8\u8BA4\u6536\u7F29\u5230\u5185\u5BB9\u5BBD\u5EA6\u4F1A\u663E\u5F97\u7A84\uFF09 */
629
+ .dwb-dir-row .dwb-input{flex:1; min-width:0}
630
+ .dwb-err{color:var(--dwb-red); font-size:12.5px; padding:8px 12px; background:color-mix(in srgb,var(--dwb-red) 10%,transparent); border-radius:8px; margin-bottom:10px; white-space:pre-wrap}
631
+ .dwb-ok{color:var(--dwb-green); font-size:12.5px; padding:8px 12px; background:color-mix(in srgb,var(--dwb-green) 10%,transparent); border-radius:8px; margin-bottom:10px; white-space:pre-wrap}
632
+
633
+ /* \u4EBA\u5458\u8BE6\u60C5\uFF08\u56FA\u5B9A\u5E03\u5C40\xB7\u8FB9\u6846\u7EBF\u98CE\u683C\uFF1A\u65E0\u5E95\u8272\u586B\u5145\uFF0C\u4EC5\u5206\u9694\u7EBF\uFF1B\u7F16\u8F91\u6001\u9632\u6324\u538B\uFF09 */
634
+ .dwb-person-detail{width:760px; max-width:calc(100% - 48px); height:500px; display:flex; flex-direction:column; margin:36px auto 32px; background:transparent; border:none; border-radius:0; padding:0 40px}
635
+ .dwb-pd-head{flex:none; height:96px; display:flex; align-items:center; gap:18px; border-bottom:1px solid var(--dwb-border-soft)}
636
+ .dwb-pd-mid{flex:1; min-width:0}
637
+ .dwb-pd-name{font-size:20px; font-weight:600; letter-spacing:.3px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
638
+ .dwb-pd-name-input{font-size:17px; font-weight:600; max-width:300px}
639
+ .dwb-pd-sub{font-size:12.5px; color:var(--dwb-text-3); margin-top:6px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis}
640
+ .dwb-pd-meta{flex:none; height:56px; display:flex; align-items:center; gap:40px; border-bottom:1px solid var(--dwb-border-soft); overflow:hidden}
641
+ .dwb-pd-meta-item{flex:none; display:flex; align-items:center; gap:10px; font-size:13px; white-space:nowrap}
642
+ .dwb-pd-meta-item span{color:var(--dwb-text-3); font-size:12px}
643
+ .dwb-pd-meta-item b{font-weight:550; color:var(--dwb-text-2)}
644
+ .dwb-pd-model{display:inline-block; max-width:240px; overflow:hidden; text-overflow:ellipsis; vertical-align:middle}
645
+ .dwb-pd-meta-item .dwb-select{width:320px}
646
+ .dwb-pd-duty{flex:1; min-height:0; display:flex; flex-direction:column; padding:18px 0 14px}
647
+ .dwb-pd-duty .dwb-ctx-title{flex:none; margin-bottom:10px}
648
+ .dwb-pd-duty-text{flex:1; overflow-y:auto; font-size:13.5px; color:var(--dwb-text-2); line-height:2.0; white-space:pre-wrap; word-break:break-word; padding:2px 0}
649
+ .dwb-pd-duty-edit{flex:1; min-height:0; height:100%; max-height:none; resize:none; line-height:2.0}
650
+ .dwb-pd-actions{flex:none; height:72px; display:flex; align-items:center; gap:10px; border-top:1px solid var(--dwb-border-soft)}
651
+ .dwb-pd-confirm-text{font-size:12.5px; color:var(--dwb-text-2); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; min-width:0}
652
+
653
+ /* \u2705 \u4EFB\u52A1\u5B8C\u6210\u5361\u7247 */
654
+ .dwb-done-card{
655
+ align-self:stretch; margin:8px 4px; padding:14px 18px;
656
+ background:color-mix(in srgb,var(--dwb-green) 10%,transparent);
657
+ border:1px solid color-mix(in srgb,var(--dwb-green) 30%,transparent);
658
+ border-left:4px solid var(--dwb-green);
659
+ border-radius:12px; display:flex; gap:12px; align-items:flex-start;
660
+ }
661
+ /* \u{1F4CA} \u8FDB\u5EA6\u6C47\u62A5\u5361\u7247 */
662
+ .dwb-progress-card{
663
+ align-self:stretch; margin:6px 4px; padding:10px 16px;
664
+ background:color-mix(in srgb,var(--dwb-accent) 6%,transparent);
665
+ border:1px solid color-mix(in srgb,var(--dwb-accent) 20%,transparent);
666
+ border-left:3px solid var(--dwb-accent);
667
+ border-radius:10px; display:flex; gap:12px; align-items:flex-start;
668
+ font-size:13px;
669
+ }
670
+
671
+ /* \u{1F64B} \u51B3\u7B56\u5361\u9009\u9879\u6309\u94AE */
672
+ .dwb-ask-options{
673
+ display:flex; flex-wrap:wrap; gap:8px; margin-top:12px;
674
+ }
675
+ .dwb-ask-option-btn{
676
+ padding:8px 16px; border-radius:10px; cursor:pointer;
677
+ border:1.5px solid color-mix(in srgb,var(--dwb-amber,var(--dsw-alias-state-business-primary)) 50%,transparent);
678
+ background:color-mix(in srgb,var(--dwb-amber,var(--dsw-alias-state-business-primary)) 8%,transparent);
679
+ color:var(--dwb-text); font-size:13px; font-weight:500;
680
+ transition:all .15s ease; text-align:left; max-width:100%;
681
+ }
682
+ .dwb-ask-option-btn:hover{
683
+ background:color-mix(in srgb,var(--dwb-amber,var(--dsw-alias-state-business-primary)) 18%,transparent);
684
+ border-color:var(--dwb-amber,var(--dsw-alias-state-business-primary));
685
+ transform:translateY(-1px);
686
+ }
687
+ .dwb-ask-option-btn:active{transform:translateY(0)}
688
+
689
+ /* \u{1F4AD} \u6B63\u5728\u5DE5\u4F5C\u6307\u793A\u5668\uFF08typing indicator\uFF0C\u56FA\u5B9A\u5728\u8F93\u5165\u6846\u4E0A\u65B9\uFF09 */
690
+ .dwb-working{
691
+ display:flex; flex-direction:column; gap:2px;
692
+ padding:4px 16px 8px; flex:none;
693
+ border-top:1px solid color-mix(in srgb,var(--dwb-border-soft,transparent) 60%,transparent);
694
+ }
695
+ .dwb-working-row{
696
+ display:flex; align-items:center; gap:8px;
697
+ font-size:12.5px; color:var(--dwb-text-secondary);
698
+ padding:4px 12px;
699
+ background:color-mix(in srgb,var(--dsw-alias-bg-layer-1,transparent) 60%,transparent);
700
+ border-radius:10px;
701
+ animation:dwb-working-in .3s ease;
702
+ }
703
+ .dwb-working-name{font-weight:600; color:var(--dwb-text)}
704
+ .dwb-working-label{opacity:.7}
705
+ .dwb-working-dots{
706
+ display:inline-flex; gap:3px; align-items:center;
707
+ }
708
+ .dwb-working-dots i{
709
+ width:5px; height:5px; border-radius:50%;
710
+ background:var(--dwb-accent,var(--dsw-alias-state-business-primary));
711
+ animation:dwb-dot-bounce 1.2s ease-in-out infinite;
712
+ }
713
+ .dwb-working-dots i:nth-child(2){animation-delay:.15s}
714
+ .dwb-working-dots i:nth-child(3){animation-delay:.3s}
715
+ @keyframes dwb-dot-bounce{
716
+ 0%,60%,100%{transform:translateY(0);opacity:.4}
717
+ 30%{transform:translateY(-4px);opacity:1}
718
+ }
719
+ @keyframes dwb-working-in{
720
+ from{opacity:0;transform:translateY(4px)}
721
+ to{opacity:1;transform:translateY(0)}
722
+ }
723
+
724
+ /* \u26A0\uFE0F \u9700\u8981\u51B3\u7B56\u5361\u7247\uFF08dwb_ask_user \u89E6\u53D1\uFF09\xB7 \u589E\u5F3A\u7248 */
725
+ .dwb-ask-card{
726
+ align-self:stretch; margin:10px 4px; padding:16px 20px;
727
+ background:color-mix(in srgb,var(--dwb-amber) 14%,transparent);
728
+ border:2px solid color-mix(in srgb,var(--dwb-amber) 60%,transparent);
729
+ border-left:6px solid var(--dwb-amber);
730
+ border-radius:14px; display:flex; gap:14px; align-items:flex-start;
731
+ box-shadow:0 2px 12px color-mix(in srgb,var(--dwb-amber) 20%,transparent);
732
+ }
733
+ .dwb-ask-user-tag{
734
+ display:inline-flex; align-items:center; gap:4px;
735
+ padding:2px 10px; border-radius:10px;
736
+ background:color-mix(in srgb,var(--dwb-amber) 25%,transparent);
737
+ color:var(--dwb-text); font-size:11px; font-weight:600; margin-bottom:6px;
738
+ }
739
+ .dwb-ask-icon{font-size:26px; flex:none; margin-top:2px}
740
+ .dwb-ask-body{font-size:14.5px; line-height:1.8; color:var(--dwb-text); white-space:pre-wrap; word-break:break-word}
741
+
742
+ /* \u7A7A\u72B6\u6001 */
743
+ .dwb-empty-wrap{flex:1; display:flex; flex-direction:column; align-items:center; justify-content:center; padding:30px; text-align:center}
744
+ .dwb-empty-icon{font-size:44px; opacity:.55; margin-bottom:14px}
745
+ .dwb-empty-title{font-size:14.5px; color:var(--dwb-text-2); font-weight:550}
746
+ .dwb-empty-hint{font-size:12px; color:var(--dwb-text-3); margin-top:8px; line-height:1.9; white-space:pre-line}
747
+ `;
748
+
749
+ // src/client/data.jsx
750
+ var import_react = require("react");
751
+
752
+ // src/client/rpc.js
753
+ async function call(endpoint, payload = {}) {
754
+ const response = await fetch("api/dsh-workbench", {
755
+ method: "POST",
756
+ credentials: "same-origin",
757
+ headers: { "content-type": "application/json" },
758
+ body: JSON.stringify({ endpoint, payload })
759
+ });
760
+ const body = await response.json().catch(() => ({ ok: false, error: "\u54CD\u5E94\u89E3\u6790\u5931\u8D25" }));
761
+ if (!body.ok) {
762
+ throw new Error(body.error ?? `RPC \u5931\u8D25: ${endpoint}`);
763
+ }
764
+ return body.data;
765
+ }
766
+
767
+ // src/client/data.jsx
768
+ function useGroups(pollMs = 2e4) {
769
+ const [groups, setGroups] = (0, import_react.useState)([]);
770
+ const [err, setErr] = (0, import_react.useState)(null);
771
+ const load = (0, import_react.useCallback)(() => {
772
+ call("groups.list").then((d) => {
773
+ setGroups(d.groups ?? []);
774
+ setErr(null);
775
+ }).catch((e) => setErr(e.message));
776
+ }, []);
777
+ (0, import_react.useEffect)(() => {
778
+ load();
779
+ if (!pollMs) return;
780
+ const t = setInterval(load, pollMs);
781
+ return () => clearInterval(t);
782
+ }, [load, pollMs]);
783
+ return { groups, err, reload: load };
784
+ }
785
+ function useRoles(pollMs = 3e4) {
786
+ const [roles, setRoles] = (0, import_react.useState)([]);
787
+ const [presets, setPresets] = (0, import_react.useState)([]);
788
+ const [err, setErr] = (0, import_react.useState)(null);
789
+ const load = (0, import_react.useCallback)(() => {
790
+ call("roles.list").then((d) => {
791
+ setRoles(d.roles ?? []);
792
+ setPresets(d.presets ?? []);
793
+ setErr(null);
794
+ }).catch((e) => setErr(e.message));
795
+ }, []);
796
+ (0, import_react.useEffect)(() => {
797
+ load();
798
+ if (!pollMs) return;
799
+ const t = setInterval(load, pollMs);
800
+ return () => clearInterval(t);
801
+ }, [load, pollMs]);
802
+ return { roles, presets, err, reload: load };
803
+ }
804
+ function useTasks(groupId) {
805
+ const [tasks, setTasks] = (0, import_react.useState)([]);
806
+ const [err, setErr] = (0, import_react.useState)(null);
807
+ const [tick, setTick] = (0, import_react.useState)(0);
808
+ (0, import_react.useEffect)(() => {
809
+ let alive = true;
810
+ call("tasks.list", { groupId: groupId || void 0 }).then((d) => {
811
+ if (alive) {
812
+ setTasks(d.tasks ?? []);
813
+ setErr(null);
814
+ }
815
+ }).catch((e) => {
816
+ if (alive) setErr(e.message);
817
+ });
818
+ return () => {
819
+ alive = false;
820
+ };
821
+ }, [groupId, tick]);
822
+ return { tasks, err, reload: () => setTick((t) => t + 1) };
823
+ }
824
+ function loadSeen() {
825
+ try {
826
+ return JSON.parse(localStorage.getItem("dwb-seen") || "{}");
827
+ } catch {
828
+ return {};
829
+ }
830
+ }
831
+ function useSeen() {
832
+ const [seen, setSeen] = (0, import_react.useState)(loadSeen);
833
+ const markSeen = (0, import_react.useCallback)((id, seq) => {
834
+ setSeen((prev) => {
835
+ if ((prev[id] ?? 0) >= seq) return prev;
836
+ const next = { ...prev, [id]: seq };
837
+ try {
838
+ localStorage.setItem("dwb-seen", JSON.stringify(next));
839
+ } catch {
840
+ }
841
+ return next;
842
+ });
843
+ }, []);
844
+ return [seen, markSeen];
845
+ }
846
+ function useModels() {
847
+ const [models, setModels] = (0, import_react.useState)([]);
848
+ const [def, setDef] = (0, import_react.useState)("");
849
+ (0, import_react.useEffect)(() => {
850
+ call("models.list").then((d) => {
851
+ setModels(d.models ?? []);
852
+ setDef(d.default ?? "");
853
+ }).catch(() => {
854
+ });
855
+ }, []);
856
+ return { models, defaultModel: def };
857
+ }
858
+
859
+ // src/client/groups.jsx
860
+ var import_react3 = __toESM(require("react"), 1);
861
+
862
+ // src/client/ui.jsx
863
+ var import_react2 = __toESM(require("react"), 1);
864
+ var import_jsx_runtime = require("react/jsx-runtime");
865
+ function hashCode(text) {
866
+ let h = 0;
867
+ const s = String(text ?? "");
868
+ for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
869
+ return Math.abs(h);
870
+ }
871
+ function avatarClass(name2) {
872
+ return `dwb-av dwb-av${hashCode(name2) % 8}`;
873
+ }
874
+ function Avatar({ name: name2, size = 36, self = false, title }) {
875
+ const label = String(name2 || "?").trim().charAt(0) || "?";
876
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
877
+ "span",
878
+ {
879
+ title: title ?? (name2 || void 0),
880
+ className: self ? "dwb-av dwb-av-self" : avatarClass(name2),
881
+ style: {
882
+ width: size,
883
+ height: size,
884
+ borderRadius: "50%",
885
+ fontSize: Math.max(11, Math.round(size * 0.42)),
886
+ display: "inline-flex",
887
+ alignItems: "center",
888
+ justifyContent: "center",
889
+ fontWeight: 600,
890
+ flex: "none",
891
+ userSelect: "none",
892
+ lineHeight: 1
893
+ },
894
+ children: label
895
+ }
896
+ );
897
+ }
898
+ function escapeRegExp(text) {
899
+ return String(text).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
900
+ }
901
+ function renderMentions(text, names) {
902
+ const str = String(text ?? "");
903
+ if (!names || names.length === 0 || !str.includes("@")) return str;
904
+ const sorted = [...new Set(names)].filter(Boolean).sort((a, b) => b.length - a.length).map(escapeRegExp);
905
+ if (sorted.length === 0) return str;
906
+ let re;
907
+ try {
908
+ re = new RegExp(`(^|[^\\w])@(${sorted.join("|")})(?=$|[\\s\uFF0C\u3002\uFF01\uFF1F,.!?\uFF1B;:\uFF1A])`, "g");
909
+ } catch {
910
+ return str;
911
+ }
912
+ const parts = [];
913
+ let last = 0;
914
+ let m;
915
+ let key = 0;
916
+ while ((m = re.exec(str)) !== null) {
917
+ const start = m.index + m[1].length;
918
+ if (start > last) parts.push(str.slice(last, start));
919
+ parts.push(/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dwb-at", children: [
920
+ "@",
921
+ m[2]
922
+ ] }, `at-${key++}`));
923
+ last = start + 1 + m[2].length;
924
+ }
925
+ if (last < str.length) parts.push(str.slice(last));
926
+ return parts;
927
+ }
928
+ var pad = (n) => String(n).padStart(2, "0");
929
+ function fmtTimeFull(ts) {
930
+ if (!ts) return "";
931
+ const d = new Date(ts);
932
+ return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
933
+ }
934
+ function fmtListTime(ts) {
935
+ if (!ts) return "";
936
+ const d = new Date(ts);
937
+ const now = /* @__PURE__ */ new Date();
938
+ if (d.toDateString() === now.toDateString()) return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
939
+ const yest = new Date(now);
940
+ yest.setDate(now.getDate() - 1);
941
+ if (d.toDateString() === yest.toDateString()) return "\u6628\u5929";
942
+ if (d.getFullYear() === now.getFullYear()) return `${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
943
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
944
+ }
945
+ function Empty({ icon = "\u{1F5E8}", title, hint, children }) {
946
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dwb-empty-wrap", children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dwb-empty-icon", children: icon }),
948
+ title && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dwb-empty-title", children: title }),
949
+ hint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dwb-empty-hint", children: hint }),
950
+ children
951
+ ] });
952
+ }
953
+
954
+ // src/client/groups.jsx
955
+ var import_jsx_runtime2 = require("react/jsx-runtime");
956
+ function GroupColumn({ groups, activeId, seen, onSelect, onNew }) {
957
+ const [q, setQ] = (0, import_react3.useState)("");
958
+ const kw = q.trim().toLowerCase();
959
+ const sorted = [...groups].sort((a, b) => (b.lastTime ?? 0) - (a.lastTime ?? 0));
960
+ const filtered = kw ? sorted.filter((g) => g.name.toLowerCase().includes(kw)) : sorted;
961
+ const hasPendingAsk = (g) => String(g.lastMessage ?? "").startsWith("\u26A0\uFE0F");
962
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-col-list", children: [
963
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-list-head", children: [
964
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-list-title-row", children: [
965
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dwb-list-title", children: "\u6D88\u606F" }),
966
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { flex: 1 } }),
967
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "dwb-icon-btn", title: "\u65B0\u5EFA\u7FA4\u7EC4", onClick: onNew, children: "\uFF0B" })
968
+ ] }),
969
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dwb-search", placeholder: "\u641C\u7D22\u7FA4\u7EC4", value: q, onChange: (e) => setQ(e.target.value) })
970
+ ] }),
971
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-list-scroll", children: [
972
+ filtered.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-list-empty", children: groups.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
973
+ "\u8FD8\u6CA1\u6709\u7FA4\u7EC4",
974
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("br", {}),
975
+ "\u70B9\u53F3\u4E0A\u89D2 \uFF0B \u521B\u5EFA"
976
+ ] }) : "\u65E0\u5339\u914D\u7FA4\u7EC4" }),
977
+ filtered.map((g) => {
978
+ const unread = Math.max(0, (g.seq || 0) - (seen[g.id] || 0));
979
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: `dwb-gitem ${g.id === activeId ? "on" : ""}`, onClick: () => onSelect(g.id), children: [
980
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Avatar, { name: g.name, size: 40 }),
981
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dwb-gmid", children: [
982
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dwb-gname", children: g.name }),
983
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dwb-gpreview", children: [
984
+ g.lastFrom ? `${g.lastFrom}\uFF1A` : "",
985
+ g.lastMessage || "\u6682\u65E0\u6D88\u606F"
986
+ ] })
987
+ ] }),
988
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dwb-gright", children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dwb-gtime", children: [
990
+ hasPendingAsk(g) ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { color: "var(--dwb-amber)", fontSize: 15, marginRight: 4 }, children: "\u{1F64B}" }) : null,
991
+ fmtListTime(g.lastTime)
992
+ ] }),
993
+ unread > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dwb-unread", children: unread > 99 ? "99+" : unread })
994
+ ] })
995
+ ] }, g.id);
996
+ })
997
+ ] })
998
+ ] });
999
+ }
1000
+ function MemberAdminPicker({ people, members, admins, onChange }) {
1001
+ const active = people.filter((p) => p.status === "active");
1002
+ const adminId = admins[0] ?? "";
1003
+ const setAdmin = (id) => {
1004
+ const nextAdmins = id ? [id] : [];
1005
+ const nextMembers = id ? [.../* @__PURE__ */ new Set([id, ...members])] : members.filter((x) => x !== adminId);
1006
+ onChange({ members: nextMembers, admins: nextAdmins });
1007
+ };
1008
+ const toggleMember = (id) => {
1009
+ if (id === adminId) return;
1010
+ onChange({
1011
+ members: members.includes(id) ? members.filter((x) => x !== id) : [...members, id],
1012
+ admins
1013
+ });
1014
+ };
1015
+ const regulars = active.filter((p) => p.id !== adminId);
1016
+ const pickCount = members.filter((id) => id !== adminId).length;
1017
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1018
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-field", children: [
1019
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "\u7BA1\u7406\u5458\uFF08\u4E00\u540D \xB7 \u666E\u901A\u6D88\u606F\u5148\u6295\u9012\u7ED9\u7BA1\u7406\u5458\uFF09" }),
1020
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { className: "dwb-select", value: adminId, onChange: (e) => setAdmin(e.target.value), children: [
1021
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children: "\uFF08\u672A\u9009\u62E9\uFF09" }),
1022
+ active.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("option", { value: p.id, children: [
1023
+ "\u{1F451} ",
1024
+ p.name
1025
+ ] }, p.id))
1026
+ ] })
1027
+ ] }),
1028
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-field", children: [
1029
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { children: [
1030
+ "\u666E\u901A\u6210\u5458\uFF08",
1031
+ pickCount,
1032
+ "\uFF09"
1033
+ ] }),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-member-pick", children: [
1035
+ active.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-field-hint", children: "\u6682\u65E0\u53EF\u7528\u4EBA\u5458\uFF0C\u8BF7\u5148\u5230\u300C\u4EBA\u5458\u300D\u65B0\u5EFA" }),
1036
+ regulars.length === 0 && active.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-field-hint", children: "\u5168\u90E8\u4EBA\u5458\u90FD\u5DF2\u6210\u4E3A\u7BA1\u7406\u5458" }),
1037
+ regulars.map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: `dwb-pick ${members.includes(p.id) ? "on" : ""}`, children: [
1038
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { type: "checkbox", checked: members.includes(p.id), onChange: () => toggleMember(p.id) }),
1039
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Avatar, { name: p.name, size: 22 }),
1040
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: p.name })
1041
+ ] }, p.id))
1042
+ ] })
1043
+ ] })
1044
+ ] });
1045
+ }
1046
+ function NewGroupModal({ roles, onClose, onCreated }) {
1047
+ const [name2, setName] = (0, import_react3.useState)("");
1048
+ const [customDir, setCustomDir] = (0, import_react3.useState)("");
1049
+ const [members, setMembers] = (0, import_react3.useState)([]);
1050
+ const [admins, setAdmins] = (0, import_react3.useState)([]);
1051
+ const [notice, setNotice] = (0, import_react3.useState)("");
1052
+ const [err, setErr] = (0, import_react3.useState)(null);
1053
+ const [busy, setBusy] = (0, import_react3.useState)(false);
1054
+ const [picking, setPicking] = (0, import_react3.useState)(false);
1055
+ async function pickDir() {
1056
+ setPicking(true);
1057
+ setErr(null);
1058
+ try {
1059
+ const { path } = await call("directory.pick");
1060
+ if (path) setCustomDir(path);
1061
+ } catch (e) {
1062
+ setErr(`\u76EE\u5F55\u9009\u62E9\u5931\u8D25\uFF1A${e.message}`);
1063
+ }
1064
+ setPicking(false);
1065
+ }
1066
+ async function create() {
1067
+ if (admins.length === 0) {
1068
+ setErr("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u540D\u7BA1\u7406\u5458");
1069
+ return;
1070
+ }
1071
+ if (!customDir.trim()) {
1072
+ setErr("\u5FC5\u987B\u9009\u62E9\u5DE5\u4F5C\u76EE\u5F55\uFF1A\u70B9\u51FB \u{1F4C1} \u9009\u62E9\u6216\u8F93\u5165\u8DEF\u5F84");
1073
+ return;
1074
+ }
1075
+ setBusy(true);
1076
+ setErr(null);
1077
+ try {
1078
+ const { group } = await call("groups.create", {
1079
+ name: name2,
1080
+ members,
1081
+ admins,
1082
+ notice,
1083
+ customDir: customDir.trim()
1084
+ });
1085
+ onCreated(group);
1086
+ } catch (e) {
1087
+ setErr(e.message);
1088
+ setBusy(false);
1089
+ }
1090
+ }
1091
+ const hasDir = customDir.trim().length > 0;
1092
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-modal-mask", onClick: (e) => {
1093
+ if (e.target === e.currentTarget) onClose();
1094
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-modal", children: [
1095
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-modal-title", children: "\u65B0\u5EFA\u7FA4\u7EC4" }),
1096
+ err && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-err", children: err }),
1097
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-field", children: [
1098
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "\u7FA4\u7EC4\u540D\u79F0" }),
1099
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dwb-input", autoFocus: true, placeholder: "\u5982 ERP\u9879\u76EE\u3001\u767B\u5F55\u7CFB\u7EDF", value: name2, onChange: (e) => setName(e.target.value) })
1100
+ ] }),
1101
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-field", children: [
1102
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "\u5DE5\u4F5C\u76EE\u5F55\uFF08\u5FC5\u9009\uFF09" }),
1103
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", gap: 6 }, className: "dwb-dir-row", children: [
1104
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dwb-input", placeholder: "\u70B9\u51FB \u{1F4C1} \u9009\u62E9\u76EE\u5F55\uFF0C\u6216\u8F93\u5165\u8DEF\u5F84\uFF08\u4E0D\u5B58\u5728\u4F1A\u81EA\u52A8\u521B\u5EFA\uFF09", value: customDir, onChange: (e) => setCustomDir(e.target.value) }),
1105
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dwb-btn", disabled: picking, onClick: pickDir, title: "DSH \u539F\u751F\u76EE\u5F55\u9009\u62E9", children: picking ? "\u2026" : "\u{1F4C1}" })
1106
+ ] }),
1107
+ hasDir ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-field-hint", style: { color: "var(--dwb-amber)" }, children: "\u6210\u5458\u5C06\u5728\u6B64\u76EE\u5F55\u8BFB\u53D6\u8D44\u6599/\u5B58\u653E\u6210\u679C\u7269\uFF0C\u7FA4\u9644\u4EF6\u4E5F\u5B58\u4E8E\u6B64\uFF1B\u5220\u9664\u7FA4\u7EC4\u65F6\u6B64\u76EE\u5F55\u5C06\u4FDD\u7559" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-field-hint", style: { color: "var(--dwb-red)" }, children: "\u5FC5\u9009\uFF1A\u8BF7\u5148\u9009\u62E9\u5DE5\u4F5C\u76EE\u5F55\uFF0C\u5426\u5219\u65E0\u6CD5\u521B\u5EFA\u7FA4\u7EC4" })
1108
+ ] }),
1109
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MemberAdminPicker, { people: roles, members, admins, onChange: ({ members: m, admins: a }) => {
1110
+ setMembers(m);
1111
+ setAdmins(a);
1112
+ } }),
1113
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-field", children: [
1114
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "\u7FA4\u516C\u544A\uFF08\u53EF\u9009\uFF09" }),
1115
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { className: "dwb-textarea", placeholder: "\u6BCF\u4F4D\u6210\u5458\u5728\u7FA4\u91CC\u53D1\u8A00\u524D\u90FD\u4F1A\u8BFB\u5230\u516C\u544A", value: notice, onChange: (e) => setNotice(e.target.value) })
1116
+ ] }),
1117
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-modal-actions", children: [
1118
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "dwb-btn", onClick: onClose, children: "\u53D6\u6D88" }),
1119
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "dwb-btn primary", disabled: !name2.trim() || !hasDir || members.length === 0 || busy, onClick: create, children: busy ? "\u521B\u5EFA\u4E2D\u2026" : "\u521B\u5EFA\u7FA4\u7EC4" })
1120
+ ] })
1121
+ ] }) });
1122
+ }
1123
+ function GroupEditModal({ group, roles, onClose, onSaved }) {
1124
+ const [name2, setName] = (0, import_react3.useState)(group?.name ?? "");
1125
+ const [notice, setNotice] = (0, import_react3.useState)(group?.notice ?? "");
1126
+ const [members, setMembers] = (0, import_react3.useState)(group?.members ?? []);
1127
+ const [admins, setAdmins] = (0, import_react3.useState)(group?.admins ?? []);
1128
+ const [err, setErr] = (0, import_react3.useState)(null);
1129
+ const [busy, setBusy] = (0, import_react3.useState)(false);
1130
+ async function save() {
1131
+ if (admins.length === 0) {
1132
+ setErr("\u8BF7\u81F3\u5C11\u4FDD\u7559\u4E00\u540D\u7BA1\u7406\u5458");
1133
+ return;
1134
+ }
1135
+ setBusy(true);
1136
+ setErr(null);
1137
+ try {
1138
+ await call("groups.update", { id: group.id, name: name2, notice, members, admins });
1139
+ onSaved();
1140
+ } catch (e) {
1141
+ setErr(e.message);
1142
+ setBusy(false);
1143
+ }
1144
+ }
1145
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-modal-mask", onClick: (e) => {
1146
+ if (e.target === e.currentTarget) onClose();
1147
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-modal", children: [
1148
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-modal-title", children: [
1149
+ "\u7F16\u8F91\u7FA4\u7EC4 \xB7 ",
1150
+ group?.name
1151
+ ] }),
1152
+ err && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dwb-err", children: err }),
1153
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-field", children: [
1154
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "\u7FA4\u7EC4\u540D\u79F0" }),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dwb-input", value: name2, onChange: (e) => setName(e.target.value) })
1156
+ ] }),
1157
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MemberAdminPicker, { people: roles, members, admins, onChange: ({ members: m, admins: a }) => {
1158
+ setMembers(m);
1159
+ setAdmins(a);
1160
+ } }),
1161
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-field", children: [
1162
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "\u7FA4\u516C\u544A" }),
1163
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { className: "dwb-textarea", placeholder: "\u6BCF\u4F4D\u6210\u5458\u5728\u7FA4\u91CC\u53D1\u8A00\u524D\u90FD\u4F1A\u8BFB\u5230\u516C\u544A", value: notice, onChange: (e) => setNotice(e.target.value) })
1164
+ ] }),
1165
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dwb-modal-actions", children: [
1166
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "dwb-btn", onClick: onClose, children: "\u53D6\u6D88" }),
1167
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { className: "dwb-btn primary", disabled: busy, onClick: save, children: busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58" })
1168
+ ] })
1169
+ ] }) });
1170
+ }
1171
+
1172
+ // src/client/chat.jsx
1173
+ var import_react4 = __toESM(require("react"), 1);
1174
+ var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
1175
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1176
+ var SYS_RE = /^(【新任务】|【任务更新】)/;
1177
+ var ASK_RE = /^⚠️ 需要您的决策/;
1178
+ var DONE_RE = /^✅ 任务完成/;
1179
+ var PROGRESS_RE = /^📊 进度汇报/;
1180
+ var GROUP_GAP_MS = 5 * 60 * 1e3;
1181
+ function isSys(line) {
1182
+ return line.from == null && SYS_RE.test(String(line.text ?? ""));
1183
+ }
1184
+ function SysNotice({ text, names }) {
1185
+ const [open, setOpen] = (0, import_react4.useState)(false);
1186
+ const seg = String(text ?? "").split("\n");
1187
+ const head = seg[0];
1188
+ const more = seg.length > 1;
1189
+ const isTask = /^(【新任务】|【任务更新】)/.test(head);
1190
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `dwb-sysmsg ${open ? "open" : ""} ${isTask ? "task-notice" : ""}`, children: [
1191
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-sysmsg-head", onClick: () => more && setOpen((v) => !v), children: [
1192
+ isTask && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-sysmsg-badge", children: "\u{1F4CB}" }),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-sysmsg-text", children: renderMentions(head, names) }),
1194
+ more && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-sys-toggle", children: open ? "\u6536\u8D77" : `\u5C55\u5F00(${seg.length - 1}\u884C)` })
1195
+ ] }),
1196
+ open && more && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-sysmsg-body", children: renderMentions(seg.slice(1).join("\n"), names) })
1197
+ ] });
1198
+ }
1199
+ function AskCard({ text, names, groupId, onReply }) {
1200
+ const raw = text.replace(ASK_RE, "").trim();
1201
+ const lines = raw.split("\n");
1202
+ const optLine = lines.find((l) => l.startsWith("\u53EF\u9009\u9879\uFF1A") || l.includes("\u53EF\u9009\u9879\uFF1A"));
1203
+ const question = lines.filter((l) => !l.startsWith("\u53EF\u9009\u9879\uFF1A") && !l.includes("\u53EF\u9009\u9879\uFF1A")).join("\n").trim();
1204
+ let options = [];
1205
+ if (optLine) {
1206
+ const optStr = optLine.replace(/^.*可选项:/, "").trim();
1207
+ options = optStr.split("|").map((s) => s.trim()).filter(Boolean);
1208
+ }
1209
+ const handleOptionClick = (opt) => {
1210
+ const letter = opt.match(/^([A-Z])/)?.[1] ?? opt;
1211
+ if (onReply) onReply(letter);
1212
+ };
1213
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ask-card", children: [
1214
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ask-icon", children: "\u{1F64B}" }),
1215
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ask-body", children: [
1216
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ask-user-tag", children: "\u{1F514} @\u4F60 \xB7 \u8BF7\u51B3\u7B56" }),
1217
+ renderMentions(question, names),
1218
+ options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ask-options", children: options.map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "dwb-ask-option-btn", onClick: () => handleOptionClick(opt), children: opt }, i)) })
1219
+ ] })
1220
+ ] });
1221
+ }
1222
+ function MessageRow({ line, prev, names, onQuickReply }) {
1223
+ const sys = isSys(line);
1224
+ const gap = !prev || !line.time || !prev.time || line.time - prev.time > GROUP_GAP_MS;
1225
+ const prevSys = prev ? isSys(prev) : false;
1226
+ const grouped = !sys && prev && !prevSys && !gap && prev.from === line.from && prev.fromName === line.fromName;
1227
+ const self = line.from == null;
1228
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.default.Fragment, { children: [
1229
+ gap && line.time && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-time-sep", children: fmtTimeFull(line.time) }),
1230
+ sys ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SysNotice, { text: line.text, names }) : PROGRESS_RE.test(line.text) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-progress-card", children: [
1231
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ask-icon", children: "\u{1F4CA}" }),
1232
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ask-body", children: renderMentions(line.text.replace(PROGRESS_RE, "").trim(), names) })
1233
+ ] }) : DONE_RE.test(line.text) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-done-card", children: [
1234
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ask-icon", children: "\u2705" }),
1235
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ask-body", children: renderMentions(line.text.replace(DONE_RE, "").trim(), names) })
1236
+ ] }) : ASK_RE.test(line.text) ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(AskCard, { text: line.text, names, onReply: onQuickReply }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `dwb-mrow ${self ? "self" : ""} ${grouped ? "grouped" : ""}`, children: [
1237
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-mavatar", children: grouped ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Avatar, { name: self ? "\u6211" : line.fromName, self, size: 32 }) }),
1238
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dwb-mbody", children: [
1239
+ !grouped && !self && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-mname", children: line.fromName }),
1240
+ String(line.text ?? "").trim() && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-bubble", children: renderMentions(line.text, names) }),
1241
+ line.attachments?.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-mfiles", children: line.attachments.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dwb-mfile", title: f.path ?? f.name, children: [
1242
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.FileTypeIcon, { path: f.name, size: 22 }),
1243
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dwb-mfile-info", children: [
1244
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-mfile-name", children: f.name }),
1245
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dwb-mfile-size", children: [
1246
+ (0, import_dsh_client_ui_primitives.fileSizeText)(f.size ?? 0),
1247
+ " \xB7 \u7FA4\u76EE\u5F55/",
1248
+ f.relPath ?? ""
1249
+ ] })
1250
+ ] })
1251
+ ] }, `${line.id}-f${i}`)) })
1252
+ ] })
1253
+ ] })
1254
+ ] }, line.id);
1255
+ }
1256
+ function WorkingIndicator({ members }) {
1257
+ const hasNames = members && members.length > 0;
1258
+ const nameStr = hasNames ? members.map((m) => m.name).join(" / ") : "\u6210\u5458";
1259
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-working", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-working-row", children: [
1260
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dwb-working-dots", children: [
1261
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", {}),
1262
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", {}),
1263
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("i", {})
1264
+ ] }),
1265
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-working-name", children: nameStr }),
1266
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-working-label", children: "\u6B63\u5728\u5DE5\u4F5C\u4E2D\u2026" })
1267
+ ] }) });
1268
+ }
1269
+ function MessageList({ lines, names, onQuickReply, approvalContent }) {
1270
+ const ref = (0, import_react4.useRef)(null);
1271
+ (0, import_react4.useEffect)(() => {
1272
+ const el = ref.current;
1273
+ if (el) el.scrollTop = el.scrollHeight;
1274
+ }, [lines.length]);
1275
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-msgs", ref, children: [
1276
+ lines.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Empty, { icon: "\u{1F4AC}", title: "\u6682\u65E0\u6D88\u606F", hint: "\u5728\u4E0B\u65B9\u8F93\u5165\u6846\u53D1\u8A00\uFF0C\u7528 @\u4EBA\u5458\u540D \u70B9\u540D\u6210\u5458\n\u65E0\u4EBA @ \u65F6\u6295\u9012\u5168\u4F53\u6210\u5458" }),
1277
+ approvalContent ? (0, import_react4.cloneElement)(approvalContent, { lines, renderMessage: (line, prev) => (0, import_jsx_runtime3.jsx)(MessageRow, { line, prev, names, onQuickReply }, `message:${line.id}`) }) : lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MessageRow, { line, prev: i > 0 ? lines[i - 1] : null, names, onQuickReply }, line.id))
1278
+ ] });
1279
+ }
1280
+ var draftKey = (id) => `dwb:draft:${id}`;
1281
+ function loadDraft(id) {
1282
+ if (!id) return "";
1283
+ try {
1284
+ return window.localStorage.getItem(draftKey(id)) ?? "";
1285
+ } catch {
1286
+ return "";
1287
+ }
1288
+ }
1289
+ function saveDraft(id, text) {
1290
+ if (!id) return;
1291
+ try {
1292
+ if (text) window.localStorage.setItem(draftKey(id), text);
1293
+ else window.localStorage.removeItem(draftKey(id));
1294
+ } catch {
1295
+ }
1296
+ }
1297
+ function fileToBase64(file) {
1298
+ return new Promise((resolve, reject) => {
1299
+ const reader = new FileReader();
1300
+ reader.onload = () => resolve(String(reader.result).split(",")[1] ?? "");
1301
+ reader.onerror = () => reject(new Error(`\u8BFB\u53D6\u6587\u4EF6\u5931\u8D25\uFF1A${file.name}`));
1302
+ reader.readAsDataURL(file);
1303
+ });
1304
+ }
1305
+ var UPLOAD_MAX_BYTES = 10 * 1024 * 1024;
1306
+ function Composer({ group, members, onSent }) {
1307
+ const gid = group?.id ?? null;
1308
+ const [text, setText] = (0, import_react4.useState)(() => loadDraft(gid));
1309
+ const [err, setErr] = (0, import_react4.useState)(null);
1310
+ const [pickerOpen, setPicker] = (0, import_react4.useState)(false);
1311
+ const [files, setFiles] = (0, import_react4.useState)([]);
1312
+ const taRef = (0, import_react4.useRef)(null);
1313
+ const fileInputRef = (0, import_react4.useRef)(null);
1314
+ const draftForRef = (0, import_react4.useRef)(gid);
1315
+ const filesRef = (0, import_react4.useRef)([]);
1316
+ filesRef.current = files;
1317
+ (0, import_react4.useEffect)(() => {
1318
+ const stale = filesRef.current.filter((f) => f.status === "ready" && f.path);
1319
+ for (const f of stale) {
1320
+ call("files.remove", { groupId: draftForRef.current, path: f.path }).catch(() => {
1321
+ });
1322
+ }
1323
+ setFiles([]);
1324
+ draftForRef.current = gid;
1325
+ setText(loadDraft(gid));
1326
+ if (taRef.current) taRef.current.style.height = "auto";
1327
+ }, [gid]);
1328
+ (0, import_react4.useEffect)(() => {
1329
+ saveDraft(draftForRef.current, text);
1330
+ }, [text]);
1331
+ function fit() {
1332
+ const el = taRef.current;
1333
+ if (!el) return;
1334
+ el.style.height = "auto";
1335
+ el.style.height = `${Math.min(336, el.scrollHeight)}px`;
1336
+ }
1337
+ function insertMention(name2) {
1338
+ const ta = taRef.current;
1339
+ const pos = ta && ta.selectionStart != null ? ta.selectionStart : text.length;
1340
+ const next = `${text.slice(0, pos)}@${name2} ${text.slice(ta && ta.selectionEnd != null ? ta.selectionEnd : text.length)}`;
1341
+ setText(next);
1342
+ setPicker(false);
1343
+ requestAnimationFrame(() => {
1344
+ ta?.focus();
1345
+ fit();
1346
+ });
1347
+ }
1348
+ async function onPick(e) {
1349
+ const picked = [...e.target.files ?? []];
1350
+ e.target.value = "";
1351
+ if (!group) return;
1352
+ for (const file of picked) {
1353
+ if (file.size > UPLOAD_MAX_BYTES) {
1354
+ setErr(`\u300C${file.name}\u300D\u8D85\u8FC7 10MB \u4E0A\u9650`);
1355
+ continue;
1356
+ }
1357
+ const key = `${file.name}:${file.size}:${Date.now()}:${Math.random().toString(36).slice(2, 6)}`;
1358
+ setFiles((list) => [...list, { key, name: file.name, size: file.size, status: "pending" }]);
1359
+ try {
1360
+ const base64 = await fileToBase64(file);
1361
+ const d = await call("files.upload", { groupId: group.id, name: file.name, base64 });
1362
+ setFiles((list) => list.map((x) => x.key === key ? { ...x, ...d.file, status: "ready" } : x));
1363
+ } catch (e2) {
1364
+ setFiles((list) => list.map((x) => x.key === key ? { ...x, status: "error", err: e2.message } : x));
1365
+ }
1366
+ }
1367
+ }
1368
+ async function removeFile(item) {
1369
+ if (item.status === "ready" && item.path && gid) {
1370
+ try {
1371
+ await call("files.remove", { groupId: gid, path: item.path });
1372
+ } catch {
1373
+ }
1374
+ }
1375
+ setFiles((list) => list.filter((x) => x.key !== item.key));
1376
+ }
1377
+ const readyFiles = files.filter((f) => f.status === "ready");
1378
+ const hasPending = files.some((f) => f.status === "pending");
1379
+ const canSend = Boolean(group) && (text.trim().length > 0 || readyFiles.length > 0) && !hasPending;
1380
+ async function send() {
1381
+ if (!canSend) return;
1382
+ setErr(null);
1383
+ try {
1384
+ await call("messages.post", {
1385
+ groupId: group.id,
1386
+ text,
1387
+ attachments: readyFiles.map((f) => ({ name: f.name, size: f.size, path: f.path }))
1388
+ });
1389
+ setText("");
1390
+ setFiles([]);
1391
+ if (taRef.current) taRef.current.style.height = "auto";
1392
+ onSent();
1393
+ } catch (e) {
1394
+ setErr(e.message);
1395
+ }
1396
+ }
1397
+ function onKeyDown(e) {
1398
+ if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
1399
+ e.preventDefault();
1400
+ send();
1401
+ }
1402
+ }
1403
+ const mentionItems = (0, import_react4.useMemo)(() => [
1404
+ { id: "dwb-mention-label", type: "label", text: "@ \u63D0\u53CA\u6210\u5458" },
1405
+ ...members.map((m) => ({ id: m.id, icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Avatar, { name: m.name, size: 20 }), label: m.name }))
1406
+ ], [members]);
1407
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-composer", children: [
1408
+ err && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-comp-notice", role: "alert", children: err }),
1409
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-comp-card", children: [
1410
+ files.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-comp-files", children: files.map((f) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: `dwb-comp-file ${f.status}`, title: f.err ?? f.path ?? f.name, children: [
1411
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.FileTypeIcon, { path: f.name, size: 16 }),
1412
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-comp-file-name", children: f.name }),
1413
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-comp-file-size", children: f.status === "pending" ? "\u4E0A\u4F20\u4E2D\u2026" : f.status === "error" ? "\u5931\u8D25" : (0, import_dsh_client_ui_primitives.fileSizeText)(f.size) }),
1414
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1415
+ "button",
1416
+ {
1417
+ type: "button",
1418
+ className: "dwb-comp-file-x",
1419
+ "aria-label": `\u79FB\u9664 ${f.name}`,
1420
+ onClick: () => removeFile(f),
1421
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.IconCloseOutlineRegular, { size: 12 })
1422
+ }
1423
+ )
1424
+ ] }, f.key)) }),
1425
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1426
+ "textarea",
1427
+ {
1428
+ ref: taRef,
1429
+ className: "dwb-ta",
1430
+ placeholder: `\u53D1\u6D88\u606F\u5230 ${group?.name ?? "\u7FA4\u7EC4"}\u2026\uFF08\u7528 @\u4EBA\u5458\u540D \u70B9\u540D\uFF09`,
1431
+ value: text,
1432
+ rows: 1,
1433
+ onChange: (e) => {
1434
+ setText(e.target.value);
1435
+ fit();
1436
+ },
1437
+ onKeyDown
1438
+ }
1439
+ ),
1440
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-comp-row", children: [
1441
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-comp-tools", children: [
1442
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: "\u6DFB\u52A0\u9644\u4EF6\uFF08\u5B58\u5165\u7FA4\u5DE5\u4F5C\u76EE\u5F55\uFF09", side: "top", delayMs: 400, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1443
+ "button",
1444
+ {
1445
+ type: "button",
1446
+ className: "dwb-comp-add",
1447
+ "aria-label": "\u6DFB\u52A0\u9644\u4EF6",
1448
+ disabled: !group,
1449
+ onClick: () => fileInputRef.current?.click(),
1450
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.IconPlusOutlineRegular, { size: 16 })
1451
+ }
1452
+ ) }),
1453
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { ref: fileInputRef, type: "file", multiple: true, hidden: true, onChange: onPick }),
1454
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1455
+ import_dsh_client_ui_primitives.Menu,
1456
+ {
1457
+ open: pickerOpen,
1458
+ onClose: () => setPicker(false),
1459
+ items: mentionItems,
1460
+ onSelect: (id) => {
1461
+ const m = members.find((x) => x.id === id);
1462
+ if (m) insertMention(m.name);
1463
+ },
1464
+ side: "top",
1465
+ align: "start",
1466
+ portal: true,
1467
+ dense: true,
1468
+ anchor: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: "@ \u63D0\u53CA\u6210\u5458", side: "top", delayMs: 400, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1469
+ "button",
1470
+ {
1471
+ type: "button",
1472
+ className: `dwb-comp-add ${pickerOpen ? "on" : ""}`,
1473
+ "aria-label": "@ \u63D0\u53CA\u6210\u5458",
1474
+ "aria-haspopup": "menu",
1475
+ "aria-expanded": pickerOpen,
1476
+ onClick: () => setPicker((v) => !v),
1477
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.IconUsersOutlineRegular, { size: 16 })
1478
+ }
1479
+ ) })
1480
+ }
1481
+ )
1482
+ ] }),
1483
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-comp-trailing", children: [
1484
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-comp-hint", children: "Enter \u53D1\u9001 \xB7 Shift+Enter \u6362\u884C" }),
1485
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.Tooltip, { label: "\u53D1\u9001", side: "top", delayMs: 400, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dwb-comp-send", disabled: !canSend, onClick: send, "aria-label": "\u53D1\u9001", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.IconSendOutlineRegular, { size: 16 }) }) })
1486
+ ] })
1487
+ ] })
1488
+ ] })
1489
+ ] });
1490
+ }
1491
+ function ContextPanel({ group, members }) {
1492
+ const { tasks } = useTasks(group?.id);
1493
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx", children: [
1494
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-sec", children: [
1495
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-title", children: [
1496
+ "\u6210\u5458\uFF08",
1497
+ members.length,
1498
+ "\uFF09"
1499
+ ] }),
1500
+ members.map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-member-row", children: [
1501
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Avatar, { name: m.name, size: 28 }),
1502
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dwb-member-name", children: [
1503
+ (group?.admins ?? []).includes(m.id) ? "\u{1F451} " : "",
1504
+ m.name
1505
+ ] }),
1506
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-dot on", title: m.status === "active" ? "\u542F\u7528" : "\u505C\u7528" })
1507
+ ] }, m.id))
1508
+ ] }),
1509
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-sec", children: [
1510
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-title", children: [
1511
+ "\u672C\u7FA4\u4EFB\u52A1\uFF08",
1512
+ tasks.length,
1513
+ "\uFF09"
1514
+ ] }),
1515
+ tasks.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ctx-empty", children: "\u6682\u65E0\u4EFB\u52A1" }),
1516
+ tasks.map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-task", children: [
1517
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `dwb-chip p-${t.priority === "\u9AD8" ? "high" : t.priority === "\u4F4E" ? "low" : "mid"}`, children: t.priority }),
1518
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dwb-ctx-task-title", children: t.title }),
1519
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: `dwb-chip st-${t.status === "\u5DF2\u5B8C\u6210" ? "done" : t.status === "\u8FDB\u884C\u4E2D" ? "doing" : "todo"}`, children: t.status })
1520
+ ] }, t.id))
1521
+ ] }),
1522
+ (group?.notice || (group?.admins ?? []).length > 0) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-sec", children: [
1523
+ group?.notice && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1524
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ctx-title", children: "\u7FA4\u516C\u544A" }),
1525
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ctx-notice", children: group.notice })
1526
+ ] }),
1527
+ (group?.admins ?? []).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
1528
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ctx-title", style: { marginTop: group?.notice ? 12 : 0 }, children: "\u7BA1\u7406\u5458" }),
1529
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-kv", children: [
1530
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u{1F451}" }),
1531
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { children: (group.admins ?? []).map((id) => members.find((m) => m.id === id)?.name ?? id).join("\u3001") })
1532
+ ] })
1533
+ ] })
1534
+ ] }),
1535
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-sec", children: [
1536
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-ctx-title", children: "\u7FA4\u4FE1\u606F" }),
1537
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-kv", children: [
1538
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u7FA4\u7EC4\u540D" }),
1539
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { children: group?.name })
1540
+ ] }),
1541
+ group?.dir && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-ctx-kv col", children: [
1542
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: "\u5DE5\u4F5C\u76EE\u5F55" }),
1543
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { className: "dwb-ctx-dir", children: group.dir })
1544
+ ] })
1545
+ ] })
1546
+ ] });
1547
+ }
1548
+ function ChatColumn({ group, roles, showCtx, onToggleCtx, onNew, onGroupsChanged, onDeleted, useSessionStatus }) {
1549
+ const [lines, setLines] = (0, import_react4.useState)([]);
1550
+ const [err, setErr] = (0, import_react4.useState)(null);
1551
+ const [tick, setTick] = (0, import_react4.useState)(0);
1552
+ const [editOpen, setEditOpen] = (0, import_react4.useState)(false);
1553
+ const [delConfirm, setDelConfirm] = (0, import_react4.useState)(false);
1554
+ const [delBusy, setDelBusy] = (0, import_react4.useState)(false);
1555
+ const [showWorking, setShowWorking] = (0, import_react4.useState)(false);
1556
+ const [workingMembers, setWorkingMembers] = (0, import_react4.useState)([]);
1557
+ const sentAtRef = (0, import_react4.useRef)(0);
1558
+ const rolesRef = (0, import_react4.useRef)(roles);
1559
+ rolesRef.current = roles;
1560
+ (0, import_react4.useEffect)(() => {
1561
+ setEditOpen(false);
1562
+ setDelConfirm(false);
1563
+ setDelBusy(false);
1564
+ setErr(null);
1565
+ sentAtRef.current = 0;
1566
+ }, [group?.id]);
1567
+ const load = (0, import_react4.useCallback)(() => {
1568
+ if (!group) {
1569
+ setLines([]);
1570
+ setShowWorking(false);
1571
+ setWorkingMembers([]);
1572
+ return;
1573
+ }
1574
+ call("messages.transcript", { groupId: group.id }).then((d) => {
1575
+ setLines(d.lines ?? []);
1576
+ setErr(null);
1577
+ const list = d.activity ?? [];
1578
+ if (list.length > 0) {
1579
+ const rs = rolesRef.current;
1580
+ setShowWorking(true);
1581
+ setWorkingMembers(list.map((a) => ({
1582
+ roleId: a.roleId,
1583
+ name: rs.find((r) => r.id === a.roleId)?.name ?? a.roleId
1584
+ })));
1585
+ } else {
1586
+ setShowWorking(false);
1587
+ setWorkingMembers([]);
1588
+ }
1589
+ }).catch((e) => setErr(e.message));
1590
+ }, [group?.id]);
1591
+ (0, import_react4.useEffect)(() => {
1592
+ load();
1593
+ if (!group) return;
1594
+ const t = setInterval(load, 3e3);
1595
+ return () => clearInterval(t);
1596
+ }, [load, tick, group?.id]);
1597
+ const handleSent = (0, import_react4.useCallback)(() => {
1598
+ load();
1599
+ onGroupsChanged?.();
1600
+ setShowWorking(true);
1601
+ setWorkingMembers([]);
1602
+ }, [load, onGroupsChanged]);
1603
+ const handleQuickReply = (0, import_react4.useCallback)(async (letter) => {
1604
+ if (!group) return;
1605
+ try {
1606
+ await call("messages.post", { groupId: group.id, text: letter });
1607
+ handleSent();
1608
+ } catch {
1609
+ }
1610
+ }, [group?.id, handleSent]);
1611
+ async function doDelete() {
1612
+ setDelBusy(true);
1613
+ try {
1614
+ await call("groups.delete", { id: group.id });
1615
+ onGroupsChanged?.();
1616
+ onDeleted?.();
1617
+ window.setTimeout(() => {
1618
+ window.alert("\u7FA4\u7EC4\u5DF2\u5220\u9664\uFF0C\u6570\u636E\u5DF2\u5F7B\u5E95\u6E05\u9664\u3002\n\nDSH \u4FA7\u680F\u4E2D\u7684\u5DE5\u4F5C\u533A\u548C\u4F1A\u8BDD\u7F13\u5B58\u9700\u91CD\u542F DSH \u540E\u624D\u4F1A\u6D88\u5931\uFF08\u8FD9\u662F DSH \u5E73\u53F0\u7684\u663E\u793A\u7F13\u5B58\u673A\u5236\uFF0C\u4E0D\u5F71\u54CD\u6570\u636E\u5B89\u5168\uFF09\u3002");
1619
+ }, 200);
1620
+ } catch (e) {
1621
+ window.alert(e.message);
1622
+ }
1623
+ setDelBusy(false);
1624
+ }
1625
+ const members = (0, import_react4.useMemo)(
1626
+ () => (group?.members ?? []).map((id) => roles.find((r) => r.id === id)).filter(Boolean),
1627
+ [group, roles]
1628
+ );
1629
+ const names = group?.memberNames ?? [];
1630
+ if (!group) {
1631
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-col-chat", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Empty, { icon: "\u{1F5C2}", title: "\u9009\u62E9\u4E00\u4E2A\u7FA4\u7EC4\u5F00\u59CB\u534F\u4F5C", hint: "\u5728\u5DE6\u4FA7\u9009\u62E9\u7FA4\u7EC4\uFF0C\u6216\u521B\u5EFA\u65B0\u7FA4\u7EC4", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "dwb-btn primary", style: { marginTop: 12 }, onClick: onNew, children: "\uFF0B \u65B0\u5EFA\u7FA4\u7EC4" }) }) });
1632
+ }
1633
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-col-chat", children: [
1634
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-chat-head", children: [
1635
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Avatar, { name: group.name, size: 30 }),
1636
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-chat-headmid", children: [
1637
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-chat-name", children: group.name }),
1638
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-chat-sub", children: [
1639
+ members.length,
1640
+ " \u540D\u6210\u5458 \xB7 ",
1641
+ group.messageCount,
1642
+ " \u6761\u6D88\u606F"
1643
+ ] })
1644
+ ] }),
1645
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-avatar-stack", children: [
1646
+ members.slice(0, 5).map((m) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Avatar, { name: m.name, size: 22 }, m.id)),
1647
+ members.length > 5 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dwb-stack-more", children: [
1648
+ "+",
1649
+ members.length - 5
1650
+ ] })
1651
+ ] }),
1652
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dwb-icon-btn", title: "\u7F16\u8F91\u7FA4\u7EC4", onClick: () => setEditOpen(true), children: "\u270E" }),
1653
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: `dwb-icon-btn ${showCtx ? "on" : ""}`, title: "\u7FA4\u4FE1\u606F", onClick: onToggleCtx, children: "\u2630" }),
1654
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dwb-icon-btn", title: "\u5220\u9664\u7FA4\u7EC4", "aria-label": "\u5220\u9664\u7FA4\u7EC4", onClick: () => setDelConfirm(true), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_dsh_client_ui_primitives.IconTrashOutlineRegular, { size: 16 }) })
1655
+ ] }),
1656
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-chat-body", children: [
1657
+ err && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-err", style: { margin: 12 }, children: err }),
1658
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MessageList, { lines, names, onQuickReply: handleQuickReply, approvalContent: GroupApprovalPanel && useSessionStatus ? (0, import_jsx_runtime3.jsx)(GroupApprovalPanel, { groupId: group.id, useSessionStatus }, group.id) : null }),
1659
+ showCtx && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ContextPanel, { group, members })
1660
+ ] }),
1661
+ showWorking && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(WorkingIndicator, { members: workingMembers }),
1662
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Composer, { group, members, onSent: handleSent }),
1663
+ editOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1664
+ GroupEditModal,
1665
+ {
1666
+ group,
1667
+ roles,
1668
+ onClose: () => setEditOpen(false),
1669
+ onSaved: () => {
1670
+ setEditOpen(false);
1671
+ onGroupsChanged?.();
1672
+ load();
1673
+ }
1674
+ }
1675
+ ),
1676
+ delConfirm && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-modal-mask", onClick: (e) => {
1677
+ if (e.target === e.currentTarget) setDelConfirm(false);
1678
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-modal", children: [
1679
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-modal-title", children: "\u5220\u9664\u7FA4\u7EC4" }),
1680
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dwb-field", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { fontSize: 14, lineHeight: 1.8 }, children: [
1681
+ "\u786E\u5B9A\u5220\u9664\u7FA4\u7EC4\u300C",
1682
+ group.name,
1683
+ "\u300D\uFF1F",
1684
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
1685
+ "\u5C06\u540C\u65F6\u5220\u9664\uFF1A",
1686
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
1687
+ "\xB7 \u5DE5\u4F5C\u76EE\u5F55\u53CA\u5168\u90E8\u6587\u4EF6\uFF08",
1688
+ group.dir,
1689
+ "\uFF09",
1690
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
1691
+ "\xB7 \u7FA4\u5185\u6240\u6709\u4EFB\u52A1\uFF08",
1692
+ group.seq ?? 0,
1693
+ " \u6761\u6D88\u606F\u5C06\u4E00\u5E76\u6E05\u9664\uFF09",
1694
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
1695
+ "\xB7 \u8BE5\u7FA4\u7684\u5168\u90E8\u6210\u5458\u4F1A\u8BDD\u8BB0\u5F55",
1696
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("br", {}),
1697
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("b", { style: { color: "var(--dwb-red)" }, children: "\u6B64\u64CD\u4F5C\u4E0D\u53EF\u64A4\u9500\u3002" })
1698
+ ] }) }),
1699
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dwb-modal-actions", children: [
1700
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "dwb-btn", onClick: () => setDelConfirm(false), children: "\u53D6\u6D88" }),
1701
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { className: "dwb-btn danger", disabled: delBusy, onClick: doDelete, children: delBusy ? "\u5220\u9664\u4E2D\u2026" : "\u786E\u8BA4\u5220\u9664" })
1702
+ ] })
1703
+ ] }) })
1704
+ ] });
1705
+ }
1706
+
1707
+ // src/client/roles.jsx
1708
+ var import_react5 = __toESM(require("react"), 1);
1709
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1710
+ function NewRoleModal({ onClose, onCreated }) {
1711
+ const { models, defaultModel } = useModels();
1712
+ const [name2, setName] = (0, import_react5.useState)("");
1713
+ const [instructions, setInstructions] = (0, import_react5.useState)("");
1714
+ const [model, setModel] = (0, import_react5.useState)("");
1715
+ const [err, setErr] = (0, import_react5.useState)(null);
1716
+ const [busy, setBusy] = (0, import_react5.useState)(false);
1717
+ async function create() {
1718
+ setBusy(true);
1719
+ setErr(null);
1720
+ try {
1721
+ await call("roles.create", { name: name2, instructions, model: model || void 0 });
1722
+ onCreated();
1723
+ } catch (e) {
1724
+ setErr(e.message);
1725
+ setBusy(false);
1726
+ }
1727
+ }
1728
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-modal-mask", onClick: (e) => {
1729
+ if (e.target === e.currentTarget) onClose();
1730
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-modal", children: [
1731
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-modal-title", children: "\u65B0\u5EFA\u4EBA\u5458" }),
1732
+ err && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-err", children: err }),
1733
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-field", children: [
1734
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { children: "\u4EBA\u5458\u540D" }),
1735
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { className: "dwb-input", autoFocus: true, placeholder: "\u5982 \u5DE5\u7A0B\u5E08A\u3001\u6D4B\u8BD5\u7532", value: name2, onChange: (e) => setName(e.target.value) })
1736
+ ] }),
1737
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-field", children: [
1738
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("label", { children: "\u804C\u8D23\u63CF\u8FF0" }),
1739
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("textarea", { className: "dwb-textarea", placeholder: "\u8BE5\u4EBA\u5458\u8D1F\u8D23\u4EC0\u4E48\uFF08\u5982\u9700\u5B9A\u4E49\u804C\u8D23\uFF0C\u76F4\u63A5\u5728\u6B64\u63CF\u8FF0\uFF09", value: instructions, onChange: (e) => setInstructions(e.target.value) })
1740
+ ] }),
1741
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-field", children: [
1742
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { children: [
1743
+ "\u6A21\u578B",
1744
+ defaultModel ? "\uFF08\u9ED8\u8BA4 " + defaultModel + "\uFF09" : ""
1745
+ ] }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("select", { className: "dwb-select", value: model, onChange: (e) => setModel(e.target.value), children: [
1747
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", children: "\u7CFB\u7EDF\u9ED8\u8BA4" }),
1748
+ models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.id, children: m.label }, m.id))
1749
+ ] })
1750
+ ] }),
1751
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-modal-actions", children: [
1752
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn", onClick: onClose, children: "\u53D6\u6D88" }),
1753
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn primary", disabled: !name2.trim() || busy, onClick: create, children: busy ? "\u521B\u5EFA\u4E2D\u2026" : "\u521B\u5EFA" })
1754
+ ] })
1755
+ ] }) });
1756
+ }
1757
+ function RolesView({ roles, groups, reload }) {
1758
+ const [sel, setSel] = (0, import_react5.useState)(null);
1759
+ const [newOpen, setNewOpen] = (0, import_react5.useState)(false);
1760
+ const selected = roles.find((r) => r.id === sel) ?? null;
1761
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1762
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-col-list", children: [
1763
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-list-head", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-list-title-row", children: [
1764
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dwb-list-title", children: "\u4EBA\u5458" }),
1765
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 } }),
1766
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-icon-btn", title: "\u65B0\u5EFA\u4EBA\u5458", onClick: () => setNewOpen(true), children: "\uFF0B" })
1767
+ ] }) }),
1768
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-list-scroll", children: [
1769
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-org-sec", children: [
1770
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-org-sec-title", children: [
1771
+ "\u5168\u90E8\u4EBA\u5458\uFF08",
1772
+ roles.length,
1773
+ "\uFF09"
1774
+ ] }),
1775
+ roles.map((r) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", className: `dwb-gitem sm ${sel === r.id ? "on" : ""}`, onClick: () => setSel(r.id), children: [
1776
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Avatar, { name: r.name, size: 30 }),
1777
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dwb-gmid", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "dwb-gname", children: r.name }) }),
1778
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dwb-dot ${r.status === "active" ? "on" : ""}` })
1779
+ ] }, r.id))
1780
+ ] }),
1781
+ roles.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-list-empty", children: [
1782
+ "\u6682\u65E0\u4EBA\u5458",
1783
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("br", {}),
1784
+ "\u70B9\u4E0A\u65B9 \uFF0B \u65B0\u5EFA"
1785
+ ] })
1786
+ ] })
1787
+ ] }),
1788
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-col-manage", children: selected ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1789
+ PersonDetail,
1790
+ {
1791
+ person: selected,
1792
+ groupCount: groups.filter((g) => (g.members ?? []).includes(selected.id)).length,
1793
+ onChanged: reload,
1794
+ onDeleted: () => {
1795
+ setSel(null);
1796
+ reload();
1797
+ }
1798
+ },
1799
+ selected.id
1800
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Empty, { icon: "\u{1F465}", title: "\u4ECE\u5DE6\u4FA7\u9009\u62E9\u4E00\u540D\u4EBA\u5458", hint: roles.length === 0 ? "\u6682\u65E0\u4EBA\u5458\uFF0C\u70B9\u5DE6\u4E0A\u89D2 \uFF0B \u65B0\u5EFA" : "\u70B9\u51FB\u5DE6\u4FA7\u4EBA\u5458\u67E5\u770B\u8BE6\u60C5\uFF0C\u652F\u6301\u7F16\u8F91\u3001\u505C\u7528\u548C\u5220\u9664" }) }),
1801
+ newOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(NewRoleModal, { onClose: () => setNewOpen(false), onCreated: () => {
1802
+ setNewOpen(false);
1803
+ reload();
1804
+ } })
1805
+ ] });
1806
+ }
1807
+ function PersonDetail({ person, groupCount, onChanged, onDeleted }) {
1808
+ const [editing, setEditing] = (0, import_react5.useState)(false);
1809
+ const [confirmDel, setConfirmDel] = (0, import_react5.useState)(false);
1810
+ const [name2, setName] = (0, import_react5.useState)(person.name);
1811
+ const [instructions, setInstructions] = (0, import_react5.useState)(person.instructions ?? "");
1812
+ const [model, setModel] = (0, import_react5.useState)(person.model ?? "");
1813
+ const [err, setErr] = (0, import_react5.useState)(null);
1814
+ const [busy, setBusy] = (0, import_react5.useState)(false);
1815
+ const { models } = useModels();
1816
+ function startEdit() {
1817
+ setName(person.name);
1818
+ setInstructions(person.instructions ?? "");
1819
+ setModel(person.model ?? "");
1820
+ setErr(null);
1821
+ setConfirmDel(false);
1822
+ setEditing(true);
1823
+ }
1824
+ async function save() {
1825
+ setBusy(true);
1826
+ setErr(null);
1827
+ const patch = {};
1828
+ if (name2.trim() && name2 !== person.name) patch.name = name2.trim();
1829
+ if (instructions !== (person.instructions ?? "")) patch.instructions = instructions;
1830
+ if (model !== (person.model ?? "")) patch.model = model || null;
1831
+ if (Object.keys(patch).length === 0) {
1832
+ setEditing(false);
1833
+ setBusy(false);
1834
+ return;
1835
+ }
1836
+ try {
1837
+ await call("roles.update", { id: person.id, patch });
1838
+ setEditing(false);
1839
+ onChanged();
1840
+ } catch (e) {
1841
+ setErr(e.message);
1842
+ }
1843
+ setBusy(false);
1844
+ }
1845
+ async function toggleStatus() {
1846
+ setErr(null);
1847
+ try {
1848
+ await call("roles.setStatus", { id: person.id, status: person.status === "active" ? "disabled" : "active" });
1849
+ onChanged();
1850
+ } catch (e) {
1851
+ setErr(e.message);
1852
+ }
1853
+ }
1854
+ async function remove() {
1855
+ setBusy(true);
1856
+ setErr(null);
1857
+ try {
1858
+ await call("roles.delete", { id: person.id, force: true });
1859
+ onDeleted();
1860
+ } catch (e) {
1861
+ setErr(e.message);
1862
+ setBusy(false);
1863
+ setConfirmDel(false);
1864
+ }
1865
+ }
1866
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-person-detail", children: [
1867
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-pd-head", children: [
1868
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Avatar, { name: person.name, size: 64 }),
1869
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-pd-mid", children: [
1870
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { className: "dwb-input dwb-pd-name-input", value: name2, onChange: (e) => setName(e.target.value) }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-pd-name", children: person.name }),
1871
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-pd-sub", children: [
1872
+ person.status === "active" ? "\u542F\u7528\u4E2D" : "\u5DF2\u505C\u7528",
1873
+ " \xB7 ",
1874
+ groupCount,
1875
+ " \u4E2A\u7FA4\u7EC4"
1876
+ ] })
1877
+ ] }),
1878
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: `dwb-dot ${person.status === "active" ? "on" : ""}`, style: { width: 10, height: 10 } })
1879
+ ] }),
1880
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-pd-meta", children: [
1881
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-pd-meta-item", children: [
1882
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u72B6\u6001" }),
1883
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("b", { children: person.status === "active" ? "\u542F\u7528" : "\u505C\u7528" })
1884
+ ] }),
1885
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-pd-meta-item", children: [
1886
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u7FA4\u7EC4" }),
1887
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("b", { children: [
1888
+ groupCount,
1889
+ " \u4E2A"
1890
+ ] })
1891
+ ] }),
1892
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-pd-meta-item", children: [
1893
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: "\u6A21\u578B" }),
1894
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("select", { className: "dwb-select", value: model, onChange: (e) => setModel(e.target.value), children: [
1895
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", children: "\u7CFB\u7EDF\u9ED8\u8BA4" }),
1896
+ models.map((m) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: m.id, children: m.label }, m.id))
1897
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("b", { className: "dwb-pd-model", children: person.model || "\u7CFB\u7EDF\u9ED8\u8BA4" })
1898
+ ] })
1899
+ ] }),
1900
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-pd-duty", children: [
1901
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "dwb-ctx-title", children: [
1902
+ "\u804C\u8D23\u63CF\u8FF0",
1903
+ editing ? "\uFF08\u7F16\u8F91\u4E2D\uFF09" : ""
1904
+ ] }),
1905
+ editing ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1906
+ "textarea",
1907
+ {
1908
+ className: "dwb-textarea dwb-pd-duty-edit",
1909
+ value: instructions,
1910
+ placeholder: "\u8BE5\u4EBA\u5458\u8D1F\u8D23\u4EC0\u4E48\uFF08\u4F5C\u4E3A\u5E38\u9A7B\u6307\u4EE4\u6CE8\u5165\uFF09",
1911
+ onChange: (e) => setInstructions(e.target.value)
1912
+ }
1913
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-pd-duty-text", children: person.instructions || "\uFF08\u672A\u6307\u5B9A\u804C\u8D23\uFF0C\u6309\u7FA4\u7EC4\u5B89\u6392\u5DE5\u4F5C\uFF09" })
1914
+ ] }),
1915
+ err && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-err", style: { margin: "0 0 10px" }, children: err }),
1916
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "dwb-pd-actions", children: editing ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1917
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn primary", disabled: busy, onClick: save, children: busy ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58" }),
1918
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn", disabled: busy, onClick: () => {
1919
+ setEditing(false);
1920
+ setErr(null);
1921
+ }, children: "\u53D6\u6D88" })
1922
+ ] }) : confirmDel ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1923
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "dwb-pd-confirm-text", children: [
1924
+ "\u5220\u9664\u300C",
1925
+ person.name,
1926
+ "\u300D\u5E76\u79FB\u9664\u5176\u7FA4\u7EC4/\u4EFB\u52A1\u5F15\u7528\uFF1F"
1927
+ ] }),
1928
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 } }),
1929
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn danger", disabled: busy, onClick: remove, children: busy ? "\u5220\u9664\u4E2D\u2026" : "\u786E\u8BA4\u5220\u9664" }),
1930
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn", disabled: busy, onClick: () => setConfirmDel(false), children: "\u53D6\u6D88" })
1931
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1932
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn", onClick: startEdit, children: "\u7F16\u8F91" }),
1933
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn", onClick: toggleStatus, children: person.status === "active" ? "\u505C\u7528" : "\u542F\u7528" }),
1934
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1 } }),
1935
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "dwb-btn danger", onClick: () => setConfirmDel(true), children: "\u5220\u9664" })
1936
+ ] }) })
1937
+ ] });
1938
+ }
1939
+
1940
+ // src/client/tasks.jsx
1941
+ var import_react6 = __toESM(require("react"), 1);
1942
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1943
+ function TasksView({ groups, roles }) {
1944
+ const [statusF, setStatusF] = import_react6.default.useState("\u5168\u90E8");
1945
+ const [groupF, setGroupF] = import_react6.default.useState("\u5168\u90E8");
1946
+ const { tasks } = useTasks(groupF === "\u5168\u90E8" ? null : groupF);
1947
+ const filtered = statusF === "\u5168\u90E8" ? tasks : tasks.filter((t) => t.status === statusF);
1948
+ const filterBtn = (label, on, onClick) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: `dwb-filter ${on ? "on" : ""}`, onClick, children: label }, label);
1949
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
1950
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-col-list", children: [
1951
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dwb-list-head", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dwb-list-title-row", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dwb-list-title", children: "\u4EFB\u52A1" }) }) }),
1952
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-list-scroll", children: [
1953
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-org-sec", children: [
1954
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dwb-org-sec-title", children: "\u72B6\u6001" }),
1955
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dwb-filters", children: ["\u5168\u90E8", "\u5F85\u529E", "\u8FDB\u884C\u4E2D", "\u5DF2\u5B8C\u6210"].map((s) => filterBtn(s, statusF === s, () => setStatusF(s))) })
1956
+ ] }),
1957
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-org-sec", children: [
1958
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dwb-org-sec-title", children: "\u7FA4\u7EC4" }),
1959
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-filters col", children: [
1960
+ filterBtn("\u5168\u90E8\u7FA4\u7EC4", groupF === "\u5168\u90E8", () => setGroupF("\u5168\u90E8")),
1961
+ groups.map((g) => filterBtn(g.name, groupF === g.id, () => setGroupF(g.id)))
1962
+ ] })
1963
+ ] })
1964
+ ] })
1965
+ ] }),
1966
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-col-manage", children: [
1967
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-manage-head", children: [
1968
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dwb-manage-title", children: "\u4EFB\u52A1\u5217\u8868" }),
1969
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "dwb-manage-sub", children: [
1970
+ filtered.length,
1971
+ " \u4E2A\u4EFB\u52A1 \xB7 \u7531\u6210\u5458\u5728\u7FA4\u804A\u4E2D\u521B\u5EFA\u4E0E\u5206\u6D3E"
1972
+ ] })
1973
+ ] }),
1974
+ filtered.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dwb-manage-empty", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Empty, { icon: "\u{1F4CB}", title: "\u6682\u65E0\u4EFB\u52A1", hint: "\u4EFB\u52A1\u7684\u521B\u5EFA\u4E0E\u5206\u6D3E\u7531\u6210\u5458\u5728\u7FA4\u804A\u4E2D\u5B8C\u6210\n\u53EF\u5728\u7FA4\u804A\u4E2D @\u6210\u5458 \u5B89\u6392\u5DE5\u4F5C" }) }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dwb-cards", children: filtered.map((t) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: `dwb-card tall ${t.status === "\u5DF2\u5B8C\u6210" ? "off" : ""}`, children: [
1975
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-card-head", children: [
1976
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: `dwb-chip p-${t.priority === "\u9AD8" ? "high" : t.priority === "\u4F4E" ? "low" : "mid"}`, children: t.priority }),
1977
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: `dwb-chip st-${t.status === "\u5DF2\u5B8C\u6210" ? "done" : t.status === "\u8FDB\u884C\u4E2D" ? "doing" : "todo"}`, children: t.status }),
1978
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dwb-card-title", children: t.title })
1979
+ ] }),
1980
+ t.description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "dwb-card-desc", children: t.description }),
1981
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "dwb-card-foot", children: [
1982
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "dwb-card-assignees", children: [
1983
+ (t.assignees ?? []).map((id) => {
1984
+ const r = roles.find((x) => x.id === id);
1985
+ return r ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Avatar, { name: r.name, size: 20, title: r.name }, id) : null;
1986
+ }),
1987
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dwb-card-sub", children: t.assigneeNames?.join("\u3001") })
1988
+ ] }),
1989
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "dwb-card-sub", children: t.groupName })
1990
+ ] })
1991
+ ] }, t.id)) })
1992
+ ] })
1993
+ ] });
1994
+ }
1995
+
1996
+ // src/client/app.jsx
1997
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1998
+ var injected = false;
1999
+ function injectStyles() {
2000
+ if (injected) return;
2001
+ injected = true;
2002
+ const style = document.createElement("style");
2003
+ style.textContent = CSS;
2004
+ style.id = "dwb-styles";
2005
+ document.head.appendChild(style);
2006
+ }
2007
+ let workbenchTitle = () => "\u534F\u4F5C\u5DE5\u4F5C\u53F0";
2008
+ function Rail({ view, onNav, updates }) {
2009
+ const item = (id, icon, label) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("button", { type: "button", className: `dwb-rail-item ${view === id ? "on" : ""}`, onClick: () => onNav(id), title: label, children: [
2010
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dwb-rail-icon", "aria-hidden": true, children: icon }),
2011
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "dwb-rail-label", children: label })
2012
+ ] }, id);
2013
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dwb-rail", children: [
2014
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "dwb-rail-logo", title: workbenchTitle(), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_dsh_client_ui_primitives2.IconUsersOutlineRegular, { size: 22 }) }),
2015
+ item("chat", "\u{1F4AC}", "\u6D88\u606F"),
2016
+ item("tasks", "\u{1F4CB}", "\u4EFB\u52A1"),
2017
+ item("org", "\u{1F465}", "\u4EBA\u5458"),
2018
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { style: { marginTop: "auto", display: "flex", flexDirection: "column", alignItems: "center", gap: 8 }, children: [
2019
+ updates.status.available && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WorkbenchUpdateUI.UpdateCloud, { onClick: () => onNav("about") }),
2020
+ item("about", "\u24D8", WorkbenchUpdateUI.t("about"))
2021
+ ] })
2022
+ ] });
2023
+ }
2024
+ function WorkbenchView({ initialView = "chat", useSessionStatus }) {
2025
+ const [view, setView] = (0, import_react7.useState)(initialView);
2026
+ const updates = WorkbenchUpdateUI.useUpdates();
2027
+ const [activeGroupId, setActiveGroupId] = (0, import_react7.useState)(null);
2028
+ const [showCtx, setShowCtx] = (0, import_react7.useState)(false);
2029
+ const [newGroupOpen, setNewGroupOpen] = (0, import_react7.useState)(false);
2030
+ const g = useGroups();
2031
+ const r = useRoles();
2032
+ const [seen, markSeen] = useSeen();
2033
+ const activeGroup = g.groups.find((x) => x.id === activeGroupId) ?? null;
2034
+ (0, import_react7.useEffect)(() => {
2035
+ if (activeGroup) markSeen(activeGroup.id, activeGroup.seq || 0);
2036
+ }, [activeGroup?.id, activeGroup?.seq, markSeen]);
2037
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "dwb-app", children: [
2038
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Rail, { view, onNav: setView, updates }),
2039
+ view === "chat" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
2040
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2041
+ GroupColumn,
2042
+ {
2043
+ groups: g.groups,
2044
+ activeId: activeGroupId,
2045
+ seen,
2046
+ onSelect: setActiveGroupId,
2047
+ onNew: () => setNewGroupOpen(true)
2048
+ }
2049
+ ),
2050
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2051
+ ChatColumn,
2052
+ {
2053
+ group: activeGroup,
2054
+ useSessionStatus,
2055
+ roles: r.roles,
2056
+ showCtx,
2057
+ onToggleCtx: () => setShowCtx((v) => !v),
2058
+ onNew: () => setNewGroupOpen(true),
2059
+ onGroupsChanged: g.reload,
2060
+ onDeleted: () => setActiveGroupId(null)
2061
+ },
2062
+ activeGroup?.id ?? "none"
2063
+ ),
2064
+ newGroupOpen && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
2065
+ NewGroupModal,
2066
+ {
2067
+ roles: r.roles,
2068
+ onClose: () => setNewGroupOpen(false),
2069
+ onCreated: (grp) => {
2070
+ setNewGroupOpen(false);
2071
+ g.reload();
2072
+ setActiveGroupId(grp?.id ?? null);
2073
+ }
2074
+ }
2075
+ )
2076
+ ] }),
2077
+ view === "org" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RolesView, { roles: r.roles, groups: g.groups, reload: r.reload }),
2078
+ view === "tasks" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TasksView, { groups: g.groups, roles: r.roles }),
2079
+ view === "about" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WorkbenchUpdateUI.UpdateAbout, { ...updates })
2080
+ ] });
2081
+ }
2082
+
2083
+ // src/client/icon.jsx
2084
+ var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
2085
+ var import_jsx_runtime7 = require("react/jsx-runtime");
2086
+ function WorkbenchIcon({ size = 18 }) {
2087
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_dsh_client_ui_primitives3.IconUsersOutlineRegular, { size });
2088
+ }
2089
+
2090
+ // src/client/main.jsx
2091
+ var name = "dsh-collab-workbench/client";
2092
+ var GroupApprovalPanel;
2093
+ var WorkbenchUpdateUI;
2094
+ var inject = ["slots", "layout", "sessions", "locale", "uiWorkspace"];
2095
+ function apply(ctx) {
2096
+ const titleNamespace = "dsh-collab-workbench-display";
2097
+ const titleDisposers = [
2098
+ ctx.locale.register(titleNamespace, "en", { title: "Collaboration Workbench" }),
2099
+ ctx.locale.register(titleNamespace, "zh", { title: "\u534F\u4F5C\u5DE5\u4F5C\u53F0" }),
2100
+ ctx.locale.register(titleNamespace, "zh-CN", { title: "\u534F\u4F5C\u5DE5\u4F5C\u53F0" })
2101
+ ];
2102
+ const translateTitle = ctx.locale.bind(titleNamespace);
2103
+ workbenchTitle = () => translateTitle("title");
2104
+ ctx.effect(() => () => {
2105
+ workbenchTitle = () => "\u534F\u4F5C\u5DE5\u4F5C\u53F0";
2106
+ for (const dispose of titleDisposers.reverse()) dispose();
2107
+ }, "collaboration workbench: localized title");
2108
+ GroupApprovalPanel = createApprovalUI(require("react"), ctx, call);
2109
+ WorkbenchUpdateUI = createUpdateUI(require("react"), ctx, call);
2110
+ injectStyles();
2111
+ window.dwbBack = () => {
2112
+ try {
2113
+ ctx.layout?.selectPanel("conversation");
2114
+ } catch {
2115
+ }
2116
+ };
2117
+ ctx.effect(() => {
2118
+ return () => {
2119
+ delete window.dwbBack;
2120
+ };
2121
+ }, "dsh-workbench: back handler");
2122
+ ctx.slots.inject("main", () => ctx.slots.register({
2123
+ name: "main",
2124
+ key: "workbench"
2125
+ }, WorkbenchView));
2126
+ ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
2127
+ name: "sidebar.panellist",
2128
+ id: "workbench",
2129
+ order: -100,
2130
+ label: () => workbenchTitle()
2131
+ }, WorkbenchIcon));
2132
+ }
2133
+
2134
+ return module.exports;
2135
+ },
2136
+ });