@goodandready/dsh-kanban 0.2.33 → 0.2.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -22,11 +22,6 @@ window.__ModuleLoader__.load({
22
22
  const ROW_CONFIG_KEY = PKG + '#' + ROW_ID
23
23
  const COLUMN_ORDER = ['backlog', 'in-progress', 'review', 'deploy', 'cleanup', 'done']
24
24
 
25
-
26
- /**
27
- * Neighbours for card repositioning. The store operates on relative neighbours
28
- * rather than numeric indexes: `afterId` above, `beforeId` below.
29
- */
30
25
  function neighboursFor(columnTasks, dragId, index) {
31
26
  const rest = (columnTasks || []).filter((t) => t.id !== dragId)
32
27
  const clamped = Math.max(0, Math.min(index, rest.length))
@@ -40,13 +35,6 @@ window.__ModuleLoader__.load({
40
35
  return (tasks || []).filter((t) => t.column === column)
41
36
  }
42
37
 
43
- /**
44
- * Task ordering within a column.
45
- *
46
- * `manual` — server-provided order: manual order with active tasks on top.
47
- * `old`/`new` sort by creation date while preserving project grouping
48
- * and "active work first".
49
- */
50
38
  function sortTasks(tasks, order) {
51
39
  const list = tasks || []
52
40
  if (order !== 'old' && order !== 'new') return list
@@ -67,22 +55,6 @@ window.__ModuleLoader__.load({
67
55
  return out
68
56
  }
69
57
 
70
- /**
71
- * Session task batch progress: sequence index and total count.
72
- *
73
- * Orders matching the agent execution order: active task first,
74
- * followed by queue time.
75
- *
76
- * @returns {Object<string, {at: number, total: number}>} keyed by task id
77
- */
78
- /**
79
- * Apply a single batch action across all selected tasks.
80
- *
81
- * Failures on individual tasks do not abort remaining tasks;
82
- * completed count is reported to the user.
83
- *
84
- * @returns {Promise<{done: number, failed: number}>}
85
- */
86
58
  async function applyToEach(ids, act) {
87
59
  let done = 0
88
60
  let failed = 0
@@ -92,12 +64,6 @@ window.__ModuleLoader__.load({
92
64
  return { done, failed }
93
65
  }
94
66
 
95
- /**
96
- * Remembered launch settings in browser localStorage.
97
- *
98
- * Stored per user browser rather than plugin settings. Handles storage
99
- * errors gracefully if localStorage is disabled or restricted.
100
- */
101
67
  const RECALL_KEY = 'dsh-kanban:launch:'
102
68
 
103
69
  function recallLaunch(board) {
@@ -114,13 +80,6 @@ window.__ModuleLoader__.load({
114
80
  } catch { /* storage unavailable */ }
115
81
  }
116
82
 
117
- /**
118
- * Re-apply remembered preset only if still available in active runtime.
119
- *
120
- * Avoids applying obsolete profiles or permission ranks removed from the build.
121
- *
122
- * @param {Array<string>} known valid choices
123
- */
124
83
  function stillOffered(value, known, fallback) {
125
84
  return typeof value === 'string' && (known || []).indexOf(value) >= 0 ? value : fallback
126
85
  }
@@ -142,14 +101,6 @@ window.__ModuleLoader__.load({
142
101
  return out
143
102
  }
144
103
 
145
- /**
146
- * Group column tasks by assignee.
147
- *
148
- * Unassigned group ('Nobody') is placed first to highlight available work,
149
- * followed alphabetically by assignee username.
150
- *
151
- * @returns {Array<{who: string, tasks: object[]}>}
152
- */
153
104
  function groupByAssignee(tasks) {
154
105
  const by = new Map()
155
106
  for (const task of tasks || []) {
@@ -168,11 +119,6 @@ window.__ModuleLoader__.load({
168
119
  return task.repo + '#' + task.issueNumber
169
120
  }
170
121
 
171
- /**
172
- * Project repository and issue number rendered separately.
173
- *
174
- * Issue number links directly to the remote issue.
175
- */
176
122
  function taskRef(task, t) {
177
123
  if (!task || !task.repo) return null
178
124
  const number = typeof task.issueNumber === 'number' ? '#' + task.issueNumber : ''
@@ -202,11 +148,6 @@ window.__ModuleLoader__.load({
202
148
  return parts.join(' · ')
203
149
  }
204
150
 
205
- /**
206
- * Compact plan summary line for card badge: "3 of 7 · writing tests".
207
- *
208
- * Only progress numbers and current item description are shown on cards.
209
- */
210
151
  function planLine(task, t) {
211
152
  const progress = task && task.plan && task.plan.progress
212
153
  if (!progress || !progress.total) return ''
@@ -215,7 +156,6 @@ window.__ModuleLoader__.load({
215
156
  return progress.text ? counter + ' · ' + progress.text : counter
216
157
  }
217
158
 
218
- /** Status badge text. Waiting indicator is rendered separately. */
219
159
  function stateLabel(task, t) {
220
160
  const state = task && task.state
221
161
  if (state === 'running') return t('state.running')
@@ -223,26 +163,12 @@ window.__ModuleLoader__.load({
223
163
  return ''
224
164
  }
225
165
 
226
- /**
227
- * Determine whether column is collapsed.
228
- *
229
- * Empty columns collapse automatically to save horizontal space;
230
- * explicit user toggle overrides automatic behavior.
231
- *
232
- * @param {object} column column data from board response
233
- * @param {object} manual map of column id -> boolean user preference
234
- */
235
166
  function isCollapsed(column, manual) {
236
167
  const own = manual && manual[column.id]
237
168
  if (own === true || own === false) return own
238
169
  return (column.count || 0) === 0
239
170
  }
240
171
 
241
- /**
242
- * Relative elapsed time string: "5m", "3h", "3d".
243
- *
244
- * Computed client-side with full ISO timestamp displayed on hover tooltip.
245
- */
246
172
  function agoText(now, then, t) {
247
173
  if (typeof then !== 'number' || then <= 0) return ''
248
174
  const delta = Math.max(0, now - then)
@@ -254,7 +180,6 @@ window.__ModuleLoader__.load({
254
180
  return t('time.day', { n: Math.floor(hours / 24) })
255
181
  }
256
182
 
257
- /** Date formatted without time. Year is included only when not the current year. */
258
183
  function shortDate(then) {
259
184
  if (typeof then !== 'number' || then <= 0) return ''
260
185
  try {
@@ -266,19 +191,11 @@ window.__ModuleLoader__.load({
266
191
  } catch { return '' }
267
192
  }
268
193
 
269
- /** Exact timestamp for tooltip. Empty tooltip if timestamp is invalid. */
270
194
  function exactAt(then) {
271
195
  if (typeof then !== 'number' || then <= 0) return ''
272
196
  try { return new Date(then).toLocaleString() } catch { return '' }
273
197
  }
274
198
 
275
- /**
276
- * Sync telemetry status message.
277
- *
278
- * Alerts user when synchronization has halted or encountered errors.
279
- *
280
- * @returns {{tone: 'ok'|'bad'|'idle', text: string, title: string}}
281
- */
282
199
  function syncLine(sync, now, t) {
283
200
  const state = (sync && sync.state) || 'never'
284
201
  if (state === 'running') return { tone: 'idle', text: t('sync.running'), title: '' }
@@ -303,13 +220,6 @@ window.__ModuleLoader__.load({
303
220
  }
304
221
  }
305
222
 
306
- /**
307
- * Accessible text color over label background color.
308
- *
309
- * Computes perceived brightness and picks black or white accordingly.
310
- *
311
- * @param {string} hex 6-character hex code without '#'
312
- */
313
223
  function readableOn(hex) {
314
224
  const n = parseInt(String(hex || ''), 16)
315
225
  if (!isFinite(n)) return ''
@@ -319,11 +229,6 @@ window.__ModuleLoader__.load({
319
229
  return (r * 299 + g * 587 + b * 114) / 1000 > 140 ? '#111' : '#fff'
320
230
  }
321
231
 
322
- /**
323
- * Color accent stripe on left border of card for priority.
324
- *
325
- * Only applies to `priority/*` labels. Tasks without priority display no stripe.
326
- */
327
232
  function priorityColor(task) {
328
233
  const values = (task && task.facets && task.facets.priority) || []
329
234
  for (const value of values) {
@@ -333,18 +238,12 @@ window.__ModuleLoader__.load({
333
238
  return ''
334
239
  }
335
240
 
336
- /** Tag style helper. Neutral style when color is not defined. */
337
241
  function tagStyle(task, name) {
338
242
  const hex = task && task.labelColors ? task.labelColors[name] : undefined
339
243
  if (!hex) return undefined
340
244
  return { background: '#' + hex, color: readableOn(hex), borderColor: 'transparent' }
341
245
  }
342
246
 
343
- /**
344
- * Render parsed task body tree safely using React elements.
345
- *
346
- * Constructs DOM elements rather than using dangerouslySetInnerHTML to prevent XSS.
347
- */
348
247
  function renderSpans(spans, keyBase) {
349
248
  return (spans || []).map((span, i) => {
350
249
  const key = keyBase + '-' + i
@@ -385,14 +284,6 @@ window.__ModuleLoader__.load({
385
284
  })
386
285
  }
387
286
 
388
- /**
389
- * Label namespace display title.
390
- *
391
- * Falls back to raw namespace name when no translation key exists.
392
- */
393
- /**
394
- * Column title: custom override if configured, otherwise localized default.
395
- */
396
287
  function columnTitle(col, t) {
397
288
  const own = col && col.name
398
289
  return own !== undefined && own !== '' ? own : t('column.' + (col && col.id))
@@ -404,11 +295,6 @@ window.__ModuleLoader__.load({
404
295
  return text === key ? ns : text
405
296
  }
406
297
 
407
- /**
408
- * Board title.
409
- *
410
- * Default 'main' and 'simple' boards use localized titles; custom board names are preserved verbatim.
411
- */
412
298
  function boardTitle(board, t) {
413
299
  const key = 'boardName.' + (board && board.id)
414
300
  const text = t(key)
@@ -434,15 +320,8 @@ window.__ModuleLoader__.load({
434
320
  }
435
321
  }
436
322
 
437
- /** Repository facet filter key matching backend dimension. */
438
323
  const REPO_FACET = 'repo'
439
324
 
440
- /**
441
- * Check whether task matches active facet filters.
442
- *
443
- * AND across namespaces, OR within each namespace.
444
- * Uses pre-parsed task.facets from server response.
445
- */
446
325
  function matchesFilters(task, selected) {
447
326
  const keys = Object.keys(selected || {})
448
327
  for (const ns of keys) {
@@ -456,14 +335,12 @@ window.__ModuleLoader__.load({
456
335
  return true
457
336
  }
458
337
 
459
- /** True if any filter is active. */
460
338
  function anySelected(selected) {
461
339
  const keys = Object.keys(selected || {})
462
340
  for (const ns of keys) if ((selected[ns] || []).length > 0) return true
463
341
  return false
464
342
  }
465
343
 
466
- /** Toggle filter value, returning a new map for React immutability. */
467
344
  function toggleValue(selected, ns, value) {
468
345
  const current = (selected || {})[ns] || []
469
346
  const next = current.indexOf(value) >= 0
@@ -475,7 +352,6 @@ window.__ModuleLoader__.load({
475
352
  return out
476
353
  }
477
354
 
478
- /** Human-readable explanation for empty state or error. */
479
355
  function errorKey(error) {
480
356
  switch (error) {
481
357
  case 'gitea-absent': return 'error.giteaAbsent'
@@ -492,11 +368,6 @@ window.__ModuleLoader__.load({
492
368
  }
493
369
  }
494
370
 
495
- /**
496
- * Open session conversation in chat panel.
497
- *
498
- * @returns {boolean} true if session opened successfully
499
- */
500
371
  function openSession(ctx, sessionId) {
501
372
  if (!sessionId) return false
502
373
  const sessions = ctx && typeof ctx.get === 'function' ? ctx.get('sessions') : undefined
@@ -509,7 +380,6 @@ window.__ModuleLoader__.load({
509
380
  }
510
381
  }
511
382
 
512
- /** Repository option label with open issue count; marks archived repositories. */
513
383
  function repoOption(r, t) {
514
384
  const full = r.fullName || ((r.owner || '') + '/' + (r.repo || ''))
515
385
  const parts = [full]
@@ -518,16 +388,12 @@ window.__ModuleLoader__.load({
518
388
  return parts.join(' ')
519
389
  }
520
390
 
521
- /** Parse 'owner/repo' string into separate parts. */
522
391
  function splitFullName(fullName) {
523
392
  const parts = String(fullName || '').split('/')
524
393
  if (parts.length !== 2 || parts[0] === '' || parts[1] === '') return undefined
525
394
  return { owner: parts[0], repo: parts[1] }
526
395
  }
527
396
 
528
- /**
529
- * Filter cards by client-side text search query: title, repo, issue number.
530
- */
531
397
  function matchesQuery(task, query) {
532
398
  const needle = String(query || '').trim().toLowerCase()
533
399
  if (needle === '') return true
@@ -540,7 +406,6 @@ window.__ModuleLoader__.load({
540
406
  return hay.indexOf(needle) >= 0
541
407
  }
542
408
 
543
- /** Retrieve session ID for header chip. */
544
409
  function chipSessionId(props, session) {
545
410
  return String((props && props.sessionId) || (session && (session.sessionId || session.id)) || '').trim()
546
411
  }
@@ -554,7 +419,6 @@ window.__ModuleLoader__.load({
554
419
  chipSessionId, matchesQuery, splitFullName, repoOption, openSession, COLUMN_ORDER,
555
420
  }
556
421
 
557
-
558
422
  function createTimeoutSignal(ms) {
559
423
  const sig = typeof AbortSignal !== 'undefined' ? AbortSignal : (typeof globalThis !== 'undefined' ? globalThis.AbortSignal : undefined)
560
424
  if (sig && typeof sig.timeout === 'function') {
@@ -689,183 +553,7 @@ window.__ModuleLoader__.load({
689
553
  return () => clearTimer(timer)
690
554
  }
691
555
 
692
-
693
- const css =
694
- '.dkb-section-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:14px 16px;display:flex;flex-direction:column;gap:8px;margin-bottom:12px}' +'.dkb-section-title{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between;gap:8px;padding-bottom:4px;border-bottom:1px solid var(--dsw-alias-border-l2)}' +'.dkb-section-badge{font-size:11px;font-weight:500;padding:2px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary)}' +'.dkb-section-badge-ok{border-color:var(--dsw-alias-state-success-secondary,#34d399);color:var(--dsw-alias-state-success-primary,#059669);background:var(--dsw-alias-state-success-secondary,rgba(16,185,129,0.1))}' +
695
- '.dkb-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none}' +
696
- '.dkb-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}' +
697
- '.dkb-page{padding:0}' +
698
- '.dkb-page .dkb-body{border-top:none;margin:0}' +
699
- '.dkb-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;display:flex;align-items:center;gap:12px;padding:14px 16px}' +
700
- '.dkb-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}' +
701
- '.dkb-title{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}' +
702
- '.dkb-sub{color:var(--dsw-alias-label-secondary);font-size:13px}' +
703
- '.dkb-chevron{color:var(--dsw-alias-label-secondary);display:inline-flex;align-items:center;justify-content:center;flex:none;transition:transform .16s}' +
704
- '.dkb-chevronOpen{transform:rotate(180deg)}' +
705
- '.dkb-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}' +
706
- '.dkb-group{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;padding:14px 0 2px}' +
707
- '.dkb-field{display:flex;flex-direction:column;gap:6px;padding:10px 0}' +
708
- '.dkb-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}' +
709
- '.dkb-hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:1.5}' +
710
- '.dkb-input{height:34px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;box-sizing:border-box;width:100%}' +
711
- '.dkb-area{min-height:96px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;padding:8px 12px;font-size:13px;box-sizing:border-box;width:100%;resize:vertical;font:inherit}' +
712
- '.dkb-input:disabled,.dkb-area:disabled{opacity:.55}' +
713
- '.dkb-foot{border-top:1px solid var(--dsw-alias-border-l2);display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px}' +
714
- '.dkb-footSplit{justify-content:space-between}' +
715
- '.dkb-footLeft{display:flex;align-items:center;gap:8px}' +
716
- '.dkb-note{min-width:0;flex:1;margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary)}' +
717
- '.dkb-failed{min-width:0;color:var(--dsw-alias-state-error-primary);flex:1;margin:0;font-size:12px;line-height:1.5}' +
718
- '.dkb-save{appearance:none;font:inherit;cursor:pointer;border:1px solid transparent;border-radius:8px;padding:5px 14px;font-size:13px;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}' +
719
- '.dkb-discard{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:5px 14px;font-size:13px;color:var(--dsw-alias-label-secondary);background:transparent}' +
720
- '.dkb-discard:disabled,.dkb-save:disabled{opacity:.4;cursor:default}' +
721
- '.dkb-entry-clone{align-items:center;gap:8px;display:flex}' +
722
- '.dkb-entry-clone[data-active]{font-weight:600}' +
723
- '.dkb-entryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}' +
724
- '.dkb-entryLabel{text-overflow:ellipsis;overflow:hidden}' +
725
- '[data-dsh-frame][data-sidebar-collapsed] .dkb-entryLabel{display:none}' +
726
- '[data-pane=conversation],[class*=centerCol]{position:relative}' +
727
- '[data-dsh-kanban-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0}' +
728
- 'html[data-dsh-kanban-active]:not([data-dsh-ssh-active]) [data-dsh-kanban-view]{display:block}' +
729
- 'html[data-dsh-kanban-active]:not([data-dsh-ssh-active]) [data-pane=conversation]>:not([data-dsh-kanban-view]),' +
730
- 'html[data-dsh-kanban-active]:not([data-dsh-ssh-active]) [class*=centerCol]>:not([data-dsh-kanban-view]){display:none!important}' +
731
- '.dkb-screen{box-sizing:border-box;background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:12px;padding:14px 16px 56px;display:flex}' +
732
- '.dkb-bar{flex:none;align-items:center;gap:8px;display:flex;flex-wrap:wrap}' +
733
- '.dkb-bar .dkb-input,.dkb-bar .dkb-search,.dkb-bar .dkb-save,.dkb-bar .dkb-discard,.dkb-bar .dkb-facetHead,.dkb-bar .dkb-back{height:30px;box-sizing:border-box;padding-top:0;padding-bottom:0;border-radius:8px;font-size:13px;line-height:28px;display:inline-flex;align-items:center}' +
734
- '.dkb-bar .dkb-facet{display:inline-flex}' +
735
- '.dkb-back{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;align-items:center;gap:6px;padding:4px 10px;font-size:13px;display:flex;flex:none}' +
736
- '.dkb-back:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}' +
737
- '.dkb-backIcon{align-items:center;display:flex}' +
738
- '.dkb-archive{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:8px}' +
739
- '.dkb-archiveList{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}' +
740
- '.dkb-archiveRow{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;align-items:center;gap:12px;padding:8px 12px;display:flex}' +
741
- '.dkb-start{display:contents}' +
742
- '.dkb-pack{color:var(--dsw-alias-label-secondary)}' +
743
- '.dkb-who{color:var(--dsw-alias-label-secondary)}' +
744
- '.dkb-sameSession{outline:1px solid var(--dsw-alias-label-secondary);outline-offset:1px}' +
745
- '.dkb-waitCount{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:0 10px;height:30px;cursor:pointer;color:var(--dsw-alias-label-primary)}' +
746
- '.dkb-archiveOpen{background:none;border:0;padding:0;text-align:left;cursor:pointer;font:inherit}' +
747
- '.dkb-archiveTitle{flex:1;font-size:13px;color:var(--dsw-alias-label-primary)}' +
748
- '.dkb-urgent{border-left-width:3px;border-left-style:solid;padding-left:9px}' +
749
- '.dkb-cardMenu{position:relative;align-self:flex-start;opacity:0;transition:opacity .12s}' +
750
- '.dkb-taskCard:hover .dkb-cardMenu,.dkb-taskCard:focus-within .dkb-cardMenu,.dkb-taskCard:focus-visible .dkb-cardMenu{opacity:1}' +
751
- '.dkb-groupHead{appearance:none;width:100%;font:inherit;color:var(--dsw-alias-label-secondary);text-align:left;cursor:pointer;background:0 0;border:0;border-top:1px solid var(--dsw-alias-border-l2);align-items:center;gap:6px;padding:8px 2px 4px;font-size:11px;font-weight:600;display:flex}' +
752
- '.dkb-groupHead:first-child{border-top:0;padding-top:2px}' +
753
- '.dkb-groupHead:hover{color:var(--dsw-alias-label-primary)}' +
754
- '.dkb-groupCount{margin-left:auto;color:var(--dsw-alias-label-tertiary);font-weight:400}' +
755
- '.dkb-shown{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px;flex:none}' +
756
- '.dkb-moveBtn{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-tertiary);background:0 0;border:0;padding:0;font-size:11px;text-decoration:underline dotted}' +
757
- '.dkb-moveBtn:hover{color:var(--dsw-alias-label-primary)}' +
758
- '.dkb-taskCard:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}' +
759
- '.dkb-sync{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px;flex:none}' +
760
- '.dkb-syncBad{color:var(--dsw-alias-state-error-primary,#e5534b);cursor:help}' +
761
- '.dkb-facet{position:relative;flex:none}' +
762
- '.dkb-facetHead{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px 10px;font-size:13px;white-space:nowrap}' +
763
- '.dkb-facetHead:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}' +
764
- '.dkb-facetOn{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}' +
765
- '.dkb-facetList{position:absolute;top:calc(100% + 4px);left:0;z-index:60;min-width:180px;max-height:280px;overflow-y:auto;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;box-shadow:var(--dsw-shadow-lv2);padding:6px;display:flex;flex-direction:column;gap:2px}' +
766
- '.dkb-facetRow{align-items:center;gap:8px;padding:4px 6px;border-radius:6px;font-size:13px;cursor:pointer;display:flex}' +
767
- '.dkb-facetRow:hover{background:var(--dsw-alias-interactive-bg-hover)}' +
768
- '.dkb-facetName{flex:1;color:var(--dsw-alias-label-primary)}' +
769
- '.dkb-barTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;margin:0;font-size:16px;font-weight:700;margin-right:auto}' +
770
- '.dkb-cols{overscroll-behavior-inline:contain;scrollbar-color:var(--dsw-alias-border-l3) var(--dsw-alias-interactive-bg-hover);scrollbar-width:thin;flex:1;gap:12px;min-height:0;padding-bottom:6px;display:flex;overflow:auto hidden}' +
771
- '.dkb-cols::-webkit-scrollbar{height:10px}' +
772
- '.dkb-cols::-webkit-scrollbar-track{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px}' +
773
- '.dkb-cols::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l3);background-clip:content-box;border:2px solid transparent;border-radius:999px}' +
774
- '.dkb-col{flex:1 1 220px;min-width:220px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;min-height:0;display:flex;overflow:hidden}' +
775
- '.dkb-colOver{border-color:var(--dsw-alias-border-l3)}' +
776
- '.dkb-colHead{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex:none;align-items:center;gap:6px;padding:10px 12px;display:flex}' +
777
- '.dkb-colHead:hover{background:var(--dsw-alias-interactive-bg-hover)}' +
778
- '.dkb-colShut{flex:0 0 auto;min-width:0;width:40px}' +
779
- '.dkb-colShut .dkb-colHead{writing-mode:vertical-rl;height:100%;width:auto;padding:12px 10px;justify-content:flex-start;gap:10px}' +
780
- '.dkb-colShut .dkb-colHead .dkb-dot{writing-mode:horizontal-tb}' +
781
- '.dkb-colShut .dkb-count{writing-mode:horizontal-tb}' +
782
- '.dkb-dot{border-radius:50%;flex:none;width:8px;height:8px}' +
783
- '.dkb-dot[data-col=backlog]{background:var(--dsw-alias-label-tertiary)}' +
784
- '.dkb-dot[data-col=in-progress]{background:var(--dsw-alias-state-warn-primary)}' +
785
- '.dkb-dot[data-col=review]{background:var(--dsw-alias-state-business-primary)}' +
786
- '.dkb-dot[data-col=deploy]{background:var(--dsw-alias-state-business-primary)}' +
787
- '.dkb-dot[data-col=cleanup]{background:var(--dsw-alias-label-tertiary)}' +
788
- '.dkb-dot[data-col=done]{background:var(--dsw-alias-state-success-primary)}' +
789
- '.dkb-colName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:13px;font-weight:700;overflow:hidden}' +
790
- '.dkb-count{min-width:20px;text-align:center;color:var(--dsw-alias-bg-base);background:var(--dsw-alias-label-tertiary);border-radius:999px;flex:none;padding:1px 8px;font-size:12px;font-weight:600}' +
791
- '.dkb-count[data-col=in-progress]{background:var(--dsw-alias-state-warn-primary)}' +
792
- '.dkb-count[data-col=review]{background:var(--dsw-alias-state-business-primary)}' +
793
- '.dkb-count[data-col=deploy]{background:var(--dsw-alias-state-business-primary)}' +
794
- '.dkb-count[data-col=done]{background:var(--dsw-alias-state-success-primary)}' +
795
- '.dkb-countOver{background:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-label-primary-inverted,#fff)}' +
796
- '.dkb-overdue{border-color:var(--dsw-alias-state-error-primary)}' +
797
- '.dkb-dueOver{color:var(--dsw-alias-state-error-primary);font-weight:600}' +
798
- '.dkb-list{flex-direction:column;flex:1;gap:8px;min-height:0;padding:2px 8px 10px;display:flex;overflow-y:auto}' +
799
- '.dkb-empty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:24px 8px;font-size:12px}' +
800
- '.dkb-taskCard{text-align:left;background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;color:var(--dsw-alias-label-primary);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;font-family:inherit;transition:box-shadow .12s,border-color .12s,transform .12s;display:flex}' +
801
- '.dkb-taskCard:hover{box-shadow:var(--dsw-shadow-lv2);border-color:var(--dsw-alias-border-l3);transform:translateY(-1px)}' +
802
- '.dkb-taskTitle{-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:13px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}' +
803
- '.dkb-mdBody{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.55;display:flex;flex-direction:column;gap:8px;max-height:46vh;overflow-y:auto}' +
804
- '.dkb-mdPara{margin:0;overflow-wrap:anywhere}' +
805
- '.dkb-mdHead{color:var(--dsw-alias-label-primary);font-weight:700;margin:6px 0 0;line-height:1.35}' +
806
- '.dkb-mdHead1{font-size:15px}' +
807
- '.dkb-mdHead2{font-size:14px}' +
808
- '.dkb-mdHead3{font-size:13px;color:var(--dsw-alias-label-secondary)}' +
809
- '.dkb-mdList{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:3px}' +
810
- '.dkb-mdCode{margin:0;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);font-size:12px;overflow-x:auto;white-space:pre}' +
811
- '.dkb-mdQuote{margin:0;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary)}' +
812
- '.dkb-mdRule{border:0;border-top:1px solid var(--dsw-alias-border-l2);margin:2px 0;width:100%}' +
813
- '.dkb-code{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:4px;padding:0 4px;font-size:12px}' +
814
- '.dkb-issueLink{color:var(--dsw-alias-label-secondary)}' +
815
- '.dkb-taskHead{align-items:center;gap:6px;flex-wrap:wrap;display:flex}' +
816
- '.dkb-ref{align-items:baseline;gap:5px;min-width:0;display:inline-flex}' +
817
- '.dkb-refRepo{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:1px 7px;font-size:11px;font-weight:600}' +
818
- '.dkb-refNum{flex:none;color:var(--dsw-alias-label-tertiary);font-size:11px;font-family:var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);letter-spacing:.02em}' +
819
- '.dkb-refLink{text-decoration:none}' +
820
- '.dkb-refLink:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}' +
821
- '.dkb-taskRef{color:var(--dsw-alias-label-tertiary);align-items:center;gap:8px;font-size:11px;display:flex}' +
822
- '.dkb-planLine{color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.35;overflow:hidden;-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box}' +
823
- '.dkb-state{border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:0 5px;font-size:10px;line-height:16px}' +
824
- '.dkb-stateLive{border-color:var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-primary);font-weight:600}' +
825
- '.dkb-batchList{list-style:none;margin:0;padding:0;max-height:200px;overflow-y:auto;display:flex;flex-direction:column;gap:4px}' +
826
- '.dkb-batchRow{align-items:baseline;gap:8px;font-size:13px;color:var(--dsw-alias-label-primary);display:flex}' +
827
- '.dkb-pick{margin:0;flex:none;opacity:0;transition:opacity .12s}' +
828
- '.dkb-taskCard:hover .dkb-pick,.dkb-taskCard:focus-within .dkb-pick,.dkb-pickOn{opacity:1}' +
829
- '.dkb-toChat{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:0;padding:0;font-size:11px;text-decoration:underline dotted}' +
830
- '.dkb-toChat:hover{color:var(--dsw-alias-label-primary)}' +
831
- '.dkb-planList{display:flex;flex-direction:column;gap:4px;margin:0;padding:0;list-style:none}' +
832
- '.dkb-planItem{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.4;display:flex;gap:8px;align-items:baseline}' +
833
- '.dkb-planItem.dkb-planDone{color:var(--dsw-alias-label-tertiary);text-decoration:line-through}' +
834
- '.dkb-planItem.dkb-planActive{color:var(--dsw-alias-label-primary);font-weight:600;text-decoration:none}' +
835
- '.dkb-planMark{flex:none;width:14px;text-align:center}' +
836
- '.dkb-tags{display:flex;flex-wrap:wrap;gap:4px}' +
837
- '.dkb-waiting{color:var(--dsw-alias-state-warn-primary);font-weight:600}' +
838
- '.dkb-waitDot{border-radius:50%;flex:none;width:8px;height:8px;background:var(--dsw-alias-state-warn-primary);display:inline-block}' +
839
- '.dkb-tag{white-space:nowrap;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover);border:1px solid transparent;border-radius:999px;padding:1px 7px;font-size:11px;line-height:1.5;font-weight:500}' +
840
- '.dkb-modal{position:absolute;inset:0;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.4));display:flex;align-items:center;justify-content:center;padding:24px 24px 84px;box-sizing:border-box;z-index:70}' +
841
- '.dkb-panel{width:min(720px,100%);max-height:100%;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;box-sizing:border-box;overflow-y:auto;display:flex;flex-direction:column;gap:12px}' +
842
- '@media (min-width: 960px){.dkb-panelWide{width:min(1100px,96%)}.dkb-drawerGrid{display:grid;grid-template-columns:300px 1fr;gap:20px;align-items:start}}' +
843
- '.dkb-drawerSidebar{display:flex;flex-direction:column;gap:12px;border-right:1px solid var(--dsw-alias-border-l2);padding-right:16px}' +
844
- '.dkb-drawerMain{display:flex;flex-direction:column;gap:12px;min-width:0}' +
845
- '.dkb-panelBody{white-space:pre-wrap;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5;margin:0}' +
846
- '.dkb-log{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}' +
847
- '.dkb-logRow{color:var(--dsw-alias-label-secondary);font-size:12px}' +
848
- '.dkb-dialog{position:absolute;inset:0;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.4));display:flex;align-items:center;justify-content:center;z-index:80}' +
849
- '.dkb-dialogBox{width:min(560px,94%);max-height:86%;overflow-y:auto;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:10px}' +
850
- '.dkb-issue{appearance:none;font:inherit;text-align:left;cursor:pointer;background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 10px;color:var(--dsw-alias-label-primary);font-size:13px}' +
851
- '.dkb-issueUsed{opacity:.5}' +
852
- '.dkb-row{display:flex;gap:8px;align-items:center}' +
853
- '.dkb-search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 240px;padding:6px 10px;font-size:13px}' +
854
- '.dkb-search::placeholder{color:var(--dsw-alias-label-tertiary)}' +
855
- '.dkb-slot{height:2px;border-radius:2px;background:transparent;flex:none}' +
856
- '.dkb-slotOn{background:var(--dsw-alias-state-business-primary)}' +
857
- '.dkb-danger{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;padding:5px 14px;font-size:13px;color:var(--dsw-alias-state-error-primary);background:transparent}' +
858
- '.dkb-danger:hover{background:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-bg-base)}' +
859
- '.dkb-quickFilterBar{display:flex;gap:6px;align-items:center;flex-wrap:wrap;padding:4px 0}' +
860
- '.dkb-filterChip{appearance:none;font:inherit;cursor:pointer;background:var(--dsw-alias-interactive-bg-hover);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:3px 10px;font-size:12px;color:var(--dsw-alias-label-secondary);transition:all .15s}' +
861
- '.dkb-filterChip:hover{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary)}' +
862
- '.dkb-filterChipActive{background:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary-inverted,#fff);border-color:transparent;font-weight:600}' +
863
- '.dkb-epicTag{background:color-mix(in srgb, var(--dsw-alias-state-business-primary) 12%, transparent);color:var(--dsw-alias-state-business-primary,#0969da);font-weight:600}' +
864
- '.dkb-progressBar{height:4px;background:var(--dsw-alias-border-l2);border-radius:2px;overflow:hidden;margin:4px 0}' +
865
- '.dkb-progressFill{height:100%;background:var(--dsw-alias-state-success-primary,#16a34a);transition:width .2s}' +
866
- '.dkb-consolePre{background:var(--dsw-alias-bg-layer-1,#0d1117);color:var(--dsw-alias-state-business-primary,#58a6ff);border:1px solid var(--dsw-alias-border-l2);padding:12px;border-radius:8px;font-family:monospace;font-size:11px;max-height:260px;overflow-y:auto;white-space:pre-wrap}'
867
-
868
-
556
+ const css = '.dkb-section-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:10px;padding:14px 16px;display:flex;flex-direction:column;gap:8px;margin-bottom:12px}.dkb-section-title{font-size:14px;font-weight:600;color:var(--dsw-alias-label-primary);display:flex;align-items:center;justify-content:space-between;gap:8px;padding-bottom:4px;border-bottom:1px solid var(--dsw-alias-border-l2)}.dkb-section-badge{font-size:11px;font-weight:500;padding:2px 8px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary)}.dkb-section-badge-ok{border-color:var(--dsw-alias-state-success-secondary,#34d399);color:var(--dsw-alias-state-success-primary,#059669);background:var(--dsw-alias-state-success-secondary,rgba(16,185,129,0.1))}.dkb-card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none}.dkb-cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.dkb-page{padding:0}.dkb-page .dkb-body{border-top:none;margin:0}.dkb-head{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;display:flex;align-items:center;gap:12px;padding:14px 16px}.dkb-headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.dkb-title{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.dkb-sub{color:var(--dsw-alias-label-secondary);font-size:13px}.dkb-chevron{color:var(--dsw-alias-label-secondary);display:inline-flex;align-items:center;justify-content:center;flex:none;transition:transform .16s}.dkb-chevronOpen{transform:rotate(180deg)}.dkb-body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.dkb-group{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;padding:14px 0 2px}.dkb-field{display:flex;flex-direction:column;gap:6px;padding:10px 0}.dkb-label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.dkb-hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:12px;line-height:1.5}.dkb-input{height:34px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;box-sizing:border-box;width:100%}.dkb-area{min-height:96px;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);border-radius:8px;padding:8px 12px;font-size:13px;box-sizing:border-box;width:100%;resize:vertical;font:inherit}.dkb-input:disabled,.dkb-area:disabled{opacity:.55}.dkb-foot{border-top:1px solid var(--dsw-alias-border-l2);display:flex;justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px}.dkb-footSplit{justify-content:space-between}.dkb-footLeft{display:flex;align-items:center;gap:8px}.dkb-note{min-width:0;flex:1;margin:0;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary)}.dkb-failed{min-width:0;color:var(--dsw-alias-state-error-primary);flex:1;margin:0;font-size:12px;line-height:1.5}.dkb-save{appearance:none;font:inherit;cursor:pointer;border:1px solid transparent;border-radius:8px;padding:5px 14px;font-size:13px;background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.dkb-discard{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:5px 14px;font-size:13px;color:var(--dsw-alias-label-secondary);background:transparent}.dkb-discard:disabled,.dkb-save:disabled{opacity:.4;cursor:default}.dkb-entry-clone{align-items:center;gap:8px;display:flex}.dkb-entry-clone[data-active]{font-weight:600}.dkb-entryIcon{flex:none;justify-content:center;align-items:center;display:inline-flex}.dkb-entryLabel{text-overflow:ellipsis;overflow:hidden}[data-dsh-frame][data-sidebar-collapsed] .dkb-entryLabel{display:none}[data-pane=conversation],[class*=centerCol]{position:relative}[data-dsh-kanban-view]{z-index:60;background:var(--dsw-alias-bg-base);display:none;position:absolute;inset:0}html[data-dsh-kanban-active]:not([data-dsh-ssh-active]) [data-dsh-kanban-view]{display:block}html[data-dsh-kanban-active]:not([data-dsh-ssh-active]) [data-pane=conversation]>:not([data-dsh-kanban-view]),html[data-dsh-kanban-active]:not([data-dsh-ssh-active]) [class*=centerCol]>:not([data-dsh-kanban-view]){display:none!important}.dkb-screen{box-sizing:border-box;background:var(--dsw-alias-bg-base);min-width:0;height:100%;min-height:0;color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);flex-direction:column;gap:12px;padding:14px 16px 56px;display:flex}.dkb-bar{flex:none;align-items:center;gap:8px;display:flex;flex-wrap:wrap}.dkb-bar .dkb-input,.dkb-bar .dkb-search,.dkb-bar .dkb-save,.dkb-bar .dkb-discard,.dkb-bar .dkb-facetHead,.dkb-bar .dkb-back{height:30px;box-sizing:border-box;padding-top:0;padding-bottom:0;border-radius:8px;font-size:13px;line-height:28px;display:inline-flex;align-items:center}.dkb-bar .dkb-facet{display:inline-flex}.dkb-back{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;align-items:center;gap:6px;padding:4px 10px;font-size:13px;display:flex;flex:none}.dkb-back:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dkb-backIcon{align-items:center;display:flex}.dkb-archive{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:8px}.dkb-archiveList{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:6px}.dkb-archiveRow{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:10px;align-items:center;gap:12px;padding:8px 12px;display:flex}.dkb-start{display:contents}.dkb-pack{color:var(--dsw-alias-label-secondary)}.dkb-who{color:var(--dsw-alias-label-secondary)}.dkb-sameSession{outline:1px solid var(--dsw-alias-label-secondary);outline-offset:1px}.dkb-waitCount{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:8px;padding:0 10px;height:30px;cursor:pointer;color:var(--dsw-alias-label-primary)}.dkb-archiveOpen{background:none;border:0;padding:0;text-align:left;cursor:pointer;font:inherit}.dkb-archiveTitle{flex:1;font-size:13px;color:var(--dsw-alias-label-primary)}.dkb-urgent{border-left-width:3px;border-left-style:solid;padding-left:9px}.dkb-cardMenu{position:relative;align-self:flex-start;opacity:0;transition:opacity .12s}.dkb-taskCard:hover .dkb-cardMenu,.dkb-taskCard:focus-within .dkb-cardMenu,.dkb-taskCard:focus-visible .dkb-cardMenu{opacity:1}.dkb-groupHead{appearance:none;width:100%;font:inherit;color:var(--dsw-alias-label-secondary);text-align:left;cursor:pointer;background:0 0;border:0;border-top:1px solid var(--dsw-alias-border-l2);align-items:center;gap:6px;padding:8px 2px 4px;font-size:11px;font-weight:600;display:flex}.dkb-groupHead:first-child{border-top:0;padding-top:2px}.dkb-groupHead:hover{color:var(--dsw-alias-label-primary)}.dkb-groupCount{margin-left:auto;color:var(--dsw-alias-label-tertiary);font-weight:400}.dkb-shown{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px;flex:none}.dkb-moveBtn{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-tertiary);background:0 0;border:0;padding:0;font-size:11px;text-decoration:underline dotted}.dkb-moveBtn:hover{color:var(--dsw-alias-label-primary)}.dkb-taskCard:focus-visible{outline:2px solid var(--dsw-alias-border-l3);outline-offset:2px}.dkb-sync{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px;flex:none}.dkb-syncBad{color:var(--dsw-alias-state-error-primary,#e5534b);cursor:help}.dkb-facet{position:relative;flex:none}.dkb-facetHead{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:4px 10px;font-size:13px;white-space:nowrap}.dkb-facetHead:hover{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-interactive-bg-hover)}.dkb-facetOn{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l3);background:var(--dsw-alias-interactive-bg-hover)}.dkb-facetList{position:absolute;top:calc(100% + 4px);left:0;z-index:60;min-width:180px;max-height:280px;overflow-y:auto;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;box-shadow:var(--dsw-shadow-lv2);padding:6px;display:flex;flex-direction:column;gap:2px}.dkb-facetRow{align-items:center;gap:8px;padding:4px 6px;border-radius:6px;font-size:13px;cursor:pointer;display:flex}.dkb-facetRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.dkb-facetName{flex:1;color:var(--dsw-alias-label-primary)}.dkb-barTitle{color:var(--dsw-alias-label-primary);white-space:nowrap;margin:0;font-size:16px;font-weight:700;margin-right:auto}.dkb-cols{overscroll-behavior-inline:contain;scrollbar-color:var(--dsw-alias-border-l3) var(--dsw-alias-interactive-bg-hover);scrollbar-width:thin;flex:1;gap:12px;min-height:0;padding-bottom:6px;display:flex;overflow:auto hidden}.dkb-cols::-webkit-scrollbar{height:10px}.dkb-cols::-webkit-scrollbar-track{background:var(--dsw-alias-interactive-bg-hover);border-radius:999px}.dkb-cols::-webkit-scrollbar-thumb{background:var(--dsw-alias-border-l3);background-clip:content-box;border:2px solid transparent;border-radius:999px}.dkb-col{flex:1 1 220px;min-width:220px;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l1);border-radius:12px;flex-direction:column;min-height:0;display:flex;overflow:hidden}.dkb-colOver{border-color:var(--dsw-alias-border-l3)}.dkb-colHead{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;flex:none;align-items:center;gap:6px;padding:10px 12px;display:flex}.dkb-colHead:hover{background:var(--dsw-alias-interactive-bg-hover)}.dkb-colShut{flex:0 0 auto;min-width:0;width:40px}.dkb-colShut .dkb-colHead{writing-mode:vertical-rl;height:100%;width:auto;padding:12px 10px;justify-content:flex-start;gap:10px}.dkb-colShut .dkb-colHead .dkb-dot{writing-mode:horizontal-tb}.dkb-colShut .dkb-count{writing-mode:horizontal-tb}.dkb-dot{border-radius:50%;flex:none;width:8px;height:8px}.dkb-dot[data-col=backlog]{background:var(--dsw-alias-label-tertiary)}.dkb-dot[data-col=in-progress]{background:var(--dsw-alias-state-warn-primary)}.dkb-dot[data-col=review]{background:var(--dsw-alias-state-business-primary)}.dkb-dot[data-col=deploy]{background:var(--dsw-alias-state-business-primary)}.dkb-dot[data-col=cleanup]{background:var(--dsw-alias-label-tertiary)}.dkb-dot[data-col=done]{background:var(--dsw-alias-state-success-primary)}.dkb-colName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:13px;font-weight:700;overflow:hidden}.dkb-count{min-width:20px;text-align:center;color:var(--dsw-alias-bg-base);background:var(--dsw-alias-label-tertiary);border-radius:999px;flex:none;padding:1px 8px;font-size:12px;font-weight:600}.dkb-count[data-col=in-progress]{background:var(--dsw-alias-state-warn-primary)}.dkb-count[data-col=review]{background:var(--dsw-alias-state-business-primary)}.dkb-count[data-col=deploy]{background:var(--dsw-alias-state-business-primary)}.dkb-count[data-col=done]{background:var(--dsw-alias-state-success-primary)}.dkb-countOver{background:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-label-primary-inverted,#fff)}.dkb-overdue{border-color:var(--dsw-alias-state-error-primary)}.dkb-dueOver{color:var(--dsw-alias-state-error-primary);font-weight:600}.dkb-list{flex-direction:column;flex:1;gap:8px;min-height:0;padding:2px 8px 10px;display:flex;overflow-y:auto}.dkb-empty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:24px 8px;font-size:12px}.dkb-taskCard{text-align:left;background:var(--dsw-alias-bg-base);border:1px solid var(--dsw-alias-border-l2);cursor:pointer;color:var(--dsw-alias-label-primary);border-radius:10px;flex-direction:column;gap:6px;padding:10px 12px;font-family:inherit;transition:box-shadow .12s,border-color .12s,transform .12s;display:flex}.dkb-taskCard:hover{box-shadow:var(--dsw-shadow-lv2);border-color:var(--dsw-alias-border-l3);transform:translateY(-1px)}.dkb-taskTitle{-webkit-line-clamp:2;-webkit-box-orient:vertical;font-size:13px;font-weight:600;line-height:1.35;display:-webkit-box;overflow:hidden}.dkb-mdBody{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.55;display:flex;flex-direction:column;gap:8px;max-height:46vh;overflow-y:auto}.dkb-mdPara{margin:0;overflow-wrap:anywhere}.dkb-mdHead{color:var(--dsw-alias-label-primary);font-weight:700;margin:6px 0 0;line-height:1.35}.dkb-mdHead1{font-size:15px}.dkb-mdHead2{font-size:14px}.dkb-mdHead3{font-size:13px;color:var(--dsw-alias-label-secondary)}.dkb-mdList{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:3px}.dkb-mdCode{margin:0;padding:8px 10px;border-radius:8px;background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);font-size:12px;overflow-x:auto;white-space:pre}.dkb-mdQuote{margin:0;padding-left:10px;border-left:2px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary)}.dkb-mdRule{border:0;border-top:1px solid var(--dsw-alias-border-l2);margin:2px 0;width:100%}.dkb-code{background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:4px;padding:0 4px;font-size:12px}.dkb-issueLink{color:var(--dsw-alias-label-secondary)}.dkb-taskHead{align-items:center;gap:6px;flex-wrap:wrap;display:flex}.dkb-ref{align-items:baseline;gap:5px;min-width:0;display:inline-flex}.dkb-refRepo{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:1px 7px;font-size:11px;font-weight:600}.dkb-refNum{flex:none;color:var(--dsw-alias-label-tertiary);font-size:11px;font-family:var(--dsw-font-family-mono,ui-monospace,SFMono-Regular,Menlo,monospace);letter-spacing:.02em}.dkb-refLink{text-decoration:none}.dkb-refLink:hover{color:var(--dsw-alias-label-primary);text-decoration:underline}.dkb-taskRef{color:var(--dsw-alias-label-tertiary);align-items:center;gap:8px;font-size:11px;display:flex}.dkb-planLine{color:var(--dsw-alias-label-secondary);font-size:11px;line-height:1.35;overflow:hidden;-webkit-line-clamp:2;-webkit-box-orient:vertical;display:-webkit-box}.dkb-state{border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:0 5px;font-size:10px;line-height:16px}.dkb-stateLive{border-color:var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-state-warn-primary);font-weight:600}.dkb-batchList{list-style:none;margin:0;padding:0;max-height:200px;overflow-y:auto;display:flex;flex-direction:column;gap:4px}.dkb-batchRow{align-items:baseline;gap:8px;font-size:13px;color:var(--dsw-alias-label-primary);display:flex}.dkb-pick{margin:0;flex:none;opacity:0;transition:opacity .12s}.dkb-taskCard:hover .dkb-pick,.dkb-taskCard:focus-within .dkb-pick,.dkb-pickOn{opacity:1}.dkb-toChat{appearance:none;font:inherit;cursor:pointer;color:var(--dsw-alias-label-secondary);background:0 0;border:0;padding:0;font-size:11px;text-decoration:underline dotted}.dkb-toChat:hover{color:var(--dsw-alias-label-primary)}.dkb-planList{display:flex;flex-direction:column;gap:4px;margin:0;padding:0;list-style:none}.dkb-planItem{color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.4;display:flex;gap:8px;align-items:baseline}.dkb-planItem.dkb-planDone{color:var(--dsw-alias-label-tertiary);text-decoration:line-through}.dkb-planItem.dkb-planActive{color:var(--dsw-alias-label-primary);font-weight:600;text-decoration:none}.dkb-planMark{flex:none;width:14px;text-align:center}.dkb-tags{display:flex;flex-wrap:wrap;gap:4px}.dkb-waiting{color:var(--dsw-alias-state-warn-primary);font-weight:600}.dkb-waitDot{border-radius:50%;flex:none;width:8px;height:8px;background:var(--dsw-alias-state-warn-primary);display:inline-block}.dkb-tag{white-space:nowrap;color:var(--dsw-alias-label-secondary);background:var(--dsw-alias-interactive-bg-hover);border:1px solid transparent;border-radius:999px;padding:1px 7px;font-size:11px;line-height:1.5;font-weight:500}.dkb-modal{position:absolute;inset:0;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.4));display:flex;align-items:center;justify-content:center;padding:24px 24px 84px;box-sizing:border-box;z-index:70}.dkb-panel{width:min(720px,100%);max-height:100%;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;box-sizing:border-box;overflow-y:auto;display:flex;flex-direction:column;gap:12px}@media (min-width:960px){.dkb-panelWide{width:min(1100px,96%)}.dkb-drawerGrid{display:grid;grid-template-columns:300px 1fr;gap:20px;align-items:start}}.dkb-drawerSidebar{display:flex;flex-direction:column;gap:12px;border-right:1px solid var(--dsw-alias-border-l2);padding-right:16px}.dkb-drawerMain{display:flex;flex-direction:column;gap:12px;min-width:0}.dkb-panelBody{white-space:pre-wrap;color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5;margin:0}.dkb-log{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}.dkb-logRow{color:var(--dsw-alias-label-secondary);font-size:12px}.dkb-dialog{position:absolute;inset:0;background:var(--dsw-alias-bg-mask-1,rgba(0,0,0,.4));display:flex;align-items:center;justify-content:center;z-index:80}.dkb-dialogBox{width:min(560px,94%);max-height:86%;overflow-y:auto;background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:12px;padding:16px;display:flex;flex-direction:column;gap:10px}.dkb-issue{appearance:none;font:inherit;text-align:left;cursor:pointer;background:0 0;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;padding:8px 10px;color:var(--dsw-alias-label-primary);font-size:13px}.dkb-issueUsed{opacity:.5}.dkb-row{display:flex;gap:8px;align-items:center}.dkb-search{min-width:120px;color:var(--dsw-alias-label-primary);background:var(--dsw-specific-input-major);border:1px solid var(--dsw-alias-border-l2);border-radius:8px;outline:none;flex:0 240px;padding:6px 10px;font-size:13px}.dkb-search::placeholder{color:var(--dsw-alias-label-tertiary)}.dkb-slot{height:2px;border-radius:2px;background:transparent;flex:none}.dkb-slotOn{background:var(--dsw-alias-state-business-primary)}.dkb-danger{appearance:none;font:inherit;cursor:pointer;border:1px solid var(--dsw-alias-state-error-primary);border-radius:8px;padding:5px 14px;font-size:13px;color:var(--dsw-alias-state-error-primary);background:transparent}.dkb-danger:hover{background:var(--dsw-alias-state-error-primary);color:var(--dsw-alias-bg-base)}.dkb-quickFilterBar{display:flex;gap:6px;align-items:center;flex-wrap:wrap;padding:4px 0}.dkb-filterChip{appearance:none;font:inherit;cursor:pointer;background:var(--dsw-alias-interactive-bg-hover);border:1px solid var(--dsw-alias-border-l2);border-radius:999px;padding:3px 10px;font-size:12px;color:var(--dsw-alias-label-secondary);transition:all .15s}.dkb-filterChip:hover{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary)}.dkb-filterChipActive{background:var(--dsw-alias-state-business-primary);color:var(--dsw-alias-label-primary-inverted,#fff);border-color:transparent;font-weight:600}.dkb-epicTag{background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 12%,transparent);color:var(--dsw-alias-state-business-primary,#0969da);font-weight:600}.dkb-progressBar{height:4px;background:var(--dsw-alias-border-l2);border-radius:2px;overflow:hidden;margin:4px 0}.dkb-progressFill{height:100%;background:var(--dsw-alias-state-success-primary,#16a34a);transition:width .2s}.dkb-consolePre{background:var(--dsw-alias-bg-layer-1,#0d1117);color:var(--dsw-alias-state-business-primary,#58a6ff);border:1px solid var(--dsw-alias-border-l2);padding:12px;border-radius:8px;font-family:monospace;font-size:11px;max-height:260px;overflow-y:auto;white-space:pre-wrap}'
869
557
 
870
558
  const en = {
871
559
  'updater.title': 'Plugin Updates',
@@ -1288,6 +976,32 @@ window.__ModuleLoader__.load({
1288
976
  'telemetry.output': 'Output',
1289
977
  'telemetry.cache': 'Cached',
1290
978
  'telemetry.cost': 'Estimated Cost',
979
+ 'group.remote': 'Remote execution (Server 111)',
980
+ 'field.remoteMode': 'Remote mode',
981
+ 'hint.remoteMode': 'Execute tasks, worktrees, and git commands on remote server 111.',
982
+ 'field.remoteHost': 'Remote host',
983
+ 'hint.remoteHost': 'Hostname or IP address of the remote server.',
984
+ 'field.remotePort': 'SSH port',
985
+ 'hint.remotePort': 'SSH port on remote server (default 22).',
986
+ 'field.remoteUser': 'SSH user',
987
+ 'hint.remoteUser': 'SSH login username on remote host.',
988
+ 'field.remoteAuthType': 'SSH auth type',
989
+ 'hint.remoteAuthType': 'SSH authentication method: key, agent, or password.',
990
+ 'field.remoteKeyPath': 'SSH private key',
991
+ 'hint.remoteKeyPath': 'Path to SSH private key file on local PC.',
992
+ 'field.remoteSudoUser': 'Sudo user',
993
+ 'hint.remoteSudoUser': 'Run remote commands under this Linux user via sudo -u.',
994
+ 'field.remoteRoot': 'Remote project root',
995
+ 'hint.remoteRoot': 'Remote projects directory (/mnt/external/Project/DEV).',
996
+ 'field.remoteServerUrl': 'DSH server URL',
997
+ 'hint.remoteServerUrl': 'Remote DSH URL (http://192.168.1.111:3080) for unified board state.',
998
+ 'field.remoteServerToken': 'Server auth token',
999
+ 'hint.remoteServerToken': 'Optional authentication token for remote DSH server.',
1000
+ 'remote.testBtn': 'Test Connection',
1001
+ 'remote.testing': 'Testing connection...',
1002
+ 'remote.ok': 'Connected to server 111',
1003
+ 'remote.failed': 'Connection failed',
1004
+ 'remote.badge': 'Remote: 111',
1291
1005
  'telemetry.limit': 'Budget Limit',
1292
1006
  }
1293
1007
 
@@ -1712,6 +1426,32 @@ window.__ModuleLoader__.load({
1712
1426
  'telemetry.output': '输出',
1713
1427
  'telemetry.cache': '缓存命中',
1714
1428
  'telemetry.cost': '预估成本',
1429
+ 'group.remote': '远程执行 (111 服务器)',
1430
+ 'field.remoteMode': '远程模式',
1431
+ 'hint.remoteMode': '在远程 111 服务器上执行任务、工作树和 git 命令。',
1432
+ 'field.remoteHost': '远程主机',
1433
+ 'hint.remoteHost': '远程服务器的主机名或 IP 地址。',
1434
+ 'field.remotePort': 'SSH 端口',
1435
+ 'hint.remotePort': '远程服务器的 SSH 端口(默认 22)。',
1436
+ 'field.remoteUser': 'SSH 用户',
1437
+ 'hint.remoteUser': '远程主机上的 SSH 登录用户名。',
1438
+ 'field.remoteAuthType': 'SSH 认证类型',
1439
+ 'hint.remoteAuthType': 'SSH 认证方式:key、agent 或 password。',
1440
+ 'field.remoteKeyPath': 'SSH 私钥路径',
1441
+ 'hint.remoteKeyPath': '本地电脑上的 SSH 私钥文件路径。',
1442
+ 'field.remoteSudoUser': 'Sudo 执行用户',
1443
+ 'hint.remoteSudoUser': '在远程通过 sudo -u 以此 Linux 用户身份运行命令。',
1444
+ 'field.remoteRoot': '远程项目根目录',
1445
+ 'hint.remoteRoot': '远程项目仓库目录 (/mnt/external/Project/DEV)。',
1446
+ 'field.remoteServerUrl': 'DSH 服务器地址',
1447
+ 'hint.remoteServerUrl': '远程 DSH 服务器地址 (http://192.168.1.111:3080),用于共享看板状态。',
1448
+ 'field.remoteServerToken': '服务器认证令牌',
1449
+ 'hint.remoteServerToken': '远程 DSH 服务器的可选认证令牌。',
1450
+ 'remote.testBtn': '测试连接',
1451
+ 'remote.testing': '正在测试连接...',
1452
+ 'remote.ok': '已连接到 111 服务器',
1453
+ 'remote.failed': '连接失败',
1454
+ 'remote.badge': '远程: 111',
1715
1455
  'telemetry.limit': '预算限额',
1716
1456
  }
1717
1457
 
@@ -1725,7 +1465,6 @@ window.__ModuleLoader__.load({
1725
1465
  return text
1726
1466
  }
1727
1467
 
1728
-
1729
1468
  const FIELDS = [
1730
1469
  { field: 'gitProvider', kind: 'text', group: 'forge' },
1731
1470
  { field: 'giteaUrl', kind: 'text', group: 'gitea' },
@@ -1763,6 +1502,16 @@ window.__ModuleLoader__.load({
1763
1502
  { field: 'verificationTimeoutMs', kind: 'number', group: 'general' },
1764
1503
  { field: 'costMeterEnabled', kind: 'bool', group: 'sync' },
1765
1504
  { field: 'taskCostLimit', kind: 'number', group: 'limits' },
1505
+ { field: 'remoteMode', kind: 'bool', group: 'remote' },
1506
+ { field: 'remoteHost', kind: 'text', group: 'remote' },
1507
+ { field: 'remotePort', kind: 'number', group: 'remote' },
1508
+ { field: 'remoteUser', kind: 'text', group: 'remote' },
1509
+ { field: 'remoteAuthType', kind: 'text', group: 'remote' },
1510
+ { field: 'remoteKeyPath', kind: 'text', group: 'remote' },
1511
+ { field: 'remoteSudoUser', kind: 'text', group: 'remote' },
1512
+ { field: 'remoteRoot', kind: 'text', group: 'remote' },
1513
+ { field: 'remoteServerUrl', kind: 'text', group: 'remote' },
1514
+ { field: 'remoteServerToken', kind: 'text', group: 'remote' },
1766
1515
  ]
1767
1516
 
1768
1517
  function hintKey(spec) {
@@ -1770,6 +1519,7 @@ window.__ModuleLoader__.load({
1770
1519
  if (spec.group === 'gitea') return 'hint.' + spec.field
1771
1520
  if (spec.group === 'github') return 'hint.' + spec.field
1772
1521
  if (spec.group === 'forge') return 'hint.' + spec.field
1522
+ if (spec.group === 'remote') return 'hint.' + spec.field
1773
1523
  if (spec.group === 'labels') return 'hint.label'
1774
1524
  if (spec.group === 'limits') return 'hint.limit'
1775
1525
  return 'hint.' + spec.field
@@ -1807,6 +1557,63 @@ window.__ModuleLoader__.load({
1807
1557
  }, h(Icon))
1808
1558
  }
1809
1559
 
1560
+ function RemoteConnectionSection(props) {
1561
+ const t = props.t || fallbackT
1562
+ const [testing, setTesting] = React.useState(false)
1563
+ const [result, setResult] = React.useState(null)
1564
+
1565
+ const handleTest = React.useCallback(async () => {
1566
+ setTesting(true)
1567
+ setResult(null)
1568
+ try {
1569
+ const res = await fetch('/api/dsh-kanban/remote/test', {
1570
+ method: 'POST',
1571
+ headers: { 'Content-Type': 'application/json' },
1572
+ })
1573
+ const data = await res.json()
1574
+ setResult(data)
1575
+ } catch (e) {
1576
+ setResult({ ok: false, error: e?.message || String(e) })
1577
+ } finally {
1578
+ setTesting(false)
1579
+ }
1580
+ }, [])
1581
+
1582
+ return h('div', {
1583
+ className: 'dkb-section-card',
1584
+ key: 'remote-test-card',
1585
+ style: { borderLeft: '3px solid var(--dsw-alias-state-business-primary, #0969da)', background: 'var(--dsw-alias-bg-layer-1, #f6f8fa)', padding: '12px 14px', marginBottom: '12px' },
1586
+ },
1587
+ h('div', {
1588
+ style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: result ? '8px' : '0' },
1589
+ },
1590
+ h('span', { style: { fontWeight: '600', fontSize: '13px' } }, '🌐 ' + t('group.remote')),
1591
+ h('button', {
1592
+ type: 'button',
1593
+ className: 'dkb-save',
1594
+ style: { padding: '3px 10px', fontSize: '12px' },
1595
+ disabled: testing,
1596
+ onClick: handleTest,
1597
+ }, testing ? t('remote.testing') : t('remote.testBtn')),
1598
+ ),
1599
+ result ? h('div', {
1600
+ style: {
1601
+ fontSize: '12px',
1602
+ padding: '6px 8px',
1603
+ borderRadius: '4px',
1604
+ background: (result.ssh?.ok || result.server?.ok)
1605
+ ? 'var(--dsw-alias-state-success-secondary, #dafbe1)'
1606
+ : 'var(--dsw-alias-state-error-secondary, #ffebe9)',
1607
+ color: (result.ssh?.ok || result.server?.ok)
1608
+ ? 'var(--dsw-alias-state-success-primary, #1a7f37)'
1609
+ : 'var(--dsw-alias-state-error-primary, #cf222e)',
1610
+ },
1611
+ },
1612
+ h('div', null, (result.ssh?.ok ? '✓ SSH: OK (' + result.ssh.latencyMs + 'ms)' : '✗ SSH: ' + (result.ssh?.error || 'unreachable'))),
1613
+ h('div', null, (result.server?.ok ? '✓ DSH Server: OK (' + result.server.latencyMs + 'ms)' : '✗ DSH Server: ' + (result.server?.error || 'unreachable'))),
1614
+ ) : null,
1615
+ )
1616
+ }
1810
1617
 
1811
1618
  function PluginUpdaterSection(props) {
1812
1619
  const t = props.t || fallbackT
@@ -2085,6 +1892,7 @@ window.__ModuleLoader__.load({
2085
1892
  t('costMeter.notice'),
2086
1893
  ),
2087
1894
  ))
1895
+ rows.push(h(RemoteConnectionSection, { t, key: 'remote-test' }))
2088
1896
  rows.push(h(PluginUpdaterSection, { t, key: 'updater' }))
2089
1897
  rows.push(h('div', { className: 'dkb-foot', key: 'foot' },
2090
1898
  note,
@@ -2100,12 +1908,10 @@ window.__ModuleLoader__.load({
2100
1908
  return h('li', { className: 'dkb-card dkb-cardOpen' }, head, h('div', { className: 'dkb-body' }, body))
2101
1909
  }
2102
1910
 
2103
- /** Back button arrow icon: 14x14 stroke currentColor. */
2104
1911
  const BACK_ICON = '<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor"'
2105
1912
  + ' stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'
2106
1913
  + '<path d="M10 3.5 5.5 8l4.5 4.5"/></svg>'
2107
1914
 
2108
-
2109
1915
  const TaskCard = React.memo(function TaskCard(props) {
2110
1916
  const { task, t, now } = props
2111
1917
  const ref = cardRef(task)
@@ -2304,10 +2110,6 @@ window.__ModuleLoader__.load({
2304
2110
  )
2305
2111
  })
2306
2112
 
2307
- /**
2308
- * Execution options: agent preset and permission rank.
2309
- * Shared across single-task and batch launch modals.
2310
- */
2311
2113
  function LaunchFields(props) {
2312
2114
  const t = props.t
2313
2115
  const rows = []
@@ -2615,9 +2417,6 @@ window.__ModuleLoader__.load({
2615
2417
  }
2616
2418
  }, [load, t])
2617
2419
 
2618
- /**
2619
- * Confirm and execute card move transition.
2620
- */
2621
2420
  const askMove = React.useCallback((task, column, where) => {
2622
2421
  if (!task) return
2623
2422
  const place = where ?? neighboursFor(tasksOf((state && state.tasks) || [], column), task.id, Infinity)
@@ -2688,6 +2487,19 @@ window.__ModuleLoader__.load({
2688
2487
  h('span', null, t('board.back')),
2689
2488
  ),
2690
2489
  h('span', { className: 'dkb-barTitle' }, t('section.label')),
2490
+ state && state.remoteMode
2491
+ ? h('span', {
2492
+ className: 'dkb-tag',
2493
+ style: {
2494
+ background: 'var(--dsw-alias-state-business-tertiary, #ddf4ff)',
2495
+ color: 'var(--dsw-alias-state-business-primary, #0969da)',
2496
+ borderColor: 'var(--dsw-alias-state-business-tertiary, #54aeff)',
2497
+ fontWeight: '600',
2498
+ fontSize: '11px',
2499
+ },
2500
+ title: 'Operating in remote mode against server 111',
2501
+ }, '🌐 ' + t('remote.badge'))
2502
+ : null,
2691
2503
  boards.length > 1
2692
2504
  ? h('select', {
2693
2505
  className: 'dkb-input', style: { width: 'auto' }, value: board,
@@ -3055,6 +2867,11 @@ window.__ModuleLoader__.load({
3055
2867
  } catch (e) { setError(t(e.key || 'error.unknown')) }
3056
2868
  },
3057
2869
  onOpenChat: (x) => {
2870
+ if (state && state.remoteMode && x.sessionId) {
2871
+ const url = 'http://192.168.1.111:3080/#/chat?sessionId=' + encodeURIComponent(x.sessionId)
2872
+ if (typeof window !== 'undefined') window.open(url, '_blank')
2873
+ return
2874
+ }
3058
2875
  if (!openSession(props.ctx, x.sessionId)) {
3059
2876
  setError(t('error.sessionNotOpened', { id: x.sessionId }))
3060
2877
  return
@@ -4640,9 +4457,6 @@ window.__ModuleLoader__.load({
4640
4457
  )
4641
4458
  }
4642
4459
 
4643
- /**
4644
- * Header task chip in chat conversation. Only rendered when session is linked to a task.
4645
- */
4646
4460
  function TaskChip(props) {
4647
4461
  const t = props.t || fallbackT
4648
4462
  const [task, setTask] = React.useState(null)
@@ -4726,8 +4540,6 @@ window.__ModuleLoader__.load({
4726
4540
  )
4727
4541
  }
4728
4542
 
4729
-
4730
-
4731
4543
  const PANEL = 'kanban'
4732
4544
  const ACTIVATE_EVENT = 'dsh-panel-activate'
4733
4545
  const ACTIVE_ATTR = 'data-dsh-kanban-active'
@@ -4740,8 +4552,6 @@ window.__ModuleLoader__.load({
4740
4552
  + ' stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'
4741
4553
  + '<rect x="2" y="2.5" width="12" height="11" rx="1.5"/><path d="M2 6.5h12M6.5 6.5v7"/></svg>'
4742
4554
 
4743
- /** Board visibility state manager. */
4744
- /** @see helpers.createToggle */
4745
4555
  function createToggle() {
4746
4556
  let open = false
4747
4557
  let wanted = undefined
@@ -4754,12 +4564,10 @@ window.__ModuleLoader__.load({
4754
4564
  for (const fn of listeners) fn()
4755
4565
  },
4756
4566
  toggle() { this.set(!open) },
4757
- /** Open board focused on a specific task. */
4758
4567
  show(taskId, boardId) {
4759
4568
  wanted = { id: String(taskId || ''), board: String(boardId || 'main') }
4760
4569
  if (open) { for (const fn of listeners) fn() } else this.set(true)
4761
4570
  },
4762
- /** Consume requested focus task ID (cleared after first read). */
4763
4571
  takeWanted() {
4764
4572
  const asked = wanted
4765
4573
  wanted = undefined
@@ -4769,7 +4577,6 @@ window.__ModuleLoader__.load({
4769
4577
  }
4770
4578
  }
4771
4579
 
4772
- /** Find native 'New Session' sidebar button. */
4773
4580
  function nativeButton() {
4774
4581
  const nodes = document.querySelectorAll('button[class*="newSession"]')
4775
4582
  for (const node of nodes) {
@@ -4778,9 +4585,6 @@ window.__ModuleLoader__.load({
4778
4585
  return undefined
4779
4586
  }
4780
4587
 
4781
- /**
4782
- * Kanban entry button cloning native button styles to match host theme.
4783
- */
4784
4588
  function createEntry(target, toggle, label) {
4785
4589
  const entry = target.cloneNode(true)
4786
4590
  entry.className = String(target.className) + ' dkb-entry-clone'
@@ -4809,7 +4613,6 @@ window.__ModuleLoader__.load({
4809
4613
  return entry
4810
4614
  }
4811
4615
 
4812
- /** Place button at end of injected extension entries. */
4813
4616
  function placeEntry(target, entry) {
4814
4617
  let anchor = target
4815
4618
  let next = target.nextElementSibling
@@ -4822,7 +4625,6 @@ window.__ModuleLoader__.load({
4822
4625
  return true
4823
4626
  }
4824
4627
 
4825
- /** Sidebar entry button persistent across shell re-renders. */
4826
4628
  function mountSidebarEntry(toggle, label) {
4827
4629
  if (typeof document === 'undefined') return () => {}
4828
4630
 
@@ -4856,7 +4658,6 @@ window.__ModuleLoader__.load({
4856
4658
  }
4857
4659
  }
4858
4660
 
4859
- /** Board overlay container mounted over conversation column. */
4860
4661
  function mountBoard(ctx, toggle) {
4861
4662
  if (typeof document === 'undefined') return () => {}
4862
4663
  let root
@@ -4913,12 +4714,6 @@ window.__ModuleLoader__.load({
4913
4714
  }
4914
4715
  }
4915
4716
 
4916
-
4917
- /**
4918
- * Register component into first available UI slot.
4919
- *
4920
- * @returns {string|undefined} name of successfully resolved slot
4921
- */
4922
4717
  function registerFirst(ctx, candidates, component) {
4923
4718
  for (const entry of candidates) {
4924
4719
  try {