dsh-taskboard 0.1.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.
Files changed (49) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +169 -0
  3. package/cordis.patch.yml +12 -0
  4. package/lib/client.js +2085 -0
  5. package/lib/host/execution.js +189 -0
  6. package/lib/host/execution.js.map +1 -0
  7. package/lib/host/protocol-text.js +37 -0
  8. package/lib/host/protocol-text.js.map +1 -0
  9. package/lib/host/routes.js +369 -0
  10. package/lib/host/routes.js.map +1 -0
  11. package/lib/host/scheduler.js +91 -0
  12. package/lib/host/scheduler.js.map +1 -0
  13. package/lib/host/sdk.js +145 -0
  14. package/lib/host/sdk.js.map +1 -0
  15. package/lib/host/store.js +112 -0
  16. package/lib/host/store.js.map +1 -0
  17. package/lib/host/tools.js +620 -0
  18. package/lib/host/tools.js.map +1 -0
  19. package/lib/index.js +91 -0
  20. package/lib/index.js.map +1 -0
  21. package/lib/invariant.js +22 -0
  22. package/lib/invariant.js.map +1 -0
  23. package/lib/shared/api.js +9 -0
  24. package/lib/shared/api.js.map +1 -0
  25. package/lib/shared/protocol.js +279 -0
  26. package/lib/shared/protocol.js.map +1 -0
  27. package/package.json +74 -0
  28. package/src/client/api.ts +90 -0
  29. package/src/client/board/NewTaskModal.tsx +8 -0
  30. package/src/client/board/TaskBoard.tsx +184 -0
  31. package/src/client/board/TaskCard.tsx +61 -0
  32. package/src/client/board/TaskDetail.tsx +210 -0
  33. package/src/client/board/TaskFormModal.tsx +257 -0
  34. package/src/client/board-mount.tsx +92 -0
  35. package/src/client/controller.ts +241 -0
  36. package/src/client/index.ts +87 -0
  37. package/src/client/sidebar-entry.ts +165 -0
  38. package/src/client/styles.ts +391 -0
  39. package/src/host/execution.ts +244 -0
  40. package/src/host/protocol-text.ts +37 -0
  41. package/src/host/routes.ts +387 -0
  42. package/src/host/scheduler.ts +107 -0
  43. package/src/host/sdk.ts +200 -0
  44. package/src/host/store.ts +139 -0
  45. package/src/host/tools.ts +631 -0
  46. package/src/index.ts +124 -0
  47. package/src/invariant.ts +22 -0
  48. package/src/shared/api.ts +98 -0
  49. package/src/shared/protocol.ts +475 -0
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Sidebar entry injection — structure ported from the working dsh-web-ui
3
+ * family implementations (dsh-ssh / dsh-client-ui-task-board, verified live
4
+ * in this shell): scope to the sidebar root (the logoRow's parent), find the
5
+ * New Session button inside it (newSession class → first direct-child BUTTON
6
+ * → aria-label/text fallbacks), and insert the entry as a direct child of
7
+ * that root next to the logo row. A body-level MutationObserver waits for
8
+ * the shell and self-heals React re-renders; a slow timer covers shells that
9
+ * mount late without further mutations.
10
+ *
11
+ * The row is plain DOM (no React tree) so it can never disturb the shell's
12
+ * reconciliation; the board view it toggles is a separate React root mounted
13
+ * in the center column (see board-mount.tsx).
14
+ *
15
+ * @module dsh-taskboard/client/sidebar-entry
16
+ */
17
+ import type { BoardController } from './controller.ts'
18
+
19
+ /** Stable data attribute identifying this entry row. */
20
+ export const ENTRY_SELECTOR = '[data-dsh-atb-entry]'
21
+
22
+ /** Inline icon (16px nav-icon look). */
23
+ const ICON = '<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2.5" width="12" height="11" rx="1.5"/><path d="M2 6.5h12M6.5 6.5v7"/></svg>'
24
+
25
+ /**
26
+ * Find the sidebar shell root element, or undefined while not yet mounted.
27
+ * (Same as the working family plugins: sidebarCol pane → logoRow owner.)
28
+ */
29
+ function sidebarRoot(): HTMLElement | undefined {
30
+ const column = document.querySelector<HTMLElement>('[data-pane="sidebar"], [class*="sidebarCol"]')
31
+ if (column === null) return undefined
32
+ const logoOwner = column.querySelector<HTMLElement>('[class*="logoRow"]')?.parentElement
33
+ return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)
34
+ }
35
+
36
+ /**
37
+ * The New Session button: nested in the logo row on current shells, a direct
38
+ * child BUTTON on the real shell (the family plugins' fallback), with
39
+ * aria-label/text fallbacks for other shells.
40
+ */
41
+ function newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {
42
+ const nested = root.querySelector<HTMLButtonElement>('button[class*="newSession"]')
43
+ if (nested !== null) return nested
44
+ for (const child of root.children) {
45
+ if (child instanceof HTMLButtonElement) return child
46
+ }
47
+ const byAria = root.querySelector<HTMLButtonElement>(
48
+ 'button[aria-label="新建会话"], button[aria-label="New Session"], button[aria-label*="新会话"], button[aria-label*="new session" i]',
49
+ )
50
+ if (byAria !== null) return byAria
51
+ const buttons = Array.from(root.querySelectorAll<HTMLButtonElement>('button'))
52
+ return buttons.find(button => /新会话|新建会话|new session/i.test(button.textContent ?? ''))
53
+ }
54
+
55
+ /** Build the entry row (a detached button; insert once the shell is up). */
56
+ function createEntry(controller: BoardController): HTMLButtonElement {
57
+ const entry = document.createElement('button')
58
+ entry.type = 'button'
59
+ entry.dataset.dshAtbEntry = ''
60
+ entry.className = 'dsh-atb-entry'
61
+ entry.setAttribute('aria-label', 'Agent 任务看板')
62
+ entry.innerHTML = `<span class="dsh-atb-entry-icon">${ICON}</span><span class="dsh-atb-entry-label">任务看板</span>`
63
+ entry.addEventListener('click', () => { controller.toggleBoard() })
64
+ return entry
65
+ }
66
+
67
+ /** Re-insert the entry after the New Session row (before the browser region). */
68
+ function placeEntry(root: HTMLElement, entry: HTMLButtonElement): boolean {
69
+ const button = newSessionButton(root)
70
+ if (button === undefined) return false
71
+ if (entry.parentElement !== root) {
72
+ // Position relative to the family block (entries injected by sibling
73
+ // plugins), never relative to transient logoRow geometry: every family
74
+ // plugin that self-heals during a re-render then lands in the same
75
+ // relative order. No append-to-end fallback: appending at the end would
76
+ // randomly reorder the block after a shell re-render.
77
+ const row = button.closest('[class*="logoRow"]')
78
+ const base = (row !== null && row.parentElement === root) ? row : button
79
+ const family = Array.from(root.children).filter(
80
+ (el): el is HTMLElement => el instanceof HTMLElement && el.matches('[data-dsh-atb-entry], [data-dsh-taskboard-entry], [data-dsh-ssh-entry]'),
81
+ )
82
+ // taskboard sits before the whole family block.
83
+ const anchor = family.length > 0 ? (family[0] ?? null) : (base.nextElementSibling ?? null)
84
+ root.insertBefore(entry, anchor)
85
+ }
86
+ return true
87
+ }
88
+
89
+ /** Debug counters (window.__atbDebug) — evidence if the entry fails to appear. */
90
+ interface AtbDebug { attempts: number; found: boolean; placed: boolean }
91
+
92
+ /**
93
+ * Mount the sidebar entry, waiting for the shell to render and self-healing
94
+ * on later React re-renders.
95
+ * @param controller - the board controller the entry toggles.
96
+ * @returns disposer removing the entry and its observers.
97
+ */
98
+ export function mountSidebarEntry(controller: BoardController): () => void {
99
+ const entry = createEntry(controller)
100
+ const debug: AtbDebug = { attempts: 0, found: false, placed: false }
101
+ ;(window as unknown as { __atbDebug?: AtbDebug }).__atbDebug = debug
102
+ let root: HTMLElement | undefined
103
+ let placed = false
104
+
105
+ const tryPlace = (): void => {
106
+ debug.attempts++
107
+ if (root !== undefined && !root.isConnected) {
108
+ rootObserver.disconnect()
109
+ root = undefined
110
+ placed = false
111
+ }
112
+ if (placed) {
113
+ if (document.body.contains(entry)) return
114
+ rootObserver.disconnect()
115
+ root = undefined
116
+ placed = false
117
+ }
118
+ root ??= sidebarRoot()
119
+ if (root === undefined) return
120
+ debug.found = newSessionButton(root) !== undefined
121
+ placed = placeEntry(root, entry)
122
+ debug.placed = placed
123
+ if (placed) {
124
+ rootObserver.observe(root, { childList: true, subtree: true })
125
+ }
126
+ }
127
+
128
+ // Body-level watcher as the whole-rebuild fallback.
129
+ const waitObserver = new MutationObserver(() => { tryPlace() })
130
+ waitObserver.observe(document.body, { childList: true, subtree: true })
131
+
132
+ // Self-heal: re-insert in the same frame when a re-render displaces the row.
133
+ const rootObserver = new MutationObserver(() => {
134
+ if (root === undefined || !root.isConnected) {
135
+ placed = false
136
+ tryPlace()
137
+ return
138
+ }
139
+ if (!root.contains(entry)) {
140
+ placed = placeEntry(root, entry)
141
+ }
142
+ })
143
+
144
+ // Belt-and-braces: a late shell mount that triggers no further mutations
145
+ // still gets periodic retries (the family plugins rely on mutation traffic
146
+ // alone; the timer costs one cheap contains-check per tick once placed).
147
+ const retry = setInterval(() => { tryPlace() }, 2_000)
148
+
149
+ const syncActive = () => {
150
+ if (controller.getSnapshot().boardOpen) entry.dataset.active = 'true'
151
+ else delete entry.dataset.active
152
+ }
153
+ const unsubscribe = controller.subscribe(syncActive)
154
+ syncActive()
155
+
156
+ tryPlace()
157
+
158
+ return () => {
159
+ clearInterval(retry)
160
+ waitObserver.disconnect()
161
+ rootObserver.disconnect()
162
+ unsubscribe()
163
+ entry.remove()
164
+ }
165
+ }
@@ -0,0 +1,391 @@
1
+ /**
2
+ * Board styles, injected as one global stylesheet with dsh-atb- prefixed
3
+ * classes. Colors ride the shell's --dsw-* design tokens where available so
4
+ * the board follows the active theme/skin; urgency accents are the fixed
5
+ * red/purple/blue of the protocol.
6
+ *
7
+ * @module dsh-taskboard/client/styles
8
+ */
9
+
10
+ /** The stylesheet text. */
11
+ export const STYLES = `
12
+ .dsh-atb-entry {
13
+ display: flex; align-items: center; gap: 8px;
14
+ width: calc(100% - 8px); margin: 2px 4px; padding: 6px 10px;
15
+ border: none; border-radius: 8px; background: transparent;
16
+ color: var(--dsw-text-secondary, inherit); font: inherit; font-size: 13px;
17
+ cursor: pointer; text-align: left;
18
+ }
19
+ .dsh-atb-entry:hover { background: var(--dsw-hover, rgba(128,128,128,.12)); color: var(--dsw-text-primary, inherit); }
20
+ .dsh-atb-entry[data-active="true"] { background: var(--dsw-active, rgba(128,128,128,.18)); color: var(--dsw-text-primary, inherit); font-weight: 500; }
21
+ .dsh-atb-entry svg { flex: none; }
22
+
23
+ html[data-dsh-atb-active] [data-pane="conversation"] > *:not([data-dsh-atb-view]) { display: none !important; }
24
+ .dsh-atb-view { display: none; }
25
+ html[data-dsh-atb-active] .dsh-atb-view { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
26
+
27
+ .dsh-atb-board { display: flex; flex-direction: column; height: 100%; min-height: 0; padding: 12px 16px; gap: 10px; box-sizing: border-box; }
28
+ .dsh-atb-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
29
+ .dsh-atb-title { font-size: 15px; font-weight: 600; margin: 0; }
30
+ .dsh-atb-count { font-size: 12px; color: var(--dsw-text-secondary, gray); }
31
+ .dsh-atb-spacer { flex: 1; }
32
+ .dsh-atb-select, .dsh-atb-input {
33
+ font: inherit; font-size: 12.5px; padding: 5px 8px; border-radius: 7px;
34
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.35));
35
+ background: var(--dsw-bg, transparent); color: inherit;
36
+ }
37
+ .dsh-atb-chip {
38
+ display: inline-flex; align-items: center; gap: 5px;
39
+ font-size: 12px; padding: 3px 9px; border-radius: 999px; cursor: pointer;
40
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.35));
41
+ background: transparent; color: var(--dsw-text-secondary, inherit);
42
+ }
43
+ .dsh-atb-chip[data-on="true"] { color: #fff; border-color: transparent; }
44
+ .dsh-atb-chip[data-urgency="urgent"][data-on="true"] { background: #e5484d; }
45
+ .dsh-atb-chip[data-urgency="normal"][data-on="true"] { background: #8e4ec6; }
46
+ .dsh-atb-chip[data-urgency="relaxed"][data-on="true"] { background: #3e63dd; }
47
+ .dsh-atb-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
48
+ .dsh-atb-dot[data-urgency="urgent"] { background: #e5484d; }
49
+ .dsh-atb-dot[data-urgency="normal"] { background: #8e4ec6; }
50
+ .dsh-atb-dot[data-urgency="relaxed"] { background: #3e63dd; }
51
+
52
+ .dsh-atb-btn {
53
+ font: inherit; font-size: 12.5px; padding: 5px 11px; border-radius: 7px; cursor: pointer;
54
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.35));
55
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.08)); color: inherit;
56
+ }
57
+ .dsh-atb-btn:hover { background: var(--dsw-hover, rgba(128,128,128,.18)); }
58
+ .dsh-atb-btn:disabled { opacity: .45; cursor: default; }
59
+ .dsh-atb-btn[data-primary="true"] { background: var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary, #1f2328)); border-color: transparent; color: var(--dsw-alias-label-primary-foreground, #fff); }
60
+ .dsh-atb-btn[data-danger="true"] { color: var(--dsw-alias-state-error-primary, #e5484d); border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary, #e5484d) 45%, transparent); }
61
+
62
+ .dsh-atb-columns { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 10px; flex: 1; min-height: 0; overflow-x: auto; }
63
+
64
+ .dsh-atb-detailpanel {
65
+ display: flex; flex-direction: column;
66
+ flex: none; max-height: 55%; min-height: 180px; overflow: hidden;
67
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.25)); border-radius: 12px;
68
+ background: var(--dsw-bg-panel, var(--dsw-bg-elevated, rgba(128,128,128,.05)));
69
+ padding: 10px 12px; box-shadow: 0 -4px 18px rgba(0,0,0,.12);
70
+ }
71
+ .dsh-atb-detailpanel .dsh-atb-detail { flex: 1; min-height: 0; }
72
+ .dsh-atb-column { display: flex; flex-direction: column; min-width: 200px; min-height: 0; border-radius: 10px; background: var(--dsw-bg-inset, rgba(128,128,128,.07)); padding: 8px; gap: 8px; }
73
+ .dsh-atb-colhead { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; padding: 2px 4px; }
74
+ .dsh-atb-colcount { font-size: 11px; font-weight: 400; color: var(--dsw-text-secondary, gray); }
75
+ .dsh-atb-cards { display: flex; flex-direction: column; gap: 8px; overflow-y: auto; min-height: 0; flex: 1; padding: 2px; }
76
+
77
+ .dsh-atb-card {
78
+ position: relative; border-radius: 9px; padding: 8px 10px 8px 13px; cursor: pointer;
79
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.1));
80
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.25));
81
+ font-size: 13px; text-align: left; color: inherit; width: 100%; box-sizing: border-box;
82
+ }
83
+ .dsh-atb-card:hover { border-color: var(--dsw-border-strong, rgba(128,128,128,.55)); }
84
+ .dsh-atb-card[draggable="true"] { cursor: grab; }
85
+ .dsh-atb-card[data-dragging] { opacity: .45; }
86
+ .dsh-atb-column[data-dragover] { outline: 2px dashed var(--dsw-border-strong, rgba(128,128,128,.55)); outline-offset: -2px; background: var(--dsw-bg-hover, rgba(128,128,128,.12)); }
87
+ .dsh-atb-card::before {
88
+ content: ""; position: absolute; left: 4px; top: 6px; bottom: 6px; width: 3.5px; border-radius: 3px;
89
+ }
90
+ .dsh-atb-card[data-urgency="urgent"]::before { background: #e5484d; }
91
+ .dsh-atb-card[data-urgency="normal"]::before { background: #8e4ec6; }
92
+ .dsh-atb-card[data-urgency="relaxed"]::before { background: #3e63dd; }
93
+ .dsh-atb-card-title { font-weight: 550; line-height: 1.35; word-break: break-word; }
94
+ .dsh-atb-card-meta { display: flex; align-items: center; gap: 6px; margin-top: 5px; font-size: 11px; color: var(--dsw-text-secondary, gray); flex-wrap: wrap; }
95
+ .dsh-atb-badge { font-size: 10.5px; padding: 1px 6px; border-radius: 5px; background: rgba(128,128,128,.18); }
96
+ .dsh-atb-badge[data-kind="blocked"] { background: rgba(229,72,77,.18); color: #e5484d; }
97
+ .dsh-atb-badge[data-kind="scheduled"] { background: rgba(62,99,221,.16); color: #3e63dd; }
98
+ .dsh-atb-badge[data-kind="trashed"] { background: rgba(229,72,77,.14); color: #e5484d; text-decoration: line-through; }
99
+ .dsh-atb-badge[data-kind="done"] { background: rgba(46,160,67,.16); color: #2ea043; }
100
+ .dsh-atb-badge[data-kind="running"] { background: rgba(229,152,42,.16); color: #e69842; }
101
+
102
+ .dsh-atb-error { font-size: 12px; color: #e5484d; padding: 4px 8px; border-radius: 6px; background: rgba(229,72,77,.1); }
103
+ .dsh-atb-empty { font-size: 12px; color: var(--dsw-text-secondary, gray); padding: 10px 4px; }
104
+
105
+ /* ---------- detail pane (polished) ---------- */
106
+ .dsh-atb-detail {
107
+ display: flex; flex-direction: column; gap: 12px; overflow-y: auto; min-height: 0; flex: 1;
108
+ padding: 2px; position: relative;
109
+ }
110
+ .dsh-atb-detail::before {
111
+ content: ""; position: sticky; top: 0; height: 3px; border-radius: 3px; flex: none;
112
+ }
113
+ .dsh-atb-detail[data-urgency="urgent"]::before { background: linear-gradient(90deg, #e5484d, rgba(229,72,77,.15)); }
114
+ .dsh-atb-detail[data-urgency="normal"]::before { background: linear-gradient(90deg, #8e4ec6, rgba(142,78,198,.15)); }
115
+ .dsh-atb-detail[data-urgency="relaxed"]::before { background: linear-gradient(90deg, #3e63dd, rgba(62,99,221,.15)); }
116
+
117
+ .dsh-atb-detail-head { display: flex; align-items: flex-start; gap: 10px; }
118
+ .dsh-atb-detail-titlewrap { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
119
+ .dsh-atb-detail-titlebar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
120
+ .dsh-atb-detail-titlebar h3 { margin: 0; font-size: 15.5px; line-height: 1.35; word-break: break-word; }
121
+ .dsh-atb-detail-close {
122
+ flex: none; width: 26px; height: 26px; border-radius: 7px; border: none; cursor: pointer;
123
+ background: transparent; color: var(--dsw-text-secondary, gray); font-size: 13px; line-height: 1;
124
+ }
125
+ .dsh-atb-detail-close:hover { background: var(--dsw-hover, rgba(128,128,128,.18)); color: inherit; }
126
+ .dsh-atb-detail-topbtns { display: flex; align-items: center; gap: 6px; flex: none; }
127
+ .dsh-atb-detail-edit {
128
+ font: inherit; font-size: 12px; padding: 4px 10px; border-radius: 7px; cursor: pointer;
129
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.32));
130
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.07)); color: var(--dsw-text-secondary, inherit);
131
+ }
132
+ .dsh-atb-detail-edit:hover { border-color: var(--dsw-alias-brand-primary, #1f2328); color: var(--dsw-alias-label-primary, inherit); }
133
+
134
+ .dsh-atb-statuspill {
135
+ flex: none; font-size: 11px; font-weight: 600; padding: 2px 9px; border-radius: 999px; letter-spacing: .02em;
136
+ }
137
+ .dsh-atb-statuspill[data-status="backlog"] { background: rgba(128,128,128,.18); color: var(--dsw-text-secondary, #888); }
138
+ .dsh-atb-statuspill[data-status="todo"] { background: rgba(62,99,221,.15); color: #3e63dd; }
139
+ .dsh-atb-statuspill[data-status="in_progress"] { background: rgba(230,152,66,.16); color: #d9822b; }
140
+ .dsh-atb-statuspill[data-status="in_review"] { background: rgba(142,78,198,.16); color: #8e4ec6; }
141
+ .dsh-atb-statuspill[data-status="done"] { background: rgba(46,160,67,.16); color: #2ea043; }
142
+ .dsh-atb-statuspill[data-status="canceled"], .dsh-atb-statuspill[data-status="archived"] { background: rgba(128,128,128,.14); color: var(--dsw-text-secondary, #888); }
143
+
144
+ .dsh-atb-detail-chips { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
145
+ .dsh-atb-chip2 {
146
+ display: inline-flex; align-items: center; gap: 4px; font-size: 11px; line-height: 1;
147
+ padding: 3px 8px; border-radius: 6px;
148
+ background: var(--dsw-bg-inset, rgba(128,128,128,.09)); color: var(--dsw-text-secondary, #999);
149
+ }
150
+ .dsh-atb-chip2-icon { font-size: 10.5px; opacity: .85; }
151
+ .dsh-atb-chip2[data-tone="urgent"] { background: rgba(229,72,77,.15); color: #e5484d; }
152
+ .dsh-atb-chip2[data-tone="normal"] { background: rgba(142,78,198,.14); color: #a06ce0; }
153
+ .dsh-atb-chip2[data-tone="relaxed"] { background: rgba(62,99,221,.13); color: #6d92e8; }
154
+ .dsh-atb-detail-sub { font-size: 11.5px; color: var(--dsw-text-secondary, gray); }
155
+
156
+ .dsh-atb-fieldcard {
157
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.22)); border-radius: 10px;
158
+ padding: 9px 11px; display: flex; flex-direction: column; gap: 5px;
159
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.06));
160
+ }
161
+ .dsh-atb-fieldcard-label {
162
+ font-size: 10.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
163
+ color: var(--dsw-text-secondary, gray);
164
+ }
165
+ .dsh-atb-fieldcard[data-kind="prompt"] .dsh-atb-fieldcard-label { color: #8e63c8; }
166
+ .dsh-atb-promptbox {
167
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
168
+ font-size: 12px; line-height: 1.55; white-space: pre-wrap; word-break: break-word;
169
+ background: var(--dsw-bg-inset, rgba(128,128,128,.08)); border-radius: 7px; padding: 8px 10px;
170
+ border: 1px dashed var(--dsw-border, rgba(128,128,128,.25));
171
+ }
172
+ .dsh-atb-desc { white-space: pre-wrap; word-break: break-word; font-size: 13px; line-height: 1.55; }
173
+
174
+ .dsh-atb-detail-actions { display: flex; flex-direction: column; gap: 8px; }
175
+ .dsh-atb-runbtn {
176
+ font: inherit; font-size: 13px; font-weight: 600; padding: 8px 14px; border-radius: 9px; cursor: pointer;
177
+ border: 1px solid transparent; background: var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary, #1f2328)); color: var(--dsw-alias-label-primary-foreground, #fff); text-align: center;
178
+ transition: filter .12s ease;
179
+ }
180
+ .dsh-atb-runbtn:hover { filter: brightness(1.1); }
181
+ .dsh-atb-movebtns { display: flex; gap: 6px; flex-wrap: wrap; }
182
+ .dsh-atb-movebtn {
183
+ font: inherit; font-size: 12px; padding: 4px 11px; border-radius: 999px; cursor: pointer;
184
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.32));
185
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.07)); color: var(--dsw-text-secondary, inherit);
186
+ transition: border-color .12s ease, color .12s ease;
187
+ }
188
+ .dsh-atb-movebtn:hover { border-color: var(--dsw-border-strong, rgba(128,128,128,.6)); color: inherit; }
189
+ .dsh-atb-movebtn[data-to="done"] { border-color: rgba(46,160,67,.55); color: #2ea043; }
190
+ .dsh-atb-movebtn[data-to="done"]:hover { background: rgba(46,160,67,.12); }
191
+ .dsh-atb-movebtn[data-to="canceled"], .dsh-atb-movebtn[data-to="archived"] { opacity: .75; }
192
+ .dsh-atb-movebtn[data-to="blocked"] { border-color: rgba(229,72,77,.45); }
193
+ .dsh-atb-movebtn[data-to="blocked"]:hover { background: rgba(229,72,77,.1); }
194
+ .dsh-atb-confirm { display: inline-flex; align-items: center; gap: 6px; }
195
+ .dsh-atb-confirm-label { font-size: 11.5px; color: var(--dsw-text-secondary, gray); }
196
+
197
+ .dsh-atb-section { font-size: 13px; display: flex; flex-direction: column; gap: 7px; }
198
+ .dsh-atb-section h4 {
199
+ margin: 0; font-size: 11px; color: var(--dsw-text-secondary, gray);
200
+ text-transform: uppercase; letter-spacing: .05em; display: flex; align-items: center; gap: 6px;
201
+ }
202
+ .dsh-atb-count2 {
203
+ font-size: 10px; font-weight: 600; padding: 0 6px; border-radius: 999px; line-height: 16px;
204
+ background: var(--dsw-bg-inset, rgba(128,128,128,.14)); color: var(--dsw-text-secondary, gray);
205
+ }
206
+ .dsh-atb-empty2 { font-size: 12px; color: var(--dsw-text-secondary, gray); padding: 8px 0; }
207
+
208
+ .dsh-atb-commentlist { display: flex; flex-direction: column; gap: 8px; }
209
+ .dsh-atb-bubble { display: flex; gap: 8px; }
210
+ .dsh-atb-bubble-avatar {
211
+ flex: none; width: 26px; height: 26px; border-radius: 8px; display: flex; align-items: center; justify-content: center;
212
+ font-size: 13px; background: var(--dsw-bg-inset, rgba(128,128,128,.12));
213
+ }
214
+ .dsh-atb-bubble[data-from="agent"] .dsh-atb-bubble-avatar { background: rgba(142,78,198,.15); }
215
+ .dsh-atb-bubble-main {
216
+ flex: 1; min-width: 0; border-radius: 10px; padding: 6px 10px;
217
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.08));
218
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.18));
219
+ }
220
+ .dsh-atb-bubble[data-from="agent"] .dsh-atb-bubble-main { border-color: rgba(142,78,198,.3); background: rgba(142,78,198,.07); }
221
+ .dsh-atb-bubble-meta { display: flex; align-items: baseline; gap: 8px; margin-bottom: 3px; }
222
+ .dsh-atb-bubble-meta b { font-size: 11.5px; font-weight: 600; color: var(--dsw-text-primary, inherit); }
223
+ .dsh-atb-bubble[data-from="agent"] .dsh-atb-bubble-meta b { color: #a06ce0; }
224
+ .dsh-atb-bubble-meta span { font-size: 10.5px; color: var(--dsw-text-secondary, gray); }
225
+ .dsh-atb-bubble-body { font-size: 12.5px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; }
226
+
227
+ .dsh-atb-composer { display: flex; gap: 7px; align-items: flex-end; margin-top: 2px; }
228
+ .dsh-atb-composer-input {
229
+ flex: 1; font: inherit; font-size: 12.5px; line-height: 1.5; padding: 7px 10px; border-radius: 9px;
230
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.32));
231
+ background: var(--dsw-bg, transparent); color: inherit; resize: vertical; min-height: 38px;
232
+ }
233
+ .dsh-atb-composer-input:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #1f2328); }
234
+ .dsh-atb-composer-send {
235
+ flex: none; font: inherit; font-size: 12.5px; padding: 7px 14px; border-radius: 9px; cursor: pointer;
236
+ border: 1px solid transparent; background: var(--dsw-alias-button-primary-fill, var(--dsw-alias-brand-primary, #1f2328)); color: var(--dsw-alias-label-primary-foreground, #fff);
237
+ }
238
+ .dsh-atb-composer-send:disabled { opacity: .4; cursor: default; }
239
+
240
+ .dsh-atb-execlist { display: flex; flex-direction: column; gap: 5px; }
241
+ .dsh-atb-exec-row {
242
+ display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 12px;
243
+ padding: 5px 9px; border-radius: 8px;
244
+ background: var(--dsw-bg-elevated, rgba(128,128,128,.07));
245
+ border: 1px solid var(--dsw-border, rgba(128,128,128,.16));
246
+ }
247
+ .dsh-atb-exec-dot { flex: none; width: 7px; height: 7px; border-radius: 50%; background: rgba(128,128,128,.5); }
248
+ .dsh-atb-exec-dot[data-outcome="succeeded"] { background: #2ea043; box-shadow: 0 0 0 3px rgba(46,160,67,.15); }
249
+ .dsh-atb-exec-dot[data-outcome="failed"] { background: #e5484d; box-shadow: 0 0 0 3px rgba(229,72,77,.15); }
250
+ .dsh-atb-exec-dot[data-outcome="running"] { background: #d9822b; box-shadow: 0 0 0 3px rgba(217,130,43,.18); animation: dsh-atb-pulse 1.6s ease-in-out infinite; }
251
+ @keyframes dsh-atb-pulse { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
252
+ .dsh-atb-exec-trigger { font-size: 11px; color: var(--dsw-text-secondary, gray); }
253
+ .dsh-atb-exec-outcome { font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: 5px; }
254
+ .dsh-atb-exec-outcome[data-outcome="succeeded"] { background: rgba(46,160,67,.15); color: #2ea043; }
255
+ .dsh-atb-exec-outcome[data-outcome="failed"] { background: rgba(229,72,77,.14); color: #e5484d; }
256
+ .dsh-atb-exec-outcome[data-outcome="running"] { background: rgba(217,130,43,.15); color: #d9822b; }
257
+ .dsh-atb-exec-outcome[data-outcome="cancelled"] { background: rgba(128,128,128,.15); color: var(--dsw-text-secondary, gray); }
258
+ .dsh-atb-exec-time { font-size: 11px; color: var(--dsw-text-secondary, gray); }
259
+ .dsh-atb-exec-session { font-size: 11px; color: var(--dsw-text-secondary, gray); }
260
+ .dsh-atb-exec-error { flex-basis: 100%; font-size: 11px; color: #e5484d; word-break: break-all; }
261
+
262
+ .dsh-atb-dangerzone {
263
+ display: flex; align-items: center; gap: 8px; margin-top: auto; padding-top: 8px;
264
+ border-top: 1px dashed var(--dsw-border, rgba(128,128,128,.25));
265
+ }
266
+
267
+ /* ---------- task form modal (create + edit, polished) ---------- */
268
+ .dsh-atb-modal-backdrop {
269
+ position: fixed; inset: 0; z-index: 80;
270
+ background: var(--dsw-alias-bg-mask-drop, rgba(28,30,36,.4)); backdrop-filter: var(--dsw-mask-blur, blur(2px));
271
+ display: flex; align-items: center; justify-content: center;
272
+ animation: dsh-atb-fade .14s ease;
273
+ }
274
+ @keyframes dsh-atb-fade { from { opacity: 0; } }
275
+ .dsh-atb-modal {
276
+ width: min(560px, calc(100vw - 48px)); max-height: calc(100vh - 80px);
277
+ display: flex; flex-direction: column; overflow: hidden; border-radius: 14px;
278
+ background: var(--dsw-alias-bg-overlay, #fff); color: var(--dsw-alias-label-primary, inherit);
279
+ border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.25));
280
+ box-shadow: var(--dsw-shadow-lv3, 0 12px 32px rgba(0,0,0,.18));
281
+ animation: dsh-atb-pop .16s ease;
282
+ }
283
+ @keyframes dsh-atb-pop { from { opacity: 0; transform: translateY(8px) scale(.98); } }
284
+ .dsh-atb-modal-head {
285
+ display: flex; align-items: center; gap: 10px;
286
+ padding: 13px 16px 11px; border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.18));
287
+ }
288
+ .dsh-atb-modal-headicon {
289
+ flex: none; width: 30px; height: 30px; border-radius: 9px;
290
+ display: flex; align-items: center; justify-content: center;
291
+ font-size: 14px; background: var(--dsw-alias-brand-primary, #1f2328); color: var(--dsw-alias-label-primary-foreground, #fff);
292
+ }
293
+ .dsh-atb-modal-headtext { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
294
+ .dsh-atb-modal-headtext h3 { margin: 0; font-size: 15px; line-height: 1.3; }
295
+ .dsh-atb-modal-headtext p { margin: 0; font-size: 11.5px; color: var(--dsw-alias-label-secondary, gray); }
296
+ .dsh-atb-modal-close {
297
+ flex: none; width: 26px; height: 26px; border-radius: 7px; border: none; cursor: pointer;
298
+ background: transparent; color: var(--dsw-alias-label-tertiary, gray); font-size: 13px; line-height: 1;
299
+ }
300
+ .dsh-atb-modal-close:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.18)); color: var(--dsw-alias-label-primary, inherit); }
301
+
302
+ .dsh-atb-modal-body {
303
+ padding: 13px 16px; overflow-y: auto;
304
+ display: grid; grid-template-columns: 1fr 1fr; gap: 11px 10px;
305
+ }
306
+ .dsh-atb-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
307
+ .dsh-atb-field[data-span="full"] { grid-column: 1 / -1; }
308
+ .dsh-atb-field-label {
309
+ display: flex; align-items: center; gap: 3px;
310
+ font-size: 11px; font-weight: 600; letter-spacing: .03em;
311
+ color: var(--dsw-alias-label-secondary, gray);
312
+ }
313
+ .dsh-atb-req { color: var(--dsw-alias-state-error-primary, #e5484d); font-style: normal; }
314
+ .dsh-atb-modal-body input, .dsh-atb-modal-body textarea, .dsh-atb-modal-body select {
315
+ font: inherit; font-size: 13px; padding: 7px 10px; border-radius: 8px;
316
+ width: 100%; box-sizing: border-box;
317
+ border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.35));
318
+ background: var(--dsw-specific-input-major, transparent); color: var(--dsw-alias-label-primary, inherit);
319
+ transition: border-color .12s ease, box-shadow .12s ease;
320
+ }
321
+ .dsh-atb-modal-body textarea { min-height: 64px; resize: vertical; }
322
+ .dsh-atb-modal-body input:focus, .dsh-atb-modal-body textarea:focus, .dsh-atb-modal-body select:focus {
323
+ outline: none; border-color: var(--dsw-alias-brand-primary, #1f2328);
324
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary, #1f2328) 18%, transparent);
325
+ }
326
+ .dsh-atb-modal-body .dsh-atb-input-bad { border-color: var(--dsw-alias-state-error-primary, #e5484d); }
327
+ .dsh-atb-modal-body .dsh-atb-input-bad:focus { box-shadow: 0 0 0 3px color-mix(in srgb, var(--dsw-alias-state-error-primary, #e5484d) 20%, transparent); }
328
+
329
+ .dsh-atb-urgency-picker { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; }
330
+ .dsh-atb-urgency-opt {
331
+ display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
332
+ padding: 8px 10px; border-radius: 9px; cursor: pointer;
333
+ border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.35));
334
+ background: transparent; color: inherit;
335
+ transition: border-color .12s ease, background .12s ease;
336
+ }
337
+ .dsh-atb-urgency-name { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; }
338
+ .dsh-atb-urgency-hint { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); }
339
+ .dsh-atb-urgency-opt:hover { border-color: var(--dsw-alias-label-tertiary, rgba(128,128,128,.6)); }
340
+ .dsh-atb-urgency-opt[data-on="true"][data-urgency="urgent"] { border-color: rgba(229,72,77,.65); background: rgba(229,72,77,.1); }
341
+ .dsh-atb-urgency-opt[data-on="true"][data-urgency="normal"] { border-color: rgba(142,78,198,.65); background: rgba(142,78,198,.1); }
342
+ .dsh-atb-urgency-opt[data-on="true"][data-urgency="relaxed"] { border-color: rgba(62,99,221,.65); background: rgba(62,99,221,.1); }
343
+
344
+ .dsh-atb-mode-picker { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
345
+ .dsh-atb-mode-opt {
346
+ display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
347
+ padding: 8px 10px; border-radius: 9px; cursor: pointer;
348
+ border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.35));
349
+ background: transparent; color: inherit;
350
+ transition: border-color .12s ease, background .12s ease;
351
+ }
352
+ .dsh-atb-mode-name { font-size: 12.5px; font-weight: 600; }
353
+ .dsh-atb-mode-hint { font-size: 10.5px; color: var(--dsw-alias-label-tertiary, gray); }
354
+ .dsh-atb-mode-opt:hover { border-color: var(--dsw-alias-label-tertiary, rgba(128,128,128,.6)); }
355
+ .dsh-atb-mode-opt[data-on="true"] { border-color: var(--dsw-alias-brand-primary, #1f2328); background: color-mix(in srgb, var(--dsw-alias-brand-primary, #1f2328) 8%, transparent); }
356
+
357
+ .dsh-atb-cron-presets { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
358
+ .dsh-atb-cron-preset {
359
+ font: inherit; font-size: 11px; padding: 2px 9px; border-radius: 999px; cursor: pointer;
360
+ border: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.35));
361
+ background: transparent; color: var(--dsw-alias-label-secondary, inherit);
362
+ }
363
+ .dsh-atb-cron-preset:hover { border-color: var(--dsw-alias-label-tertiary, rgba(128,128,128,.6)); color: inherit; }
364
+ .dsh-atb-cron-preset[data-on="true"] { border-color: transparent; background: var(--dsw-alias-brand-primary, #1f2328); color: var(--dsw-alias-label-primary-foreground, #fff); }
365
+ .dsh-atb-cron-next { margin-left: auto; font-size: 11px; color: var(--dsw-alias-label-tertiary, gray); }
366
+
367
+ .dsh-atb-modal-foot {
368
+ display: flex; align-items: center; gap: 10px;
369
+ padding: 11px 16px; border-top: 1px solid var(--dsw-alias-border-l2, rgba(128,128,128,.18));
370
+ background: var(--dsw-alias-bg-layer-1, rgba(128,128,128,.04));
371
+ }
372
+ .dsh-atb-modal-hint { flex: 1; min-width: 0; font-size: 11.5px; color: var(--dsw-alias-label-tertiary, gray); }
373
+ .dsh-atb-modal-hint[data-tone="bad"] { color: var(--dsw-alias-state-error-primary, #e5484d); }
374
+ .dsh-atb-modal-footbtns { display: flex; gap: 8px; }
375
+
376
+ .dsh-atb-secondary { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; }
377
+ .dsh-atb-link { color: var(--dsw-alias-state-business-primary, #3e63dd); cursor: pointer; text-decoration: none; }
378
+ .dsh-atb-link:hover { text-decoration: underline; }
379
+ `
380
+
381
+ let injected = false
382
+
383
+ /** Inject the stylesheet once (idempotent). */
384
+ export function injectStyles(): void {
385
+ if (injected || typeof document === 'undefined') return
386
+ const style = document.createElement('style')
387
+ style.id = 'dsh-taskboard-styles'
388
+ style.textContent = STYLES
389
+ document.head.append(style)
390
+ injected = true
391
+ }