@fonlan/dsh-task-kanban 0.1.1 → 0.2.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/lib/client.js +90 -54
- package/lib/client.js.map +1 -1
- package/lib/index.js +78 -5
- package/lib/types/server/runner.d.ts +21 -0
- package/package.json +1 -1
- package/src/client/board.css +46 -10
- package/src/client/board.tsx +65 -22
- package/src/client/locales.ts +8 -2
- package/src/server/runner.ts +100 -5
package/lib/client.js
CHANGED
|
@@ -273,6 +273,9 @@ window.__ModuleLoader__.load({
|
|
|
273
273
|
plan: "实现方案",
|
|
274
274
|
summary: "摘要",
|
|
275
275
|
phases: "阶段",
|
|
276
|
+
phaseGoal: "目标",
|
|
277
|
+
phaseSessions: "会话",
|
|
278
|
+
phaseConclusion: "结论",
|
|
276
279
|
error: "错误",
|
|
277
280
|
requirementLabel: "需求文本",
|
|
278
281
|
requirementPlaceholder: "描述你要实现的需求……",
|
|
@@ -283,7 +286,7 @@ window.__ModuleLoader__.load({
|
|
|
283
286
|
modelPlaceholder: "(使用默认模型)",
|
|
284
287
|
noWorkspace: "没有可用工作区,请先在侧边栏添加",
|
|
285
288
|
refresh: "刷新",
|
|
286
|
-
settingsTitle: "
|
|
289
|
+
settingsTitle: "任务看板",
|
|
287
290
|
maxParallelWorkers: "全局并行 worker 数",
|
|
288
291
|
defaultModel: "默认模型",
|
|
289
292
|
saved: "已保存",
|
|
@@ -337,6 +340,9 @@ window.__ModuleLoader__.load({
|
|
|
337
340
|
plan: "Plan",
|
|
338
341
|
summary: "Summary",
|
|
339
342
|
phases: "Phases",
|
|
343
|
+
phaseGoal: "Goal",
|
|
344
|
+
phaseSessions: "Sessions",
|
|
345
|
+
phaseConclusion: "Conclusion",
|
|
340
346
|
error: "Error",
|
|
341
347
|
requirementLabel: "Requirement",
|
|
342
348
|
requirementPlaceholder: "Describe what you want to build…",
|
|
@@ -347,7 +353,7 @@ window.__ModuleLoader__.load({
|
|
|
347
353
|
modelPlaceholder: "(use default model)",
|
|
348
354
|
noWorkspace: "No workspaces available — add one in the sidebar first",
|
|
349
355
|
refresh: "Refresh",
|
|
350
|
-
settingsTitle: "Task Kanban
|
|
356
|
+
settingsTitle: "Task Kanban",
|
|
351
357
|
maxParallelWorkers: "Global parallel workers",
|
|
352
358
|
defaultModel: "Default model",
|
|
353
359
|
saved: "Saved",
|
|
@@ -369,7 +375,7 @@ window.__ModuleLoader__.load({
|
|
|
369
375
|
};
|
|
370
376
|
//#endregion
|
|
371
377
|
//#region \0dsh-css:/Users/fonlan/Repos/dsh-task-kanban/src/client/board.css.mjs
|
|
372
|
-
const css = "/* @fonlan/dsh-task-kanban board styles (kb- prefixed, plain CSS).\n Surfaces use the DSH theme tokens (--dsw-alias-*) so they follow the\n shell's selected light/dark appearance; the var() fallbacks cover shells\n that do not define them. */\n.kb-board {\n height: 100%;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n padding: 12px 16px;\n overflow: hidden;\n color: var(--dsw-alias-label-primary, var(--dsh-fg, #e6e6e6));\n background: var(--dsw-alias-bg-base, var(--dsh-bg, #141418));\n font: 14px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif;\n}\n.kb-empty {\n align-items: center;\n justify-content: center;\n}\n.kb-empty-text { opacity: 0.6; }\n.kb-board-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding-bottom: 10px;\n border-bottom: 1px solid rgba(128, 128, 128, 0.25);\n flex: 0 0 auto;\n}\n.kb-board-actions { display: flex; gap: 8px; align-items: center; flex: none; }\n.kb-board-actions .kb-btn { flex: none; }\n.kb-ws-select {\n max-width: 320px;\n min-width: 0;\n flex: 0 1 auto;\n padding: 6px 10px;\n border-radius: 6px;\n border: 1px solid rgba(128, 128, 128, 0.35);\n background: rgba(128, 128, 128, 0.12);\n color: inherit;\n font-size: 14px;\n}\n.kb-btn {\n padding: 6px 14px;\n border-radius: 6px;\n border: 1px solid rgba(128, 128, 128, 0.35);\n background: rgba(128, 128, 128, 0.12);\n color: inherit;\n font-size: 13px;\n white-space: nowrap;\n cursor: pointer;\n}\n.kb-btn:hover { background: rgba(128, 128, 128, 0.22); }\n.kb-btn:disabled { opacity: 0.5; cursor: default; }\n.kb-btn-primary { background: #3b82f6; border-color: #3b82f6; color: #fff; }\n.kb-btn-primary:hover { background: #2f6fe0; }\n.kb-btn-danger { background: rgba(239, 68, 68, 0.15); border-color: rgba(239, 68, 68, 0.5); color: #f87171; }\n.kb-btn-small { padding: 2px 8px; font-size: 12px; }\n.kb-lanes {\n flex: 1 1 auto;\n display: flex;\n gap: 12px;\n overflow-x: auto;\n overflow-y: hidden;\n padding-top: 10px;\n min-height: 0;\n}\n.kb-lane {\n flex: 1 1 0;\n min-width: 220px;\n max-width: 320px;\n display: flex;\n flex-direction: column;\n background: rgba(128, 128, 128, 0.07);\n border-radius: 8px;\n border: 1px solid rgba(128, 128, 128, 0.12);\n overflow: hidden;\n}\n.kb-lane-drop { border-style: dashed; border-color: rgba(59, 130, 246, 0.45); }\n.kb-lane-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 10px;\n font-weight: 600;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary, var(--dsh-fg-soft, #b8b8c0));\n border-bottom: 1px solid rgba(128, 128, 128, 0.12);\n flex: 0 0 auto;\n}\n.kb-lane-count {\n background: rgba(128, 128, 128, 0.18);\n border-radius: 10px;\n padding: 0 8px;\n font-size: 12px;\n}\n.kb-lane-body {\n flex: 1 1 auto;\n overflow-y: auto;\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.kb-card {\n background: rgba(128, 128, 128, 0.1);\n border: 1px solid rgba(128, 128, 128, 0.2);\n border-radius: 8px;\n padding: 8px 10px;\n cursor: pointer;\n user-select: none;\n}\n.kb-card:hover { border-color: rgba(128, 128, 128, 0.45); }\n.kb-card[draggable='true'] { cursor: grab; }\n.kb-card-running { border-color: rgba(59, 130, 246, 0.6); }\n.kb-card-error { border-color: rgba(239, 68, 68, 0.6); }\n.kb-card-title { font-weight: 600; font-size: 13px; margin-bottom: 6px; word-break: break-all; }\n.kb-card-meta { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; font-size: 12px; }\n.kb-card-badge {\n background: rgba(128, 128, 128, 0.2);\n border-radius: 4px;\n padding: 1px 6px;\n}\n.kb-card-running .kb-card-badge { background: rgba(59, 130, 246, 0.3); }\n.kb-card-error .kb-card-badge { background: rgba(239, 68, 68, 0.3); }\n.kb-card-phase { opacity: 0.8; }\n.kb-card-skill {\n background: rgba(139, 92, 246, 0.22);\n border-radius: 4px;\n padding: 1px 6px;\n}\n.kb-card-model { opacity: 0.55; }\n.kb-card-error-text { margin-top: 6px; font-size: 12px; color: #f87171; word-break: break-all; }\n.kb-card-worktree { margin-top: 4px; font-size: 11px; opacity: 0.5; word-break: break-all; }\n.kb-toast {\n position: fixed;\n left: 50%;\n bottom: 24px;\n transform: translateX(-50%);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-primary);\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n z-index: 2147483000;\n max-width: 70vw;\n}\n.kb-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 2147482900;\n}\n.kb-modal {\n width: 460px;\n max-width: 90vw;\n max-height: 85vh;\n overflow-y: auto;\n background: var(--dsw-alias-bg-layer-1);\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n padding: 18px;\n color: var(--dsw-alias-label-primary);\n}\n.kb-modal-wide { width: 640px; }\n/* Task-detail bottom panel: docks at the bottom of the board page, fills the\n full board width and exactly half of the board height (the lanes column\n above flexes to the remaining space). */\n.kb-detail-panel {\n flex: 0 0 50%;\n min-height: 0;\n display: flex;\n flex-direction: column;\n margin-top: 10px;\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n overflow: hidden;\n color: var(--dsw-alias-label-primary);\n}\n.kb-detail-panel-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(128, 128, 128, 0.2);\n flex: 0 0 auto;\n}\n.kb-detail-panel-head .kb-modal-title { margin-bottom: 0; }\n.kb-detail-panel-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n padding: 14px;\n}\n.kb-detail-close {\n flex: none;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 1px solid transparent;\n border-radius: 6px;\n background: transparent;\n color: inherit;\n font-size: 15px;\n line-height: 1;\n opacity: 0.75;\n cursor: pointer;\n}\n.kb-detail-close:hover { background: rgba(128, 128, 128, 0.18); opacity: 1; }\n.kb-modal-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; display: flex; align-items: center; gap: 10px; }\n.kb-field { display: block; margin-bottom: 12px; }\n.kb-field > span { display: block; font-size: 12px; opacity: 0.75; margin-bottom: 4px; }\n.kb-field-hint {\n display: block;\n font-size: 11px;\n opacity: 0.55;\n margin-top: 4px;\n}\n.kb-field-hint-skill {\n opacity: 0.9;\n color: #a78bfa;\n}\n.kb-skill-menu-wrap { position: relative; }\n.kb-skill-menu {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 20;\n margin: 2px 0 0;\n padding: 4px;\n list-style: none;\n max-height: 180px;\n overflow-y: auto;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-overlay);\n color: var(--dsw-alias-label-primary);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);\n}\n.kb-skill-menu-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n.kb-skill-menu-item-active { background: var(--dsw-alias-interactive-bg-hover); }\n.kb-skill-menu-name { font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }\n.kb-skill-menu-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.kb-field-row { display: flex; align-items: center; gap: 10px; justify-content: space-between; }\n.kb-input {\n width: 100%;\n box-sizing: border-box;\n padding: 7px 10px;\n border-radius: 6px;\n border: 1px solid rgba(128, 128, 128, 0.35);\n background: rgba(128, 128, 128, 0.1);\n color: inherit;\n font-size: 13px;\n}\n.kb-input-number { width: 90px; }\n.kb-textarea { min-height: 110px; resize: vertical; font-family: inherit; }\n.kb-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }\n.kb-detail-section { margin-bottom: 12px; }\n.kb-detail-label { font-size: 12px; opacity: 0.65; margin-bottom: 4px; }\n.kb-detail-skill {\n display: inline-block;\n font-size: 11px;\n background: rgba(139, 92, 246, 0.22);\n border-radius: 4px;\n padding: 1px 6px;\n margin-bottom: 4px;\n}\n.kb-detail-text { white-space: pre-wrap; word-break: break-all; font-size: 13px; }\n.kb-detail-error .kb-detail-text { color: #f87171; }\n.kb-detail-status {\n font-size: 12px;\n font-weight: 600;\n border-radius: 4px;\n padding: 1px 8px;\n background: rgba(128, 128, 128, 0.2);\n}\n.kb-status-running, .kb-status-merging { background: rgba(59, 130, 246, 0.3) !important; }\n.kb-status-error { background: rgba(239, 68, 68, 0.3) !important; color: #f87171; }\n.kb-status-merged { background: rgba(34, 197, 94, 0.3) !important; }\n.kb-plan-summary { font-size: 13px; margin-bottom: 10px; }\n.kb-plan-phases { margin: 0; padding-left: 20px; }\n.kb-plan-phase { margin-bottom: 10px; }\n.kb-plan-phase-current .kb-plan-phase-title { color: #60a5fa; }\n.kb-plan-phase-head { display: flex; gap: 8px; align-items: baseline; }\n.kb-plan-phase-title { font-weight: 600; font-size: 13px; }\n.kb-plan-phase-id { font-size: 11px; opacity: 0.5; }\n.kb-plan-phase-goal { font-size: 12px; opacity: 0.85; margin: 2px 0 6px; white-space: pre-wrap; }\n.kb-session-links { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }\n.kb-session-attempt { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }\n.kb-session-summary { font-size: 12px; opacity: 0.75; }\n.kb-settings { display: flex; flex-direction: column; gap: 12px; max-width: 420px; }\n.kb-settings-saved { font-size: 12px; color: #4ade80; }\n.kb-footer-action {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 32px;\n height: 32px;\n border-radius: 6px;\n border: none;\n background: transparent;\n /* same foreground as the settings trigger (its trigger css uses this var) */\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n}\n.kb-footer-action:hover { background: rgba(128, 128, 128, 0.18); }\n.kb-footer-action-active { background: rgba(59, 130, 246, 0.25); }\n/* Wide mode borrows the settings trigger's metrics (34px tall, 14px label,\n 18px icon) so both controls read as siblings on the same line. */\n.kb-footer-action-wide {\n width: auto;\n height: 34px;\n padding: 0 8px;\n border-radius: 12px;\n font-size: 14px;\n}\n.kb-footer-action-wide .kb-footer-label {\n font-size: 14px;\n line-height: 22px;\n}\n/* Theme following is handled by the --dsw-alias-* tokens above: they change\n value with the shell's selected light/dark appearance, so no OS-level\n prefers-color-scheme override is needed (and one would fight the app\n theme when the two disagree). */\n/* Same-line placement WITHOUT moving any DOM: the sidebar foot area becomes\n a reversed flex row, so its LAST child (the settings area) renders on the\n LEFT and the FIRST child (footer actions = the kanban button) on the RIGHT\n — exactly \"settings, then kanban\". The settings trigger flexes to fill the\n remaining width; the button keeps its content width. */\n.kb-foot-row {\n display: flex;\n flex-direction: row-reverse;\n align-items: stretch;\n gap: 4px;\n}\n.kb-foot-row > div:first-child {\n flex: none;\n width: auto;\n min-width: 0;\n display: flex;\n align-items: center;\n}\n.kb-foot-row > div:last-child {\n flex: 1;\n min-width: 0;\n}\n/* Rail (collapsed sidebar): column-reverse stacks the settings circle on top\n and the kanban icon below it, both centered; the kanban button borrows the\n settings trigger's rail chrome (36px circle) so the two match. */\n.kb-foot-rail {\n flex-direction: column-reverse;\n align-items: center;\n}\n.kb-foot-rail .kb-footer-action {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n margin: 8px 0 10px;\n}\n.kb-footer-label {\n max-width: 110px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n/* Narrow screens (mobile drawer / auto-collapse breakpoint): the drawer's\n foot area is ~256-280px but the footer actions row carries the kanban\n button PLUS mobile-nav's drawer actions (Files / session log), which\n together exceed the row and squeeze the settings trigger to zero width\n (row-reverse overflows left, clipping the button). Below the shell's\n auto-collapse breakpoint, stack the settings row above the actions and\n let the actions wrap so nothing is squeezed or clipped. */\n@media (max-width: 1023px) {\n .kb-foot-row {\n flex-direction: column-reverse;\n align-items: stretch;\n }\n .kb-foot-row > div:first-child {\n flex: none;\n width: 100%;\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n gap: 4px;\n padding-top: 4px;\n }\n .kb-foot-row > div:last-child {\n flex: none;\n width: 100%;\n }\n /* The footer-actions entries are display:contents wrappers, so the kanban\n button itself is a flex item of the wrapping actions row; pull it to the\n first wrap position so it sits directly below the settings row (the\n mobile-nav drawer actions follow on their own wrapped line). */\n .kb-foot-row .kb-footer-action {\n order: -1;\n }\n}";
|
|
378
|
+
const css = "/* @fonlan/dsh-task-kanban board styles (kb- prefixed, plain CSS).\n Surfaces use the DSH theme tokens (--dsw-alias-*) so they follow the\n shell's selected light/dark appearance; the var() fallbacks cover shells\n that do not define them. */\n.kb-board {\n height: 100%;\n display: flex;\n flex-direction: column;\n box-sizing: border-box;\n padding: 12px 16px;\n overflow: hidden;\n color: var(--dsw-alias-label-primary, var(--dsh-fg, #e6e6e6));\n background: var(--dsw-alias-bg-base, var(--dsh-bg, #141418));\n font: 14px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', sans-serif;\n}\n.kb-empty {\n align-items: center;\n justify-content: center;\n}\n.kb-empty-text { opacity: 0.6; }\n.kb-board-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding-bottom: 10px;\n border-bottom: 1px solid rgba(128, 128, 128, 0.25);\n flex: 0 0 auto;\n}\n/* Left-aligned group: workspace selector + new-task button sit adjacent. */\n.kb-board-title {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n flex: 0 1 auto;\n}\n.kb-board-title .kb-btn { flex: none; }\n.kb-ws-select {\n max-width: 320px;\n min-width: 0;\n flex: 0 1 auto;\n padding: 6px 10px;\n border-radius: 6px;\n border: 1px solid rgba(128, 128, 128, 0.35);\n background: rgba(128, 128, 128, 0.12);\n color: inherit;\n font-size: 14px;\n}\n.kb-btn {\n padding: 6px 14px;\n border-radius: 6px;\n border: 1px solid rgba(128, 128, 128, 0.35);\n background: rgba(128, 128, 128, 0.12);\n color: inherit;\n font-size: 13px;\n white-space: nowrap;\n cursor: pointer;\n}\n.kb-btn:hover { background: rgba(128, 128, 128, 0.22); }\n.kb-btn:disabled { opacity: 0.5; cursor: default; }\n.kb-btn-primary { background: #3b82f6; border-color: #3b82f6; color: #fff; }\n.kb-btn-primary:hover { background: #2f6fe0; }\n.kb-btn-danger { background: rgba(239, 68, 68, 0.15); border-color: rgba(239, 68, 68, 0.5); color: #f87171; }\n.kb-btn-small { padding: 2px 8px; font-size: 12px; }\n.kb-lanes {\n flex: 1 1 auto;\n display: flex;\n gap: 12px;\n overflow-x: auto;\n overflow-y: hidden;\n padding-top: 10px;\n min-height: 0;\n}\n.kb-lane {\n flex: 1 1 0;\n min-width: 220px;\n max-width: 320px;\n display: flex;\n flex-direction: column;\n background: rgba(128, 128, 128, 0.07);\n border-radius: 8px;\n border: 1px solid rgba(128, 128, 128, 0.12);\n overflow: hidden;\n}\n.kb-lane-drop { border-style: dashed; border-color: rgba(59, 130, 246, 0.45); }\n.kb-lane-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 10px;\n font-weight: 600;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary, var(--dsh-fg-soft, #b8b8c0));\n border-bottom: 1px solid rgba(128, 128, 128, 0.12);\n flex: 0 0 auto;\n}\n.kb-lane-count {\n background: rgba(128, 128, 128, 0.18);\n border-radius: 10px;\n padding: 0 8px;\n font-size: 12px;\n}\n.kb-lane-body {\n flex: 1 1 auto;\n overflow-y: auto;\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.kb-card {\n background: rgba(128, 128, 128, 0.1);\n border: 1px solid rgba(128, 128, 128, 0.2);\n border-radius: 8px;\n padding: 8px 10px;\n cursor: pointer;\n user-select: none;\n}\n.kb-card:hover { border-color: rgba(128, 128, 128, 0.45); }\n.kb-card[draggable='true'] { cursor: grab; }\n.kb-card-running { border-color: rgba(59, 130, 246, 0.6); }\n.kb-card-error { border-color: rgba(239, 68, 68, 0.6); }\n.kb-card-title { font-weight: 600; font-size: 13px; margin-bottom: 6px; word-break: break-all; }\n.kb-card-meta { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; font-size: 12px; }\n.kb-card-badge {\n background: rgba(128, 128, 128, 0.2);\n border-radius: 4px;\n padding: 1px 6px;\n}\n.kb-card-running .kb-card-badge { background: rgba(59, 130, 246, 0.3); }\n.kb-card-error .kb-card-badge { background: rgba(239, 68, 68, 0.3); }\n.kb-card-phase { opacity: 0.8; }\n.kb-card-skill {\n background: rgba(139, 92, 246, 0.22);\n border-radius: 4px;\n padding: 1px 6px;\n}\n.kb-card-model { opacity: 0.55; }\n.kb-card-error-text { margin-top: 6px; font-size: 12px; color: #f87171; word-break: break-all; }\n.kb-card-worktree { margin-top: 4px; font-size: 11px; opacity: 0.5; word-break: break-all; }\n.kb-toast {\n position: fixed;\n left: 50%;\n bottom: 24px;\n transform: translateX(-50%);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n color: var(--dsw-alias-label-primary);\n padding: 8px 16px;\n border-radius: 8px;\n font-size: 13px;\n z-index: 2147483000;\n max-width: 70vw;\n}\n.kb-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 2147482900;\n}\n.kb-modal {\n width: 460px;\n max-width: 90vw;\n max-height: 85vh;\n overflow-y: auto;\n background: var(--dsw-alias-bg-layer-1);\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n padding: 18px;\n color: var(--dsw-alias-label-primary);\n}\n.kb-modal-wide { width: 640px; }\n/* Task-detail bottom panel: docks at the bottom of the board page, fills the\n full board width and exactly half of the board height (the lanes column\n above flexes to the remaining space). */\n.kb-detail-panel {\n flex: 0 0 50%;\n min-height: 0;\n display: flex;\n flex-direction: column;\n margin-top: 10px;\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 12px;\n overflow: hidden;\n color: var(--dsw-alias-label-primary);\n}\n.kb-detail-panel-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid rgba(128, 128, 128, 0.2);\n flex: 0 0 auto;\n}\n.kb-detail-panel-head .kb-modal-title { margin-bottom: 0; }\n.kb-detail-panel-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow-y: auto;\n padding: 14px;\n}\n.kb-detail-close {\n flex: none;\n width: 28px;\n height: 28px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: 1px solid transparent;\n border-radius: 6px;\n background: transparent;\n color: inherit;\n font-size: 15px;\n line-height: 1;\n opacity: 0.75;\n cursor: pointer;\n}\n.kb-detail-close:hover { background: rgba(128, 128, 128, 0.18); opacity: 1; }\n.kb-modal-title { font-size: 16px; font-weight: 700; margin-bottom: 14px; display: flex; align-items: center; gap: 10px; }\n.kb-field { display: block; margin-bottom: 12px; }\n.kb-field > span { display: block; font-size: 12px; opacity: 0.75; margin-bottom: 4px; }\n.kb-field-hint {\n display: block;\n font-size: 11px;\n opacity: 0.55;\n margin-top: 4px;\n}\n.kb-field-hint-skill {\n opacity: 0.9;\n color: #a78bfa;\n}\n.kb-skill-menu-wrap { position: relative; }\n.kb-skill-menu {\n position: absolute;\n top: 100%;\n left: 0;\n right: 0;\n z-index: 20;\n margin: 2px 0 0;\n padding: 4px;\n list-style: none;\n max-height: 180px;\n overflow-y: auto;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-overlay);\n color: var(--dsw-alias-label-primary);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);\n}\n.kb-skill-menu-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 12px;\n color: var(--dsw-alias-label-primary);\n}\n.kb-skill-menu-item-active { background: var(--dsw-alias-interactive-bg-hover); }\n.kb-skill-menu-name { font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }\n.kb-skill-menu-desc {\n color: var(--dsw-alias-label-tertiary);\n font-size: 11px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.kb-field-row { display: flex; align-items: center; gap: 10px; justify-content: space-between; }\n.kb-input {\n width: 100%;\n box-sizing: border-box;\n padding: 7px 10px;\n border-radius: 6px;\n border: 1px solid rgba(128, 128, 128, 0.35);\n background: rgba(128, 128, 128, 0.1);\n color: inherit;\n font-size: 13px;\n}\n.kb-input-number { width: 90px; }\n.kb-textarea { min-height: 110px; resize: vertical; font-family: inherit; }\n.kb-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 14px; }\n.kb-detail-section { margin-bottom: 12px; }\n.kb-detail-label { font-size: 12px; opacity: 0.65; margin-bottom: 4px; }\n.kb-detail-skill {\n display: inline-block;\n font-size: 11px;\n background: rgba(139, 92, 246, 0.22);\n border-radius: 4px;\n padding: 1px 6px;\n margin-bottom: 4px;\n}\n.kb-detail-text { white-space: pre-wrap; word-break: break-all; font-size: 13px; }\n.kb-detail-error .kb-detail-text { color: #f87171; }\n.kb-detail-status {\n font-size: 12px;\n font-weight: 600;\n border-radius: 4px;\n padding: 1px 8px;\n background: rgba(128, 128, 128, 0.2);\n}\n.kb-status-running, .kb-status-merging { background: rgba(59, 130, 246, 0.3) !important; }\n.kb-status-error { background: rgba(239, 68, 68, 0.3) !important; color: #f87171; }\n.kb-status-merged { background: rgba(34, 197, 94, 0.3) !important; }\n.kb-plan-summary { font-size: 13px; margin-bottom: 10px; }\n/* Phase cards: each plan phase is a card with an ordered number (kept from the\n parent <ol>) and three labeled sections (goal / sessions / conclusion) split\n by section titles and thin divider lines. */\n.kb-plan-phases { margin: 0; padding-left: 24px; }\n.kb-plan-phase {\n margin-bottom: 10px;\n padding: 8px 10px;\n background: rgba(128, 128, 128, 0.08);\n border: 1px solid rgba(128, 128, 128, 0.2);\n border-radius: 8px;\n}\n.kb-plan-phase-head {\n display: flex;\n gap: 8px;\n align-items: baseline;\n padding-bottom: 6px;\n margin-bottom: 6px;\n border-bottom: 1px solid rgba(128, 128, 128, 0.16);\n}\n.kb-plan-phase-title { font-weight: 600; font-size: 13px; }\n.kb-plan-phase-id { font-size: 11px; opacity: 0.5; }\n.kb-plan-phase-body { display: flex; flex-direction: column; }\n/* Section isolation: label on top, thin divider between adjacent sections. */\n.kb-plan-phase-section { display: flex; flex-direction: column; }\n.kb-plan-phase-section + .kb-plan-phase-section {\n margin-top: 6px;\n padding-top: 6px;\n border-top: 1px solid rgba(128, 128, 128, 0.14);\n}\n.kb-plan-phase-section-title { font-size: 11px; opacity: 0.55; margin-bottom: 3px; }\n.kb-plan-phase-goal { font-size: 12px; opacity: 0.85; white-space: pre-wrap; word-break: break-all; }\n.kb-plan-phase-sessions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }\n.kb-plan-phase-conclusion { font-size: 12px; opacity: 0.85; white-space: pre-wrap; word-break: break-all; }\n.kb-plan-phase-conclusion-item + .kb-plan-phase-conclusion-item { margin-top: 6px; }\n/* Running phase keeps the blue highlight; completed phases get success color. */\n.kb-plan-phase-current { border-color: rgba(59, 130, 246, 0.6); background: rgba(59, 130, 246, 0.08); }\n.kb-plan-phase-current .kb-plan-phase-title { color: #60a5fa; }\n.kb-plan-phase-completed { border-color: rgba(34, 197, 94, 0.45); background: rgba(34, 197, 94, 0.06); }\n.kb-plan-phase-completed .kb-plan-phase-title { color: #4ade80; }\n.kb-session-links { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }\n.kb-settings { display: flex; flex-direction: column; gap: 12px; max-width: 420px; }\n.kb-settings-saved { font-size: 12px; color: #4ade80; }\n.kb-footer-action {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n width: 32px;\n height: 32px;\n border-radius: 6px;\n border: none;\n background: transparent;\n /* same foreground as the settings trigger (its trigger css uses this var) */\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n}\n.kb-footer-action:hover { background: rgba(128, 128, 128, 0.18); }\n.kb-footer-action-active { background: rgba(59, 130, 246, 0.25); }\n/* Wide mode borrows the settings trigger's metrics (34px tall, 14px label,\n 18px icon) so both controls read as siblings on the same line. */\n.kb-footer-action-wide {\n width: auto;\n height: 34px;\n padding: 0 8px;\n border-radius: 12px;\n font-size: 14px;\n}\n.kb-footer-action-wide .kb-footer-label {\n font-size: 14px;\n line-height: 22px;\n}\n/* Theme following is handled by the --dsw-alias-* tokens above: they change\n value with the shell's selected light/dark appearance, so no OS-level\n prefers-color-scheme override is needed (and one would fight the app\n theme when the two disagree). */\n/* Same-line placement WITHOUT moving any DOM: the sidebar foot area becomes\n a reversed flex row, so its LAST child (the settings area) renders on the\n LEFT and the FIRST child (footer actions = the kanban button) on the RIGHT\n — exactly \"settings, then kanban\". The settings trigger flexes to fill the\n remaining width; the button keeps its content width. */\n.kb-foot-row {\n display: flex;\n flex-direction: row-reverse;\n align-items: stretch;\n gap: 4px;\n}\n.kb-foot-row > div:first-child {\n flex: none;\n width: auto;\n min-width: 0;\n display: flex;\n align-items: center;\n}\n.kb-foot-row > div:last-child {\n flex: 1;\n min-width: 0;\n}\n/* Rail (collapsed sidebar): column-reverse stacks the settings circle on top\n and the kanban icon below it, both centered; the kanban button borrows the\n settings trigger's rail chrome (36px circle) so the two match. */\n.kb-foot-rail {\n flex-direction: column-reverse;\n align-items: center;\n}\n.kb-foot-rail .kb-footer-action {\n width: 36px;\n height: 36px;\n border-radius: 50%;\n margin: 8px 0 10px;\n}\n.kb-footer-label {\n max-width: 110px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n/* Narrow screens (mobile drawer / auto-collapse breakpoint): the drawer's\n foot area is ~256-280px but the footer actions row carries the kanban\n button PLUS mobile-nav's drawer actions (Files / session log), which\n together exceed the row and squeeze the settings trigger to zero width\n (row-reverse overflows left, clipping the button). Below the shell's\n auto-collapse breakpoint, stack the settings row above the actions and\n let the actions wrap so nothing is squeezed or clipped. */\n@media (max-width: 1023px) {\n .kb-foot-row {\n flex-direction: column-reverse;\n align-items: stretch;\n }\n .kb-foot-row > div:first-child {\n flex: none;\n width: 100%;\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-start;\n gap: 4px;\n padding-top: 4px;\n }\n .kb-foot-row > div:last-child {\n flex: none;\n width: 100%;\n }\n /* The footer-actions entries are display:contents wrappers, so the kanban\n button itself is a flex item of the wrapping actions row; pull it to the\n first wrap position so it sits directly below the settings row (the\n mobile-nav drawer actions follow on their own wrapped line). */\n .kb-foot-row .kb-footer-action {\n order: -1;\n }\n}";
|
|
373
379
|
const tagId = "@fonlan/dsh-task-kanban/board.css";
|
|
374
380
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
375
381
|
const tag = document.createElement("style");
|
|
@@ -536,11 +542,11 @@ window.__ModuleLoader__.load({
|
|
|
536
542
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
537
543
|
className: "kb-board",
|
|
538
544
|
children: [
|
|
539
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
545
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
540
546
|
className: "kb-board-header",
|
|
541
|
-
children:
|
|
547
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
542
548
|
className: "kb-board-title",
|
|
543
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
549
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
544
550
|
className: "kb-ws-select",
|
|
545
551
|
value: selectedPath,
|
|
546
552
|
onChange: (e) => {
|
|
@@ -553,16 +559,13 @@ window.__ModuleLoader__.load({
|
|
|
553
559
|
value: w.path,
|
|
554
560
|
children: w.title
|
|
555
561
|
}, w.workspaceId))
|
|
556
|
-
})
|
|
557
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
558
|
-
className: "kb-board-actions",
|
|
559
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
562
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
560
563
|
type: "button",
|
|
561
564
|
className: "kb-btn kb-btn-primary",
|
|
562
565
|
onClick: () => setNewTaskOpen(true),
|
|
563
566
|
children: t("newTask")
|
|
564
|
-
})
|
|
565
|
-
})
|
|
567
|
+
})]
|
|
568
|
+
})
|
|
566
569
|
}),
|
|
567
570
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
568
571
|
className: "kb-lanes",
|
|
@@ -965,30 +968,12 @@ window.__ModuleLoader__.load({
|
|
|
965
968
|
children: card.plan.summary
|
|
966
969
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
|
|
967
970
|
className: "kb-plan-phases",
|
|
968
|
-
children: card.plan.phases.map((phase, i) => /* @__PURE__ */ (0, react_jsx_runtime.
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
className: "kb-plan-phase-title",
|
|
975
|
-
children: phase.title
|
|
976
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
977
|
-
className: "kb-plan-phase-id",
|
|
978
|
-
children: phase.id
|
|
979
|
-
})]
|
|
980
|
-
}),
|
|
981
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
982
|
-
className: "kb-plan-phase-goal",
|
|
983
|
-
children: phase.goal
|
|
984
|
-
}),
|
|
985
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PhaseSessions, {
|
|
986
|
-
phaseIndex: i,
|
|
987
|
-
card,
|
|
988
|
-
t,
|
|
989
|
-
onOpenSession
|
|
990
|
-
})
|
|
991
|
-
]
|
|
971
|
+
children: card.plan.phases.map((phase, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PhaseCard, {
|
|
972
|
+
phase,
|
|
973
|
+
phaseIndex: i,
|
|
974
|
+
card,
|
|
975
|
+
t,
|
|
976
|
+
onOpenSession
|
|
992
977
|
}, phase.id))
|
|
993
978
|
})]
|
|
994
979
|
})]
|
|
@@ -1078,26 +1063,77 @@ window.__ModuleLoader__.load({
|
|
|
1078
1063
|
})]
|
|
1079
1064
|
});
|
|
1080
1065
|
}
|
|
1081
|
-
|
|
1066
|
+
/** One plan phase rendered as a card with goal / sessions / conclusion sections. */
|
|
1067
|
+
function PhaseCard({ phase, phaseIndex, card, t, onOpenSession }) {
|
|
1082
1068
|
const attempts = card.sessions.phases.filter((a) => a.phaseIndex === phaseIndex);
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
children:
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
]
|
|
1097
|
-
}), a.summary !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1098
|
-
className: "kb-session-summary",
|
|
1099
|
-
children: a.summary
|
|
1069
|
+
const summaries = attempts.filter((a) => a.summary !== void 0 && a.summary.trim() !== "").map((a) => a.summary);
|
|
1070
|
+
const isCurrent = phaseIndex === card.currentPhase && card.status === "running";
|
|
1071
|
+
const isCompleted = summaries.length > 0;
|
|
1072
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
1073
|
+
className: "kb-plan-phase" + (isCurrent ? " kb-plan-phase-current" : "") + (isCompleted ? " kb-plan-phase-completed" : ""),
|
|
1074
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1075
|
+
className: "kb-plan-phase-head",
|
|
1076
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1077
|
+
className: "kb-plan-phase-title",
|
|
1078
|
+
children: phase.title
|
|
1079
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1080
|
+
className: "kb-plan-phase-id",
|
|
1081
|
+
children: phase.id
|
|
1100
1082
|
})]
|
|
1083
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1084
|
+
className: "kb-plan-phase-body",
|
|
1085
|
+
children: [
|
|
1086
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1087
|
+
className: "kb-plan-phase-section",
|
|
1088
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1089
|
+
className: "kb-plan-phase-section-title",
|
|
1090
|
+
children: t("phaseGoal")
|
|
1091
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1092
|
+
className: "kb-plan-phase-goal",
|
|
1093
|
+
children: phase.goal
|
|
1094
|
+
})]
|
|
1095
|
+
}),
|
|
1096
|
+
attempts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1097
|
+
className: "kb-plan-phase-section",
|
|
1098
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1099
|
+
className: "kb-plan-phase-section-title",
|
|
1100
|
+
children: t("phaseSessions")
|
|
1101
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PhaseSessions, {
|
|
1102
|
+
attempts,
|
|
1103
|
+
t,
|
|
1104
|
+
onOpenSession
|
|
1105
|
+
})]
|
|
1106
|
+
}),
|
|
1107
|
+
summaries.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1108
|
+
className: "kb-plan-phase-section",
|
|
1109
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1110
|
+
className: "kb-plan-phase-section-title",
|
|
1111
|
+
children: t("phaseConclusion")
|
|
1112
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1113
|
+
className: "kb-plan-phase-conclusion",
|
|
1114
|
+
children: summaries.map((s, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1115
|
+
className: "kb-plan-phase-conclusion-item",
|
|
1116
|
+
children: s
|
|
1117
|
+
}, idx))
|
|
1118
|
+
})]
|
|
1119
|
+
})
|
|
1120
|
+
]
|
|
1121
|
+
})]
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
/** Open-session buttons for one phase's attempts (retries append more). */
|
|
1125
|
+
function PhaseSessions({ attempts, t, onOpenSession }) {
|
|
1126
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1127
|
+
className: "kb-plan-phase-sessions",
|
|
1128
|
+
children: attempts.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1129
|
+
type: "button",
|
|
1130
|
+
className: "kb-btn kb-btn-small",
|
|
1131
|
+
onClick: () => onOpenSession(a.sessionId),
|
|
1132
|
+
children: [
|
|
1133
|
+
t("openSession"),
|
|
1134
|
+
" · ",
|
|
1135
|
+
a.sessionId.slice(0, 13)
|
|
1136
|
+
]
|
|
1101
1137
|
}, a.sessionId))
|
|
1102
1138
|
});
|
|
1103
1139
|
}
|