@goodandready/dsh-kanban 0.2.19 → 0.2.20
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/board-tool.js +18 -48
- package/lib/client.js +85 -176
- package/lib/commands.js +21 -38
- package/lib/config.js +29 -54
- package/lib/cron.js +13 -13
- package/lib/events.js +1 -1
- package/lib/filters.js +58 -82
- package/lib/git-provider.js +23 -23
- package/lib/gitea.js +51 -109
- package/lib/github.js +27 -31
- package/lib/import.js +46 -67
- package/lib/index.js +66 -132
- package/lib/intake.js +18 -26
- package/lib/launcher.js +100 -167
- package/lib/lifecycle.js +11 -17
- package/lib/markdown.js +16 -23
- package/lib/metrics.js +23 -25
- package/lib/order.js +25 -28
- package/lib/outbound.js +25 -26
- package/lib/plan.js +23 -45
- package/lib/power.js +2 -2
- package/lib/progress-dump.js +17 -17
- package/lib/routes.js +86 -145
- package/lib/store.js +59 -77
- package/lib/sync.js +56 -98
- package/lib/time.js +8 -17
- package/lib/transitions.js +36 -57
- package/lib/webhook.js +16 -23
- package/lib/workflow-bridge.js +57 -57
- package/lib/worktree.js +58 -58
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Browser half of dsh-kanban: board screen and settings card.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// cordis.patch.yml.
|
|
5
|
-
//
|
|
3
|
+
// The identifier below must match `name` in package.json and `name:` in
|
|
4
|
+
// cordis.patch.yml. A mismatch produces NO log error: the loader silently fails
|
|
5
|
+
// to resolve the package, the server half works, and the UI never appears.
|
|
6
6
|
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
// — тесты поднимают этот файл через node:vm и проверяют их без браузера.
|
|
7
|
+
// This module is loaded by ModuleLoader and cannot import repository files;
|
|
8
|
+
// pure helpers live here and are exported as `helpers` for node:vm testing.
|
|
10
9
|
window.__ModuleLoader__.load({
|
|
11
10
|
id: '@goodandready/dsh-kanban',
|
|
12
11
|
factory: (require) => {
|
|
@@ -25,8 +24,8 @@ window.__ModuleLoader__.load({
|
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Neighbours for card repositioning. The store operates on relative neighbours
|
|
28
|
+
* rather than numeric indexes: `afterId` above, `beforeId` below.
|
|
30
29
|
*/
|
|
31
30
|
function neighboursFor(columnTasks, dragId, index) {
|
|
32
31
|
const rest = (columnTasks || []).filter((t) => t.id !== dragId)
|
|
@@ -42,13 +41,11 @@ window.__ModuleLoader__.load({
|
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
/**
|
|
45
|
-
*
|
|
44
|
+
* Task ordering within a column.
|
|
46
45
|
*
|
|
47
|
-
* `manual` —
|
|
48
|
-
* `old`/`new`
|
|
49
|
-
*
|
|
50
|
-
* проекта, а идущая работа остаётся наверху группы. Иначе заголовки
|
|
51
|
-
* проектов пришлось бы рисовать над каждой карточкой.
|
|
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".
|
|
52
49
|
*/
|
|
53
50
|
function sortTasks(tasks, order) {
|
|
54
51
|
const list = tasks || []
|
|
@@ -71,21 +68,18 @@ window.__ModuleLoader__.load({
|
|
|
71
68
|
}
|
|
72
69
|
|
|
73
70
|
/**
|
|
74
|
-
*
|
|
71
|
+
* Session task batch progress: sequence index and total count.
|
|
75
72
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* же, в каком агент их берёт: идущая первой, дальше по времени постановки.
|
|
73
|
+
* Orders matching the agent execution order: active task first,
|
|
74
|
+
* followed by queue time.
|
|
79
75
|
*
|
|
80
|
-
* @returns {Object<string, {at: number, total: number}>}
|
|
76
|
+
* @returns {Object<string, {at: number, total: number}>} keyed by task id
|
|
81
77
|
*/
|
|
82
78
|
/**
|
|
83
|
-
*
|
|
79
|
+
* Apply a single batch action across all selected tasks.
|
|
84
80
|
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* и не съедается молча: итог называется числом, иначе человек останется
|
|
88
|
-
* уверен, что в архиве десять карточек, а их там семь.
|
|
81
|
+
* Failures on individual tasks do not abort remaining tasks;
|
|
82
|
+
* completed count is reported to the user.
|
|
89
83
|
*
|
|
90
84
|
* @returns {Promise<{done: number, failed: number}>}
|
|
91
85
|
*/
|
|
@@ -99,12 +93,10 @@ window.__ModuleLoader__.load({
|
|
|
99
93
|
}
|
|
100
94
|
|
|
101
95
|
/**
|
|
102
|
-
*
|
|
96
|
+
* Remembered launch settings in browser localStorage.
|
|
103
97
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* окно, запрет на данные сайта) — тогда просто нечего вспомнить, и окно
|
|
107
|
-
* запуска обязано открыться как обычно.
|
|
98
|
+
* Stored per user browser rather than plugin settings. Handles storage
|
|
99
|
+
* errors gracefully if localStorage is disabled or restricted.
|
|
108
100
|
*/
|
|
109
101
|
const RECALL_KEY = 'dsh-kanban:launch:'
|
|
110
102
|
|
|
@@ -123,13 +115,11 @@ window.__ModuleLoader__.load({
|
|
|
123
115
|
}
|
|
124
116
|
|
|
125
117
|
/**
|
|
126
|
-
*
|
|
118
|
+
* Re-apply remembered preset only if still available in active runtime.
|
|
127
119
|
*
|
|
128
|
-
*
|
|
129
|
-
* задача пошла бы не с теми правами или не собралась бы вовсе. Тогда
|
|
130
|
-
* возвращаемся к тому, что предлагает харнесс.
|
|
120
|
+
* Avoids applying obsolete profiles or permission ranks removed from the build.
|
|
131
121
|
*
|
|
132
|
-
* @param {Array<string>} known
|
|
122
|
+
* @param {Array<string>} known valid choices
|
|
133
123
|
*/
|
|
134
124
|
function stillOffered(value, known, fallback) {
|
|
135
125
|
return typeof value === 'string' && (known || []).indexOf(value) >= 0 ? value : fallback
|
|
@@ -153,11 +143,10 @@ window.__ModuleLoader__.load({
|
|
|
153
143
|
}
|
|
154
144
|
|
|
155
145
|
/**
|
|
156
|
-
*
|
|
146
|
+
* Group column tasks by assignee.
|
|
157
147
|
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* того, кто первым попался в списке.
|
|
148
|
+
* Unassigned group ('Nobody') is placed first to highlight available work,
|
|
149
|
+
* followed alphabetically by assignee username.
|
|
161
150
|
*
|
|
162
151
|
* @returns {Array<{who: string, tasks: object[]}>}
|
|
163
152
|
*/
|
|
@@ -180,13 +169,9 @@ window.__ModuleLoader__.load({
|
|
|
180
169
|
}
|
|
181
170
|
|
|
182
171
|
/**
|
|
183
|
-
*
|
|
172
|
+
* Project repository and issue number rendered separately.
|
|
184
173
|
*
|
|
185
|
-
*
|
|
186
|
-
* разные вещи: имя проекта — принадлежность, номер — адрес.
|
|
187
|
-
*
|
|
188
|
-
* Номер становится ссылкой на issue: чаще всего она нужна именно с
|
|
189
|
-
* карточки, а раньше пряталась в окне задачи.
|
|
174
|
+
* Issue number links directly to the remote issue.
|
|
190
175
|
*/
|
|
191
176
|
function taskRef(task, t) {
|
|
192
177
|
if (!task || !task.repo) return null
|
|
@@ -218,11 +203,9 @@ window.__ModuleLoader__.load({
|
|
|
218
203
|
}
|
|
219
204
|
|
|
220
205
|
/**
|
|
221
|
-
*
|
|
206
|
+
* Compact plan summary line for card badge: "3 of 7 · writing tests".
|
|
222
207
|
*
|
|
223
|
-
*
|
|
224
|
-
* на карточку едут только числа и текущий пункт; сам список остаётся в
|
|
225
|
-
* окне задачи.
|
|
208
|
+
* Only progress numbers and current item description are shown on cards.
|
|
226
209
|
*/
|
|
227
210
|
function planLine(task, t) {
|
|
228
211
|
const progress = task && task.plan && task.plan.progress
|
|
@@ -232,10 +215,7 @@ window.__ModuleLoader__.load({
|
|
|
232
215
|
return progress.text ? counter + ' · ' + progress.text : counter
|
|
233
216
|
}
|
|
234
217
|
|
|
235
|
-
/**
|
|
236
|
-
* Подпись состояния. Ожидание рисуется отдельной точкой и здесь молчит,
|
|
237
|
-
* иначе одно и то же сказано дважды.
|
|
238
|
-
*/
|
|
218
|
+
/** Status badge text. Waiting indicator is rendered separately. */
|
|
239
219
|
function stateLabel(task, t) {
|
|
240
220
|
const state = task && task.state
|
|
241
221
|
if (state === 'running') return t('state.running')
|
|
@@ -244,14 +224,13 @@ window.__ModuleLoader__.load({
|
|
|
244
224
|
}
|
|
245
225
|
|
|
246
226
|
/**
|
|
247
|
-
*
|
|
227
|
+
* Determine whether column is collapsed.
|
|
248
228
|
*
|
|
249
|
-
*
|
|
250
|
-
*
|
|
251
|
-
* иначе развёрнутая пустая колонка схлопывалась бы у него на глазах.
|
|
229
|
+
* Empty columns collapse automatically to save horizontal space;
|
|
230
|
+
* explicit user toggle overrides automatic behavior.
|
|
252
231
|
*
|
|
253
|
-
* @param {object} column
|
|
254
|
-
* @param {object} manual
|
|
232
|
+
* @param {object} column column data from board response
|
|
233
|
+
* @param {object} manual map of column id -> boolean user preference
|
|
255
234
|
*/
|
|
256
235
|
function isCollapsed(column, manual) {
|
|
257
236
|
const own = manual && manual[column.id]
|
|
@@ -260,12 +239,9 @@ window.__ModuleLoader__.load({
|
|
|
260
239
|
}
|
|
261
240
|
|
|
262
241
|
/**
|
|
263
|
-
*
|
|
242
|
+
* Relative elapsed time string: "5m", "3h", "3d".
|
|
264
243
|
*
|
|
265
|
-
*
|
|
266
|
-
* настроек, а лишний круг данных дороже пяти строк здесь. Абсолютную дату
|
|
267
|
-
* отдаём подсказкой при наведении: точная дата требует чтения и сравнения
|
|
268
|
-
* с сегодняшним числом, а на доску смотрят ради скорости.
|
|
244
|
+
* Computed client-side with full ISO timestamp displayed on hover tooltip.
|
|
269
245
|
*/
|
|
270
246
|
function agoText(now, then, t) {
|
|
271
247
|
if (typeof then !== 'number' || then <= 0) return ''
|
|
@@ -278,7 +254,7 @@ window.__ModuleLoader__.load({
|
|
|
278
254
|
return t('time.day', { n: Math.floor(hours / 24) })
|
|
279
255
|
}
|
|
280
256
|
|
|
281
|
-
/**
|
|
257
|
+
/** Date formatted without time. Year is included only when not the current year. */
|
|
282
258
|
function shortDate(then) {
|
|
283
259
|
if (typeof then !== 'number' || then <= 0) return ''
|
|
284
260
|
try {
|
|
@@ -290,17 +266,16 @@ window.__ModuleLoader__.load({
|
|
|
290
266
|
} catch { return '' }
|
|
291
267
|
}
|
|
292
268
|
|
|
293
|
-
/**
|
|
269
|
+
/** Exact timestamp for tooltip. Empty tooltip if timestamp is invalid. */
|
|
294
270
|
function exactAt(then) {
|
|
295
271
|
if (typeof then !== 'number' || then <= 0) return ''
|
|
296
272
|
try { return new Date(then).toLocaleString() } catch { return '' }
|
|
297
273
|
}
|
|
298
274
|
|
|
299
275
|
/**
|
|
300
|
-
*
|
|
276
|
+
* Sync telemetry status message.
|
|
301
277
|
*
|
|
302
|
-
*
|
|
303
|
-
* «ничего не изменилось» от «мы ослепли» иначе нечем.
|
|
278
|
+
* Alerts user when synchronization has halted or encountered errors.
|
|
304
279
|
*
|
|
305
280
|
* @returns {{tone: 'ok'|'bad'|'idle', text: string, title: string}}
|
|
306
281
|
*/
|
|
@@ -329,13 +304,11 @@ window.__ModuleLoader__.load({
|
|
|
329
304
|
}
|
|
330
305
|
|
|
331
306
|
/**
|
|
332
|
-
*
|
|
307
|
+
* Accessible text color over label background color.
|
|
333
308
|
*
|
|
334
|
-
*
|
|
335
|
-
* делает сам Gitea. Без этого `priority/low` на светло-зелёном становится
|
|
336
|
-
* нечитаемой, а `type/security` на тёмно-красном сливается.
|
|
309
|
+
* Computes perceived brightness and picks black or white accordingly.
|
|
337
310
|
*
|
|
338
|
-
* @param {string} hex
|
|
311
|
+
* @param {string} hex 6-character hex code without '#'
|
|
339
312
|
*/
|
|
340
313
|
function readableOn(hex) {
|
|
341
314
|
const n = parseInt(String(hex || ''), 16)
|
|
@@ -347,11 +320,9 @@ window.__ModuleLoader__.load({
|
|
|
347
320
|
}
|
|
348
321
|
|
|
349
322
|
/**
|
|
350
|
-
*
|
|
323
|
+
* Color accent stripe on left border of card for priority.
|
|
351
324
|
*
|
|
352
|
-
*
|
|
353
|
-
* нельзя. Задача без срочности остаётся без полосы, а не получает серую —
|
|
354
|
-
* пустая полоса читалась бы как «низкий приоритет», а это неправда.
|
|
325
|
+
* Only applies to `priority/*` labels. Tasks without priority display no stripe.
|
|
355
326
|
*/
|
|
356
327
|
function priorityColor(task) {
|
|
357
328
|
const values = (task && task.facets && task.facets.priority) || []
|
|
@@ -362,7 +333,7 @@ window.__ModuleLoader__.load({
|
|
|
362
333
|
return ''
|
|
363
334
|
}
|
|
364
335
|
|
|
365
|
-
/**
|
|
336
|
+
/** Tag style helper. Neutral style when color is not defined. */
|
|
366
337
|
function tagStyle(task, name) {
|
|
367
338
|
const hex = task && task.labelColors ? task.labelColors[name] : undefined
|
|
368
339
|
if (!hex) return undefined
|
|
@@ -370,12 +341,9 @@ window.__ModuleLoader__.load({
|
|
|
370
341
|
}
|
|
371
342
|
|
|
372
343
|
/**
|
|
373
|
-
*
|
|
344
|
+
* Render parsed task body tree safely using React elements.
|
|
374
345
|
*
|
|
375
|
-
*
|
|
376
|
-
* `dangerouslySetInnerHTML` здесь означал бы выполнение чужого кода в
|
|
377
|
-
* интерфейсе владельца. При сборке элементами подстановка невозможна по
|
|
378
|
-
* построению — не потому, что кто-то не забыл про экранирование.
|
|
346
|
+
* Constructs DOM elements rather than using dangerouslySetInnerHTML to prevent XSS.
|
|
379
347
|
*/
|
|
380
348
|
function renderSpans(spans, keyBase) {
|
|
381
349
|
return (spans || []).map((span, i) => {
|
|
@@ -418,18 +386,12 @@ window.__ModuleLoader__.load({
|
|
|
418
386
|
}
|
|
419
387
|
|
|
420
388
|
/**
|
|
421
|
-
*
|
|
389
|
+
* Label namespace display title.
|
|
422
390
|
*
|
|
423
|
-
*
|
|
424
|
-
* меток в Gitea меняют без нас, и подставлять свой перевод новому
|
|
425
|
-
* пространству значило бы врать о том, чего мы не знаем.
|
|
391
|
+
* Falls back to raw namespace name when no translation key exists.
|
|
426
392
|
*/
|
|
427
393
|
/**
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
* Настройка одна на все колонки — парами `идентификатор=Название` через
|
|
431
|
-
* запятую. Шесть отдельных полей ради переименования, которое делают раз в
|
|
432
|
-
* жизни, засорили бы карточку настроек сильнее, чем помогли.
|
|
394
|
+
* Column title: custom override if configured, otherwise localized default.
|
|
433
395
|
*/
|
|
434
396
|
function columnTitle(col, t) {
|
|
435
397
|
const own = col && col.name
|
|
@@ -443,11 +405,9 @@ window.__ModuleLoader__.load({
|
|
|
443
405
|
}
|
|
444
406
|
|
|
445
407
|
/**
|
|
446
|
-
*
|
|
408
|
+
* Board title.
|
|
447
409
|
*
|
|
448
|
-
*
|
|
449
|
-
* названия лежат в базе на одном языке и на другом интерфейсе остались бы
|
|
450
|
-
* чужими. Переименованную человеком доску не трогаем — это его слово.
|
|
410
|
+
* Default 'main' and 'simple' boards use localized titles; custom board names are preserved verbatim.
|
|
451
411
|
*/
|
|
452
412
|
function boardTitle(board, t) {
|
|
453
413
|
const key = 'boardName.' + (board && board.id)
|
|
@@ -474,18 +434,14 @@ window.__ModuleLoader__.load({
|
|
|
474
434
|
}
|
|
475
435
|
}
|
|
476
436
|
|
|
477
|
-
/**
|
|
437
|
+
/** Repository facet filter key matching backend dimension. */
|
|
478
438
|
const REPO_FACET = 'repo'
|
|
479
439
|
|
|
480
440
|
/**
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
* Между пространствами — И, внутри пространства — ИЛИ: срочность у задачи
|
|
484
|
-
* одна, и «И» внутри пространства всегда давало бы пусто.
|
|
441
|
+
* Check whether task matches active facet filters.
|
|
485
442
|
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
* пространство и начинается значение.
|
|
443
|
+
* AND across namespaces, OR within each namespace.
|
|
444
|
+
* Uses pre-parsed task.facets from server response.
|
|
489
445
|
*/
|
|
490
446
|
function matchesFilters(task, selected) {
|
|
491
447
|
const keys = Object.keys(selected || {})
|
|
@@ -500,14 +456,14 @@ window.__ModuleLoader__.load({
|
|
|
500
456
|
return true
|
|
501
457
|
}
|
|
502
458
|
|
|
503
|
-
/**
|
|
459
|
+
/** True if any filter is active. */
|
|
504
460
|
function anySelected(selected) {
|
|
505
461
|
const keys = Object.keys(selected || {})
|
|
506
462
|
for (const ns of keys) if ((selected[ns] || []).length > 0) return true
|
|
507
463
|
return false
|
|
508
464
|
}
|
|
509
465
|
|
|
510
|
-
/**
|
|
466
|
+
/** Toggle filter value, returning a new map for React immutability. */
|
|
511
467
|
function toggleValue(selected, ns, value) {
|
|
512
468
|
const current = (selected || {})[ns] || []
|
|
513
469
|
const next = current.indexOf(value) >= 0
|
|
@@ -519,10 +475,7 @@ window.__ModuleLoader__.load({
|
|
|
519
475
|
return out
|
|
520
476
|
}
|
|
521
477
|
|
|
522
|
-
/**
|
|
523
|
-
* Человеческая причина отказа. Пустой список без объяснения читается как
|
|
524
|
-
* «issue нет», и человек идёт искать несуществующую проблему.
|
|
525
|
-
*/
|
|
478
|
+
/** Human-readable explanation for empty state or error. */
|
|
526
479
|
function errorKey(error) {
|
|
527
480
|
switch (error) {
|
|
528
481
|
case 'gitea-absent': return 'error.giteaAbsent'
|
|
@@ -539,13 +492,9 @@ window.__ModuleLoader__.load({
|
|
|
539
492
|
}
|
|
540
493
|
|
|
541
494
|
/**
|
|
542
|
-
*
|
|
543
|
-
*
|
|
544
|
-
* Служба сессий берётся через `get`, а не объявляется в `inject`: если её в
|
|
545
|
-
* сборке не окажется, доска обязана сказать об этом человеку, а не
|
|
546
|
-
* отказаться загружаться целиком.
|
|
495
|
+
* Open session conversation in chat panel.
|
|
547
496
|
*
|
|
548
|
-
* @returns {boolean}
|
|
497
|
+
* @returns {boolean} true if session opened successfully
|
|
549
498
|
*/
|
|
550
499
|
function openSession(ctx, sessionId) {
|
|
551
500
|
if (!sessionId) return false
|
|
@@ -559,10 +508,7 @@ window.__ModuleLoader__.load({
|
|
|
559
508
|
}
|
|
560
509
|
}
|
|
561
510
|
|
|
562
|
-
/**
|
|
563
|
-
* Подпись репозитория в списке: имя и сколько в нём открытых задач.
|
|
564
|
-
* Архивный помечается словом, а не только положением в списке.
|
|
565
|
-
*/
|
|
511
|
+
/** Repository option label with open issue count; marks archived repositories. */
|
|
566
512
|
function repoOption(r, t) {
|
|
567
513
|
const full = r.fullName || ((r.owner || '') + '/' + (r.repo || ''))
|
|
568
514
|
const parts = [full]
|
|
@@ -571,12 +517,7 @@ window.__ModuleLoader__.load({
|
|
|
571
517
|
return parts.join(' ')
|
|
572
518
|
}
|
|
573
519
|
|
|
574
|
-
/**
|
|
575
|
-
* Разобрать `владелец/репозиторий` в пару.
|
|
576
|
-
*
|
|
577
|
-
* Владельца отдельным полем не спрашиваем: он однозначно следует из
|
|
578
|
-
* выбранного репозитория, а лишнее поле — лишний способ ошибиться.
|
|
579
|
-
*/
|
|
520
|
+
/** Parse 'owner/repo' string into separate parts. */
|
|
580
521
|
function splitFullName(fullName) {
|
|
581
522
|
const parts = String(fullName || '').split('/')
|
|
582
523
|
if (parts.length !== 2 || parts[0] === '' || parts[1] === '') return undefined
|
|
@@ -584,10 +525,7 @@ window.__ModuleLoader__.load({
|
|
|
584
525
|
}
|
|
585
526
|
|
|
586
527
|
/**
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
* Фильтр держится на стороне браузера намеренно: доска и так вся в памяти,
|
|
590
|
-
* а поход на сервер за каждой буквой сделал бы набор дёрганым.
|
|
528
|
+
* Filter cards by client-side text search query: title, repo, issue number.
|
|
591
529
|
*/
|
|
592
530
|
function matchesQuery(task, query) {
|
|
593
531
|
const needle = String(query || '').trim().toLowerCase()
|
|
@@ -601,10 +539,7 @@ window.__ModuleLoader__.load({
|
|
|
601
539
|
return hay.indexOf(needle) >= 0
|
|
602
540
|
}
|
|
603
541
|
|
|
604
|
-
/**
|
|
605
|
-
* Идентификатор сессии для чипа. Ядро отдаёт его либо свойством, либо
|
|
606
|
-
* через useSession — берём первое, что есть.
|
|
607
|
-
*/
|
|
542
|
+
/** Retrieve session ID for header chip. */
|
|
608
543
|
function chipSessionId(props, session) {
|
|
609
544
|
return String((props && props.sessionId) || (session && (session.sessionId || session.id)) || '').trim()
|
|
610
545
|
}
|
|
@@ -2036,7 +1971,7 @@ window.__ModuleLoader__.load({
|
|
|
2036
1971
|
return h('li', { className: 'dkb-card dkb-cardOpen' }, head, h('div', { className: 'dkb-body' }, body))
|
|
2037
1972
|
}
|
|
2038
1973
|
|
|
2039
|
-
/**
|
|
1974
|
+
/** Back button arrow icon: 14x14 stroke currentColor. */
|
|
2040
1975
|
const BACK_ICON = '<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor"'
|
|
2041
1976
|
+ ' stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'
|
|
2042
1977
|
+ '<path d="M10 3.5 5.5 8l4.5 4.5"/></svg>'
|
|
@@ -2192,14 +2127,8 @@ window.__ModuleLoader__.load({
|
|
|
2192
2127
|
})
|
|
2193
2128
|
|
|
2194
2129
|
/**
|
|
2195
|
-
*
|
|
2196
|
-
*
|
|
2197
|
-
* Одни и те же поля стоят и в окне задачи, и в окне пачки — пачка идёт
|
|
2198
|
-
* ОДНОЙ сессией, значит и профиль с доступом у неё один. Две копии полей
|
|
2199
|
-
* разошлись бы при первой правке.
|
|
2200
|
-
*
|
|
2201
|
-
* Пустого списка не бывает наполовину: службы у ядра необязательны, и если
|
|
2202
|
-
* выбирать не из чего, поле не рисуется вовсе.
|
|
2130
|
+
* Execution options: agent preset and permission rank.
|
|
2131
|
+
* Shared across single-task and batch launch modals.
|
|
2203
2132
|
*/
|
|
2204
2133
|
function LaunchFields(props) {
|
|
2205
2134
|
const t = props.t
|
|
@@ -2483,10 +2412,7 @@ window.__ModuleLoader__.load({
|
|
|
2483
2412
|
}, [load, t])
|
|
2484
2413
|
|
|
2485
2414
|
/**
|
|
2486
|
-
*
|
|
2487
|
-
*
|
|
2488
|
-
* Общая для мыши и для меню на карточке: путь другой, последствия те же,
|
|
2489
|
-
* и разойтись им нельзя.
|
|
2415
|
+
* Confirm and execute card move transition.
|
|
2490
2416
|
*/
|
|
2491
2417
|
const askMove = React.useCallback((task, column, where) => {
|
|
2492
2418
|
if (!task) return
|
|
@@ -4312,9 +4238,7 @@ window.__ModuleLoader__.load({
|
|
|
4312
4238
|
}
|
|
4313
4239
|
|
|
4314
4240
|
/**
|
|
4315
|
-
*
|
|
4316
|
-
* штатное положение: тогда чип не рисуется вовсе. Пока ответ не пришёл,
|
|
4317
|
-
* тоже ничего — мигающая заглушка в шапке хуже её отсутствия.
|
|
4241
|
+
* Header task chip in chat conversation. Only rendered when session is linked to a task.
|
|
4318
4242
|
*/
|
|
4319
4243
|
function TaskChip(props) {
|
|
4320
4244
|
const t = props.t || fallbackT
|
|
@@ -4413,7 +4337,7 @@ window.__ModuleLoader__.load({
|
|
|
4413
4337
|
+ ' stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'
|
|
4414
4338
|
+ '<rect x="2" y="2.5" width="12" height="11" rx="1.5"/><path d="M2 6.5h12M6.5 6.5v7"/></svg>'
|
|
4415
4339
|
|
|
4416
|
-
/**
|
|
4340
|
+
/** Board visibility state manager. */
|
|
4417
4341
|
/** @see helpers.createToggle */
|
|
4418
4342
|
function createToggle() {
|
|
4419
4343
|
let open = false
|
|
@@ -4427,12 +4351,12 @@ window.__ModuleLoader__.load({
|
|
|
4427
4351
|
for (const fn of listeners) fn()
|
|
4428
4352
|
},
|
|
4429
4353
|
toggle() { this.set(!open) },
|
|
4430
|
-
/**
|
|
4354
|
+
/** Open board focused on a specific task. */
|
|
4431
4355
|
show(taskId, boardId) {
|
|
4432
4356
|
wanted = { id: String(taskId || ''), board: String(boardId || 'main') }
|
|
4433
4357
|
if (open) { for (const fn of listeners) fn() } else this.set(true)
|
|
4434
4358
|
},
|
|
4435
|
-
/**
|
|
4359
|
+
/** Consume requested focus task ID (cleared after first read). */
|
|
4436
4360
|
takeWanted() {
|
|
4437
4361
|
const asked = wanted
|
|
4438
4362
|
wanted = undefined
|
|
@@ -4442,12 +4366,7 @@ window.__ModuleLoader__.load({
|
|
|
4442
4366
|
}
|
|
4443
4367
|
}
|
|
4444
4368
|
|
|
4445
|
-
/**
|
|
4446
|
-
* Родная кнопка «Новая сессия».
|
|
4447
|
-
*
|
|
4448
|
-
* Отсеиваем узлы, у которых в классах есть `newSessionLabel`: это подпись
|
|
4449
|
-
* внутри кнопки, а не сама кнопка.
|
|
4450
|
-
*/
|
|
4369
|
+
/** Find native 'New Session' sidebar button. */
|
|
4451
4370
|
function nativeButton() {
|
|
4452
4371
|
const nodes = document.querySelectorAll('button[class*="newSession"]')
|
|
4453
4372
|
for (const node of nodes) {
|
|
@@ -4457,11 +4376,7 @@ window.__ModuleLoader__.load({
|
|
|
4457
4376
|
}
|
|
4458
4377
|
|
|
4459
4378
|
/**
|
|
4460
|
-
*
|
|
4461
|
-
*
|
|
4462
|
-
* Классы оболочки берутся у самой оболочки, а не переписываются у себя:
|
|
4463
|
-
* повторять чужие стили руками — значит расходиться с ними при первом же
|
|
4464
|
-
* обновлении интерфейса. Соседний плагин «Мастерская» поступает так же.
|
|
4379
|
+
* Kanban entry button cloning native button styles to match host theme.
|
|
4465
4380
|
*/
|
|
4466
4381
|
function createEntry(target, toggle, label) {
|
|
4467
4382
|
const entry = target.cloneNode(true)
|
|
@@ -4491,7 +4406,7 @@ window.__ModuleLoader__.load({
|
|
|
4491
4406
|
return entry
|
|
4492
4407
|
}
|
|
4493
4408
|
|
|
4494
|
-
/**
|
|
4409
|
+
/** Place button at end of injected extension entries. */
|
|
4495
4410
|
function placeEntry(target, entry) {
|
|
4496
4411
|
let anchor = target
|
|
4497
4412
|
let next = target.nextElementSibling
|
|
@@ -4504,7 +4419,7 @@ window.__ModuleLoader__.load({
|
|
|
4504
4419
|
return true
|
|
4505
4420
|
}
|
|
4506
4421
|
|
|
4507
|
-
/**
|
|
4422
|
+
/** Sidebar entry button persistent across shell re-renders. */
|
|
4508
4423
|
function mountSidebarEntry(toggle, label) {
|
|
4509
4424
|
if (typeof document === 'undefined') return () => {}
|
|
4510
4425
|
|
|
@@ -4538,7 +4453,7 @@ window.__ModuleLoader__.load({
|
|
|
4538
4453
|
}
|
|
4539
4454
|
}
|
|
4540
4455
|
|
|
4541
|
-
/**
|
|
4456
|
+
/** Board overlay container mounted over conversation column. */
|
|
4542
4457
|
function mountBoard(ctx, toggle) {
|
|
4543
4458
|
if (typeof document === 'undefined') return () => {}
|
|
4544
4459
|
let root
|
|
@@ -4597,15 +4512,9 @@ window.__ModuleLoader__.load({
|
|
|
4597
4512
|
|
|
4598
4513
|
|
|
4599
4514
|
/**
|
|
4600
|
-
*
|
|
4601
|
-
*
|
|
4602
|
-
* Имя слота в сборке подтвердить нельзя: строки слотов в собранном
|
|
4603
|
-
* интерфейсе не литеральны. Регистрация в несуществующий слот проходит
|
|
4604
|
-
* молча и без ошибки в журнале, поэтому единственный надёжный способ —
|
|
4605
|
-
* перебрать кандидатов и запомнить, какой сработал. Соседний плагин
|
|
4606
|
-
* dsh-gitea поступает так же со своей карточкой настроек.
|
|
4515
|
+
* Register component into first available UI slot.
|
|
4607
4516
|
*
|
|
4608
|
-
* @returns {string|undefined}
|
|
4517
|
+
* @returns {string|undefined} name of successfully resolved slot
|
|
4609
4518
|
*/
|
|
4610
4519
|
function registerFirst(ctx, candidates, component) {
|
|
4611
4520
|
for (const entry of candidates) {
|