dsh-taskboard 0.2.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -6
- package/lib/client.js +1839 -74
- package/lib/host/execution.js +199 -54
- package/lib/host/execution.js.map +1 -1
- package/lib/host/git.js +327 -0
- package/lib/host/git.js.map +1 -0
- package/lib/host/protocol-text.js +5 -3
- package/lib/host/protocol-text.js.map +1 -1
- package/lib/host/routes.js +435 -5
- package/lib/host/routes.js.map +1 -1
- package/lib/host/store.js +12 -0
- package/lib/host/store.js.map +1 -1
- package/lib/host/templates.js +166 -0
- package/lib/host/templates.js.map +1 -0
- package/lib/host/tools.js +217 -3
- package/lib/host/tools.js.map +1 -1
- package/lib/index.js +23 -3
- package/lib/index.js.map +1 -1
- package/lib/shared/api.js.map +1 -1
- package/lib/shared/protocol.js +286 -1
- package/lib/shared/protocol.js.map +1 -1
- package/package.json +74 -74
- package/src/client/api.ts +47 -3
- package/src/client/board/ImportModal.tsx +182 -0
- package/src/client/board/TaskBoard.tsx +118 -3
- package/src/client/board/TaskCard.tsx +9 -0
- package/src/client/board/TaskDetail.tsx +360 -4
- package/src/client/board/TaskFormModal.tsx +193 -12
- package/src/client/board/TemplateManager.tsx +121 -0
- package/src/client/controller.ts +238 -11
- package/src/client/index.ts +18 -1
- package/src/client/styles.ts +198 -0
- package/src/host/execution.ts +301 -67
- package/src/host/git.ts +370 -0
- package/src/host/protocol-text.ts +5 -3
- package/src/host/routes.ts +483 -5
- package/src/host/store.ts +13 -0
- package/src/host/templates.ts +143 -0
- package/src/host/tools.ts +215 -2
- package/src/index.ts +30 -1
- package/src/shared/api.ts +89 -3
- package/src/shared/protocol.ts +408 -0
- package/src/shared/version.ts +1 -1
package/src/client/controller.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module dsh-taskboard/client/controller
|
|
9
9
|
*/
|
|
10
|
-
import type { ChangeEvent, UpdateTaskBody, WorkspaceView } from '../shared/api.ts'
|
|
11
|
-
import type { TaskLedger, TaskRecord, Urgency } from '../shared/protocol.ts'
|
|
10
|
+
import type { ChangeEvent, DiagnosticsResponse, DiffResponse, ImportCommitResponse, ImportPreviewResponse, TaskTemplate, TaskTemplateSpec, UpdateTaskBody, WorkspaceView } from '../shared/api.ts'
|
|
11
|
+
import type { ChecklistItem, IsolationMode, TaskLedger, TaskRecord, Urgency } from '../shared/protocol.ts'
|
|
12
12
|
import { emptyLedger } from '../shared/protocol.ts'
|
|
13
13
|
import type { TaskboardClient } from './api.ts'
|
|
14
14
|
import type { SessionJumpResult } from './session-jump.ts'
|
|
@@ -27,6 +27,26 @@ export type SortBy = 'default' | 'updated' | 'urgency' | 'created'
|
|
|
27
27
|
/** localStorage key for persisted view state (filters + sort). */
|
|
28
28
|
const VIEW_KEY = 'dsh-taskboard-view-v1'
|
|
29
29
|
|
|
30
|
+
/** localStorage key for the remembered isolation toggle choice (0.3.0). */
|
|
31
|
+
const ISOLATION_KEY = 'dsh-taskboard-isolation-v1'
|
|
32
|
+
|
|
33
|
+
/** Load the remembered default isolation (worktree unless explicitly turned off). */
|
|
34
|
+
export function loadDefaultIsolation(): IsolationMode {
|
|
35
|
+
try {
|
|
36
|
+
const raw = localStorage.getItem(ISOLATION_KEY)
|
|
37
|
+
return raw === 'none' ? 'none' : 'worktree'
|
|
38
|
+
} catch {
|
|
39
|
+
return 'worktree'
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Remember the isolation toggle choice across forms (best effort). */
|
|
44
|
+
export function saveDefaultIsolation(mode: IsolationMode): void {
|
|
45
|
+
try {
|
|
46
|
+
localStorage.setItem(ISOLATION_KEY, mode)
|
|
47
|
+
} catch { /* storage unavailable — choice just won't persist */ }
|
|
48
|
+
}
|
|
49
|
+
|
|
30
50
|
/** Load the persisted view state (never throws; fresh on any parse error). */
|
|
31
51
|
function loadView(): { workspaceId?: string; urgencies: Urgency[]; sortBy: SortBy } {
|
|
32
52
|
try {
|
|
@@ -62,6 +82,18 @@ export interface ControllerState {
|
|
|
62
82
|
editingId?: string
|
|
63
83
|
/** Secondary (canceled/archived/trashed) tab visible. */
|
|
64
84
|
secondaryOpen: boolean
|
|
85
|
+
/** Health-diagnostics panel (⚙) visible. */
|
|
86
|
+
diagOpen: boolean
|
|
87
|
+
/** Last fetched diagnostics payload (⚙ panel). */
|
|
88
|
+
diagnostics?: DiagnosticsResponse
|
|
89
|
+
/** Task templates (0.4.0), lazy-loaded when the new-task menu opens. */
|
|
90
|
+
templates: TaskTemplate[]
|
|
91
|
+
/** Template manager modal visible. */
|
|
92
|
+
tplManagerOpen: boolean
|
|
93
|
+
/** Import modal visible (0.4.0). */
|
|
94
|
+
importOpen: boolean
|
|
95
|
+
/** Fields a chosen template prefills into the create form (consumed on open). */
|
|
96
|
+
templatePrefill?: TaskTemplateSpec
|
|
65
97
|
/** Transient error surface (action failures); cleared on next success. */
|
|
66
98
|
error?: string
|
|
67
99
|
}
|
|
@@ -78,6 +110,10 @@ function initialState(): ControllerState {
|
|
|
78
110
|
sortBy: view.sortBy,
|
|
79
111
|
composerOpen: false,
|
|
80
112
|
secondaryOpen: false,
|
|
113
|
+
diagOpen: false,
|
|
114
|
+
templates: [],
|
|
115
|
+
tplManagerOpen: false,
|
|
116
|
+
importOpen: false,
|
|
81
117
|
}
|
|
82
118
|
}
|
|
83
119
|
|
|
@@ -209,18 +245,29 @@ export class BoardController {
|
|
|
209
245
|
/** Select a task (open detail). */
|
|
210
246
|
select(id?: string): void { this.setState({ selectedId: id }) }
|
|
211
247
|
|
|
212
|
-
/** Show/hide the task form (create mode when opening). */
|
|
213
|
-
setComposer(open: boolean): void { this.setState({ composerOpen: open, editingId: undefined }) }
|
|
248
|
+
/** Show/hide the task form (create mode when opening); always blank (no template prefill). */
|
|
249
|
+
setComposer(open: boolean): void { this.setState({ composerOpen: open, editingId: undefined, templatePrefill: undefined }) }
|
|
250
|
+
|
|
251
|
+
/** Open the create form prefilled from a chosen template (0.4.0). */
|
|
252
|
+
newFromTemplate(spec: TaskTemplateSpec): void {
|
|
253
|
+
this.setState({ composerOpen: true, editingId: undefined, templatePrefill: spec })
|
|
254
|
+
}
|
|
214
255
|
|
|
215
|
-
/** Open the form modal editing an existing task. */
|
|
216
|
-
openEditor(id: string): void { this.setState({ composerOpen: true, editingId: id }) }
|
|
256
|
+
/** Open the form modal editing an existing task (clears any template prefill). */
|
|
257
|
+
openEditor(id: string): void { this.setState({ composerOpen: true, editingId: id, templatePrefill: undefined }) }
|
|
217
258
|
|
|
218
259
|
/** Close the form modal whatever its mode. */
|
|
219
|
-
closeForm(): void { this.setState({ composerOpen: false, editingId: undefined }) }
|
|
260
|
+
closeForm(): void { this.setState({ composerOpen: false, editingId: undefined, templatePrefill: undefined }) }
|
|
220
261
|
|
|
221
262
|
/** Toggle the secondary tab. */
|
|
222
263
|
toggleSecondary(): void { this.setState({ secondaryOpen: !this.state.secondaryOpen }) }
|
|
223
264
|
|
|
265
|
+
/** Whether a workspace passed git detection (form toggle enablement). */
|
|
266
|
+
gitAvailable(workspaceId: string | undefined): boolean {
|
|
267
|
+
if (workspaceId === undefined) return true
|
|
268
|
+
return this.state.workspaces.find(w => w.id === workspaceId)?.gitAvailable === true
|
|
269
|
+
}
|
|
270
|
+
|
|
224
271
|
/**
|
|
225
272
|
* Install the session-jump bridge (built from the runtime sessions service
|
|
226
273
|
* by the client entry). Without it openSession reports 'unavailable'.
|
|
@@ -315,6 +362,34 @@ export class BoardController {
|
|
|
315
362
|
}
|
|
316
363
|
}
|
|
317
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Toggle one checklist item as the USER (0.4.0): flips the item, records
|
|
367
|
+
* `checkedBy: 'user'`, keeps other items as they are (one update call).
|
|
368
|
+
*/
|
|
369
|
+
async toggleChecklistItem(task: TaskRecord, itemId: string): Promise<void> {
|
|
370
|
+
const items: ChecklistItem[] = (task.checklist ?? []).map(item => item.id === itemId
|
|
371
|
+
? (item.checked
|
|
372
|
+
? { id: item.id, text: item.text, checked: false }
|
|
373
|
+
: { id: item.id, text: item.text, checked: true, checkedBy: 'user', checkedAt: Date.now(), ...(item.note !== undefined ? { note: item.note } : {}) })
|
|
374
|
+
: item)
|
|
375
|
+
try {
|
|
376
|
+
await this.client.update(task.id, { ifVersion: task.version, checklist: items })
|
|
377
|
+
await this.refresh()
|
|
378
|
+
} catch (error) {
|
|
379
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Diff view (0.4.0): one execution's commit or changed path; errors surface via throw. */
|
|
384
|
+
async fetchDiff(taskId: string, query: { execution: string; commit?: string; path?: string }): Promise<DiffResponse | undefined> {
|
|
385
|
+
try {
|
|
386
|
+
return await this.client.diff(taskId, query)
|
|
387
|
+
} catch (error) {
|
|
388
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
389
|
+
return undefined
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
318
393
|
/** Append a user comment. */
|
|
319
394
|
async comment(id: string, body: string): Promise<void> {
|
|
320
395
|
try {
|
|
@@ -325,10 +400,10 @@ export class BoardController {
|
|
|
325
400
|
}
|
|
326
401
|
}
|
|
327
402
|
|
|
328
|
-
/** Trigger a manual run (fresh in-project session, pinned model)
|
|
329
|
-
async run(id: string): Promise<void> {
|
|
403
|
+
/** Trigger a manual run (fresh in-project session, pinned model); `reuse` = 续跑. */
|
|
404
|
+
async run(id: string, reuse = false): Promise<void> {
|
|
330
405
|
try {
|
|
331
|
-
await this.client.run(id)
|
|
406
|
+
await this.client.run(id, reuse ? { reuse: true } : {})
|
|
332
407
|
await this.refresh()
|
|
333
408
|
} catch (error) {
|
|
334
409
|
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
@@ -345,6 +420,56 @@ export class BoardController {
|
|
|
345
420
|
}
|
|
346
421
|
}
|
|
347
422
|
|
|
423
|
+
/**
|
|
424
|
+
* ⇥ 合并 (detail page): merge the task branch into the main worktree.
|
|
425
|
+
* @returns the outcome; `noop` means the branch had no new commits (nothing merged).
|
|
426
|
+
*/
|
|
427
|
+
async mergeBranch(id: string): Promise<{ ok: true; noop?: boolean } | { ok: false; error: string }> {
|
|
428
|
+
try {
|
|
429
|
+
const value = await this.client.mergeBranch(id)
|
|
430
|
+
await this.refresh()
|
|
431
|
+
return value.noop === true ? { ok: true, noop: true } : { ok: true }
|
|
432
|
+
} catch (error) {
|
|
433
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) }
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* 🗑 删除 worktree (detail page), optionally deleting the task branch too.
|
|
439
|
+
* @returns the outcome; failures carry the git message for an alert.
|
|
440
|
+
*/
|
|
441
|
+
async removeWorktree(id: string, deleteBranch: boolean): Promise<{ ok: true; branchError?: string } | { ok: false; error: string }> {
|
|
442
|
+
try {
|
|
443
|
+
const value = await this.client.worktreeRemove(id, { deleteBranch })
|
|
444
|
+
await this.refresh()
|
|
445
|
+
return value.branchError !== undefined ? { ok: true, branchError: value.branchError } : { ok: true }
|
|
446
|
+
} catch (error) {
|
|
447
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) }
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** Open the ⚙ diagnostics panel and fetch a fresh snapshot. */
|
|
452
|
+
openDiagnostics(): void {
|
|
453
|
+
this.setState({ diagOpen: true })
|
|
454
|
+
void this.client.diagnostics()
|
|
455
|
+
.then(diagnostics => this.setState({ diagnostics }))
|
|
456
|
+
.catch(error => this.setState({ error: error instanceof Error ? error.message : String(error) }))
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** Close the ⚙ diagnostics panel. */
|
|
460
|
+
closeDiagnostics(): void { this.setState({ diagOpen: false }) }
|
|
461
|
+
|
|
462
|
+
/** Clean one orphan worktree (⚙ panel); refreshes the diagnostics payload. */
|
|
463
|
+
async cleanupOrphan(workspaceId: string, taskId: string): Promise<void> {
|
|
464
|
+
try {
|
|
465
|
+
await this.client.worktreeCleanup(workspaceId, taskId)
|
|
466
|
+
const diagnostics = await this.client.diagnostics()
|
|
467
|
+
this.setState({ diagnostics, error: undefined })
|
|
468
|
+
} catch (error) {
|
|
469
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
348
473
|
/** Soft-delete (agent parity) then optional purge. */
|
|
349
474
|
async remove(id: string, ifVersion: number, purge: boolean): Promise<void> {
|
|
350
475
|
try {
|
|
@@ -356,7 +481,7 @@ export class BoardController {
|
|
|
356
481
|
}
|
|
357
482
|
}
|
|
358
483
|
|
|
359
|
-
/** Duplicate a task into a fresh todo card (same project/urgency/prompt/execution/model). */
|
|
484
|
+
/** Duplicate a task into a fresh todo card (same project/urgency/prompt/execution/model/isolation/checklist). */
|
|
360
485
|
async duplicate(task: TaskRecord): Promise<void> {
|
|
361
486
|
try {
|
|
362
487
|
await this.client.create({
|
|
@@ -369,6 +494,9 @@ export class BoardController {
|
|
|
369
494
|
? { mode: 'scheduled', cron: task.execution.cron }
|
|
370
495
|
: { mode: 'claim' },
|
|
371
496
|
model: task.model,
|
|
497
|
+
isolation: task.isolation,
|
|
498
|
+
...(task.presetId !== undefined ? { presetId: task.presetId } : {}),
|
|
499
|
+
...(task.checklist !== undefined && task.checklist.length > 0 ? { checklist: task.checklist.map(i => i.text) } : {}),
|
|
372
500
|
})
|
|
373
501
|
await this.refresh()
|
|
374
502
|
} catch (error) {
|
|
@@ -376,6 +504,105 @@ export class BoardController {
|
|
|
376
504
|
}
|
|
377
505
|
}
|
|
378
506
|
|
|
507
|
+
// ------------------------------------------------ templates (0.4.0)
|
|
508
|
+
/** Load the template list (best effort; errors surface). */
|
|
509
|
+
async loadTemplates(): Promise<TaskTemplate[]> {
|
|
510
|
+
try {
|
|
511
|
+
const value = await this.client.templates()
|
|
512
|
+
this.setState({ templates: value.templates, error: undefined })
|
|
513
|
+
return value.templates
|
|
514
|
+
} catch (error) {
|
|
515
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
516
|
+
return []
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/** Open the + 新建任务 dropdown's template list fresh (called on menu open). */
|
|
521
|
+
prepareTemplateMenu(): void {
|
|
522
|
+
if (this.state.templates.length === 0) void this.loadTemplates()
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** Open the template manager modal. */
|
|
526
|
+
openTemplateManager(): void {
|
|
527
|
+
this.setState({ tplManagerOpen: true })
|
|
528
|
+
void this.loadTemplates()
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** Close the template manager modal. */
|
|
532
|
+
closeTemplateManager(): void { this.setState({ tplManagerOpen: false }) }
|
|
533
|
+
|
|
534
|
+
/** Create or replace a template; refreshes the list. */
|
|
535
|
+
async upsertTemplate(body: { id?: string; name: string; task: TaskTemplateSpec }): Promise<boolean> {
|
|
536
|
+
try {
|
|
537
|
+
await this.client.templateUpsert(body)
|
|
538
|
+
await this.loadTemplates()
|
|
539
|
+
return true
|
|
540
|
+
} catch (error) {
|
|
541
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
542
|
+
return false
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** Delete a template by id; refreshes the list. */
|
|
547
|
+
async deleteTemplate(id: string): Promise<void> {
|
|
548
|
+
try {
|
|
549
|
+
await this.client.templateDelete(id)
|
|
550
|
+
await this.loadTemplates()
|
|
551
|
+
} catch (error) {
|
|
552
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/** 存为模板 from a task card: carries every configurable field incl. checklist texts. */
|
|
557
|
+
async saveAsTemplate(task: TaskRecord): Promise<boolean> {
|
|
558
|
+
return this.upsertTemplate({
|
|
559
|
+
name: task.title.slice(0, 60),
|
|
560
|
+
task: {
|
|
561
|
+
title: task.title,
|
|
562
|
+
description: task.description.length > 0 ? task.description : undefined,
|
|
563
|
+
prompt: task.prompt.length > 0 ? task.prompt : undefined,
|
|
564
|
+
urgency: task.urgency,
|
|
565
|
+
execution: task.execution.mode === 'scheduled' && task.execution.cron !== undefined
|
|
566
|
+
? { mode: 'scheduled', cron: task.execution.cron }
|
|
567
|
+
: { mode: 'claim' },
|
|
568
|
+
model: task.model,
|
|
569
|
+
isolation: task.isolation,
|
|
570
|
+
...(task.presetId !== undefined ? { presetId: task.presetId } : {}),
|
|
571
|
+
...(task.checklist !== undefined && task.checklist.length > 0 ? { checklist: task.checklist.map(i => i.text) } : {}),
|
|
572
|
+
},
|
|
573
|
+
})
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// ------------------------------------------------ import (0.4.0)
|
|
577
|
+
/** Open the import modal. */
|
|
578
|
+
openImport(): void { this.setState({ importOpen: true }) }
|
|
579
|
+
|
|
580
|
+
/** Close the import modal. */
|
|
581
|
+
closeImport(): void { this.setState({ importOpen: false }) }
|
|
582
|
+
|
|
583
|
+
/** Dry-run an import file: classify its tasks against the live ledger. */
|
|
584
|
+
async importPreview(file: unknown): Promise<ImportPreviewResponse['plan'] | undefined> {
|
|
585
|
+
try {
|
|
586
|
+
const value = await this.client.importPreview(file)
|
|
587
|
+
return value.plan
|
|
588
|
+
} catch (error) {
|
|
589
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
590
|
+
return undefined
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/** Commit an import; refreshes the ledger afterwards. */
|
|
595
|
+
async importCommit(mode: 'merge' | 'replace', ledger: unknown): Promise<ImportCommitResponse | undefined> {
|
|
596
|
+
try {
|
|
597
|
+
const value = await this.client.importCommit(mode, ledger)
|
|
598
|
+
await this.refresh()
|
|
599
|
+
return value
|
|
600
|
+
} catch (error) {
|
|
601
|
+
this.setState({ error: error instanceof Error ? error.message : String(error) })
|
|
602
|
+
return undefined
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
379
606
|
/** Download the whole ledger as a JSON backup file. */
|
|
380
607
|
exportJson(): void {
|
|
381
608
|
const stamp = new Date()
|
package/src/client/index.ts
CHANGED
|
@@ -24,12 +24,15 @@ export const name = 'dsh-taskboard/client'
|
|
|
24
24
|
/** Required client services (fiber inject waiting). */
|
|
25
25
|
export const inject = ['connection']
|
|
26
26
|
|
|
27
|
-
/** Narrow connection face for the model catalog. */
|
|
27
|
+
/** Narrow connection face for the model catalog + preset roster. */
|
|
28
28
|
interface ConnectionFace {
|
|
29
29
|
api: {
|
|
30
30
|
llm: {
|
|
31
31
|
models(payload: Record<string, never>): Promise<{ result: { ok: true; value: { groups: Array<{ id: string; name: string; models: Array<{ id: string; name?: string }> }> } } | { ok: false } }>
|
|
32
32
|
}
|
|
33
|
+
agentPresets?: {
|
|
34
|
+
list(payload: Record<string, never>): Promise<{ result: { ok: true; value: { presets: Array<{ id: string; name?: string; isDefault: boolean }> } } | { ok: false } }>
|
|
35
|
+
}
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -64,6 +67,20 @@ export function apply(ctx: ClientContextFace): void {
|
|
|
64
67
|
}
|
|
65
68
|
return out
|
|
66
69
|
}
|
|
70
|
+
|
|
71
|
+
// Preset roster for the composer (0.3.3): agentPreset.list over the
|
|
72
|
+
// connection RPC — [{id, name}] plus which one is the deployment
|
|
73
|
+
// default (the form pre-selects it on create).
|
|
74
|
+
type PresetRow = { id: string; name?: string }
|
|
75
|
+
;(controller as unknown as { presetCatalog?: () => Promise<{ presets: PresetRow[]; defaultId?: string }> }).presetCatalog = async (): Promise<{ presets: PresetRow[]; defaultId?: string }> => {
|
|
76
|
+
const list = connection.api.agentPresets
|
|
77
|
+
if (list === undefined) return { presets: [] }
|
|
78
|
+
const response = await list.list({})
|
|
79
|
+
if (!response.result.ok) return { presets: [] }
|
|
80
|
+
const presets = response.result.value.presets.map((p: { id: string; name?: string }) => ({ id: p.id, name: p.name }))
|
|
81
|
+
const def = response.result.value.presets.find((p: { id: string; isDefault: boolean }) => p.isDefault)
|
|
82
|
+
return { presets, ...(def !== undefined ? { defaultId: def.id } : {}) }
|
|
83
|
+
}
|
|
67
84
|
}
|
|
68
85
|
|
|
69
86
|
// Session navigation for execution rows: resolved LAZILY on every jump —
|
package/src/client/styles.ts
CHANGED
|
@@ -468,6 +468,204 @@ html[data-dsh-atb-active] .dsh-atb-view { display: flex; flex-direction: column;
|
|
|
468
468
|
color: var(--dsw-alias-label-primary, inherit);
|
|
469
469
|
}
|
|
470
470
|
.dsh-atb-alert .dsh-atb-btn { padding: 6px 28px; font-size: 13px; }
|
|
471
|
+
|
|
472
|
+
/* ---------- 0.3.0 isolation ---------- */
|
|
473
|
+
.dsh-atb-isolation-note { display: block; margin-top: 6px; font-size: 11px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
474
|
+
.dsh-atb-mode-picker[data-disabled="true"] .dsh-atb-mode-opt { cursor: not-allowed; opacity: .55; }
|
|
475
|
+
.dsh-atb-iso-none { font-size: 12.5px; color: var(--dsw-alias-label-secondary, inherit); }
|
|
476
|
+
.dsh-atb-iso-facts { display: flex; flex-wrap: wrap; gap: 6px 12px; margin-bottom: 8px; }
|
|
477
|
+
.dsh-atb-iso-fact { font-size: 11.5px; color: var(--dsw-alias-label-secondary, inherit); }
|
|
478
|
+
.dsh-atb-iso-fact b { font-weight: 600; color: var(--dsw-alias-state-business-primary, #3e63dd); }
|
|
479
|
+
.dsh-atb-iso-commits { display: flex; flex-direction: column; gap: 3px; margin-bottom: 8px; }
|
|
480
|
+
.dsh-atb-iso-commit { display: flex; gap: 8px; font-size: 11.5px; align-items: baseline; }
|
|
481
|
+
.dsh-atb-iso-commit code {
|
|
482
|
+
font-family: ui-monospace, Consolas, monospace; font-size: 10.5px;
|
|
483
|
+
color: var(--dsh-alias-state-business-primary, #3e63dd); flex-shrink: 0;
|
|
484
|
+
}
|
|
485
|
+
.dsh-atb-iso-commit span { word-break: break-all; color: var(--dsw-alias-label-secondary, inherit); }
|
|
486
|
+
.dsh-atb-iso-more { font-size: 11px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
487
|
+
.dsh-atb-iso-nocommit { font-size: 11.5px; color: var(--dsw-alias-label-tertiary, gray); margin-bottom: 8px; }
|
|
488
|
+
.dsh-atb-iso-dirty {
|
|
489
|
+
font-size: 11.5px; color: var(--dsw-alias-state-error-primary, #e5484d);
|
|
490
|
+
background: rgba(229,72,77,.09); border: 1px solid rgba(229,72,77,.35);
|
|
491
|
+
border-radius: 8px; padding: 6px 10px; margin-bottom: 8px;
|
|
492
|
+
}
|
|
493
|
+
.dsh-atb-iso-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
494
|
+
.dsh-atb-iso-hint { font-size: 11px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
495
|
+
|
|
496
|
+
/* ---------- 0.3.0 diagnostics ---------- */
|
|
497
|
+
.dsh-atb-diag { max-width: 520px; width: min(520px, 92vw); }
|
|
498
|
+
.dsh-atb-diag-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 14px; }
|
|
499
|
+
.dsh-atb-diag-item {
|
|
500
|
+
display: flex; flex-direction: column; align-items: center; gap: 2px;
|
|
501
|
+
padding: 10px 6px; border-radius: 10px;
|
|
502
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25));
|
|
503
|
+
background: var(--dsw-alias-bg-layer-1, rgba(128,128,128,.04));
|
|
504
|
+
}
|
|
505
|
+
.dsh-atb-diag-item b { font-size: 18px; font-weight: 700; }
|
|
506
|
+
.dsh-atb-diag-item span { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
507
|
+
.dsh-atb-diag-item[data-bad="true"] b { color: var(--dsw-alias-state-error-primary, #e5484d); }
|
|
508
|
+
.dsh-atb-diag-sec h4 { margin: 0 0 8px; font-size: 12.5px; }
|
|
509
|
+
.dsh-atb-diag-orphans { display: flex; flex-direction: column; gap: 6px; }
|
|
510
|
+
.dsh-atb-diag-orphan {
|
|
511
|
+
display: flex; align-items: center; gap: 10px; justify-content: space-between;
|
|
512
|
+
padding: 7px 10px; border-radius: 8px;
|
|
513
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25));
|
|
514
|
+
}
|
|
515
|
+
.dsh-atb-diag-orphan-path { font-size: 11.5px; font-family: ui-monospace, Consolas, monospace; word-break: break-all; }
|
|
516
|
+
|
|
517
|
+
/* ---------- 0.4.0 checklist ---------- */
|
|
518
|
+
.dsh-atb-cke { display: flex; flex-direction: column; gap: 6px; }
|
|
519
|
+
.dsh-atb-cke-row { display: flex; align-items: center; gap: 8px; }
|
|
520
|
+
.dsh-atb-cke-box { flex-shrink: 0; width: 15px; height: 15px; cursor: pointer; }
|
|
521
|
+
.dsh-atb-cke-text {
|
|
522
|
+
flex: 1; min-width: 0; font-size: 12.5px; padding: 6px 9px;
|
|
523
|
+
border-radius: 8px; border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.3));
|
|
524
|
+
background: var(--dsw-alias-bg-layer-1, transparent); color: inherit;
|
|
525
|
+
}
|
|
526
|
+
.dsh-atb-cke-del {
|
|
527
|
+
flex-shrink: 0; border: none; background: none; cursor: pointer; padding: 4px;
|
|
528
|
+
color: var(--dsw-alias-label-tertiary, gray); font-size: 12px; border-radius: 6px;
|
|
529
|
+
}
|
|
530
|
+
.dsh-atb-cke-del:hover { color: var(--dsw-alias-state-error-primary, #e5484d); background: rgba(229,72,77,.08); }
|
|
531
|
+
.dsh-atb-cke-add {
|
|
532
|
+
align-self: flex-start; border: 1px dashed var(--dsw-alias-border-l2, rgba(128,128,128,.4));
|
|
533
|
+
background: none; color: var(--dsw-alias-label-secondary, inherit); cursor: pointer;
|
|
534
|
+
font-size: 11.5px; padding: 5px 12px; border-radius: 8px;
|
|
535
|
+
}
|
|
536
|
+
.dsh-atb-cke-add:hover { color: var(--dsw-alias-state-business-primary, #3e63dd); border-color: var(--dsw-alias-state-business-primary, #3e63dd); }
|
|
537
|
+
.dsh-atb-cke-hint { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
538
|
+
|
|
539
|
+
.dsh-atb-cl-progress { margin-left: 8px; font-size: 11px; font-weight: 400; color: var(--dsw-alias-label-tertiary, gray); }
|
|
540
|
+
.dsh-atb-cl-progress[data-tone="bad"] { color: var(--dsw-alias-state-error-primary, #e5484d); font-weight: 600; }
|
|
541
|
+
.dsh-atb-cl-items { display: flex; flex-direction: column; gap: 5px; }
|
|
542
|
+
.dsh-atb-cl-item {
|
|
543
|
+
display: flex; align-items: baseline; gap: 9px; padding: 6px 9px; border-radius: 8px;
|
|
544
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.22));
|
|
545
|
+
background: var(--dsw-alias-bg-layer-1, rgba(128,128,128,.03)); cursor: pointer;
|
|
546
|
+
}
|
|
547
|
+
.dsh-atb-cl-item:hover { border-color: var(--dsw-alias-border-l2, rgba(128,128,128,.4)); }
|
|
548
|
+
.dsh-atb-cl-item input { flex-shrink: 0; transform: translateY(1px); cursor: pointer; }
|
|
549
|
+
.dsh-atb-cl-item[data-checked="true"] .dsh-atb-cl-text { text-decoration: line-through; color: var(--dsw-alias-label-tertiary, gray); }
|
|
550
|
+
.dsh-atb-cl-item[data-alert="true"] {
|
|
551
|
+
border-color: rgba(229,72,77,.45); background: rgba(229,72,77,.06);
|
|
552
|
+
}
|
|
553
|
+
.dsh-atb-cl-text { flex: 1; min-width: 0; font-size: 12.5px; word-break: break-word; }
|
|
554
|
+
.dsh-atb-cl-meta { flex-shrink: 0; font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); display: flex; flex-direction: column; gap: 2px; align-items: flex-end; }
|
|
555
|
+
.dsh-atb-cl-note { max-width: 280px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--dsw-alias-label-secondary, inherit); }
|
|
556
|
+
|
|
557
|
+
/* ---------- 0.4.0 report ---------- */
|
|
558
|
+
.dsh-atb-rpt-summary { font-size: 12.5px; line-height: 1.6; white-space: pre-wrap; word-break: break-word; margin-bottom: 8px; }
|
|
559
|
+
.dsh-atb-rpt-sec { margin-bottom: 8px; }
|
|
560
|
+
.dsh-atb-rpt-label { font-size: 11px; color: var(--dsw-alias-label-tertiary, gray); margin-bottom: 4px; }
|
|
561
|
+
.dsh-atb-rpt-list { margin: 0; padding-left: 18px; font-size: 12px; line-height: 1.55; word-break: break-all; }
|
|
562
|
+
.dsh-atb-rpt-risk {
|
|
563
|
+
font-size: 12px; line-height: 1.55; white-space: pre-wrap; word-break: break-word;
|
|
564
|
+
color: var(--dsw-alias-state-warn-primary, #f5a524);
|
|
565
|
+
background: rgba(245,165,36,.08); border: 1px solid rgba(245,165,36,.3);
|
|
566
|
+
border-radius: 8px; padding: 6px 10px;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/* ---------- 0.4.0 diff viewer ---------- */
|
|
570
|
+
.dsh-atb-iso-commit { display: flex; flex-direction: column; gap: 3px; }
|
|
571
|
+
.dsh-atb-iso-commit-btn {
|
|
572
|
+
display: flex; gap: 8px; font-size: 11.5px; align-items: baseline; text-align: left;
|
|
573
|
+
border: none; background: none; padding: 2px 4px; margin: 0 -4px; border-radius: 6px; cursor: pointer;
|
|
574
|
+
color: inherit; width: fit-content; max-width: 100%;
|
|
575
|
+
}
|
|
576
|
+
.dsh-atb-iso-commit-btn:hover { background: var(--dsw-alias-bg-layer-2, rgba(128,128,128,.08)); }
|
|
577
|
+
.dsh-atb-iso-commit-btn code {
|
|
578
|
+
font-family: ui-monospace, Consolas, monospace; font-size: 10.5px;
|
|
579
|
+
color: var(--dsw-alias-state-business-primary, #3e63dd); flex-shrink: 0;
|
|
580
|
+
}
|
|
581
|
+
.dsh-atb-iso-commit-btn span { word-break: break-all; color: var(--dsw-alias-label-secondary, inherit); }
|
|
582
|
+
.dsh-atb-iso-commit[data-open="true"] > .dsh-atb-iso-commit-btn code { font-weight: 700; }
|
|
583
|
+
.dsh-atb-iso-dirty { display: flex; flex-direction: column; gap: 6px;
|
|
584
|
+
font-size: 11.5px; color: var(--dsw-alias-state-error-primary, #e5484d);
|
|
585
|
+
background: rgba(229,72,77,.09); border: 1px solid rgba(229,72,77,.35);
|
|
586
|
+
border-radius: 8px; padding: 6px 10px; margin-bottom: 8px;
|
|
587
|
+
}
|
|
588
|
+
.dsh-atb-iso-dirty-toggle { border: none; background: none; cursor: pointer; padding: 0; text-align: left; color: inherit; font-size: inherit; }
|
|
589
|
+
.dsh-atb-iso-dirty-files { display: flex; flex-direction: column; gap: 2px; }
|
|
590
|
+
.dsh-atb-iso-dirty-file {
|
|
591
|
+
border: none; background: none; cursor: pointer; text-align: left; padding: 1px 2px;
|
|
592
|
+
font-size: 11px; color: var(--dsw-alias-label-secondary, inherit); border-radius: 4px; word-break: break-all;
|
|
593
|
+
}
|
|
594
|
+
.dsh-atb-iso-dirty-file:hover { background: rgba(128,128,128,.1); color: var(--dsw-alias-state-business-primary, #3e63dd); }
|
|
595
|
+
.dsh-atb-iso-dirty-file code { font-family: ui-monospace, Consolas, monospace; font-size: 10px; margin-right: 6px; }
|
|
596
|
+
.dsh-atb-diffview { margin-top: 6px; border-radius: 8px; overflow: hidden;
|
|
597
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25)); }
|
|
598
|
+
.dsh-atb-diffview-head { display: flex; align-items: center; gap: 10px; padding: 5px 10px;
|
|
599
|
+
background: var(--dsw-alias-bg-layer-2, rgba(128,128,128,.08)); }
|
|
600
|
+
.dsh-atb-diffview-title { font-family: ui-monospace, Consolas, monospace; font-size: 10.5px;
|
|
601
|
+
color: var(--dsw-alias-state-business-primary, #3e63dd); word-break: break-all; }
|
|
602
|
+
.dsh-atb-diffview-hint { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
603
|
+
.dsh-atb-diffview-error { padding: 8px 10px; font-size: 11.5px; color: var(--dsw-alias-state-error-primary, #e5484d); }
|
|
604
|
+
.dsh-atb-diffview-pre {
|
|
605
|
+
margin: 0; padding: 8px 10px; max-height: 340px; overflow: auto;
|
|
606
|
+
font-family: ui-monospace, Consolas, monospace; font-size: 10.5px; line-height: 1.5;
|
|
607
|
+
white-space: pre; color: var(--dsw-alias-label-secondary, inherit);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/* ---------- 0.4.0 new-task menu + template manager + import ---------- */
|
|
611
|
+
.dsh-atb-newmenu { position: relative; display: inline-flex; }
|
|
612
|
+
.dsh-atb-newmenu-backdrop { position: fixed; inset: 0; z-index: 40; }
|
|
613
|
+
.dsh-atb-newmenu-list {
|
|
614
|
+
position: absolute; top: calc(100% + 4px); left: 0; z-index: 41; min-width: 180px;
|
|
615
|
+
display: flex; flex-direction: column; padding: 5px; border-radius: 10px;
|
|
616
|
+
background: var(--dsw-alias-bg-overlay, #fff); color: var(--dsw-alias-label-primary, inherit);
|
|
617
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25));
|
|
618
|
+
box-shadow: var(--dsw-shadow-lv3, 0 12px 32px rgba(0,0,0,.18));
|
|
619
|
+
}
|
|
620
|
+
.dsh-atb-newmenu-opt {
|
|
621
|
+
border: none; background: none; text-align: left; cursor: pointer; padding: 7px 10px;
|
|
622
|
+
font-size: 12.5px; color: inherit; border-radius: 7px; white-space: nowrap;
|
|
623
|
+
}
|
|
624
|
+
.dsh-atb-newmenu-opt:hover { background: var(--dsw-alias-bg-layer-2, rgba(128,128,128,.1)); }
|
|
625
|
+
.dsh-atb-newmenu-sep { height: 1px; margin: 4px 6px; background: var(--dsw-alias-border-l2, rgba(128,128,128,.25)); }
|
|
626
|
+
|
|
627
|
+
.dsh-atb-tplm { max-width: 560px; width: min(560px, 92vw); }
|
|
628
|
+
.dsh-atb-tplm-list { display: flex; flex-direction: column; gap: 8px; }
|
|
629
|
+
.dsh-atb-tplm-row {
|
|
630
|
+
display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px;
|
|
631
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25));
|
|
632
|
+
}
|
|
633
|
+
.dsh-atb-tplm-name {
|
|
634
|
+
flex: 0 0 160px; font-size: 12.5px; padding: 5px 8px; border-radius: 7px;
|
|
635
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.3));
|
|
636
|
+
background: var(--dsw-alias-bg-layer-1, transparent); color: inherit;
|
|
637
|
+
}
|
|
638
|
+
.dsh-atb-tplm-meta { flex: 1; min-width: 0; font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
639
|
+
.dsh-atb-tplm-btns { display: flex; gap: 6px; flex-shrink: 0; }
|
|
640
|
+
|
|
641
|
+
.dsh-atb-imp { max-width: 600px; width: min(600px, 92vw); }
|
|
642
|
+
.dsh-atb-imp-picker { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
|
643
|
+
.dsh-atb-imp-picker input[type="file"] { font-size: 12px; }
|
|
644
|
+
.dsh-atb-imp-filename { font-size: 11.5px; color: var(--dsw-alias-state-business-primary, #3e63dd); word-break: break-all; }
|
|
645
|
+
.dsh-atb-imp-note { font-size: 11px; color: var(--dsw-alias-label-tertiary, gray); margin-bottom: 10px; }
|
|
646
|
+
.dsh-atb-imp-error { font-size: 12px; color: var(--dsw-alias-state-error-primary, #e5484d); margin-bottom: 8px; }
|
|
647
|
+
.dsh-atb-imp-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 12px; }
|
|
648
|
+
.dsh-atb-imp-stat {
|
|
649
|
+
display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 9px 6px; border-radius: 9px;
|
|
650
|
+
border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25));
|
|
651
|
+
}
|
|
652
|
+
.dsh-atb-imp-stat b { font-size: 17px; font-weight: 700; }
|
|
653
|
+
.dsh-atb-imp-stat span { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); }
|
|
654
|
+
.dsh-atb-imp-stat[data-tone="ok"] b { color: var(--dsw-alias-state-success-primary, #30a46c); }
|
|
655
|
+
.dsh-atb-imp-stat[data-tone="warn"] b { color: var(--dsw-alias-state-warn-primary, #f5a524); }
|
|
656
|
+
.dsh-atb-imp-stat[data-tone="bad"] b { color: var(--dsw-alias-state-error-primary, #e5484d); }
|
|
657
|
+
.dsh-atb-imp-sec h4 { margin: 0 0 6px; font-size: 12px; }
|
|
658
|
+
.dsh-atb-imp-sec { margin-bottom: 10px; }
|
|
659
|
+
.dsh-atb-imp-list { display: flex; flex-direction: column; gap: 4px; max-height: 160px; overflow-y: auto; }
|
|
660
|
+
.dsh-atb-imp-row {
|
|
661
|
+
display: flex; align-items: center; gap: 10px; justify-content: space-between;
|
|
662
|
+
padding: 5px 9px; border-radius: 7px; border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.2));
|
|
663
|
+
}
|
|
664
|
+
.dsh-atb-imp-row[data-tone="bad"] { border-color: rgba(229,72,77,.35); }
|
|
665
|
+
.dsh-atb-imp-row-title { font-size: 12px; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
666
|
+
.dsh-atb-imp-row-status { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); flex-shrink: 0; }
|
|
667
|
+
.dsh-atb-imp-result { font-size: 12px; color: var(--dsw-alias-state-success-primary, #30a46c); margin-top: 10px; }
|
|
668
|
+
.dsh-atb-badge[data-kind="checklist"] { color: var(--dsw-alias-label-secondary, inherit); }
|
|
471
669
|
`
|
|
472
670
|
|
|
473
671
|
let injected = false
|