@fonlan/dsh-task-kanban 0.2.7 → 0.2.9
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 +29 -57
- package/lib/client.js.map +1 -1
- package/lib/index.js +19 -10
- package/lib/types/index.d.ts +21 -1
- package/lib/types/server/runner.d.ts +6 -5
- package/lib/types/server/settings.d.ts +29 -1
- package/package.json +40 -39
- package/src/client/board.tsx +5 -12
- package/src/client/index.tsx +4 -3
- package/src/client/kanban-state.ts +38 -32
- package/src/server/runner.ts +33 -18
- package/src/server/settings.ts +9 -1
package/lib/client.js
CHANGED
|
@@ -174,59 +174,39 @@ window.__ModuleLoader__.load({
|
|
|
174
174
|
function getClient() {
|
|
175
175
|
return getState().client;
|
|
176
176
|
}
|
|
177
|
-
/** The shell's panel face, when this runtime provides it. */
|
|
178
|
-
function layoutOf(ctx) {
|
|
179
|
-
if (ctx === null) return null;
|
|
180
|
-
try {
|
|
181
|
-
const layout = ctx.layout;
|
|
182
|
-
return typeof layout?.selectPanel === "function" ? layout : null;
|
|
183
|
-
} catch {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
/** Hand the center column back to the Conversation. */
|
|
188
|
-
function leaveKanbanPanel(ctx = getClient()) {
|
|
189
|
-
try {
|
|
190
|
-
layoutOf(ctx)?.selectPanel(null);
|
|
191
|
-
} catch (error) {
|
|
192
|
-
console.error("[@fonlan/dsh-task-kanban] cannot leave the kanban panel:", error);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
177
|
/**
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
* Conversation, so the session view renders.
|
|
178
|
+
* Open one session — or a durable direct-parent subagent address — in the
|
|
179
|
+
* center column, leaving the kanban panel.
|
|
199
180
|
*
|
|
200
|
-
*
|
|
201
|
-
* (
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
181
|
+
* Navigation is a view-owner concern, not a session-controller one: `ctx.sessions`
|
|
182
|
+
* (`ISessions`) exposes retention (`retain`/`using`/`retainInfo`), creation,
|
|
183
|
+
* fork, search and catalog reads, and has no `open`/`openSubagent` at all
|
|
184
|
+
* ("navigation belongs to view owners"). The view owner the shell itself uses is
|
|
185
|
+
* `ctx.uiWorkspace.openSession(target)` (dsh-client-ui-workspace): it retains the
|
|
186
|
+
* session AND reveals the Conversation panel as ONE navigation action. Routing
|
|
187
|
+
* every open through it therefore replaces the old wrapper around
|
|
188
|
+
* `sessions.open`/`openSubagent`, which existed only to hand the center column
|
|
189
|
+
* back when a sidebar click selected a session while the board was showing.
|
|
206
190
|
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
191
|
+
* @param target - session id (or subagent address) to show.
|
|
192
|
+
* @returns whether the runtime provided the navigation face; `false` means the
|
|
193
|
+
* session could not be shown (a client without ui-workspace).
|
|
209
194
|
*/
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return open.call(sessions, id);
|
|
218
|
-
});
|
|
219
|
-
bound.kbBound = true;
|
|
220
|
-
sessions.open = bound;
|
|
195
|
+
function openSessionView(target, ctx = getClient()) {
|
|
196
|
+
if (ctx === null) return false;
|
|
197
|
+
let uiWorkspace;
|
|
198
|
+
try {
|
|
199
|
+
uiWorkspace = ctx.uiWorkspace;
|
|
200
|
+
} catch {
|
|
201
|
+
return false;
|
|
221
202
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
sessions.openSubagent = bound;
|
|
203
|
+
if (typeof uiWorkspace?.openSession !== "function") return false;
|
|
204
|
+
try {
|
|
205
|
+
uiWorkspace.openSession(target);
|
|
206
|
+
return true;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error("[@fonlan/dsh-task-kanban] cannot open the session view:", error);
|
|
209
|
+
return false;
|
|
230
210
|
}
|
|
231
211
|
}
|
|
232
212
|
//#endregion
|
|
@@ -529,14 +509,7 @@ window.__ModuleLoader__.load({
|
|
|
529
509
|
t
|
|
530
510
|
]);
|
|
531
511
|
const openSession = (0, react.useCallback)((sessionId) => {
|
|
532
|
-
|
|
533
|
-
leaveKanbanPanel(ctx);
|
|
534
|
-
if (ctx === null) return;
|
|
535
|
-
try {
|
|
536
|
-
ctx.sessions.open(sessionId);
|
|
537
|
-
} catch {
|
|
538
|
-
showToast(t("sessionNotListed"));
|
|
539
|
-
}
|
|
512
|
+
if (!openSessionView(sessionId)) showToast(t("sessionNotListed"));
|
|
540
513
|
}, [showToast, t]);
|
|
541
514
|
if (items.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
542
515
|
className: "kb-board kb-empty",
|
|
@@ -1484,7 +1457,6 @@ window.__ModuleLoader__.load({
|
|
|
1484
1457
|
/** Client plugin body. */
|
|
1485
1458
|
function apply(ctx) {
|
|
1486
1459
|
setClient(ctx);
|
|
1487
|
-
bindSessionNavigation(ctx);
|
|
1488
1460
|
const t = ctx.locale.bind(LOCALE_NS);
|
|
1489
1461
|
ctx.effect(() => {
|
|
1490
1462
|
const off = ctx.locale.register(LOCALE_NS, {
|
package/lib/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":["useState","useCallback","useMemo","useRef","primitives","useState","useCallback","useRef"],"sources":["../../../src/shared/lanes.ts","../../../src/client/api.ts","../../../src/client/kanban-state.ts","../../../src/client/locales.ts","../../../src/client/board.tsx","../../../src/client/icons.tsx","../../../src/client/sections.tsx","../../../src/client/index.tsx"],"sourcesContent":["/**\n * Lane mapping and drag rules (shared by the board UI and tests).\n */\nimport type { KanbanCard, Lane } from './card.js'\n\n/** Which lane a card renders in, based on status + error stage. */\nexport function laneOf(card: KanbanCard): Lane {\n if (card.status === 'error') {\n if (card.error?.stage !== undefined) return card.error.stage\n const kind = card.error?.kind\n if (kind === 'phase_failed' || kind === 'no_base_branch' || kind === 'worktree_failed') return 'running'\n if (kind === 'merge_failed') return 'completed'\n return 'demand'\n }\n switch (card.status) {\n case 'draft':\n case 'refining':\n case 'planned':\n return 'demand'\n case 'queued':\n return 'queue'\n case 'running':\n return 'running'\n case 'completed':\n case 'merging':\n return 'completed'\n case 'merged':\n return 'merged'\n }\n}\n\n/** Lanes a drop onto `to` may come from. */\nexport const DROP_RULES: Record<Lane, Lane[]> = {\n demand: ['queue'],\n queue: ['demand', 'running'],\n running: [],\n completed: [],\n merged: [],\n}\n\nexport function canDrop(from: Lane, to: Lane): boolean {\n return DROP_RULES[to].includes(from)\n}\n","import type { KanbanCard, KanbanSettingsShape, Lane, ModelOption } from '../shared/card'\n\nexport class KanbanApiError extends Error {\n constructor(readonly code: string, message: string) {\n super(message)\n }\n}\n\nasync function call<T>(method: string, payload: Record<string, unknown> = {}): Promise<T> {\n let response: Response\n try {\n response = await fetch(`/plugins/@fonlan/dsh-task-kanban/api/${method}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n } catch (error) {\n throw new KanbanApiError('network', error instanceof Error ? error.message : String(error))\n }\n const parsed: { ok?: boolean; value?: unknown; error?: { code?: string; message?: string } } | null\n = await response.json().catch(() => null)\n if (!response.ok || parsed === null || parsed.ok !== true || parsed.value === undefined) {\n throw new KanbanApiError(\n parsed?.error?.code ?? 'http',\n parsed?.error?.message ?? `HTTP ${response.status}`,\n )\n }\n return parsed.value as T\n}\n\n/** One row of the host `skills/list` catalog (same shape DSH's chat autocomplete uses). */\nexport interface SkillOption {\n name: string\n description: string\n whenToUse?: string\n modelInvocable: boolean\n}\n\n/**\n * Call the host gateway's `skills/list` unary RPC over the same-origin HTTP\n * bridge (the exact wire shape DSH's chat input uses for its /-autocomplete).\n *\n * DSH ≥0.1.5 addresses Remote endpoints as the canonical `<namespace>/<method>`\n * pair — `/api/skills/list` with the method `skills/list` — and carries named\n * wire arguments inside a single `args` field, so the request body is\n * `{ args: { request: { sessionId } } }` (the descriptor's parameter wire name\n * is `request`). The older dotted `skill.list` endpoint no longer exists and\n * answers HTTP 404.\n *\n * The requested `sessionId` scopes the catalog to that session's agent\n * preset, which is where the filesystem skill roots are registered.\n */\nexport async function gatewaySkillList(sessionId: string): Promise<SkillOption[]> {\n let response: Response\n try {\n response = await fetch('/api/skills/list', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n type: 'client-request',\n rpcId: `task-kanban-${Math.random().toString(36).slice(2)}`,\n method: 'skills/list',\n payload: { args: { request: { sessionId } } },\n }),\n })\n } catch (error) {\n throw new KanbanApiError('network', error instanceof Error ? error.message : String(error))\n }\n const parsed: { type?: string; result?: { ok?: boolean; value?: { skills?: SkillOption[] }; error?: { code?: string; message?: string } } } | null\n = await response.json().catch(() => null)\n if (!response.ok || parsed === null || parsed.type !== 'server-response' || parsed.result?.ok !== true) {\n throw new KanbanApiError(\n parsed?.result?.error?.code ?? 'http',\n parsed?.result?.error?.message ?? `HTTP ${response.status}`,\n )\n }\n return parsed.result.value?.skills ?? []\n}\n\n/** One row of the host `agentPresets.list()` roster. */\nexport interface PresetOption {\n id: string\n name?: string\n description?: string\n}\n\n/** Reasoning efforts of one provider/model route (`reasoning.options`). */\nexport interface ReasoningOptions {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n /** Client-side marker: the fetch failed; the stored effort must be kept. */\n failed?: boolean\n}\n\nexport const api = {\n list: (workspacePath: string) => call<KanbanCard[]>('list', { workspacePath }),\n create: (workspacePath: string, requirement: string, model: string, provider?: string, skill?: string) =>\n call<KanbanCard>('create', { workspacePath, requirement, model, provider, ...(skill !== undefined && skill !== '' ? { skill } : {}) }),\n move: (cardId: string, toLane: Lane) => call<{ ok: boolean; message?: string }>('move', { cardId, toLane }),\n stop: (cardId: string) => call<{ ok: boolean; message?: string }>('stop', { cardId }),\n retry: (cardId: string) => call<{ ok: boolean; message?: string }>('retry', { cardId }),\n remove: (cardId: string) => call<{ ok: boolean; message?: string }>('remove', { cardId }),\n settingsGet: () => call<KanbanSettingsShape>('settings.get'),\n settingsSet: (patch: Partial<KanbanSettingsShape>) => call<KanbanSettingsShape>('settings.set', patch),\n models: () => call<ModelOption[]>('models.list'),\n presets: () => call<PresetOption[]>('presets.list'),\n reasoningOptions: (provider: string, model: string) =>\n call<ReasoningOptions>('reasoning.options', { provider, model }),\n}\n","import type { Context } from '@deepseek-ai/cordis'\ntype ClientContext = Context\n\n/**\n * Panel id shared by this plugin's main-panel key (`main`) and its sidebar\n * entry (`sidebar.panellist`): the shell selects the panel by this id, so the\n * two registrations must agree.\n */\nexport const KANBAN_PANEL_ID = 'task-kanban'\n\n/**\n * Structural view of the client shell's panel-navigation face (`ctx.layout`).\n * Declared locally (no value import) so the plugin keeps working on a runtime\n * whose layout types differ.\n */\ninterface LayoutLike {\n selectPanel(panelId: string | null): void\n}\n\n/**\n * HMR-safe owner context, keyed on the global symbol registry.\n *\n * The client-hmr hot swap re-evaluates this module (a fresh `lib/client.js`\n * bundle) WITHOUT reloading the page; a module-level cell would then be\n * duplicated per evaluation and an old copy's helper would read a stale owner.\n * `Symbol.for` makes every bundle copy share one cell.\n */\nconst STATE_KEY = Symbol.for('@fonlan/dsh-task-kanban/state')\n\ninterface KanbanState {\n client: ClientContext | null\n}\n\nfunction getState(): KanbanState {\n const g = globalThis as unknown as Record<symbol, KanbanState | undefined>\n let state = g[STATE_KEY]\n if (state === undefined) {\n state = { client: null }\n g[STATE_KEY] = state\n }\n return state\n}\n\nexport function setClient(ctx: ClientContext | null): void {\n getState().client = ctx\n}\nexport function getClient(): ClientContext | null {\n return getState().client\n}\n\n/** The shell's panel face, when this runtime provides it. */\nfunction layoutOf(ctx: ClientContext | null): LayoutLike | null {\n if (ctx === null) return null\n try {\n const layout = (ctx as unknown as { layout?: LayoutLike }).layout\n return typeof layout?.selectPanel === 'function' ? layout : null\n } catch {\n return null\n }\n}\n\n/** Select the kanban panel. The sidebar's own entry does this; kept for in-app callers. */\nexport function showKanbanPanel(ctx: ClientContext | null = getClient()): void {\n try {\n layoutOf(ctx)?.selectPanel(KANBAN_PANEL_ID)\n } catch (error) {\n console.error('[@fonlan/dsh-task-kanban] cannot select the kanban panel:', error)\n }\n}\n\n/** Hand the center column back to the Conversation. */\nexport function leaveKanbanPanel(ctx: ClientContext | null = getClient()): void {\n try {\n layoutOf(ctx)?.selectPanel(null)\n } catch (error) {\n console.error('[@fonlan/dsh-task-kanban] cannot leave the kanban panel:', error)\n }\n}\n\n/**\n * Route session navigation through the panel selection: opening any session\n * while the board is the active panel first returns the center column to the\n * Conversation, so the session view renders.\n *\n * Every sidebar path that selects a session funnels through `sessions.open`\n * (session rows, search results, fork results, New Session) or\n * `sessions.openSubagent` (catalog children) — including re-clicking the\n * already-current session, which never changes `list.current` and therefore\n * cannot be caught by a list-store subscription. Wrapping the two entry points\n * covers all of them in one place.\n *\n * The wrapper is idempotent: a `kbBound` marker on the wrapper prevents a\n * second bind (plugin re-apply / HMR) from stacking another layer.\n */\nexport function bindSessionNavigation(ctx: ClientContext): void {\n const sessions = ctx.sessions\n if (sessions === undefined) return\n\n const open = sessions.open\n if (typeof open === 'function' && (open as unknown as { kbBound?: boolean }).kbBound !== true) {\n const bound = ((id: Parameters<typeof open>[0]) => {\n leaveKanbanPanel(ctx)\n return open.call(sessions, id)\n }) as typeof open\n ;(bound as unknown as { kbBound?: boolean }).kbBound = true\n sessions.open = bound\n }\n\n const openSubagent = sessions.openSubagent\n if (typeof openSubagent === 'function' && (openSubagent as unknown as { kbBound?: boolean }).kbBound !== true) {\n const bound = ((address: Parameters<typeof openSubagent>[0]) => {\n leaveKanbanPanel(ctx)\n return openSubagent.call(sessions, address)\n }) as typeof openSubagent\n ;(bound as unknown as { kbBound?: boolean }).kbBound = true\n sessions.openSubagent = bound\n }\n}\n","// Locale namespace = the plugin's cordis id (kebab). DSH flat namespace keys\n// (settings + locale) use the internal id `task-kanban`; the scoped npm package\n// name is reserved for package-bound identifiers (exported `name`, module-loader\n// id, the /plugins/<pkg>/… route base).\nexport const LOCALE_NS = 'task-kanban'\n\nexport const zh = {\n kanban: '任务看板',\n openBoard: '打开任务看板',\n closeBoard: '返回对话',\n newTask: '新建任务',\n requirement: '需求',\n project: '所属项目',\n model: '模型',\n cancel: '取消',\n close: '关闭',\n addAndRefine: '添加并细化',\n laneDemand: '需求',\n laneQueue: '队列',\n laneRunning: '运行中',\n laneCompleted: '已完成',\n laneMerged: '已合并',\n statusDraft: '未细化',\n statusRefining: '细化中',\n statusPlanned: '已规划',\n statusQueued: '排队中',\n statusRunning: '实现中',\n statusCompleted: '已完成',\n statusMerging: '合并中',\n statusMerged: '已合并',\n statusError: '出错',\n phaseProgress: '{current}/{total}',\n retry: '重试',\n stop: '停止',\n delete: '删除',\n deleteConfirm: '确定删除该任务?其 worktree 会被清理。',\n openSession: '打开会话',\n plan: '实现方案',\n summary: '摘要',\n phases: '阶段',\n phaseGoal: '目标',\n phaseSessions: '会话',\n phaseConclusion: '结论',\n phaseNum: 'phase {n}',\n error: '错误',\n requirementLabel: '需求文本',\n requirementPlaceholder: '描述你要实现的需求……',\n skillGestureHint: '开头输入 /skill-name 可指定细化时使用的 Skill(如 /grill-me)',\n skillHint: '细化时将使用 Skill「{skill}」,并可与该会话交流打磨需求',\n skillMenuUserOnly: '仅用户可调',\n skillBadge: '细化 Skill:/{skill}',\n modelPlaceholder: '(使用默认模型)',\n noWorkspace: '没有可用工作区,请先在侧边栏添加',\n refresh: '刷新',\n settingsTitle: '任务看板',\n sectionSub: '全局并行 worker 数与各会话类型的默认模型、推理强度、Agent 预设',\n maxParallelWorkers: '全局并行 worker 数',\n settingsRefineGroup: '细化需求会话默认',\n settingsPhaseGroup: 'Phase 实现会话默认',\n reasoningEffort: '推理强度',\n agentPreset: 'Agent 预设',\n defaultOption: '(默认)',\n saved: '已保存',\n loadError: '加载任务失败',\n noPlan: '该卡片还没有实现计划,请先点击\"添加并细化\"',\n dragNoPlan: '还没有实现计划的卡片不能进入队列',\n actionFailed: '操作失败:{message}',\n sessionNotListed: '该会话不在当前列表,可能来自其他工作区',\n interrupted: '已中断',\n noBaseBranch: '项目没有 main/master 分支',\n worktreeFailed: 'worktree 创建失败',\n refineFailed: '细化失败',\n phaseFailed: '阶段执行失败',\n mergeFailed: '合并失败',\n createFailed: '创建失败',\n refinementLabel: '需求细化',\n mergeLabel: '合并',\n commitLabel: '提交',\n}\n\nexport const en = {\n kanban: 'Task Kanban',\n openBoard: 'Open Task Kanban',\n closeBoard: 'Back to chat',\n newTask: 'New Task',\n requirement: 'Requirement',\n project: 'Project',\n model: 'Model',\n cancel: 'Cancel',\n close: 'Close',\n addAndRefine: 'Add & Refine',\n laneDemand: 'Demands',\n laneQueue: 'Queue',\n laneRunning: 'Running',\n laneCompleted: 'Completed',\n laneMerged: 'Merged',\n statusDraft: 'Unrefined',\n statusRefining: 'Refining',\n statusPlanned: 'Planned',\n statusQueued: 'Queued',\n statusRunning: 'Running',\n statusCompleted: 'Completed',\n statusMerging: 'Merging',\n statusMerged: 'Merged',\n statusError: 'Error',\n phaseProgress: '{current}/{total}',\n retry: 'Retry',\n stop: 'Stop',\n delete: 'Delete',\n deleteConfirm: 'Delete this task? Its worktree will be cleaned up.',\n openSession: 'Open session',\n plan: 'Plan',\n summary: 'Summary',\n phases: 'Phases',\n phaseGoal: 'Goal',\n phaseSessions: 'Sessions',\n phaseConclusion: 'Conclusion',\n phaseNum: 'Phase {n}',\n error: 'Error',\n requirementLabel: 'Requirement',\n requirementPlaceholder: 'Describe what you want to build…',\n skillGestureHint: 'Start with /skill-name to choose the refinement skill (e.g. /grill-me)',\n skillHint: 'Refinement will use Skill “{skill}” — talk to this session to refine the requirement',\n skillMenuUserOnly: 'User-only',\n skillBadge: 'Refine skill: /{skill}',\n modelPlaceholder: '(use default model)',\n noWorkspace: 'No workspaces available — add one in the sidebar first',\n refresh: 'Refresh',\n settingsTitle: 'Task Kanban',\n sectionSub: 'Global parallel workers and per-session-type defaults for model, reasoning effort, and agent preset',\n maxParallelWorkers: 'Global parallel workers',\n settingsRefineGroup: 'Refinement session defaults',\n settingsPhaseGroup: 'Phase implementation defaults',\n reasoningEffort: 'Reasoning effort',\n agentPreset: 'Agent preset',\n defaultOption: '(default)',\n saved: 'Saved',\n loadError: 'Failed to load tasks',\n noPlan: 'This card has no plan yet — click \"Add & Refine\" first',\n dragNoPlan: 'Cards without a plan cannot enter the queue',\n actionFailed: 'Action failed: {message}',\n sessionNotListed: 'Session is not listed here; it may belong to another workspace',\n interrupted: 'Interrupted',\n noBaseBranch: 'Project has no main/master branch',\n worktreeFailed: 'Worktree creation failed',\n refineFailed: 'Refinement failed',\n phaseFailed: 'Phase failed',\n mergeFailed: 'Merge failed',\n createFailed: 'Creation failed',\n refinementLabel: 'Refinement',\n mergeLabel: 'Merge',\n commitLabel: 'Commit',\n}","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { KanbanCard, Lane, PhaseAttempt, PlanPhase } from '../shared/card'\nimport { DROP_RULES, laneOf } from '../shared/lanes'\nimport { api, gatewaySkillList, type SkillOption } from './api'\nimport { getClient, leaveKanbanPanel } from './kanban-state'\nimport { en, zh } from './locales'\nimport './board.css'\n\ninterface BoardProps {\n t?: (key: string, params?: Record<string, unknown>) => string\n useWorkspaces?: (selector: (state: unknown) => unknown) => unknown\n useSessions?: (selector: (state: unknown) => unknown) => unknown\n}\n\n/** Session-list snapshot shape the standard `useSessions` feed carries. */\ninterface SessionListStateLike {\n current?: string\n /** Listed session ids (the feed's own order). */\n ids?: string[]\n}\n\nfunction firstLine(text: string): string {\n return text.split('\\n').find((l) => l.trim() !== '')?.trim() ?? ''\n}\n\n/** The leading `/skill-name` gesture at the start of the requirement input. */\nconst SKILL_GESTURE = /^\\s*\\/([a-z0-9]+(?:-[a-z0-9]+)*)(?=\\s|$)/\n\n/** Extract a leading `/skill-name` token plus the cleaned remainder. */\nfunction parseSkillGesture(raw: string): { skill?: string; requirement: string } {\n const m = SKILL_GESTURE.exec(raw)\n if (m === null) return { requirement: raw.trim() }\n return { skill: m[1], requirement: raw.slice(m[0].length).trim() }\n}\n\nfunction statusLabel(card: KanbanCard, t: (key: string) => string): string {\n if (card.status === 'error') {\n switch (card.error?.kind) {\n case 'interrupted': return t('interrupted')\n case 'no_base_branch': return t('noBaseBranch')\n case 'worktree_failed': return t('worktreeFailed')\n case 'refine_failed': return t('refineFailed')\n case 'phase_failed': return t('phaseFailed')\n case 'merge_failed': return t('mergeFailed')\n default: return t('createFailed')\n }\n }\n switch (card.status) {\n case 'draft': return t('statusDraft')\n case 'refining': return t('statusRefining')\n case 'planned': return t('statusPlanned')\n case 'queued': return t('statusQueued')\n case 'running': return t('statusRunning')\n case 'completed': return t('statusCompleted')\n case 'merging': return t('statusMerging')\n case 'merged': return t('statusMerged')\n }\n}\n\ninterface WorkspaceRow {\n workspaceId: string\n path: string\n title: string\n}\n\nconst LANE_ORDER: Array<{ key: Lane; labelKey: string }> = [\n { key: 'demand', labelKey: 'laneDemand' },\n { key: 'queue', labelKey: 'laneQueue' },\n { key: 'running', labelKey: 'laneRunning' },\n { key: 'completed', labelKey: 'laneCompleted' },\n { key: 'merged', labelKey: 'laneMerged' },\n]\n\nconst WS_STORAGE_KEY = '@fonlan/dsh-task-kanban:workspace'\n\nexport function BoardRoot(props: BoardProps): JSX.Element {\n const t = props.t ?? ((key: string, params?: Record<string, unknown>) => {\n const dict = (zh as Record<string, string>)[key] ?? (en as Record<string, string>)[key]\n if (dict === undefined || params === undefined) return dict ?? key\n return dict.replace(/\\{(\\w+)\\}/g, (m, name: string) => (params[name] as string) ?? m)\n })\n const useWorkspaces = props.useWorkspaces\n const wsState = useWorkspaces !== undefined ? (useWorkspaces((s: unknown) => s) as { items?: WorkspaceRow[] } | undefined) : undefined\n const items: WorkspaceRow[] = wsState?.items ?? []\n const useSessions = props.useSessions\n const sessionState = useSessions !== undefined ? (useSessions((s: unknown) => s) as SessionListStateLike | undefined) : undefined\n // The skill catalog is addressed by session (`skills/list`), so the /-menu\n // needs one. With no selection (fresh browser, board opened straight from the\n // footer) fall back to the most recent listed session: any session resolves a\n // preset scope, whereas no session silently leaves the menu empty.\n const currentSessionId = sessionState?.current ?? sessionState?.ids?.[0]\n\n const [selectedPath, setSelectedPath] = useState<string>(() => {\n try {\n return localStorage.getItem(WS_STORAGE_KEY) ?? ''\n } catch {\n return ''\n }\n })\n const [cards, setCards] = useState<KanbanCard[]>([])\n const [toast, setToast] = useState<string | null>(null)\n const [detailId, setDetailId] = useState<string | null>(null)\n const [newTaskOpen, setNewTaskOpen] = useState(false)\n\n useEffect(() => {\n if (selectedPath === '' && items.length > 0) {\n setSelectedPath(items[0].path)\n }\n }, [selectedPath, items])\n\n const showToast = useCallback((message: string) => {\n setToast(message)\n window.setTimeout(() => setToast(null), 3500)\n }, [])\n\n const refresh = useCallback(async () => {\n if (selectedPath === '') return\n try {\n setCards(await api.list(selectedPath))\n } catch (error) {\n showToast(error instanceof Error ? error.message : String(error))\n }\n }, [selectedPath, showToast])\n\n useEffect(() => {\n void refresh()\n const timer = window.setInterval(() => { void refresh() }, 2500)\n return () => window.clearInterval(timer)\n }, [refresh])\n\n const byLane = useMemo(() => {\n const map: Record<Lane, KanbanCard[]> = { demand: [], queue: [], running: [], completed: [], merged: [] }\n for (const card of cards) map[laneOf(card)].push(card)\n for (const lane of LANE_ORDER) map[lane.key].sort((a, b) => a.createdAt - b.createdAt)\n return map\n }, [cards])\n\n const handleDrop = useCallback(async (target: Lane, event: React.DragEvent) => {\n event.preventDefault()\n const cardId = event.dataTransfer.getData('text/plain')\n if (cardId === '') return\n const card = cards.find((c) => c.id === cardId)\n if (card === undefined) return\n if (target === 'queue' && card.status !== 'running' && card.plan === undefined) {\n showToast(t('dragNoPlan'))\n return\n }\n try {\n const result = await api.move(cardId, target)\n if (!result.ok) {\n showToast(t('actionFailed', { message: result.message ?? '' }))\n }\n void refresh()\n } catch (error) {\n showToast(error instanceof Error ? error.message : String(error))\n }\n }, [cards, refresh, showToast, t])\n\n const openSession = useCallback((sessionId: string) => {\n const ctx = getClient()\n // Return the center column to the Conversation first: `sessions.open` is\n // wrapped by bindSessionNavigation as well, but a runtime without that\n // binding must still leave the board before the session renders.\n leaveKanbanPanel(ctx)\n if (ctx === null) return\n try {\n ;(ctx.sessions as { open(id: string): void }).open(sessionId)\n } catch {\n showToast(t('sessionNotListed'))\n }\n }, [showToast, t])\n\n if (items.length === 0) {\n return (\n <div className=\"kb-board kb-empty\">\n <div className=\"kb-empty-text\">{t('noWorkspace')}</div>\n </div>\n )\n }\n\n return (\n <div className=\"kb-board\">\n <div className=\"kb-board-header\">\n <div className=\"kb-board-title\">\n <select\n className=\"kb-ws-select\"\n value={selectedPath}\n onChange={(e) => {\n setSelectedPath(e.target.value)\n try {\n localStorage.setItem(WS_STORAGE_KEY, e.target.value)\n } catch {\n // storage unavailable\n }\n }}\n >\n {items.map((w) => (\n <option key={w.workspaceId} value={w.path}>{w.title}</option>\n ))}\n </select>\n <button type=\"button\" className=\"kb-btn kb-btn-primary\" onClick={() => setNewTaskOpen(true)}>{t('newTask')}</button>\n </div>\n </div>\n\n <div className=\"kb-lanes\">\n {LANE_ORDER.map((lane) => (\n <div\n key={lane.key}\n className={'kb-lane' + (DROP_RULES[lane.key].length > 0 ? ' kb-lane-drop' : '')}\n onDragOver={(e) => {\n if (DROP_RULES[lane.key].length > 0) e.preventDefault()\n }}\n onDrop={(e) => { void handleDrop(lane.key, e) }}\n >\n <div className=\"kb-lane-head\">\n <span className=\"kb-lane-title\">{t(lane.labelKey)}</span>\n <span className=\"kb-lane-count\">{byLane[lane.key].length}</span>\n </div>\n <div className=\"kb-lane-body\">\n {byLane[lane.key].map((card) => (\n <CardView\n key={card.id}\n card={card}\n t={t}\n onToggle={() => setDetailId(detailId === card.id ? null : card.id)}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n\n {toast !== null && <div className=\"kb-toast\">{toast}</div>}\n\n {newTaskOpen && (\n <NewTaskModal\n t={t}\n workspaces={items}\n selectedPath={selectedPath}\n sessionId={currentSessionId}\n onClose={() => setNewTaskOpen(false)}\n onCreated={() => {\n setNewTaskOpen(false)\n void refresh()\n }}\n onError={(message) => showToast(message)}\n />\n )}\n\n {detailId !== null && (\n <DetailPanel\n card={cards.find((c) => c.id === detailId) ?? null}\n t={t}\n onClose={() => setDetailId(null)}\n onChanged={() => {\n setDetailId(null)\n void refresh()\n }}\n onToast={showToast}\n onOpenSession={openSession}\n />\n )}\n </div>\n )\n}\n\ninterface CardViewProps {\n card: KanbanCard\n t: (key: string, params?: Record<string, unknown>) => string\n onToggle: () => void\n}\n\nfunction CardView({ card, t, onToggle }: CardViewProps): JSX.Element {\n const draggable = card.status === 'planned' || card.status === 'queued' || card.status === 'running'\n const title = card.plan !== undefined && card.plan.title !== '' ? card.plan.title : firstLine(card.requirement)\n const badge = statusLabel(card, t)\n const phaseInfo = card.status === 'running' || card.status === 'error'\n ? card.currentPhase < card.phaseCount ? t('phaseProgress', { current: card.currentPhase + 1, total: card.phaseCount }) : ''\n : ''\n return (\n <div\n className={'kb-card' + (card.status === 'error' ? ' kb-card-error' : '') + (card.status === 'running' ? ' kb-card-running' : '')}\n draggable={draggable}\n onDragStart={(e) => {\n e.dataTransfer.setData('text/plain', card.id)\n e.dataTransfer.effectAllowed = 'move'\n }}\n onClick={onToggle}\n >\n <div className=\"kb-card-title\">{title}</div>\n <div className=\"kb-card-meta\">\n <span className=\"kb-card-badge\">{badge}</span>\n {phaseInfo !== '' && <span className=\"kb-card-phase\">{phaseInfo}</span>}\n {card.skill !== undefined && card.skill !== '' && <span className=\"kb-card-skill\">/ {card.skill}</span>}\n {card.model !== '' && <span className=\"kb-card-model\">{card.model}</span>}\n </div>\n {card.status === 'error' && card.error !== undefined && (\n <div className=\"kb-card-error-text\">{card.error.message}</div>\n )}\n {card.status === 'running' && card.gitMode === true && (\n <div className=\"kb-card-worktree\">{card.worktreePath ?? ''}</div>\n )}\n </div>\n )\n}\n\ninterface NewTaskModalProps {\n t: (key: string, params?: Record<string, unknown>) => string\n workspaces: WorkspaceRow[]\n selectedPath: string\n /** Current session id, scoping the host skill.list catalog lookup. */\n sessionId?: string\n onClose: () => void\n onCreated: () => void\n onError: (message: string) => void\n}\n\n/** An in-progress leading `/query` token (menu-open state). */\nconst SKILL_MENU_RE = /^\\s*\\/([a-z0-9-]*)$/\n\nfunction NewTaskModal({ t, workspaces, selectedPath, sessionId, onClose, onCreated, onError }: NewTaskModalProps): JSX.Element {\n const [requirement, setRequirement] = useState('')\n const [project, setProject] = useState(selectedPath)\n const [model, setModel] = useState('')\n const [modelProvider, setModelProvider] = useState('')\n const [models, setModels] = useState<Array<{ provider: string; id: string; name?: string }>>([])\n const [skills, setSkills] = useState<SkillOption[]>([])\n const [menuQuery, setMenuQuery] = useState<string | null>(null)\n const [menuIndex, setMenuIndex] = useState(0)\n const [busy, setBusy] = useState(false)\n const textareaRef = useRef<HTMLTextAreaElement | null>(null)\n\n useEffect(() => {\n let alive = true\n api.models()\n .then((list) => { if (alive) setModels(list) })\n .catch(() => { if (alive) setModels([]) })\n return () => { alive = false }\n }, [])\n\n // Load the host skill catalog once (scoped to the current session), the\n // same feed DSH's chat input uses for its /-autocomplete.\n useEffect(() => {\n if (sessionId === undefined) return\n let alive = true\n gatewaySkillList(sessionId)\n .then((list) => { if (alive) setSkills(list) })\n .catch(() => { if (alive) setSkills([]) })\n return () => { alive = false }\n }, [sessionId])\n\n const matches = skills.filter((s) => s.name.startsWith(menuQuery ?? ''))\n const menuVisible = menuQuery !== null && matches.length > 0\n const selected = menuVisible ? matches[Math.min(menuIndex, matches.length - 1)] : undefined\n\n /** Re-evaluate the leading-token menu state from the current value + caret. */\n const syncMenu = (value: string, caret: number): void => {\n const head = value.slice(0, caret)\n const m = SKILL_MENU_RE.exec(head)\n if (m !== null) {\n setMenuQuery(m[1])\n setMenuIndex(0)\n return\n }\n setMenuQuery(null)\n }\n\n const selectSkill = (name: string): void => {\n const el = textareaRef.current\n const caret = el?.selectionStart ?? requirement.length\n const head = requirement.slice(0, caret)\n const m = SKILL_MENU_RE.exec(head)\n // Replace the leading `/query` token with `/name `.\n const inserted = `/${name} `\n const newValue = m !== null ? inserted + requirement.slice(caret) : inserted + requirement\n setRequirement(newValue)\n setMenuQuery(null)\n requestAnimationFrame(() => {\n const next = textareaRef.current\n if (next !== null) {\n next.focus()\n const pos = inserted.length\n next.setSelectionRange(pos, pos)\n }\n })\n }\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>): void => {\n if (!menuVisible) return\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setMenuIndex((i) => (i + 1) % matches.length)\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setMenuIndex((i) => (i - 1 + matches.length) % matches.length)\n } else if (e.key === 'Enter') {\n e.preventDefault()\n if (selected !== undefined) selectSkill(selected.name)\n } else if (e.key === 'Escape') {\n e.preventDefault()\n setMenuQuery(null)\n } else if (e.key === 'Tab') {\n e.preventDefault()\n if (selected !== undefined) selectSkill(selected.name)\n }\n }\n\n const submit = async (): Promise<void> => {\n if (requirement.trim() === '') {\n onError(t('requirementLabel') + ' ' + t('error'))\n return\n }\n if (project === '') {\n onError(t('noWorkspace'))\n return\n }\n const parsed = parseSkillGesture(requirement)\n setBusy(true)\n try {\n await api.create(project, parsed.requirement, model, modelProvider, parsed.skill)\n onCreated()\n } catch (error) {\n onError(error instanceof Error ? error.message : String(error))\n setBusy(false)\n }\n }\n\n const activeSkill = parseSkillGesture(requirement).skill\n\n return (\n <div className=\"kb-modal-backdrop\" onClick={busy ? undefined : onClose}>\n <div className=\"kb-modal\" onClick={(e) => e.stopPropagation()}>\n <div className=\"kb-modal-title\">{t('newTask')}</div>\n <label className=\"kb-field\">\n <span>{t('requirementLabel')}</span>\n <div className=\"kb-skill-menu-wrap\">\n <textarea\n ref={textareaRef}\n className=\"kb-input kb-textarea\"\n value={requirement}\n placeholder={t('requirementPlaceholder')}\n onChange={(e) => {\n const value = e.target.value\n setRequirement(value)\n syncMenu(value, e.target.selectionStart ?? value.length)\n }}\n onKeyDown={onKeyDown}\n />\n {menuVisible && (\n <ul className=\"kb-skill-menu\" role=\"listbox\">\n {matches.map((s, i) => (\n <li\n key={s.name}\n role=\"option\"\n aria-selected={i === menuIndex}\n className={'kb-skill-menu-item' + (i === menuIndex ? ' kb-skill-menu-item-active' : '')}\n onMouseDown={(e) => { e.preventDefault(); selectSkill(s.name) }}\n onMouseEnter={() => setMenuIndex(i)}\n >\n <span className=\"kb-skill-menu-name\">/{s.name}</span>\n <span className=\"kb-skill-menu-desc\">\n {s.modelInvocable ? s.description : `${t('skillMenuUserOnly')} · ${s.description}`}\n </span>\n </li>\n ))}\n </ul>\n )}\n </div>\n {activeSkill !== undefined ? (\n <span className=\"kb-field-hint kb-field-hint-skill\">{t('skillHint', { skill: activeSkill })}</span>\n ) : (\n <span className=\"kb-field-hint\">{t('skillGestureHint')}</span>\n )}\n </label>\n <label className=\"kb-field\">\n <span>{t('project')}</span>\n <select className=\"kb-input\" value={project} onChange={(e) => setProject(e.target.value)}>\n {workspaces.map((w) => (\n <option key={w.workspaceId} value={w.path}>{w.title}</option>\n ))}\n </select>\n </label>\n <label className=\"kb-field\">\n <span>{t('model')}</span>\n <select\n className=\"kb-input\"\n value={model}\n onChange={(e) => {\n const id = e.target.value\n setModel(id)\n const found = models.find((m) => m.id === id)\n setModelProvider(found?.provider ?? '')\n }}\n >\n <option value=\"\">{t('modelPlaceholder')}</option>\n {models.map((m) => (\n <option key={m.provider + '/' + m.id} value={m.id}>{m.name ?? m.id}</option>\n ))}\n </select>\n </label>\n <div className=\"kb-modal-actions\">\n <button type=\"button\" className=\"kb-btn\" onClick={onClose} disabled={busy}>{t('cancel')}</button>\n <button type=\"button\" className=\"kb-btn kb-btn-primary\" onClick={() => { void submit() }} disabled={busy}>\n {t('addAndRefine')}\n </button>\n </div>\n </div>\n </div>\n )\n}\n\ninterface DetailPanelProps {\n card: KanbanCard | null\n t: (key: string, params?: Record<string, unknown>) => string\n onClose: () => void\n onChanged: () => void\n onToast: (message: string) => void\n onOpenSession: (sessionId: string) => void\n}\n\nfunction DetailPanel({ card, t, onClose, onChanged, onToast, onOpenSession }: DetailPanelProps): JSX.Element | null {\n const [busy, setBusy] = useState(false)\n if (card === null) return null\n\n const act = async (fn: () => Promise<unknown>): Promise<void> => {\n setBusy(true)\n try {\n await fn()\n onChanged()\n } catch (error) {\n onToast(error instanceof Error ? error.message : String(error))\n setBusy(false)\n }\n }\n\n const remove = async (): Promise<void> => {\n if (!window.confirm(t('deleteConfirm'))) return\n await act(() => api.remove(card!.id))\n }\n\n return (\n <div className=\"kb-detail-panel\">\n <div className=\"kb-detail-panel-head\">\n <div className=\"kb-modal-title\">\n {card.plan !== undefined && card.plan.title !== '' ? card.plan.title : firstLine(card.requirement)}\n <span className={'kb-detail-status kb-status-' + card.status}>{statusLabel(card, t)}</span>\n </div>\n <button type=\"button\" className=\"kb-detail-close\" aria-label={t('close')} title={t('close')} onClick={onClose}>✕</button>\n </div>\n\n <div className=\"kb-detail-panel-body\">\n <div className=\"kb-detail-section\">\n <div className=\"kb-detail-label\">{t('requirement')}</div>\n {card.skill !== undefined && card.skill !== '' && (\n <div className=\"kb-detail-skill\">{t('skillBadge', { skill: card.skill })}</div>\n )}\n <div className=\"kb-detail-text\">{card.requirement}</div>\n </div>\n\n {card.plan !== undefined && (\n <div className=\"kb-detail-section\">\n <div className=\"kb-detail-label\">{t('plan')}</div>\n <div className=\"kb-detail-plan\">\n <div className=\"kb-plan-summary\">{card.plan.summary}</div>\n <ol className=\"kb-plan-phases\">\n {card.plan.phases.map((phase, i) => (\n <PhaseCard\n key={phase.id}\n phase={phase}\n phaseIndex={i}\n card={card}\n t={t}\n onOpenSession={onOpenSession}\n />\n ))}\n </ol>\n </div>\n </div>\n )}\n\n {card.sessions.refinement.length > 0 && (\n <div className=\"kb-detail-section\">\n <div className=\"kb-detail-label\">{t('refinementLabel')}</div>\n <div className=\"kb-session-links\">\n {card.sessions.refinement.map((sid) => (\n <button key={sid} type=\"button\" className=\"kb-btn kb-btn-small\" onClick={() => onOpenSession(sid)}>\n {t('openSession')} · {sid.slice(0, 13)}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {card.status === 'error' && card.error !== undefined && (\n <div className=\"kb-detail-section kb-detail-error\">\n <div className=\"kb-detail-label\">{t('error')}</div>\n <div className=\"kb-detail-text\">{card.error.message}</div>\n </div>\n )}\n\n {card.merge?.mergeCommit !== undefined && (\n <div className=\"kb-detail-section\">\n <div className=\"kb-detail-label\">{t('mergeLabel')}</div>\n <div className=\"kb-detail-text\">{t('commitLabel')} {card.merge.mergeCommit.slice(0, 12)}</div>\n </div>\n )}\n\n <div className=\"kb-modal-actions\">\n {card.status === 'error' && (\n <button type=\"button\" className=\"kb-btn kb-btn-primary\" disabled={busy} onClick={() => { void act(() => api.retry(card!.id)) }}>\n {t('retry')}\n </button>\n )}\n {(card.status === 'running' || card.status === 'merging') && (\n <button type=\"button\" className=\"kb-btn\" disabled={busy} onClick={() => { void act(() => api.stop(card!.id)) }}>\n {t('stop')}\n </button>\n )}\n {card.status !== 'running' && card.status !== 'refining' && card.status !== 'merging' && (\n <button type=\"button\" className=\"kb-btn kb-btn-danger\" disabled={busy} onClick={() => { void remove() }}>\n {t('delete')}\n </button>\n )}\n <button type=\"button\" className=\"kb-btn\" onClick={onClose}>{t('cancel')}</button>\n </div>\n </div>\n </div>\n )\n}\n\n/** One plan phase rendered as a card with goal / sessions / conclusion sections. */\nfunction PhaseCard({ phase, phaseIndex, card, t, onOpenSession }: {\n phase: PlanPhase\n phaseIndex: number\n card: KanbanCard\n t: (key: string, params?: Record<string, unknown>) => string\n onOpenSession: (sessionId: string) => void\n}): JSX.Element {\n const attempts = card.sessions.phases.filter((a) => a.phaseIndex === phaseIndex)\n const summaries = attempts\n .filter((a) => a.summary !== undefined && a.summary.trim() !== '')\n .map((a) => a.summary as string)\n const isCurrent = phaseIndex === card.currentPhase && card.status === 'running'\n // A phase with at least one concluded attempt counts as completed.\n const isCompleted = summaries.length > 0\n const cls = 'kb-plan-phase'\n + (isCurrent ? ' kb-plan-phase-current' : '')\n + (isCompleted ? ' kb-plan-phase-completed' : '')\n const planTitle = card.plan?.title ?? ''\n return (\n <li className={cls}>\n <div className=\"kb-plan-phase-head\">\n <span className=\"kb-plan-phase-title\">{phase.title}</span>\n <span className=\"kb-plan-phase-id\">{phase.id}</span>\n </div>\n <div className=\"kb-plan-phase-body\">\n <div className=\"kb-plan-phase-section\">\n <div className=\"kb-plan-phase-section-title\">{t('phaseGoal')}</div>\n <div className=\"kb-plan-phase-goal\">{phase.goal}</div>\n </div>\n {attempts.length > 0 && (\n <div className=\"kb-plan-phase-section\">\n <div className=\"kb-plan-phase-section-title\">{t('phaseSessions')}</div>\n <PhaseSessions attempts={attempts} planTitle={planTitle} phaseIndex={phaseIndex} t={t} onOpenSession={onOpenSession} />\n </div>\n )}\n {summaries.length > 0 && (\n <div className=\"kb-plan-phase-section\">\n <div className=\"kb-plan-phase-section-title\">{t('phaseConclusion')}</div>\n <div className=\"kb-plan-phase-conclusion\">\n {summaries.map((s, idx) => (\n <div key={idx} className=\"kb-plan-phase-conclusion-item\">{s}</div>\n ))}\n </div>\n </div>\n )}\n </div>\n </li>\n )\n}\n\n/**\n * Open-session buttons for one phase's attempts (retries append more). The\n * button labels the session with the deterministic \"{plan title} · phase N\"\n * format (the requested fallback when the host never auto-generated a title);\n * the full session id stays available as the native tooltip.\n */\nfunction PhaseSessions({ attempts, planTitle, phaseIndex, t, onOpenSession }: {\n attempts: PhaseAttempt[]\n planTitle: string\n phaseIndex: number\n t: (key: string, params?: Record<string, unknown>) => string\n onOpenSession: (sessionId: string) => void\n}): JSX.Element {\n return (\n <div className=\"kb-plan-phase-sessions\">\n {attempts.map((a) => (\n <button key={a.sessionId} type=\"button\" className=\"kb-btn kb-btn-small\" title={a.sessionId} onClick={() => onOpenSession(a.sessionId)}>\n {t('openSession')} · {planTitle !== '' ? `${planTitle} · ${t('phaseNum', { n: phaseIndex + 1 })}` : a.sessionId.slice(0, 13)}\n </button>\n ))}\n </div>\n )\n}","import * as primitives from '@deepseek-ai/dsh-client-ui-primitives'\n\n/** Props every shell icon accepts: square edge in px, color rides currentColor. */\nexport interface IconProps {\n size?: number\n className?: string\n}\n\n/** One shell icon component. */\nexport type IconComponent = (props: IconProps) => JSX.Element\n\n/** The runtime export table, read by capability instead of by one hard-coded name. */\nconst table = primitives as unknown as Record<string, IconComponent | undefined>\n\n/** First name the runtime actually provides (a missing export is `undefined`). */\nfunction firstDefined(...names: string[]): IconComponent | undefined {\n for (const name of names) {\n let candidate: IconComponent | undefined\n try {\n candidate = table[name]\n } catch {\n // Some module namespaces reject unknown names instead of yielding\n // `undefined`; a rename must never throw on its way to the fallback.\n continue\n }\n if (typeof candidate === 'function') return candidate\n }\n return undefined\n}\n\n/**\n * Self-contained 16×16 checklist glyph (the 0.1.7 artwork) used when the shell\n * primitives expose none of the names above: the footer entry must render\n * something even on a runtime whose icon exports we do not know.\n */\nfunction ChecklistFallback({ size = 14, className }: IconProps): JSX.Element {\n return (\n <svg\n width={size}\n height={size}\n className={className}\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n strokeWidth={1}\n >\n <path d=\"M3.75 6.25C4.7165 6.25 5.5 5.4665 5.5 4.5C5.5 3.5335 4.7165 2.75 3.75 2.75C2.7835 2.75 2 3.5335 2 4.5C2 5.4665 2.7835 6.25 3.75 6.25Z\" stroke=\"currentColor\" />\n <path d=\"M7.5 4.5H13.5\" stroke=\"currentColor\" />\n <path d=\"M3.75 13.25C4.7165 13.25 5.5 12.4665 5.5 11.5C5.5 10.5335 4.7165 9.75 3.75 9.75C2.7835 9.75 2 10.5335 2 11.5C2 12.4665 2.7835 13.25 3.75 13.25Z\" stroke=\"currentColor\" />\n <path d=\"M7.5 11.5H13.5\" stroke=\"currentColor\" />\n </svg>\n )\n}\n\n/**\n * The checklist icon used by the sidebar panel row entry.\n *\n * DSH 0.1.7 renamed every size-suffixed shell icon (`IconChecklistOutline14`)\n * into stroke-weight variants (`…Regular` 1px / `…Medium` 1.3px) inside\n * `@deepseek-ai/dsh-client-ui-primitives`. A stale named import resolves to\n * `undefined` and React then throws \"Element type is invalid\" while rendering\n * the slot entry — the entry is dropped and the button silently disappears\n * (exactly what upgrading to 0.1.7 did to this plugin). Resolve the name at\n * runtime, newest first, so the next rename degrades to another shipped\n * variant instead of taking the button down with it.\n */\nexport const ChecklistIcon: IconComponent =\n firstDefined(\n 'IconChecklistOutlineRegular',\n 'IconChecklistOutlineMedium',\n 'IconChecklistOutline',\n 'IconChecklistOutline14',\n ) ?? ChecklistFallback\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport { ChecklistIcon } from './icons'\nimport { api, type PresetOption, type ReasoningOptions } from './api'\nimport type { KanbanSettingsShape, ModelOption } from '../shared/card'\n\n/**\n * Sidebar panel-row glyph for the board (`sidebar.panellist`).\n *\n * The shell owns the row: it renders the button, its label, tooltip and active\n * state, and calls `layout.selectPanel(id)` on click. The entry contributes\n * only the glyph, so this component must stay purely presentational.\n */\nexport function KanbanPanelIcon({ size }: { size: number }): JSX.Element {\n return <ChecklistIcon size={size} />\n}\n\ninterface SettingsSectionProps {\n t?: (key: string) => string\n}\n\n/** Fallback shape while settings load (server always resolves the full set). */\nconst EMPTY_SETTINGS: KanbanSettingsShape = {\n maxParallelWorkers: 1,\n refinementModel: '',\n refinementProvider: '',\n refinementReasoningEffort: '',\n refinementPreset: '',\n phaseModel: '',\n phaseProvider: '',\n phaseReasoningEffort: '',\n phasePreset: '',\n}\n\n/** The setting field names of one session-type group. */\nconst GROUP_FIELDS = {\n refinement: {\n model: 'refinementModel',\n provider: 'refinementProvider',\n effort: 'refinementReasoningEffort',\n preset: 'refinementPreset',\n },\n phase: {\n model: 'phaseModel',\n provider: 'phaseProvider',\n effort: 'phaseReasoningEffort',\n preset: 'phasePreset',\n },\n} as const\n\ntype SessionGroupKey = keyof typeof GROUP_FIELDS\n\ninterface SessionDefaultsGroupProps {\n kind: SessionGroupKey\n settings: KanbanSettingsShape\n models: ModelOption[]\n presets: PresetOption[]\n /** Reasoning efforts already fetched, keyed `provider/model`. */\n efforts: Record<string, ReasoningOptions>\n tr: (key: string) => string\n onCommit: (patch: Partial<KanbanSettingsShape>) => Promise<void>\n loadEfforts: (provider: string, model: string) => void\n}\n\n/** Model / reasoning effort / agent preset defaults for one session type. */\nfunction SessionDefaultsGroup({ kind, settings, models, presets, efforts, tr, onCommit, loadEfforts }: SessionDefaultsGroupProps): JSX.Element {\n const fields = GROUP_FIELDS[kind]\n const model = settings[fields.model]\n const provider = settings[fields.provider]\n const effort = settings[fields.effort]\n const preset = settings[fields.preset]\n const effortKey = model !== '' && provider !== '' ? `${provider}/${model}` : ''\n const effortOptions = effortKey !== '' ? (efforts[effortKey]?.efforts ?? []) : []\n\n const handleModel = (id: string): void => {\n const found = models.find((m) => m.id === id)\n const nextProvider = found?.provider ?? ''\n void onCommit({ [fields.model]: id, [fields.provider]: nextProvider } as Partial<KanbanSettingsShape>)\n if (id !== '' && nextProvider !== '') loadEfforts(nextProvider, id)\n }\n\n return (\n <div className=\"kb-settings-group\">\n <div className=\"kb-settings-group-title\">{tr(kind === 'refinement' ? 'settingsRefineGroup' : 'settingsPhaseGroup')}</div>\n <label className=\"kb-field kb-field-row\">\n <span>{tr('model')}</span>\n <select className=\"kb-input\" value={model} onChange={(e) => handleModel(e.target.value)}>\n <option value=\"\">{tr('defaultOption')}</option>\n {models.map((m) => (\n <option key={m.provider + '/' + m.id} value={m.id}>{m.name ?? m.id}</option>\n ))}\n </select>\n </label>\n <label className=\"kb-field kb-field-row\">\n <span>{tr('reasoningEffort')}</span>\n <select\n className=\"kb-input\"\n value={effort}\n disabled={model === ''}\n onChange={(e) => void onCommit({ [fields.effort]: e.target.value } as Partial<KanbanSettingsShape>)}\n >\n <option value=\"\">{tr('defaultOption')}</option>\n {effortOptions.map((o) => (\n <option key={o.id} value={o.id}>{o.name}</option>\n ))}\n </select>\n </label>\n <label className=\"kb-field kb-field-row\">\n <span>{tr('agentPreset')}</span>\n <select\n className=\"kb-input\"\n value={preset}\n onChange={(e) => void onCommit({ [fields.preset]: e.target.value } as Partial<KanbanSettingsShape>)}\n >\n <option value=\"\">{tr('defaultOption')}</option>\n {presets.map((p) => (\n <option key={p.id} value={p.id}>{p.name ?? p.id}</option>\n ))}\n </select>\n </label>\n </div>\n )\n}\n\n/** Plugin settings page (设置 -> 侧栏「任务看板」): workers + default model + per-session-type defaults. */\nexport function KanbanSettingsSection({ t }: SettingsSectionProps): JSX.Element {\n const tr = t ?? ((key: string) => key)\n const [settings, setSettings] = useState<KanbanSettingsShape>(EMPTY_SETTINGS)\n const [models, setModels] = useState<ModelOption[]>([])\n const [presets, setPresets] = useState<PresetOption[]>([])\n const [efforts, setEfforts] = useState<Record<string, ReasoningOptions>>({})\n const [saved, setSaved] = useState(false)\n\n const commit = useCallback(async (patch: Partial<KanbanSettingsShape>): Promise<void> => {\n try {\n const next = await api.settingsSet(patch)\n setSettings(next)\n setSaved(true)\n window.setTimeout(() => setSaved(false), 1500)\n } catch {\n // keep the previous value\n }\n }, [])\n\n // Refs so async callbacks (effort fetch) read the freshest settings/commit.\n const settingsRef = useRef(settings)\n settingsRef.current = settings\n const commitRef = useRef(commit)\n commitRef.current = commit\n\n useEffect(() => {\n let alive = true\n api.settingsGet()\n .then((s) => { if (alive) setSettings(s) })\n .catch(() => undefined)\n api.models()\n .then((list) => { if (alive) setModels(list) })\n .catch(() => undefined)\n api.presets()\n .then((list) => { if (alive) setPresets(list) })\n .catch(() => undefined)\n return () => { alive = false }\n }, [])\n\n /** Reasoning routes already requested (dedupe across mount + model changes). */\n const effortFetched = useRef<Set<string>>(new Set())\n\n /**\n * Fetch the reasoning efforts of one model route (once per route). A failed\n * fetch is marked `failed` so the stored effort is preserved (see the reset\n * effect below) instead of being wiped by a transient error.\n */\n const loadEfforts = useCallback((provider: string, model: string): void => {\n const key = `${provider}/${model}`\n if (effortFetched.current.has(key)) return\n effortFetched.current.add(key)\n api.reasoningOptions(provider, model)\n .then((opts) => setEfforts((prev) => ({ ...prev, [key]: opts })))\n .catch(() => setEfforts((prev) => ({ ...prev, [key]: { efforts: [], failed: true } })))\n }, [])\n\n // Fetch efforts for the models already configured in settings, so the\n // effort selects show their levels (and validate the stored effort) as soon\n // as settings load — the fetch previously ran only on model change.\n useEffect(() => {\n for (const kind of ['refinement', 'phase'] as const) {\n const fields = GROUP_FIELDS[kind]\n const model = settings[fields.model]\n const provider = settings[fields.provider]\n if (model !== '' && provider !== '') loadEfforts(provider, model)\n }\n }, [settings.refinementModel, settings.refinementProvider, settings.phaseModel, settings.phaseProvider, loadEfforts])\n\n // When the efforts of the selected model arrive, drop a stored effort the\n // model no longer offers (e.g. the user switched the model). A failed fetch\n // (`failed: true`) keeps the stored effort: the model may just be\n // temporarily unresolvable, and wiping the user's choice would be worse.\n useEffect(() => {\n const s = settingsRef.current\n for (const kind of ['refinement', 'phase'] as const) {\n const fields = GROUP_FIELDS[kind]\n const model = s[fields.model]\n const provider = s[fields.provider]\n const effort = s[fields.effort]\n if (effort === '' || model === '' || provider === '') continue\n const opts = efforts[`${provider}/${model}`]\n if (opts === undefined || opts.failed === true) continue\n if (!opts.efforts.some((o) => o.id === effort)) {\n void commitRef.current({ [fields.effort]: '' } as Partial<KanbanSettingsShape>)\n }\n }\n }, [efforts])\n\n return (\n <div className=\"kb-page\">\n <header className=\"kb-page-head\">\n <h3 className=\"kb-page-title\">{tr('settingsTitle')}</h3>\n <p className=\"kb-page-sub\">{tr('sectionSub')}</p>\n </header>\n <div className=\"kb-settings\">\n <label className=\"kb-field kb-field-row\">\n <span>{tr('maxParallelWorkers')}</span>\n <input\n className=\"kb-input kb-input-number\"\n type=\"number\"\n min={1}\n step={1}\n value={settings.maxParallelWorkers}\n onChange={(e) => {\n const value = Math.max(1, Math.floor(Number(e.target.value) || 1))\n setSettings((s) => ({ ...s, maxParallelWorkers: value }))\n void commit({ maxParallelWorkers: value })\n }}\n />\n </label>\n <SessionDefaultsGroup\n kind=\"refinement\"\n settings={settings}\n models={models}\n presets={presets}\n efforts={efforts}\n tr={tr}\n onCommit={commit}\n loadEfforts={loadEfforts}\n />\n <SessionDefaultsGroup\n kind=\"phase\"\n settings={settings}\n models={models}\n presets={presets}\n efforts={efforts}\n tr={tr}\n onCommit={commit}\n loadEfforts={loadEfforts}\n />\n {saved && <div className=\"kb-settings-saved\">{tr('saved')}</div>}\n </div>\n </div>\n )\n}","import type { Context } from '@deepseek-ai/cordis'\ntype ClientContext = Context\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { BoardRoot } from './board'\nimport { KanbanPanelIcon, KanbanSettingsSection } from './sections'\nimport { LOCALE_NS, zh, en } from './locales'\nimport { KANBAN_PANEL_ID, setClient, bindSessionNavigation } from './kanban-state'\n\n/** The settings sidebar entry this plugin's page owns (must stay stable). */\nconst SETTINGS_SECTION_ID = 'task-kanban'\n\n/** Services required before mounting (provided by the client runtime). */\nexport const inject = ['slots', 'sessions', 'workspaces', 'locale']\n\n/** Client plugin body. */\nexport function apply(ctx: ClientContext): void {\n setClient(ctx)\n // Selecting a session while the board is open must return to the Conversation.\n bindSessionNavigation(ctx)\n\n // Live translate bound to the active locale (labels re-read it per call).\n const t = ctx.locale.bind(LOCALE_NS)\n ctx.effect(() => {\n const off = ctx.locale.register(LOCALE_NS, { zh, en })\n return () => off()\n }, 'task-kanban: dictionaries')\n\n // The board is a GLOBAL MAIN PANEL: the shell renders one `main` entry per\n // selected panel id, so keying it (instead of replacing `main.conversation`)\n // keeps the Conversation mounted and selectable beside the board, and the\n // sidebar's own panel row owns the button that selects it (the\n // `sidebar.panellist` entry below). Nothing of this plugin sits in the\n // sidebar foot any more, so the Settings trigger can never be squeezed.\n ctx.slots.inject('main', () =>\n ctx.slots.register({\n name: 'main',\n key: KANBAN_PANEL_ID,\n locale: LOCALE_NS,\n } as never, BoardRoot as never),\n )\n\n // Sidebar panel row: the shell renders the row (label, tooltip, active state)\n // and calls `layout.selectPanel(id)` on click; the entry supplies the glyph.\n ctx.slots.inject('sidebar.panellist', () =>\n ctx.slots.register({\n name: 'sidebar.panellist',\n id: KANBAN_PANEL_ID,\n order: 20,\n label: () => t('kanban'),\n locale: LOCALE_NS,\n }, KanbanPanelIcon as never),\n )\n\n // The plugin's own settings page (设置 -> 侧栏「任务看板」) rides the\n // task-kanban settings namespace and registers into the settings.section\n // list slot, which gives it one entry in the settings sidebar and renders\n // its content into the panel's content column.\n ctx.slots.inject('settings.section', () =>\n ctx.slots.register({\n name: 'settings.section',\n id: SETTINGS_SECTION_ID,\n order: 200,\n label: () => t('settingsTitle'),\n locale: LOCALE_NS,\n }, KanbanSettingsSection as never),\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMA,SAAgB,OAAO,MAAwB;GAC7C,IAAI,KAAK,WAAW,SAAS;IAC3B,IAAI,KAAK,OAAO,UAAU,KAAA,GAAW,OAAO,KAAK,MAAM;IACvD,MAAM,OAAO,KAAK,OAAO;IACzB,IAAI,SAAS,kBAAkB,SAAS,oBAAoB,SAAS,mBAAmB,OAAO;IAC/F,IAAI,SAAS,gBAAgB,OAAO;IACpC,OAAO;GACT;GACA,QAAQ,KAAK,QAAb;IACE,KAAK;IACL,KAAK;IACL,KAAK,WACH,OAAO;IACT,KAAK,UACH,OAAO;IACT,KAAK,WACH,OAAO;IACT,KAAK;IACL,KAAK,WACH,OAAO;IACT,KAAK,UACH,OAAO;GACX;EACF;;EAGA,MAAa,aAAmC;GAC9C,QAAQ,CAAC,OAAO;GAChB,OAAO,CAAC,UAAU,SAAS;GAC3B,SAAS,CAAC;GACV,WAAW,CAAC;GACZ,QAAQ,CAAC;EACX;;;ECpCA,IAAa,iBAAb,cAAoC,MAAM;GACnB;GAArB,YAAY,MAAuB,SAAiB;IAClD,MAAM,OAAO;IADM,KAAA,OAAA;GAErB;EACF;EAEA,eAAe,KAAQ,QAAgB,UAAmC,CAAC,GAAe;GACxF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,MAAM,wCAAwC,UAAU;KACvE,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C,MAAM,KAAK,UAAU,OAAO;IAC9B,CAAC;GACH,SAAS,OAAO;IACd,MAAM,IAAI,eAAe,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;GAC5F;GACA,MAAM,SACF,MAAM,SAAS,KAAK,CAAC,CAAC,YAAY,IAAI;GAC1C,IAAI,CAAC,SAAS,MAAM,WAAW,QAAQ,OAAO,OAAO,QAAQ,OAAO,UAAU,KAAA,GAC5E,MAAM,IAAI,eACR,QAAQ,OAAO,QAAQ,QACvB,QAAQ,OAAO,WAAW,QAAQ,SAAS,QAC7C;GAEF,OAAO,OAAO;EAChB;;;;;;;;;;;;;;;EAwBA,eAAsB,iBAAiB,WAA2C;GAChF,IAAI;GACJ,IAAI;IACF,WAAW,MAAM,MAAM,oBAAoB;KACzC,QAAQ;KACR,SAAS,EAAE,gBAAgB,mBAAmB;KAC9C,MAAM,KAAK,UAAU;MACnB,MAAM;MACN,OAAO,eAAe,KAAK,OAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC;MACxD,QAAQ;MACR,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE;KAC9C,CAAC;IACH,CAAC;GACH,SAAS,OAAO;IACd,MAAM,IAAI,eAAe,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;GAC5F;GACA,MAAM,SACF,MAAM,SAAS,KAAK,CAAC,CAAC,YAAY,IAAI;GAC1C,IAAI,CAAC,SAAS,MAAM,WAAW,QAAQ,OAAO,SAAS,qBAAqB,OAAO,QAAQ,OAAO,MAChG,MAAM,IAAI,eACR,QAAQ,QAAQ,OAAO,QAAQ,QAC/B,QAAQ,QAAQ,OAAO,WAAW,QAAQ,SAAS,QACrD;GAEF,OAAO,OAAO,OAAO,OAAO,UAAU,CAAC;EACzC;EAiBA,MAAa,MAAM;GACjB,OAAO,kBAA0B,KAAmB,QAAQ,EAAE,cAAc,CAAC;GAC7E,SAAS,eAAuB,aAAqB,OAAe,UAAmB,UACrF,KAAiB,UAAU;IAAE;IAAe;IAAa;IAAO;IAAU,GAAI,UAAU,KAAA,KAAa,UAAU,KAAK,EAAE,MAAM,IAAI,CAAC;GAAG,CAAC;GACvI,OAAO,QAAgB,WAAiB,KAAwC,QAAQ;IAAE;IAAQ;GAAO,CAAC;GAC1G,OAAO,WAAmB,KAAwC,QAAQ,EAAE,OAAO,CAAC;GACpF,QAAQ,WAAmB,KAAwC,SAAS,EAAE,OAAO,CAAC;GACtF,SAAS,WAAmB,KAAwC,UAAU,EAAE,OAAO,CAAC;GACxF,mBAAmB,KAA0B,cAAc;GAC3D,cAAc,UAAwC,KAA0B,gBAAgB,KAAK;GACrG,cAAc,KAAoB,aAAa;GAC/C,eAAe,KAAqB,cAAc;GAClD,mBAAmB,UAAkB,UACnC,KAAuB,qBAAqB;IAAE;IAAU;GAAM,CAAC;EACnE;;;;;;;;ECpGA,MAAa,kBAAkB;;;;;;;;;EAmB/B,MAAM,YAAY,OAAO,IAAI,+BAA+B;EAM5D,SAAS,WAAwB;GAC/B,MAAM,IAAI;GACV,IAAI,QAAQ,EAAE;GACd,IAAI,UAAU,KAAA,GAAW;IACvB,QAAQ,EAAE,QAAQ,KAAK;IACvB,EAAE,aAAa;GACjB;GACA,OAAO;EACT;EAEA,SAAgB,UAAU,KAAiC;GACzD,SAAS,CAAC,CAAC,SAAS;EACtB;EACA,SAAgB,YAAkC;GAChD,OAAO,SAAS,CAAC,CAAC;EACpB;;EAGA,SAAS,SAAS,KAA8C;GAC9D,IAAI,QAAQ,MAAM,OAAO;GACzB,IAAI;IACF,MAAM,SAAU,IAA2C;IAC3D,OAAO,OAAO,QAAQ,gBAAgB,aAAa,SAAS;GAC9D,QAAQ;IACN,OAAO;GACT;EACF;;EAYA,SAAgB,iBAAiB,MAA4B,UAAU,GAAS;GAC9E,IAAI;IACF,SAAS,GAAG,CAAC,EAAE,YAAY,IAAI;GACjC,SAAS,OAAO;IACd,QAAQ,MAAM,4DAA4D,KAAK;GACjF;EACF;;;;;;;;;;;;;;;;EAiBA,SAAgB,sBAAsB,KAA0B;GAC9D,MAAM,WAAW,IAAI;GACrB,IAAI,aAAa,KAAA,GAAW;GAE5B,MAAM,OAAO,SAAS;GACtB,IAAI,OAAO,SAAS,cAAe,KAA0C,YAAY,MAAM;IAC7F,MAAM,UAAU,OAAmC;KACjD,iBAAiB,GAAG;KACpB,OAAO,KAAK,KAAK,UAAU,EAAE;IAC/B;IACC,MAA4C,UAAU;IACvD,SAAS,OAAO;GAClB;GAEA,MAAM,eAAe,SAAS;GAC9B,IAAI,OAAO,iBAAiB,cAAe,aAAkD,YAAY,MAAM;IAC7G,MAAM,UAAU,YAAgD;KAC9D,iBAAiB,GAAG;KACpB,OAAO,aAAa,KAAK,UAAU,OAAO;IAC5C;IACC,MAA4C,UAAU;IACvD,SAAS,eAAe;GAC1B;EACF;;;ECjHA,MAAa,YAAY;EAEzB,MAAa,KAAK;GAChB,QAAQ;GACR,WAAW;GACX,YAAY;GACZ,SAAS;GACT,aAAa;GACb,SAAS;GACT,OAAO;GACP,QAAQ;GACR,OAAO;GACP,cAAc;GACd,YAAY;GACZ,WAAW;GACX,aAAa;GACb,eAAe;GACf,YAAY;GACZ,aAAa;GACb,gBAAgB;GAChB,eAAe;GACf,cAAc;GACd,eAAe;GACf,iBAAiB;GACjB,eAAe;GACf,cAAc;GACd,aAAa;GACb,eAAe;GACf,OAAO;GACP,MAAM;GACN,QAAQ;GACR,eAAe;GACf,aAAa;GACb,MAAM;GACN,SAAS;GACT,QAAQ;GACR,WAAW;GACX,eAAe;GACf,iBAAiB;GACjB,UAAU;GACV,OAAO;GACP,kBAAkB;GAClB,wBAAwB;GACxB,kBAAkB;GAClB,WAAW;GACX,mBAAmB;GACnB,YAAY;GACZ,kBAAkB;GAClB,aAAa;GACb,SAAS;GACT,eAAe;GACf,YAAY;GACZ,oBAAoB;GACpB,qBAAqB;GACrB,oBAAoB;GACpB,iBAAiB;GACjB,aAAa;GACb,eAAe;GACf,OAAO;GACP,WAAW;GACX,QAAQ;GACR,YAAY;GACZ,cAAc;GACd,kBAAkB;GAClB,aAAa;GACb,cAAc;GACd,gBAAgB;GAChB,cAAc;GACd,aAAa;GACb,aAAa;GACb,cAAc;GACd,iBAAiB;GACjB,YAAY;GACZ,aAAa;EACf;EAEA,MAAa,KAAK;GAChB,QAAQ;GACR,WAAW;GACX,YAAY;GACZ,SAAS;GACT,aAAa;GACb,SAAS;GACT,OAAO;GACP,QAAQ;GACR,OAAO;GACP,cAAc;GACd,YAAY;GACZ,WAAW;GACX,aAAa;GACb,eAAe;GACf,YAAY;GACZ,aAAa;GACb,gBAAgB;GAChB,eAAe;GACf,cAAc;GACd,eAAe;GACf,iBAAiB;GACjB,eAAe;GACf,cAAc;GACd,aAAa;GACb,eAAe;GACf,OAAO;GACP,MAAM;GACN,QAAQ;GACR,eAAe;GACf,aAAa;GACb,MAAM;GACN,SAAS;GACT,QAAQ;GACR,WAAW;GACX,eAAe;GACf,iBAAiB;GACjB,UAAU;GACV,OAAO;GACP,kBAAkB;GAClB,wBAAwB;GACxB,kBAAkB;GAClB,WAAW;GACX,mBAAmB;GACnB,YAAY;GACZ,kBAAkB;GAClB,aAAa;GACb,SAAS;GACT,eAAe;GACf,YAAY;GACZ,oBAAoB;GACpB,qBAAqB;GACrB,oBAAoB;GACpB,iBAAiB;GACjB,aAAa;GACb,eAAe;GACf,OAAO;GACP,WAAW;GACX,QAAQ;GACR,YAAY;GACZ,cAAc;GACd,kBAAkB;GAClB,aAAa;GACb,cAAc;GACd,gBAAgB;GAChB,cAAc;GACd,aAAa;GACb,aAAa;GACb,cAAc;GACd,iBAAiB;GACjB,YAAY;GACZ,aAAa;EACf;;;;;;;;;;;;;;ECnIA,SAAS,UAAU,MAAsB;GACvC,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC,EAAE,KAAK,KAAK;EAClE;;EAGA,MAAM,gBAAgB;;EAGtB,SAAS,kBAAkB,KAAsD;GAC/E,MAAM,IAAI,cAAc,KAAK,GAAG;GAChC,IAAI,MAAM,MAAM,OAAO,EAAE,aAAa,IAAI,KAAK,EAAE;GACjD,OAAO;IAAE,OAAO,EAAE;IAAI,aAAa,IAAI,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK;GAAE;EACnE;EAEA,SAAS,YAAY,MAAkB,GAAoC;GACzE,IAAI,KAAK,WAAW,SAClB,QAAQ,KAAK,OAAO,MAApB;IACE,KAAK,eAAe,OAAO,EAAE,aAAa;IAC1C,KAAK,kBAAkB,OAAO,EAAE,cAAc;IAC9C,KAAK,mBAAmB,OAAO,EAAE,gBAAgB;IACjD,KAAK,iBAAiB,OAAO,EAAE,cAAc;IAC7C,KAAK,gBAAgB,OAAO,EAAE,aAAa;IAC3C,KAAK,gBAAgB,OAAO,EAAE,aAAa;IAC3C,SAAS,OAAO,EAAE,cAAc;GAClC;GAEF,QAAQ,KAAK,QAAb;IACE,KAAK,SAAS,OAAO,EAAE,aAAa;IACpC,KAAK,YAAY,OAAO,EAAE,gBAAgB;IAC1C,KAAK,WAAW,OAAO,EAAE,eAAe;IACxC,KAAK,UAAU,OAAO,EAAE,cAAc;IACtC,KAAK,WAAW,OAAO,EAAE,eAAe;IACxC,KAAK,aAAa,OAAO,EAAE,iBAAiB;IAC5C,KAAK,WAAW,OAAO,EAAE,eAAe;IACxC,KAAK,UAAU,OAAO,EAAE,cAAc;GACxC;EACF;EAQA,MAAM,aAAqD;GACzD;IAAE,KAAK;IAAU,UAAU;GAAa;GACxC;IAAE,KAAK;IAAS,UAAU;GAAY;GACtC;IAAE,KAAK;IAAW,UAAU;GAAc;GAC1C;IAAE,KAAK;IAAa,UAAU;GAAgB;GAC9C;IAAE,KAAK;IAAU,UAAU;GAAa;EAC1C;EAEA,MAAM,iBAAiB;EAEvB,SAAgB,UAAU,OAAgC;GACxD,MAAM,IAAI,MAAM,OAAO,KAAa,WAAqC;IACvE,MAAM,OAAQ,GAA8B,QAAS,GAA8B;IACnF,IAAI,SAAS,KAAA,KAAa,WAAW,KAAA,GAAW,OAAO,QAAQ;IAC/D,OAAO,KAAK,QAAQ,eAAe,GAAG,SAAkB,OAAO,SAAoB,CAAC;GACtF;GACA,MAAM,gBAAgB,MAAM;GAE5B,MAAM,SADU,kBAAkB,KAAA,IAAa,eAAe,MAAe,CAAC,IAA+C,KAAA,EAAA,EACtF,SAAS,CAAC;GACjD,MAAM,cAAc,MAAM;GAC1B,MAAM,eAAe,gBAAgB,KAAA,IAAa,aAAa,MAAe,CAAC,IAAyC,KAAA;GAKxH,MAAM,mBAAmB,cAAc,WAAW,cAAc,MAAM;GAEtE,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,OAAuB;IAC7D,IAAI;KACF,OAAO,aAAa,QAAQ,cAAc,KAAK;IACjD,QAAQ;KACN,OAAO;IACT;GACF,CAAC;GACD,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAuB,CAAC,CAAC;GACnD,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAwB,IAAI;GACtD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAwB,IAAI;GAC5D,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAAS,KAAK;GAEpD,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,iBAAiB,MAAM,MAAM,SAAS,GACxC,gBAAgB,MAAM,EAAE,CAAC,IAAI;GAEjC,GAAG,CAAC,cAAc,KAAK,CAAC;GAExB,MAAM,aAAA,GAAYC,MAAAA,YAAAA,EAAa,YAAoB;IACjD,SAAS,OAAO;IAChB,OAAO,iBAAiB,SAAS,IAAI,GAAG,IAAI;GAC9C,GAAG,CAAC,CAAC;GAEL,MAAM,WAAA,GAAUA,MAAAA,YAAAA,CAAY,YAAY;IACtC,IAAI,iBAAiB,IAAI;IACzB,IAAI;KACF,SAAS,MAAM,IAAI,KAAK,YAAY,CAAC;IACvC,SAAS,OAAO;KACd,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;IAClE;GACF,GAAG,CAAC,cAAc,SAAS,CAAC;GAE5B,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,QAAa;IACb,MAAM,QAAQ,OAAO,kBAAkB;KAAE,QAAa;IAAE,GAAG,IAAI;IAC/D,aAAa,OAAO,cAAc,KAAK;GACzC,GAAG,CAAC,OAAO,CAAC;GAEZ,MAAM,UAAA,GAASC,MAAAA,QAAAA,OAAc;IAC3B,MAAM,MAAkC;KAAE,QAAQ,CAAC;KAAG,OAAO,CAAC;KAAG,SAAS,CAAC;KAAG,WAAW,CAAC;KAAG,QAAQ,CAAC;IAAE;IACxG,KAAK,MAAM,QAAQ,OAAO,IAAI,OAAO,IAAI,EAAE,CAAC,KAAK,IAAI;IACrD,KAAK,MAAM,QAAQ,YAAY,IAAI,KAAK,IAAI,CAAC,MAAM,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;IACrF,OAAO;GACT,GAAG,CAAC,KAAK,CAAC;GAEV,MAAM,cAAA,GAAaD,MAAAA,YAAAA,CAAY,OAAO,QAAc,UAA2B;IAC7E,MAAM,eAAe;IACrB,MAAM,SAAS,MAAM,aAAa,QAAQ,YAAY;IACtD,IAAI,WAAW,IAAI;IACnB,MAAM,OAAO,MAAM,MAAM,MAAM,EAAE,OAAO,MAAM;IAC9C,IAAI,SAAS,KAAA,GAAW;IACxB,IAAI,WAAW,WAAW,KAAK,WAAW,aAAa,KAAK,SAAS,KAAA,GAAW;KAC9E,UAAU,EAAE,YAAY,CAAC;KACzB;IACF;IACA,IAAI;KACF,MAAM,SAAS,MAAM,IAAI,KAAK,QAAQ,MAAM;KAC5C,IAAI,CAAC,OAAO,IACV,UAAU,EAAE,gBAAgB,EAAE,SAAS,OAAO,WAAW,GAAG,CAAC,CAAC;KAEhE,QAAa;IACf,SAAS,OAAO;KACd,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;IAClE;GACF,GAAG;IAAC;IAAO;IAAS;IAAW;GAAC,CAAC;GAEjC,MAAM,eAAA,GAAcA,MAAAA,YAAAA,EAAa,cAAsB;IACrD,MAAM,MAAM,UAAU;IAItB,iBAAiB,GAAG;IACpB,IAAI,QAAQ,MAAM;IAClB,IAAI;KACD,IAAK,SAAwC,KAAK,SAAS;IAC9D,QAAQ;KACN,UAAU,EAAE,kBAAkB,CAAC;IACjC;GACF,GAAG,CAAC,WAAW,CAAC,CAAC;GAEjB,IAAI,MAAM,WAAW,GACnB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAU;IACb,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,WAAU;KAAiB,UAAA,EAAE,aAAa;IAAO,CAAA;GACnD,CAAA;GAIT,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MACb,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QACE,WAAU;QACV,OAAO;QACP,WAAW,MAAM;SACf,gBAAgB,EAAE,OAAO,KAAK;SAC9B,IAAI;UACF,aAAa,QAAQ,gBAAgB,EAAE,OAAO,KAAK;SACrD,QAAQ,CAER;QACF;QAEC,UAAA,MAAM,KAAK,MACV,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAA4B,OAAO,EAAE;SAAO,UAAA,EAAE;QAAc,GAA/C,EAAE,WAA6C,CAC7D;OACK,CAAA,GACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,MAAK;QAAS,WAAU;QAAwB,eAAe,eAAe,IAAI;QAAI,UAAA,EAAE,SAAS;OAAU,CAAA,CAChH;;KACF,CAAA;KAEL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MACZ,UAAA,WAAW,KAAK,SACf,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAEE,WAAW,aAAa,WAAW,KAAK,IAAI,CAAC,SAAS,IAAI,kBAAkB;OAC5E,aAAa,MAAM;QACjB,IAAI,WAAW,KAAK,IAAI,CAAC,SAAS,GAAG,EAAE,eAAe;OACxD;OACA,SAAS,MAAM;QAAE,WAAgB,KAAK,KAAK,CAAC;OAAE;OANhD,UAAA,CAQE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAU;QAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAU;SAAiB,UAAA,EAAE,KAAK,QAAQ;QAAQ,CAAA,GACxD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAU;SAAiB,UAAA,OAAO,KAAK,IAAI,CAAC;QAAa,CAAA,CAC5D;OACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QACZ,UAAA,OAAO,KAAK,IAAI,CAAC,KAAK,SACrB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAEQ;SACH;SACH,gBAAgB,YAAY,aAAa,KAAK,KAAK,OAAO,KAAK,EAAE;QAClE,GAJM,KAAK,EAIX,CACF;OACE,CAAA,CACF;MArBE,GAAA,KAAK,GAqBP,CACN;KACE,CAAA;KAEJ,UAAU,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAY,UAAA;KAAW,CAAA;KAExD,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,cAAD;MACK;MACH,YAAY;MACE;MACd,WAAW;MACX,eAAe,eAAe,KAAK;MACnC,iBAAiB;OACf,eAAe,KAAK;OACpB,QAAa;MACf;MACA,UAAU,YAAY,UAAU,OAAO;KACxC,CAAA;KAGF,aAAa,QACZ,iBAAA,GAAA,kBAAA,IAAA,CAAC,aAAD;MACE,MAAM,MAAM,MAAM,MAAM,EAAE,OAAO,QAAQ,KAAK;MAC3C;MACH,eAAe,YAAY,IAAI;MAC/B,iBAAiB;OACf,YAAY,IAAI;OAChB,QAAa;MACf;MACA,SAAS;MACT,eAAe;KAChB,CAAA;IAEA;;EAET;EAQA,SAAS,SAAS,EAAE,MAAM,GAAG,YAAwC;GACnE,MAAM,YAAY,KAAK,WAAW,aAAa,KAAK,WAAW,YAAY,KAAK,WAAW;GAC3F,MAAM,QAAQ,KAAK,SAAS,KAAA,KAAa,KAAK,KAAK,UAAU,KAAK,KAAK,KAAK,QAAQ,UAAU,KAAK,WAAW;GAC9G,MAAM,QAAQ,YAAY,MAAM,CAAC;GACjC,MAAM,YAAY,KAAK,WAAW,aAAa,KAAK,WAAW,UAC3D,KAAK,eAAe,KAAK,aAAa,EAAE,iBAAiB;IAAE,SAAS,KAAK,eAAe;IAAG,OAAO,KAAK;GAAW,CAAC,IAAI,KACvH;GACJ,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IACE,WAAW,aAAa,KAAK,WAAW,UAAU,mBAAmB,OAAO,KAAK,WAAW,YAAY,qBAAqB;IAClH;IACX,cAAc,MAAM;KAClB,EAAE,aAAa,QAAQ,cAAc,KAAK,EAAE;KAC5C,EAAE,aAAa,gBAAgB;IACjC;IACA,SAAS;IAPX,UAAA;KASE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAiB,UAAA;KAAW,CAAA;KAC3C,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAiB,UAAA;OAAY,CAAA;OAC5C,cAAc,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAiB,UAAA;OAAgB,CAAA;OACrE,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU,MAAM,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;QAAM,WAAU;QAAhB,UAAA,CAAgC,MAAG,KAAK,KAAY;;OACrG,KAAK,UAAU,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,WAAU;QAAiB,UAAA,KAAK;OAAY,CAAA;MACrE;;KACJ,KAAK,WAAW,WAAW,KAAK,UAAU,KAAA,KACzC,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAsB,UAAA,KAAK,MAAM;KAAa,CAAA;KAE9D,KAAK,WAAW,aAAa,KAAK,YAAY,QAC7C,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAAoB,UAAA,KAAK,gBAAgB;KAAQ,CAAA;IAE/D;;EAET;;EAcA,MAAM,gBAAgB;EAEtB,SAAS,aAAa,EAAE,GAAG,YAAY,cAAc,WAAW,SAAS,WAAW,WAA2C;GAC7H,MAAM,CAAC,aAAa,mBAAA,GAAkBD,MAAAA,SAAAA,CAAS,EAAE;GACjD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,YAAY;GACnD,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAS,EAAE;GACrC,MAAM,CAAC,eAAe,qBAAA,GAAoBA,MAAAA,SAAAA,CAAS,EAAE;GACrD,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAiE,CAAC,CAAC;GAC/F,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAwB,CAAC,CAAC;GACtD,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAwB,IAAI;GAC9D,MAAM,CAAC,WAAW,iBAAA,GAAgBA,MAAAA,SAAAA,CAAS,CAAC;GAC5C,MAAM,CAAC,MAAM,YAAA,GAAWA,MAAAA,SAAAA,CAAS,KAAK;GACtC,MAAM,eAAA,GAAcG,MAAAA,OAAAA,CAAmC,IAAI;GAE3D,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,QAAQ;IACZ,IAAI,OAAO,CAAC,CACT,MAAM,SAAS;KAAE,IAAI,OAAO,UAAU,IAAI;IAAE,CAAC,CAAC,CAC9C,YAAY;KAAE,IAAI,OAAO,UAAU,CAAC,CAAC;IAAE,CAAC;IAC3C,aAAa;KAAE,QAAQ;IAAM;GAC/B,GAAG,CAAC,CAAC;GAIL,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,cAAc,KAAA,GAAW;IAC7B,IAAI,QAAQ;IACZ,iBAAiB,SAAS,CAAC,CACxB,MAAM,SAAS;KAAE,IAAI,OAAO,UAAU,IAAI;IAAE,CAAC,CAAC,CAC9C,YAAY;KAAE,IAAI,OAAO,UAAU,CAAC,CAAC;IAAE,CAAC;IAC3C,aAAa;KAAE,QAAQ;IAAM;GAC/B,GAAG,CAAC,SAAS,CAAC;GAEd,MAAM,UAAU,OAAO,QAAQ,MAAM,EAAE,KAAK,WAAW,aAAa,EAAE,CAAC;GACvE,MAAM,cAAc,cAAc,QAAQ,QAAQ,SAAS;GAC3D,MAAM,WAAW,cAAc,QAAQ,KAAK,IAAI,WAAW,QAAQ,SAAS,CAAC,KAAK,KAAA;;GAGlF,MAAM,YAAY,OAAe,UAAwB;IACvD,MAAM,OAAO,MAAM,MAAM,GAAG,KAAK;IACjC,MAAM,IAAI,cAAc,KAAK,IAAI;IACjC,IAAI,MAAM,MAAM;KACd,aAAa,EAAE,EAAE;KACjB,aAAa,CAAC;KACd;IACF;IACA,aAAa,IAAI;GACnB;GAEA,MAAM,eAAe,SAAuB;IAE1C,MAAM,QADK,YAAY,SACL,kBAAkB,YAAY;IAChD,MAAM,OAAO,YAAY,MAAM,GAAG,KAAK;IACvC,MAAM,IAAI,cAAc,KAAK,IAAI;IAEjC,MAAM,WAAW,IAAI,KAAK;IAC1B,MAAM,WAAW,MAAM,OAAO,WAAW,YAAY,MAAM,KAAK,IAAI,WAAW;IAC/E,eAAe,QAAQ;IACvB,aAAa,IAAI;IACjB,4BAA4B;KAC1B,MAAM,OAAO,YAAY;KACzB,IAAI,SAAS,MAAM;MACjB,KAAK,MAAM;MACX,MAAM,MAAM,SAAS;MACrB,KAAK,kBAAkB,KAAK,GAAG;KACjC;IACF,CAAC;GACH;GAEA,MAAM,aAAa,MAAsD;IACvE,IAAI,CAAC,aAAa;IAClB,IAAI,EAAE,QAAQ,aAAa;KACzB,EAAE,eAAe;KACjB,cAAc,OAAO,IAAI,KAAK,QAAQ,MAAM;IAC9C,OAAO,IAAI,EAAE,QAAQ,WAAW;KAC9B,EAAE,eAAe;KACjB,cAAc,OAAO,IAAI,IAAI,QAAQ,UAAU,QAAQ,MAAM;IAC/D,OAAO,IAAI,EAAE,QAAQ,SAAS;KAC5B,EAAE,eAAe;KACjB,IAAI,aAAa,KAAA,GAAW,YAAY,SAAS,IAAI;IACvD,OAAO,IAAI,EAAE,QAAQ,UAAU;KAC7B,EAAE,eAAe;KACjB,aAAa,IAAI;IACnB,OAAO,IAAI,EAAE,QAAQ,OAAO;KAC1B,EAAE,eAAe;KACjB,IAAI,aAAa,KAAA,GAAW,YAAY,SAAS,IAAI;IACvD;GACF;GAEA,MAAM,SAAS,YAA2B;IACxC,IAAI,YAAY,KAAK,MAAM,IAAI;KAC7B,QAAQ,EAAE,kBAAkB,IAAI,MAAM,EAAE,OAAO,CAAC;KAChD;IACF;IACA,IAAI,YAAY,IAAI;KAClB,QAAQ,EAAE,aAAa,CAAC;KACxB;IACF;IACA,MAAM,SAAS,kBAAkB,WAAW;IAC5C,QAAQ,IAAI;IACZ,IAAI;KACF,MAAM,IAAI,OAAO,SAAS,OAAO,aAAa,OAAO,eAAe,OAAO,KAAK;KAChF,UAAU;IACZ,SAAS,OAAO;KACd,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;KAC9D,QAAQ,KAAK;IACf;GACF;GAEA,MAAM,cAAc,kBAAkB,WAAW,CAAC,CAAC;GAEnD,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAU;IAAoB,SAAS,OAAO,KAAA,IAAY;IAC7D,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAW,UAAU,MAAM,EAAE,gBAAgB;KAA5D,UAAA;MACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAU;OAAkB,UAAA,EAAE,SAAS;MAAO,CAAA;MACnD,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;OAAO,WAAU;OAAjB,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,EAAE,kBAAkB,EAAQ,CAAA;QACnC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;UACE,KAAK;UACL,WAAU;UACV,OAAO;UACP,aAAa,EAAE,wBAAwB;UACvC,WAAW,MAAM;WACf,MAAM,QAAQ,EAAE,OAAO;WACvB,eAAe,KAAK;WACpB,SAAS,OAAO,EAAE,OAAO,kBAAkB,MAAM,MAAM;UACzD;UACW;SACZ,CAAA,GACA,eACC,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;UAAI,WAAU;UAAgB,MAAK;UAChC,UAAA,QAAQ,KAAK,GAAG,MACf,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;WAEE,MAAK;WACL,iBAAe,MAAM;WACrB,WAAW,wBAAwB,MAAM,YAAY,+BAA+B;WACpF,cAAc,MAAM;YAAE,EAAE,eAAe;YAAG,YAAY,EAAE,IAAI;WAAE;WAC9D,oBAAoB,aAAa,CAAC;WANpC,UAAA,CAQE,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;YAAM,WAAU;YAAhB,UAAA,CAAqC,KAAE,EAAE,IAAW;WACpD,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;YAAM,WAAU;YACb,UAAA,EAAE,iBAAiB,EAAE,cAAc,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE;WACjE,CAAA,CACJ;UAXG,GAAA,EAAE,IAWL,CACL;SACC,CAAA,CAEH;;QACJ,gBAAgB,KAAA,IACf,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAU;SAAqC,UAAA,EAAE,aAAa,EAAE,OAAO,YAAY,CAAC;QAAQ,CAAA,IAElG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,WAAU;SAAiB,UAAA,EAAE,kBAAkB;QAAQ,CAAA;OAE1D;;MACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;OAAO,WAAU;OAAjB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,EAAE,SAAS,EAAQ,CAAA,GAC1B,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,WAAU;QAAW,OAAO;QAAS,WAAW,MAAM,WAAW,EAAE,OAAO,KAAK;QACpF,UAAA,WAAW,KAAK,MACf,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAA4B,OAAO,EAAE;SAAO,UAAA,EAAE;QAAc,GAA/C,EAAE,WAA6C,CAC7D;OACK,CAAA,CACH;;MACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;OAAO,WAAU;OAAjB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,EAAE,OAAO,EAAQ,CAAA,GACxB,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;QACE,WAAU;QACV,OAAO;QACP,WAAW,MAAM;SACf,MAAM,KAAK,EAAE,OAAO;SACpB,SAAS,EAAE;SACX,MAAM,QAAQ,OAAO,MAAM,MAAM,EAAE,OAAO,EAAE;SAC5C,iBAAiB,OAAO,YAAY,EAAE;QACxC;QARF,UAAA,CAUE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAAQ,OAAM;SAAI,UAAA,EAAE,kBAAkB;QAAU,CAAA,GAC/C,OAAO,KAAK,MACX,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAAsC,OAAO,EAAE;SAAK,UAAA,EAAE,QAAQ,EAAE;QAAW,GAA9D,EAAE,WAAW,MAAM,EAAE,EAAyC,CAC5E,CACK;OACH,CAAA,CAAA;;MACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,MAAK;QAAS,WAAU;QAAS,SAAS;QAAS,UAAU;QAAO,UAAA,EAAE,QAAQ;OAAU,CAAA,GAChG,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,MAAK;QAAS,WAAU;QAAwB,eAAe;SAAE,OAAY;QAAE;QAAG,UAAU;QACjG,UAAA,EAAE,cAAc;OACX,CAAA,CACL;;KACF;;GACF,CAAA;EAET;EAWA,SAAS,YAAY,EAAE,MAAM,GAAG,SAAS,WAAW,SAAS,iBAAuD;GAClH,MAAM,CAAC,MAAM,YAAA,GAAWH,MAAAA,SAAAA,CAAS,KAAK;GACtC,IAAI,SAAS,MAAM,OAAO;GAE1B,MAAM,MAAM,OAAO,OAA8C;IAC/D,QAAQ,IAAI;IACZ,IAAI;KACF,MAAM,GAAG;KACT,UAAU;IACZ,SAAS,OAAO;KACd,QAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;KAC9D,QAAQ,KAAK;IACf;GACF;GAEA,MAAM,SAAS,YAA2B;IACxC,IAAI,CAAC,OAAO,QAAQ,EAAE,eAAe,CAAC,GAAG;IACzC,MAAM,UAAU,IAAI,OAAO,KAAM,EAAE,CAAC;GACtC;GAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACG,KAAK,SAAS,KAAA,KAAa,KAAK,KAAK,UAAU,KAAK,KAAK,KAAK,QAAQ,UAAU,KAAK,WAAW,GACjG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,WAAW,gCAAgC,KAAK;OAAS,UAAA,YAAY,MAAM,CAAC;MAAQ,CAAA,CACvF;KACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MAAQ,MAAK;MAAS,WAAU;MAAkB,cAAY,EAAE,OAAO;MAAG,OAAO,EAAE,OAAO;MAAG,SAAS;MAAS,UAAA;KAAS,CAAA,CACrH;IAEL,CAAA,GAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,WAAU;SAAmB,UAAA,EAAE,aAAa;QAAO,CAAA;QACvD,KAAK,UAAU,KAAA,KAAa,KAAK,UAAU,MAC1C,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,WAAU;SAAmB,UAAA,EAAE,cAAc,EAAE,OAAO,KAAK,MAAM,CAAC;QAAO,CAAA;QAEhF,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,WAAU;SAAkB,UAAA,KAAK;QAAiB,CAAA;OACpD;;MAEJ,KAAK,SAAS,KAAA,KACb,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAAmB,UAAA,EAAE,MAAM;OAAO,CAAA,GACjD,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAU;QAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,WAAU;SAAmB,UAAA,KAAK,KAAK;QAAa,CAAA,GACzD,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;SAAI,WAAU;SACX,UAAA,KAAK,KAAK,OAAO,KAAK,OAAO,MAC5B,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;UAES;UACP,YAAY;UACN;UACH;UACY;SAChB,GANM,MAAM,EAMZ,CACF;QACC,CAAA,CACD;OACF,CAAA,CAAA;;MAGN,KAAK,SAAS,WAAW,SAAS,KACjC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAAmB,UAAA,EAAE,iBAAiB;OAAO,CAAA,GAC5D,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QACZ,UAAA,KAAK,SAAS,WAAW,KAAK,QAC7B,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;SAAkB,MAAK;SAAS,WAAU;SAAsB,eAAe,cAAc,GAAG;SAAhG,UAAA;UACG,EAAE,aAAa;UAAE;UAAI,IAAI,MAAM,GAAG,EAAE;SAC/B;QAFK,GAAA,GAEL,CACT;OACE,CAAA,CACF;;MAGN,KAAK,WAAW,WAAW,KAAK,UAAU,KAAA,KACzC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAAmB,UAAA,EAAE,OAAO;OAAO,CAAA,GAClD,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAAkB,UAAA,KAAK,MAAM;OAAa,CAAA,CACtD;;MAGN,KAAK,OAAO,gBAAgB,KAAA,KAC3B,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAAmB,UAAA,EAAE,YAAY;OAAO,CAAA,GACvD,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,WAAU;QAAf,UAAA;SAAiC,EAAE,aAAa;SAAE;SAAE,KAAK,MAAM,YAAY,MAAM,GAAG,EAAE;QAAO;OAC1F,CAAA,CAAA;;MAGP,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QACG,KAAK,WAAW,WACf,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAAQ,MAAK;SAAS,WAAU;SAAwB,UAAU;SAAM,eAAe;UAAE,UAAe,IAAI,MAAM,KAAM,EAAE,CAAC;SAAE;SAC1H,UAAA,EAAE,OAAO;QACJ,CAAA;SAER,KAAK,WAAW,aAAa,KAAK,WAAW,cAC7C,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAAQ,MAAK;SAAS,WAAU;SAAS,UAAU;SAAM,eAAe;UAAE,UAAe,IAAI,KAAK,KAAM,EAAE,CAAC;SAAE;SAC1G,UAAA,EAAE,MAAM;QACH,CAAA;QAET,KAAK,WAAW,aAAa,KAAK,WAAW,cAAc,KAAK,WAAW,aAC1E,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAAQ,MAAK;SAAS,WAAU;SAAuB,UAAU;SAAM,eAAe;UAAE,OAAY;SAAE;SACnG,UAAA,EAAE,QAAQ;QACL,CAAA;QAEV,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAAQ,MAAK;SAAS,WAAU;SAAS,SAAS;SAAU,UAAA,EAAE,QAAQ;QAAU,CAAA;OAC7E;;KACF;IACF,CAAA,CAAA;;EAET;;EAGA,SAAS,UAAU,EAAE,OAAO,YAAY,MAAM,GAAG,iBAMjC;GACd,MAAM,WAAW,KAAK,SAAS,OAAO,QAAQ,MAAM,EAAE,eAAe,UAAU;GAC/E,MAAM,YAAY,SACf,QAAQ,MAAM,EAAE,YAAY,KAAA,KAAa,EAAE,QAAQ,KAAK,MAAM,EAAE,CAAC,CACjE,KAAK,MAAM,EAAE,OAAiB;GACjC,MAAM,YAAY,eAAe,KAAK,gBAAgB,KAAK,WAAW;GAEtE,MAAM,cAAc,UAAU,SAAS;GACvC,MAAM,MAAM,mBACP,YAAY,2BAA2B,OACvC,cAAc,6BAA6B;GAChD,MAAM,YAAY,KAAK,MAAM,SAAS;GACtC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,MAAD;IAAI,WAAW;IAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAuB,UAAA,MAAM;KAAY,CAAA,GACzD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAU;MAAoB,UAAA,MAAM;KAAS,CAAA,CAChD;IACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAA+B,UAAA,EAAE,WAAW;OAAO,CAAA,GAClE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAAsB,UAAA,MAAM;OAAU,CAAA,CAClD;;MACJ,SAAS,SAAS,KACjB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAA+B,UAAA,EAAE,eAAe;OAAO,CAAA,GACtE,iBAAA,GAAA,kBAAA,IAAA,CAAC,eAAD;QAAyB;QAAqB;QAAuB;QAAe;QAAkB;OAAgB,CAAA,CACnH;;MAEN,UAAU,SAAS,KAClB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,WAAU;OAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QAA+B,UAAA,EAAE,iBAAiB;OAAO,CAAA,GACxE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,WAAU;QACZ,UAAA,UAAU,KAAK,GAAG,QACjB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAe,WAAU;SAAiC,UAAA;QAAO,GAAvD,GAAuD,CAClE;OACE,CAAA,CACF;;KAEJ;IACH,CAAA,CAAA;;EAER;;;;;;;EAQA,SAAS,cAAc,EAAE,UAAU,WAAW,YAAY,GAAG,iBAM7C;GACd,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,WAAU;IACZ,UAAA,SAAS,KAAK,MACb,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;KAA0B,MAAK;KAAS,WAAU;KAAsB,OAAO,EAAE;KAAW,eAAe,cAAc,EAAE,SAAS;KAApI,UAAA;MACG,EAAE,aAAa;MAAE;MAAI,cAAc,KAAK,GAAG,UAAU,KAAK,EAAE,YAAY,EAAE,GAAG,aAAa,EAAE,CAAC,MAAM,EAAE,UAAU,MAAM,GAAG,EAAE;KACrH;IAFK,GAAA,EAAE,SAEP,CACT;GACE,CAAA;EAET;;;;ECnrBA,MAAM,QAAQI;;EAGd,SAAS,aAAa,GAAG,OAA4C;GACnE,KAAK,MAAM,QAAQ,OAAO;IACxB,IAAI;IACJ,IAAI;KACF,YAAY,MAAM;IACpB,QAAQ;KAGN;IACF;IACA,IAAI,OAAO,cAAc,YAAY,OAAO;GAC9C;EAEF;;;;;;EAOA,SAAS,kBAAkB,EAAE,OAAO,IAAI,aAAqC;GAC3E,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IACE,OAAO;IACP,QAAQ;IACG;IACX,SAAQ;IACR,MAAK;IACL,OAAM;IACN,eAAY;IACZ,aAAa;IARf,UAAA;KAUE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,GAAE;MAAwI,QAAO;KAAgB,CAAA;KACvK,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,GAAE;MAAgB,QAAO;KAAgB,CAAA;KAC/C,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,GAAE;MAAkJ,QAAO;KAAgB,CAAA;KACjL,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,GAAE;MAAiB,QAAO;KAAgB,CAAA;IAC7C;;EAET;;;;;;;;;;;;;EAcA,MAAa,gBACX,aACE,+BACA,8BACA,wBACA,wBACF,KAAK;;;;;;;;;;EC7DP,SAAgB,gBAAgB,EAAE,QAAuC;GACvE,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,eAAD,EAAqB,KAAO,CAAA;EACrC;;EAOA,MAAM,iBAAsC;GAC1C,oBAAoB;GACpB,iBAAiB;GACjB,oBAAoB;GACpB,2BAA2B;GAC3B,kBAAkB;GAClB,YAAY;GACZ,eAAe;GACf,sBAAsB;GACtB,aAAa;EACf;;EAGA,MAAM,eAAe;GACnB,YAAY;IACV,OAAO;IACP,UAAU;IACV,QAAQ;IACR,QAAQ;GACV;GACA,OAAO;IACL,OAAO;IACP,UAAU;IACV,QAAQ;IACR,QAAQ;GACV;EACF;;EAiBA,SAAS,qBAAqB,EAAE,MAAM,UAAU,QAAQ,SAAS,SAAS,IAAI,UAAU,eAAuD;GAC7I,MAAM,SAAS,aAAa;GAC5B,MAAM,QAAQ,SAAS,OAAO;GAC9B,MAAM,WAAW,SAAS,OAAO;GACjC,MAAM,SAAS,SAAS,OAAO;GAC/B,MAAM,SAAS,SAAS,OAAO;GAC/B,MAAM,YAAY,UAAU,MAAM,aAAa,KAAK,GAAG,SAAS,GAAG,UAAU;GAC7E,MAAM,gBAAgB,cAAc,KAAM,QAAQ,UAAU,EAAE,WAAW,CAAC,IAAK,CAAC;GAEhF,MAAM,eAAe,OAAqB;IAExC,MAAM,eADQ,OAAO,MAAM,MAAM,EAAE,OAAO,EACjB,CAAC,EAAE,YAAY;IACxC,SAAc;MAAG,OAAO,QAAQ;MAAK,OAAO,WAAW;IAAa,CAAiC;IACrG,IAAI,OAAO,MAAM,iBAAiB,IAAI,YAAY,cAAc,EAAE;GACpE;GAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAU;MAA2B,UAAA,GAAG,SAAS,eAAe,wBAAwB,oBAAoB;KAAO,CAAA;KACxH,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;MAAO,WAAU;MAAjB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,GAAG,OAAO,EAAQ,CAAA,GACzB,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;OAAQ,WAAU;OAAW,OAAO;OAAO,WAAW,MAAM,YAAY,EAAE,OAAO,KAAK;OAAtF,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,OAAM;QAAI,UAAA,GAAG,eAAe;OAAU,CAAA,GAC7C,OAAO,KAAK,MACX,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAsC,OAAO,EAAE;QAAK,UAAA,EAAE,QAAQ,EAAE;OAAW,GAA9D,EAAE,WAAW,MAAM,EAAE,EAAyC,CAC5E,CACK;MACH,CAAA,CAAA;;KACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;MAAO,WAAU;MAAjB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,GAAG,iBAAiB,EAAQ,CAAA,GACnC,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;OACE,WAAU;OACV,OAAO;OACP,UAAU,UAAU;OACpB,WAAW,MAAM,KAAK,SAAS,GAAG,OAAO,SAAS,EAAE,OAAO,MAAM,CAAiC;OAJpG,UAAA,CAME,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,OAAM;QAAI,UAAA,GAAG,eAAe;OAAU,CAAA,GAC7C,cAAc,KAAK,MAClB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAmB,OAAO,EAAE;QAAK,UAAA,EAAE;OAAa,GAAnC,EAAE,EAAiC,CACjD,CACK;MACH,CAAA,CAAA;;KACP,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;MAAO,WAAU;MAAjB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,GAAG,aAAa,EAAQ,CAAA,GAC/B,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;OACE,WAAU;OACV,OAAO;OACP,WAAW,MAAM,KAAK,SAAS,GAAG,OAAO,SAAS,EAAE,OAAO,MAAM,CAAiC;OAHpG,UAAA,CAKE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAQ,OAAM;QAAI,UAAA,GAAG,eAAe;OAAU,CAAA,GAC7C,QAAQ,KAAK,MACZ,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QAAmB,OAAO,EAAE;QAAK,UAAA,EAAE,QAAQ,EAAE;OAAW,GAA3C,EAAE,EAAyC,CACzD,CACK;MACH,CAAA,CAAA;;IACJ;;EAET;;EAGA,SAAgB,sBAAsB,EAAE,KAAwC;GAC9E,MAAM,KAAK,OAAO,QAAgB;GAClC,MAAM,CAAC,UAAU,gBAAA,GAAeC,MAAAA,SAAAA,CAA8B,cAAc;GAC5E,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAwB,CAAC,CAAC;GACtD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAyB,CAAC,CAAC;GACzD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAA2C,CAAC,CAAC;GAC3E,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAS,KAAK;GAExC,MAAM,UAAA,GAASC,MAAAA,YAAAA,CAAY,OAAO,UAAuD;IACvF,IAAI;KACF,MAAM,OAAO,MAAM,IAAI,YAAY,KAAK;KACxC,YAAY,IAAI;KAChB,SAAS,IAAI;KACb,OAAO,iBAAiB,SAAS,KAAK,GAAG,IAAI;IAC/C,QAAQ,CAER;GACF,GAAG,CAAC,CAAC;GAGL,MAAM,eAAA,GAAcC,MAAAA,OAAAA,CAAO,QAAQ;GACnC,YAAY,UAAU;GACtB,MAAM,aAAA,GAAYA,MAAAA,OAAAA,CAAO,MAAM;GAC/B,UAAU,UAAU;GAEpB,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,QAAQ;IACZ,IAAI,YAAY,CAAC,CACd,MAAM,MAAM;KAAE,IAAI,OAAO,YAAY,CAAC;IAAE,CAAC,CAAC,CAC1C,YAAY,KAAA,CAAS;IACxB,IAAI,OAAO,CAAC,CACT,MAAM,SAAS;KAAE,IAAI,OAAO,UAAU,IAAI;IAAE,CAAC,CAAC,CAC9C,YAAY,KAAA,CAAS;IACxB,IAAI,QAAQ,CAAC,CACV,MAAM,SAAS;KAAE,IAAI,OAAO,WAAW,IAAI;IAAE,CAAC,CAAC,CAC/C,YAAY,KAAA,CAAS;IACxB,aAAa;KAAE,QAAQ;IAAM;GAC/B,GAAG,CAAC,CAAC;;GAGL,MAAM,iBAAA,GAAgBA,MAAAA,OAAAA,iBAAoB,IAAI,IAAI,CAAC;;;;;;GAOnD,MAAM,eAAA,GAAcD,MAAAA,YAAAA,EAAa,UAAkB,UAAwB;IACzE,MAAM,MAAM,GAAG,SAAS,GAAG;IAC3B,IAAI,cAAc,QAAQ,IAAI,GAAG,GAAG;IACpC,cAAc,QAAQ,IAAI,GAAG;IAC7B,IAAI,iBAAiB,UAAU,KAAK,CAAC,CAClC,MAAM,SAAS,YAAY,UAAU;KAAE,GAAG;MAAO,MAAM;IAAK,EAAE,CAAC,CAAC,CAChE,YAAY,YAAY,UAAU;KAAE,GAAG;MAAO,MAAM;MAAE,SAAS,CAAC;MAAG,QAAQ;KAAK;IAAE,EAAE,CAAC;GAC1F,GAAG,CAAC,CAAC;GAKL,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,KAAK,MAAM,QAAQ,CAAC,cAAc,OAAO,GAAY;KACnD,MAAM,SAAS,aAAa;KAC5B,MAAM,QAAQ,SAAS,OAAO;KAC9B,MAAM,WAAW,SAAS,OAAO;KACjC,IAAI,UAAU,MAAM,aAAa,IAAI,YAAY,UAAU,KAAK;IAClE;GACF,GAAG;IAAC,SAAS;IAAiB,SAAS;IAAoB,SAAS;IAAY,SAAS;IAAe;GAAW,CAAC;GAMpH,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,IAAI,YAAY;IACtB,KAAK,MAAM,QAAQ,CAAC,cAAc,OAAO,GAAY;KACnD,MAAM,SAAS,aAAa;KAC5B,MAAM,QAAQ,EAAE,OAAO;KACvB,MAAM,WAAW,EAAE,OAAO;KAC1B,MAAM,SAAS,EAAE,OAAO;KACxB,IAAI,WAAW,MAAM,UAAU,MAAM,aAAa,IAAI;KACtD,MAAM,OAAO,QAAQ,GAAG,SAAS,GAAG;KACpC,IAAI,SAAS,KAAA,KAAa,KAAK,WAAW,MAAM;KAChD,IAAI,CAAC,KAAK,QAAQ,MAAM,MAAM,EAAE,OAAO,MAAM,GAC3C,UAAe,QAAQ,GAAG,OAAO,SAAS,GAAG,CAAiC;IAElF;GACF,GAAG,CAAC,OAAO,CAAC;GAEZ,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;KAAQ,WAAU;KAAlB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;MAAI,WAAU;MAAiB,UAAA,GAAG,eAAe;KAAM,CAAA,GACvD,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD;MAAG,WAAU;MAAe,UAAA,GAAG,YAAY;KAAK,CAAA,CAC1C;IACR,CAAA,GAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,WAAU;KAAf,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,SAAD;OAAO,WAAU;OAAjB,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,GAAG,oBAAoB,EAAQ,CAAA,GACtC,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;QACE,WAAU;QACV,MAAK;QACL,KAAK;QACL,MAAM;QACN,OAAO,SAAS;QAChB,WAAW,MAAM;SACf,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,CAAC;SACjE,aAAa,OAAO;UAAE,GAAG;UAAG,oBAAoB;SAAM,EAAE;SACxD,OAAY,EAAE,oBAAoB,MAAM,CAAC;QAC3C;OACD,CAAA,CACI;;MACP,iBAAA,GAAA,kBAAA,IAAA,CAAC,sBAAD;OACE,MAAK;OACK;OACF;OACC;OACA;OACL;OACJ,UAAU;OACG;MACd,CAAA;MACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,sBAAD;OACE,MAAK;OACK;OACF;OACC;OACA;OACL;OACJ,UAAU;OACG;MACd,CAAA;MACA,SAAS,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,WAAU;OAAqB,UAAA,GAAG,OAAO;MAAO,CAAA;KAC5D;IACF,CAAA,CAAA;;EAET;;;;ECrPA,MAAM,sBAAsB;;EAG5B,MAAa,SAAS;GAAC;GAAS;GAAY;GAAc;EAAQ;;EAGlE,SAAgB,MAAM,KAA0B;GAC9C,UAAU,GAAG;GAEb,sBAAsB,GAAG;GAGzB,MAAM,IAAI,IAAI,OAAO,KAAK,SAAS;GACnC,IAAI,aAAa;IACf,MAAM,MAAM,IAAI,OAAO,SAAS,WAAW;KAAE;KAAI;IAAG,CAAC;IACrD,aAAa,IAAI;GACnB,GAAG,2BAA2B;GAQ9B,IAAI,MAAM,OAAO,cACf,IAAI,MAAM,SAAS;IACjB,MAAM;IACN,KAAK;IACL,QAAQ;GACV,GAAY,SAAkB,CAChC;GAIA,IAAI,MAAM,OAAO,2BACf,IAAI,MAAM,SAAS;IACjB,MAAM;IACN,IAAI;IACJ,OAAO;IACP,aAAa,EAAE,QAAQ;IACvB,QAAQ;GACV,GAAG,eAAwB,CAC7B;GAMA,IAAI,MAAM,OAAO,0BACf,IAAI,MAAM,SAAS;IACjB,MAAM;IACN,IAAI;IACJ,OAAO;IACP,aAAa,EAAE,eAAe;IAC9B,QAAQ;GACV,GAAG,qBAA8B,CACnC;EACF"}
|
|
1
|
+
{"version":3,"file":"client.js","names":["useState","useCallback","useMemo","useRef","primitives","useState","useCallback","useRef"],"sources":["../../../src/shared/lanes.ts","../../../src/client/api.ts","../../../src/client/kanban-state.ts","../../../src/client/locales.ts","../../../src/client/board.tsx","../../../src/client/icons.tsx","../../../src/client/sections.tsx","../../../src/client/index.tsx"],"sourcesContent":["/**\n * Lane mapping and drag rules (shared by the board UI and tests).\n */\nimport type { KanbanCard, Lane } from './card.js'\n\n/** Which lane a card renders in, based on status + error stage. */\nexport function laneOf(card: KanbanCard): Lane {\n if (card.status === 'error') {\n if (card.error?.stage !== undefined) return card.error.stage\n const kind = card.error?.kind\n if (kind === 'phase_failed' || kind === 'no_base_branch' || kind === 'worktree_failed') return 'running'\n if (kind === 'merge_failed') return 'completed'\n return 'demand'\n }\n switch (card.status) {\n case 'draft':\n case 'refining':\n case 'planned':\n return 'demand'\n case 'queued':\n return 'queue'\n case 'running':\n return 'running'\n case 'completed':\n case 'merging':\n return 'completed'\n case 'merged':\n return 'merged'\n }\n}\n\n/** Lanes a drop onto `to` may come from. */\nexport const DROP_RULES: Record<Lane, Lane[]> = {\n demand: ['queue'],\n queue: ['demand', 'running'],\n running: [],\n completed: [],\n merged: [],\n}\n\nexport function canDrop(from: Lane, to: Lane): boolean {\n return DROP_RULES[to].includes(from)\n}\n","import type { KanbanCard, KanbanSettingsShape, Lane, ModelOption } from '../shared/card'\n\nexport class KanbanApiError extends Error {\n constructor(readonly code: string, message: string) {\n super(message)\n }\n}\n\nasync function call<T>(method: string, payload: Record<string, unknown> = {}): Promise<T> {\n let response: Response\n try {\n response = await fetch(`/plugins/@fonlan/dsh-task-kanban/api/${method}`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n } catch (error) {\n throw new KanbanApiError('network', error instanceof Error ? error.message : String(error))\n }\n const parsed: { ok?: boolean; value?: unknown; error?: { code?: string; message?: string } } | null\n = await response.json().catch(() => null)\n if (!response.ok || parsed === null || parsed.ok !== true || parsed.value === undefined) {\n throw new KanbanApiError(\n parsed?.error?.code ?? 'http',\n parsed?.error?.message ?? `HTTP ${response.status}`,\n )\n }\n return parsed.value as T\n}\n\n/** One row of the host `skills/list` catalog (same shape DSH's chat autocomplete uses). */\nexport interface SkillOption {\n name: string\n description: string\n whenToUse?: string\n modelInvocable: boolean\n}\n\n/**\n * Call the host gateway's `skills/list` unary RPC over the same-origin HTTP\n * bridge (the exact wire shape DSH's chat input uses for its /-autocomplete).\n *\n * DSH ≥0.1.5 addresses Remote endpoints as the canonical `<namespace>/<method>`\n * pair — `/api/skills/list` with the method `skills/list` — and carries named\n * wire arguments inside a single `args` field, so the request body is\n * `{ args: { request: { sessionId } } }` (the descriptor's parameter wire name\n * is `request`). The older dotted `skill.list` endpoint no longer exists and\n * answers HTTP 404.\n *\n * The requested `sessionId` scopes the catalog to that session's agent\n * preset, which is where the filesystem skill roots are registered.\n */\nexport async function gatewaySkillList(sessionId: string): Promise<SkillOption[]> {\n let response: Response\n try {\n response = await fetch('/api/skills/list', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n type: 'client-request',\n rpcId: `task-kanban-${Math.random().toString(36).slice(2)}`,\n method: 'skills/list',\n payload: { args: { request: { sessionId } } },\n }),\n })\n } catch (error) {\n throw new KanbanApiError('network', error instanceof Error ? error.message : String(error))\n }\n const parsed: { type?: string; result?: { ok?: boolean; value?: { skills?: SkillOption[] }; error?: { code?: string; message?: string } } } | null\n = await response.json().catch(() => null)\n if (!response.ok || parsed === null || parsed.type !== 'server-response' || parsed.result?.ok !== true) {\n throw new KanbanApiError(\n parsed?.result?.error?.code ?? 'http',\n parsed?.result?.error?.message ?? `HTTP ${response.status}`,\n )\n }\n return parsed.result.value?.skills ?? []\n}\n\n/** One row of the host `agentPresets.list()` roster. */\nexport interface PresetOption {\n id: string\n name?: string\n description?: string\n}\n\n/** Reasoning efforts of one provider/model route (`reasoning.options`). */\nexport interface ReasoningOptions {\n efforts: Array<{ id: string; name: string; description?: string }>\n defaultEffort?: string\n /** Client-side marker: the fetch failed; the stored effort must be kept. */\n failed?: boolean\n}\n\nexport const api = {\n list: (workspacePath: string) => call<KanbanCard[]>('list', { workspacePath }),\n create: (workspacePath: string, requirement: string, model: string, provider?: string, skill?: string) =>\n call<KanbanCard>('create', { workspacePath, requirement, model, provider, ...(skill !== undefined && skill !== '' ? { skill } : {}) }),\n move: (cardId: string, toLane: Lane) => call<{ ok: boolean; message?: string }>('move', { cardId, toLane }),\n stop: (cardId: string) => call<{ ok: boolean; message?: string }>('stop', { cardId }),\n retry: (cardId: string) => call<{ ok: boolean; message?: string }>('retry', { cardId }),\n remove: (cardId: string) => call<{ ok: boolean; message?: string }>('remove', { cardId }),\n settingsGet: () => call<KanbanSettingsShape>('settings.get'),\n settingsSet: (patch: Partial<KanbanSettingsShape>) => call<KanbanSettingsShape>('settings.set', patch),\n models: () => call<ModelOption[]>('models.list'),\n presets: () => call<PresetOption[]>('presets.list'),\n reasoningOptions: (provider: string, model: string) =>\n call<ReasoningOptions>('reasoning.options', { provider, model }),\n}\n","import type { Context } from '@deepseek-ai/cordis'\ntype ClientContext = Context\n\n/**\n * Panel id shared by this plugin's main-panel key (`main`) and its sidebar\n * entry (`sidebar.panellist`): the shell selects the panel by this id, so the\n * two registrations must agree.\n */\nexport const KANBAN_PANEL_ID = 'task-kanban'\n\n/**\n * Structural view of the client shell's panel-navigation face (`ctx.layout`).\n * Declared locally (no value import) so the plugin keeps working on a runtime\n * whose layout types differ.\n */\ninterface LayoutLike {\n selectPanel(panelId: string | null): void\n}\n\n/**\n * Structural view of the client shell's workspace-navigation face\n * (`ctx.uiWorkspace`, dsh-client-ui-workspace). Declared locally (no value\n * import) so the plugin keeps working on a runtime whose workspace types differ.\n */\ninterface UiWorkspaceLike {\n /** Select a Session (id, or a direct-parent subagent address) and show it. */\n openSession(target: string): void\n}\n\n/**\n * HMR-safe owner context, keyed on the global symbol registry.\n *\n * The client-hmr hot swap re-evaluates this module (a fresh `lib/client.js`\n * bundle) WITHOUT reloading the page; a module-level cell would then be\n * duplicated per evaluation and an old copy's helper would read a stale owner.\n * `Symbol.for` makes every bundle copy share one cell.\n */\nconst STATE_KEY = Symbol.for('@fonlan/dsh-task-kanban/state')\n\ninterface KanbanState {\n client: ClientContext | null\n}\n\nfunction getState(): KanbanState {\n const g = globalThis as unknown as Record<symbol, KanbanState | undefined>\n let state = g[STATE_KEY]\n if (state === undefined) {\n state = { client: null }\n g[STATE_KEY] = state\n }\n return state\n}\n\nexport function setClient(ctx: ClientContext | null): void {\n getState().client = ctx\n}\nexport function getClient(): ClientContext | null {\n return getState().client\n}\n\n/** The shell's panel face, when this runtime provides it. */\nfunction layoutOf(ctx: ClientContext | null): LayoutLike | null {\n if (ctx === null) return null\n try {\n const layout = (ctx as unknown as { layout?: LayoutLike }).layout\n return typeof layout?.selectPanel === 'function' ? layout : null\n } catch {\n return null\n }\n}\n\n/** Select the kanban panel. The sidebar's own entry does this; kept for in-app callers. */\nexport function showKanbanPanel(ctx: ClientContext | null = getClient()): void {\n try {\n layoutOf(ctx)?.selectPanel(KANBAN_PANEL_ID)\n } catch (error) {\n console.error('[@fonlan/dsh-task-kanban] cannot select the kanban panel:', error)\n }\n}\n\n/** Hand the center column back to the Conversation. */\nexport function leaveKanbanPanel(ctx: ClientContext | null = getClient()): void {\n try {\n layoutOf(ctx)?.selectPanel(null)\n } catch (error) {\n console.error('[@fonlan/dsh-task-kanban] cannot leave the kanban panel:', error)\n }\n}\n\n/**\n * Open one session — or a durable direct-parent subagent address — in the\n * center column, leaving the kanban panel.\n *\n * Navigation is a view-owner concern, not a session-controller one: `ctx.sessions`\n * (`ISessions`) exposes retention (`retain`/`using`/`retainInfo`), creation,\n * fork, search and catalog reads, and has no `open`/`openSubagent` at all\n * (\"navigation belongs to view owners\"). The view owner the shell itself uses is\n * `ctx.uiWorkspace.openSession(target)` (dsh-client-ui-workspace): it retains the\n * session AND reveals the Conversation panel as ONE navigation action. Routing\n * every open through it therefore replaces the old wrapper around\n * `sessions.open`/`openSubagent`, which existed only to hand the center column\n * back when a sidebar click selected a session while the board was showing.\n *\n * @param target - session id (or subagent address) to show.\n * @returns whether the runtime provided the navigation face; `false` means the\n * session could not be shown (a client without ui-workspace).\n */\nexport function openSessionView(target: string, ctx: ClientContext | null = getClient()): boolean {\n if (ctx === null) return false\n let uiWorkspace: UiWorkspaceLike | undefined\n try {\n uiWorkspace = (ctx as unknown as { uiWorkspace?: UiWorkspaceLike }).uiWorkspace\n } catch {\n return false\n }\n if (typeof uiWorkspace?.openSession !== 'function') return false\n try {\n uiWorkspace.openSession(target)\n return true\n } catch (error) {\n console.error('[@fonlan/dsh-task-kanban] cannot open the session view:', error)\n return false\n }\n}\n","// Locale namespace = the plugin's cordis id (kebab). DSH flat namespace keys\n// (settings + locale) use the internal id `task-kanban`; the scoped npm package\n// name is reserved for package-bound identifiers (exported `name`, module-loader\n// id, the /plugins/<pkg>/… route base).\nexport const LOCALE_NS = 'task-kanban'\n\nexport const zh = {\n kanban: '任务看板',\n openBoard: '打开任务看板',\n closeBoard: '返回对话',\n newTask: '新建任务',\n requirement: '需求',\n project: '所属项目',\n model: '模型',\n cancel: '取消',\n close: '关闭',\n addAndRefine: '添加并细化',\n laneDemand: '需求',\n laneQueue: '队列',\n laneRunning: '运行中',\n laneCompleted: '已完成',\n laneMerged: '已合并',\n statusDraft: '未细化',\n statusRefining: '细化中',\n statusPlanned: '已规划',\n statusQueued: '排队中',\n statusRunning: '实现中',\n statusCompleted: '已完成',\n statusMerging: '合并中',\n statusMerged: '已合并',\n statusError: '出错',\n phaseProgress: '{current}/{total}',\n retry: '重试',\n stop: '停止',\n delete: '删除',\n deleteConfirm: '确定删除该任务?其 worktree 会被清理。',\n openSession: '打开会话',\n plan: '实现方案',\n summary: '摘要',\n phases: '阶段',\n phaseGoal: '目标',\n phaseSessions: '会话',\n phaseConclusion: '结论',\n phaseNum: 'phase {n}',\n error: '错误',\n requirementLabel: '需求文本',\n requirementPlaceholder: '描述你要实现的需求……',\n skillGestureHint: '开头输入 /skill-name 可指定细化时使用的 Skill(如 /grill-me)',\n skillHint: '细化时将使用 Skill「{skill}」,并可与该会话交流打磨需求',\n skillMenuUserOnly: '仅用户可调',\n skillBadge: '细化 Skill:/{skill}',\n modelPlaceholder: '(使用默认模型)',\n noWorkspace: '没有可用工作区,请先在侧边栏添加',\n refresh: '刷新',\n settingsTitle: '任务看板',\n sectionSub: '全局并行 worker 数与各会话类型的默认模型、推理强度、Agent 预设',\n maxParallelWorkers: '全局并行 worker 数',\n settingsRefineGroup: '细化需求会话默认',\n settingsPhaseGroup: 'Phase 实现会话默认',\n reasoningEffort: '推理强度',\n agentPreset: 'Agent 预设',\n defaultOption: '(默认)',\n saved: '已保存',\n loadError: '加载任务失败',\n noPlan: '该卡片还没有实现计划,请先点击\"添加并细化\"',\n dragNoPlan: '还没有实现计划的卡片不能进入队列',\n actionFailed: '操作失败:{message}',\n sessionNotListed: '该会话不在当前列表,可能来自其他工作区',\n interrupted: '已中断',\n noBaseBranch: '项目没有 main/master 分支',\n worktreeFailed: 'worktree 创建失败',\n refineFailed: '细化失败',\n phaseFailed: '阶段执行失败',\n mergeFailed: '合并失败',\n createFailed: '创建失败',\n refinementLabel: '需求细化',\n mergeLabel: '合并',\n commitLabel: '提交',\n}\n\nexport const en = {\n kanban: 'Task Kanban',\n openBoard: 'Open Task Kanban',\n closeBoard: 'Back to chat',\n newTask: 'New Task',\n requirement: 'Requirement',\n project: 'Project',\n model: 'Model',\n cancel: 'Cancel',\n close: 'Close',\n addAndRefine: 'Add & Refine',\n laneDemand: 'Demands',\n laneQueue: 'Queue',\n laneRunning: 'Running',\n laneCompleted: 'Completed',\n laneMerged: 'Merged',\n statusDraft: 'Unrefined',\n statusRefining: 'Refining',\n statusPlanned: 'Planned',\n statusQueued: 'Queued',\n statusRunning: 'Running',\n statusCompleted: 'Completed',\n statusMerging: 'Merging',\n statusMerged: 'Merged',\n statusError: 'Error',\n phaseProgress: '{current}/{total}',\n retry: 'Retry',\n stop: 'Stop',\n delete: 'Delete',\n deleteConfirm: 'Delete this task? Its worktree will be cleaned up.',\n openSession: 'Open session',\n plan: 'Plan',\n summary: 'Summary',\n phases: 'Phases',\n phaseGoal: 'Goal',\n phaseSessions: 'Sessions',\n phaseConclusion: 'Conclusion',\n phaseNum: 'Phase {n}',\n error: 'Error',\n requirementLabel: 'Requirement',\n requirementPlaceholder: 'Describe what you want to build…',\n skillGestureHint: 'Start with /skill-name to choose the refinement skill (e.g. /grill-me)',\n skillHint: 'Refinement will use Skill “{skill}” — talk to this session to refine the requirement',\n skillMenuUserOnly: 'User-only',\n skillBadge: 'Refine skill: /{skill}',\n modelPlaceholder: '(use default model)',\n noWorkspace: 'No workspaces available — add one in the sidebar first',\n refresh: 'Refresh',\n settingsTitle: 'Task Kanban',\n sectionSub: 'Global parallel workers and per-session-type defaults for model, reasoning effort, and agent preset',\n maxParallelWorkers: 'Global parallel workers',\n settingsRefineGroup: 'Refinement session defaults',\n settingsPhaseGroup: 'Phase implementation defaults',\n reasoningEffort: 'Reasoning effort',\n agentPreset: 'Agent preset',\n defaultOption: '(default)',\n saved: 'Saved',\n loadError: 'Failed to load tasks',\n noPlan: 'This card has no plan yet — click \"Add & Refine\" first',\n dragNoPlan: 'Cards without a plan cannot enter the queue',\n actionFailed: 'Action failed: {message}',\n sessionNotListed: 'Session is not listed here; it may belong to another workspace',\n interrupted: 'Interrupted',\n noBaseBranch: 'Project has no main/master branch',\n worktreeFailed: 'Worktree creation failed',\n refineFailed: 'Refinement failed',\n phaseFailed: 'Phase failed',\n mergeFailed: 'Merge failed',\n createFailed: 'Creation failed',\n refinementLabel: 'Refinement',\n mergeLabel: 'Merge',\n commitLabel: 'Commit',\n}","import { useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport type { KanbanCard, Lane, PhaseAttempt, PlanPhase } from '../shared/card'\nimport { DROP_RULES, laneOf } from '../shared/lanes'\nimport { api, gatewaySkillList, type SkillOption } from './api'\nimport { openSessionView } from './kanban-state'\nimport { en, zh } from './locales'\nimport './board.css'\n\ninterface BoardProps {\n t?: (key: string, params?: Record<string, unknown>) => string\n useWorkspaces?: (selector: (state: unknown) => unknown) => unknown\n useSessions?: (selector: (state: unknown) => unknown) => unknown\n}\n\n/** Session-list snapshot shape the standard `useSessions` feed carries. */\ninterface SessionListStateLike {\n current?: string\n /** Listed session ids (the feed's own order). */\n ids?: string[]\n}\n\nfunction firstLine(text: string): string {\n return text.split('\\n').find((l) => l.trim() !== '')?.trim() ?? ''\n}\n\n/** The leading `/skill-name` gesture at the start of the requirement input. */\nconst SKILL_GESTURE = /^\\s*\\/([a-z0-9]+(?:-[a-z0-9]+)*)(?=\\s|$)/\n\n/** Extract a leading `/skill-name` token plus the cleaned remainder. */\nfunction parseSkillGesture(raw: string): { skill?: string; requirement: string } {\n const m = SKILL_GESTURE.exec(raw)\n if (m === null) return { requirement: raw.trim() }\n return { skill: m[1], requirement: raw.slice(m[0].length).trim() }\n}\n\nfunction statusLabel(card: KanbanCard, t: (key: string) => string): string {\n if (card.status === 'error') {\n switch (card.error?.kind) {\n case 'interrupted': return t('interrupted')\n case 'no_base_branch': return t('noBaseBranch')\n case 'worktree_failed': return t('worktreeFailed')\n case 'refine_failed': return t('refineFailed')\n case 'phase_failed': return t('phaseFailed')\n case 'merge_failed': return t('mergeFailed')\n default: return t('createFailed')\n }\n }\n switch (card.status) {\n case 'draft': return t('statusDraft')\n case 'refining': return t('statusRefining')\n case 'planned': return t('statusPlanned')\n case 'queued': return t('statusQueued')\n case 'running': return t('statusRunning')\n case 'completed': return t('statusCompleted')\n case 'merging': return t('statusMerging')\n case 'merged': return t('statusMerged')\n }\n}\n\ninterface WorkspaceRow {\n workspaceId: string\n path: string\n title: string\n}\n\nconst LANE_ORDER: Array<{ key: Lane; labelKey: string }> = [\n { key: 'demand', labelKey: 'laneDemand' },\n { key: 'queue', labelKey: 'laneQueue' },\n { key: 'running', labelKey: 'laneRunning' },\n { key: 'completed', labelKey: 'laneCompleted' },\n { key: 'merged', labelKey: 'laneMerged' },\n]\n\nconst WS_STORAGE_KEY = '@fonlan/dsh-task-kanban:workspace'\n\nexport function BoardRoot(props: BoardProps): JSX.Element {\n const t = props.t ?? ((key: string, params?: Record<string, unknown>) => {\n const dict = (zh as Record<string, string>)[key] ?? (en as Record<string, string>)[key]\n if (dict === undefined || params === undefined) return dict ?? key\n return dict.replace(/\\{(\\w+)\\}/g, (m, name: string) => (params[name] as string) ?? m)\n })\n const useWorkspaces = props.useWorkspaces\n const wsState = useWorkspaces !== undefined ? (useWorkspaces((s: unknown) => s) as { items?: WorkspaceRow[] } | undefined) : undefined\n const items: WorkspaceRow[] = wsState?.items ?? []\n const useSessions = props.useSessions\n const sessionState = useSessions !== undefined ? (useSessions((s: unknown) => s) as SessionListStateLike | undefined) : undefined\n // The skill catalog is addressed by session (`skills/list`), so the /-menu\n // needs one. With no selection (fresh browser, board opened straight from the\n // footer) fall back to the most recent listed session: any session resolves a\n // preset scope, whereas no session silently leaves the menu empty.\n const currentSessionId = sessionState?.current ?? sessionState?.ids?.[0]\n\n const [selectedPath, setSelectedPath] = useState<string>(() => {\n try {\n return localStorage.getItem(WS_STORAGE_KEY) ?? ''\n } catch {\n return ''\n }\n })\n const [cards, setCards] = useState<KanbanCard[]>([])\n const [toast, setToast] = useState<string | null>(null)\n const [detailId, setDetailId] = useState<string | null>(null)\n const [newTaskOpen, setNewTaskOpen] = useState(false)\n\n useEffect(() => {\n if (selectedPath === '' && items.length > 0) {\n setSelectedPath(items[0].path)\n }\n }, [selectedPath, items])\n\n const showToast = useCallback((message: string) => {\n setToast(message)\n window.setTimeout(() => setToast(null), 3500)\n }, [])\n\n const refresh = useCallback(async () => {\n if (selectedPath === '') return\n try {\n setCards(await api.list(selectedPath))\n } catch (error) {\n showToast(error instanceof Error ? error.message : String(error))\n }\n }, [selectedPath, showToast])\n\n useEffect(() => {\n void refresh()\n const timer = window.setInterval(() => { void refresh() }, 2500)\n return () => window.clearInterval(timer)\n }, [refresh])\n\n const byLane = useMemo(() => {\n const map: Record<Lane, KanbanCard[]> = { demand: [], queue: [], running: [], completed: [], merged: [] }\n for (const card of cards) map[laneOf(card)].push(card)\n for (const lane of LANE_ORDER) map[lane.key].sort((a, b) => a.createdAt - b.createdAt)\n return map\n }, [cards])\n\n const handleDrop = useCallback(async (target: Lane, event: React.DragEvent) => {\n event.preventDefault()\n const cardId = event.dataTransfer.getData('text/plain')\n if (cardId === '') return\n const card = cards.find((c) => c.id === cardId)\n if (card === undefined) return\n if (target === 'queue' && card.status !== 'running' && card.plan === undefined) {\n showToast(t('dragNoPlan'))\n return\n }\n try {\n const result = await api.move(cardId, target)\n if (!result.ok) {\n showToast(t('actionFailed', { message: result.message ?? '' }))\n }\n void refresh()\n } catch (error) {\n showToast(error instanceof Error ? error.message : String(error))\n }\n }, [cards, refresh, showToast, t])\n\n const openSession = useCallback((sessionId: string) => {\n // The shell's view owner retains the session and reveals the Conversation\n // in one action, so the board itself needs no panel teardown; a runtime\n // without that face cannot show the session at all.\n if (!openSessionView(sessionId)) showToast(t('sessionNotListed'))\n }, [showToast, t])\n\n if (items.length === 0) {\n return (\n <div className=\"kb-board kb-empty\">\n <div className=\"kb-empty-text\">{t('noWorkspace')}</div>\n </div>\n )\n }\n\n return (\n <div className=\"kb-board\">\n <div className=\"kb-board-header\">\n <div className=\"kb-board-title\">\n <select\n className=\"kb-ws-select\"\n value={selectedPath}\n onChange={(e) => {\n setSelectedPath(e.target.value)\n try {\n localStorage.setItem(WS_STORAGE_KEY, e.target.value)\n } catch {\n // storage unavailable\n }\n }}\n >\n {items.map((w) => (\n <option key={w.workspaceId} value={w.path}>{w.title}</option>\n ))}\n </select>\n <button type=\"button\" className=\"kb-btn kb-btn-primary\" onClick={() => setNewTaskOpen(true)}>{t('newTask')}</button>\n </div>\n </div>\n\n <div className=\"kb-lanes\">\n {LANE_ORDER.map((lane) => (\n <div\n key={lane.key}\n className={'kb-lane' + (DROP_RULES[lane.key].length > 0 ? ' kb-lane-drop' : '')}\n onDragOver={(e) => {\n if (DROP_RULES[lane.key].length > 0) e.preventDefault()\n }}\n onDrop={(e) => { void handleDrop(lane.key, e) }}\n >\n <div className=\"kb-lane-head\">\n <span className=\"kb-lane-title\">{t(lane.labelKey)}</span>\n <span className=\"kb-lane-count\">{byLane[lane.key].length}</span>\n </div>\n <div className=\"kb-lane-body\">\n {byLane[lane.key].map((card) => (\n <CardView\n key={card.id}\n card={card}\n t={t}\n onToggle={() => setDetailId(detailId === card.id ? null : card.id)}\n />\n ))}\n </div>\n </div>\n ))}\n </div>\n\n {toast !== null && <div className=\"kb-toast\">{toast}</div>}\n\n {newTaskOpen && (\n <NewTaskModal\n t={t}\n workspaces={items}\n selectedPath={selectedPath}\n sessionId={currentSessionId}\n onClose={() => setNewTaskOpen(false)}\n onCreated={() => {\n setNewTaskOpen(false)\n void refresh()\n }}\n onError={(message) => showToast(message)}\n />\n )}\n\n {detailId !== null && (\n <DetailPanel\n card={cards.find((c) => c.id === detailId) ?? null}\n t={t}\n onClose={() => setDetailId(null)}\n onChanged={() => {\n setDetailId(null)\n void refresh()\n }}\n onToast={showToast}\n onOpenSession={openSession}\n />\n )}\n </div>\n )\n}\n\ninterface CardViewProps {\n card: KanbanCard\n t: (key: string, params?: Record<string, unknown>) => string\n onToggle: () => void\n}\n\nfunction CardView({ card, t, onToggle }: CardViewProps): JSX.Element {\n const draggable = card.status === 'planned' || card.status === 'queued' || card.status === 'running'\n const title = card.plan !== undefined && card.plan.title !== '' ? card.plan.title : firstLine(card.requirement)\n const badge = statusLabel(card, t)\n const phaseInfo = card.status === 'running' || card.status === 'error'\n ? card.currentPhase < card.phaseCount ? t('phaseProgress', { current: card.currentPhase + 1, total: card.phaseCount }) : ''\n : ''\n return (\n <div\n className={'kb-card' + (card.status === 'error' ? ' kb-card-error' : '') + (card.status === 'running' ? ' kb-card-running' : '')}\n draggable={draggable}\n onDragStart={(e) => {\n e.dataTransfer.setData('text/plain', card.id)\n e.dataTransfer.effectAllowed = 'move'\n }}\n onClick={onToggle}\n >\n <div className=\"kb-card-title\">{title}</div>\n <div className=\"kb-card-meta\">\n <span className=\"kb-card-badge\">{badge}</span>\n {phaseInfo !== '' && <span className=\"kb-card-phase\">{phaseInfo}</span>}\n {card.skill !== undefined && card.skill !== '' && <span className=\"kb-card-skill\">/ {card.skill}</span>}\n {card.model !== '' && <span className=\"kb-card-model\">{card.model}</span>}\n </div>\n {card.status === 'error' && card.error !== undefined && (\n <div className=\"kb-card-error-text\">{card.error.message}</div>\n )}\n {card.status === 'running' && card.gitMode === true && (\n <div className=\"kb-card-worktree\">{card.worktreePath ?? ''}</div>\n )}\n </div>\n )\n}\n\ninterface NewTaskModalProps {\n t: (key: string, params?: Record<string, unknown>) => string\n workspaces: WorkspaceRow[]\n selectedPath: string\n /** Current session id, scoping the host skill.list catalog lookup. */\n sessionId?: string\n onClose: () => void\n onCreated: () => void\n onError: (message: string) => void\n}\n\n/** An in-progress leading `/query` token (menu-open state). */\nconst SKILL_MENU_RE = /^\\s*\\/([a-z0-9-]*)$/\n\nfunction NewTaskModal({ t, workspaces, selectedPath, sessionId, onClose, onCreated, onError }: NewTaskModalProps): JSX.Element {\n const [requirement, setRequirement] = useState('')\n const [project, setProject] = useState(selectedPath)\n const [model, setModel] = useState('')\n const [modelProvider, setModelProvider] = useState('')\n const [models, setModels] = useState<Array<{ provider: string; id: string; name?: string }>>([])\n const [skills, setSkills] = useState<SkillOption[]>([])\n const [menuQuery, setMenuQuery] = useState<string | null>(null)\n const [menuIndex, setMenuIndex] = useState(0)\n const [busy, setBusy] = useState(false)\n const textareaRef = useRef<HTMLTextAreaElement | null>(null)\n\n useEffect(() => {\n let alive = true\n api.models()\n .then((list) => { if (alive) setModels(list) })\n .catch(() => { if (alive) setModels([]) })\n return () => { alive = false }\n }, [])\n\n // Load the host skill catalog once (scoped to the current session), the\n // same feed DSH's chat input uses for its /-autocomplete.\n useEffect(() => {\n if (sessionId === undefined) return\n let alive = true\n gatewaySkillList(sessionId)\n .then((list) => { if (alive) setSkills(list) })\n .catch(() => { if (alive) setSkills([]) })\n return () => { alive = false }\n }, [sessionId])\n\n const matches = skills.filter((s) => s.name.startsWith(menuQuery ?? ''))\n const menuVisible = menuQuery !== null && matches.length > 0\n const selected = menuVisible ? matches[Math.min(menuIndex, matches.length - 1)] : undefined\n\n /** Re-evaluate the leading-token menu state from the current value + caret. */\n const syncMenu = (value: string, caret: number): void => {\n const head = value.slice(0, caret)\n const m = SKILL_MENU_RE.exec(head)\n if (m !== null) {\n setMenuQuery(m[1])\n setMenuIndex(0)\n return\n }\n setMenuQuery(null)\n }\n\n const selectSkill = (name: string): void => {\n const el = textareaRef.current\n const caret = el?.selectionStart ?? requirement.length\n const head = requirement.slice(0, caret)\n const m = SKILL_MENU_RE.exec(head)\n // Replace the leading `/query` token with `/name `.\n const inserted = `/${name} `\n const newValue = m !== null ? inserted + requirement.slice(caret) : inserted + requirement\n setRequirement(newValue)\n setMenuQuery(null)\n requestAnimationFrame(() => {\n const next = textareaRef.current\n if (next !== null) {\n next.focus()\n const pos = inserted.length\n next.setSelectionRange(pos, pos)\n }\n })\n }\n\n const onKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>): void => {\n if (!menuVisible) return\n if (e.key === 'ArrowDown') {\n e.preventDefault()\n setMenuIndex((i) => (i + 1) % matches.length)\n } else if (e.key === 'ArrowUp') {\n e.preventDefault()\n setMenuIndex((i) => (i - 1 + matches.length) % matches.length)\n } else if (e.key === 'Enter') {\n e.preventDefault()\n if (selected !== undefined) selectSkill(selected.name)\n } else if (e.key === 'Escape') {\n e.preventDefault()\n setMenuQuery(null)\n } else if (e.key === 'Tab') {\n e.preventDefault()\n if (selected !== undefined) selectSkill(selected.name)\n }\n }\n\n const submit = async (): Promise<void> => {\n if (requirement.trim() === '') {\n onError(t('requirementLabel') + ' ' + t('error'))\n return\n }\n if (project === '') {\n onError(t('noWorkspace'))\n return\n }\n const parsed = parseSkillGesture(requirement)\n setBusy(true)\n try {\n await api.create(project, parsed.requirement, model, modelProvider, parsed.skill)\n onCreated()\n } catch (error) {\n onError(error instanceof Error ? error.message : String(error))\n setBusy(false)\n }\n }\n\n const activeSkill = parseSkillGesture(requirement).skill\n\n return (\n <div className=\"kb-modal-backdrop\" onClick={busy ? undefined : onClose}>\n <div className=\"kb-modal\" onClick={(e) => e.stopPropagation()}>\n <div className=\"kb-modal-title\">{t('newTask')}</div>\n <label className=\"kb-field\">\n <span>{t('requirementLabel')}</span>\n <div className=\"kb-skill-menu-wrap\">\n <textarea\n ref={textareaRef}\n className=\"kb-input kb-textarea\"\n value={requirement}\n placeholder={t('requirementPlaceholder')}\n onChange={(e) => {\n const value = e.target.value\n setRequirement(value)\n syncMenu(value, e.target.selectionStart ?? value.length)\n }}\n onKeyDown={onKeyDown}\n />\n {menuVisible && (\n <ul className=\"kb-skill-menu\" role=\"listbox\">\n {matches.map((s, i) => (\n <li\n key={s.name}\n role=\"option\"\n aria-selected={i === menuIndex}\n className={'kb-skill-menu-item' + (i === menuIndex ? ' kb-skill-menu-item-active' : '')}\n onMouseDown={(e) => { e.preventDefault(); selectSkill(s.name) }}\n onMouseEnter={() => setMenuIndex(i)}\n >\n <span className=\"kb-skill-menu-name\">/{s.name}</span>\n <span className=\"kb-skill-menu-desc\">\n {s.modelInvocable ? s.description : `${t('skillMenuUserOnly')} · ${s.description}`}\n </span>\n </li>\n ))}\n </ul>\n )}\n </div>\n {activeSkill !== undefined ? (\n <span className=\"kb-field-hint kb-field-hint-skill\">{t('skillHint', { skill: activeSkill })}</span>\n ) : (\n <span className=\"kb-field-hint\">{t('skillGestureHint')}</span>\n )}\n </label>\n <label className=\"kb-field\">\n <span>{t('project')}</span>\n <select className=\"kb-input\" value={project} onChange={(e) => setProject(e.target.value)}>\n {workspaces.map((w) => (\n <option key={w.workspaceId} value={w.path}>{w.title}</option>\n ))}\n </select>\n </label>\n <label className=\"kb-field\">\n <span>{t('model')}</span>\n <select\n className=\"kb-input\"\n value={model}\n onChange={(e) => {\n const id = e.target.value\n setModel(id)\n const found = models.find((m) => m.id === id)\n setModelProvider(found?.provider ?? '')\n }}\n >\n <option value=\"\">{t('modelPlaceholder')}</option>\n {models.map((m) => (\n <option key={m.provider + '/' + m.id} value={m.id}>{m.name ?? m.id}</option>\n ))}\n </select>\n </label>\n <div className=\"kb-modal-actions\">\n <button type=\"button\" className=\"kb-btn\" onClick={onClose} disabled={busy}>{t('cancel')}</button>\n <button type=\"button\" className=\"kb-btn kb-btn-primary\" onClick={() => { void submit() }} disabled={busy}>\n {t('addAndRefine')}\n </button>\n </div>\n </div>\n </div>\n )\n}\n\ninterface DetailPanelProps {\n card: KanbanCard | null\n t: (key: string, params?: Record<string, unknown>) => string\n onClose: () => void\n onChanged: () => void\n onToast: (message: string) => void\n onOpenSession: (sessionId: string) => void\n}\n\nfunction DetailPanel({ card, t, onClose, onChanged, onToast, onOpenSession }: DetailPanelProps): JSX.Element | null {\n const [busy, setBusy] = useState(false)\n if (card === null) return null\n\n const act = async (fn: () => Promise<unknown>): Promise<void> => {\n setBusy(true)\n try {\n await fn()\n onChanged()\n } catch (error) {\n onToast(error instanceof Error ? error.message : String(error))\n setBusy(false)\n }\n }\n\n const remove = async (): Promise<void> => {\n if (!window.confirm(t('deleteConfirm'))) return\n await act(() => api.remove(card!.id))\n }\n\n return (\n <div className=\"kb-detail-panel\">\n <div className=\"kb-detail-panel-head\">\n <div className=\"kb-modal-title\">\n {card.plan !== undefined && card.plan.title !== '' ? card.plan.title : firstLine(card.requirement)}\n <span className={'kb-detail-status kb-status-' + card.status}>{statusLabel(card, t)}</span>\n </div>\n <button type=\"button\" className=\"kb-detail-close\" aria-label={t('close')} title={t('close')} onClick={onClose}>✕</button>\n </div>\n\n <div className=\"kb-detail-panel-body\">\n <div className=\"kb-detail-section\">\n <div className=\"kb-detail-label\">{t('requirement')}</div>\n {card.skill !== undefined && card.skill !== '' && (\n <div className=\"kb-detail-skill\">{t('skillBadge', { skill: card.skill })}</div>\n )}\n <div className=\"kb-detail-text\">{card.requirement}</div>\n </div>\n\n {card.plan !== undefined && (\n <div className=\"kb-detail-section\">\n <div className=\"kb-detail-label\">{t('plan')}</div>\n <div className=\"kb-detail-plan\">\n <div className=\"kb-plan-summary\">{card.plan.summary}</div>\n <ol className=\"kb-plan-phases\">\n {card.plan.phases.map((phase, i) => (\n <PhaseCard\n key={phase.id}\n phase={phase}\n phaseIndex={i}\n card={card}\n t={t}\n onOpenSession={onOpenSession}\n />\n ))}\n </ol>\n </div>\n </div>\n )}\n\n {card.sessions.refinement.length > 0 && (\n <div className=\"kb-detail-section\">\n <div className=\"kb-detail-label\">{t('refinementLabel')}</div>\n <div className=\"kb-session-links\">\n {card.sessions.refinement.map((sid) => (\n <button key={sid} type=\"button\" className=\"kb-btn kb-btn-small\" onClick={() => onOpenSession(sid)}>\n {t('openSession')} · {sid.slice(0, 13)}\n </button>\n ))}\n </div>\n </div>\n )}\n\n {card.status === 'error' && card.error !== undefined && (\n <div className=\"kb-detail-section kb-detail-error\">\n <div className=\"kb-detail-label\">{t('error')}</div>\n <div className=\"kb-detail-text\">{card.error.message}</div>\n </div>\n )}\n\n {card.merge?.mergeCommit !== undefined && (\n <div className=\"kb-detail-section\">\n <div className=\"kb-detail-label\">{t('mergeLabel')}</div>\n <div className=\"kb-detail-text\">{t('commitLabel')} {card.merge.mergeCommit.slice(0, 12)}</div>\n </div>\n )}\n\n <div className=\"kb-modal-actions\">\n {card.status === 'error' && (\n <button type=\"button\" className=\"kb-btn kb-btn-primary\" disabled={busy} onClick={() => { void act(() => api.retry(card!.id)) }}>\n {t('retry')}\n </button>\n )}\n {(card.status === 'running' || card.status === 'merging') && (\n <button type=\"button\" className=\"kb-btn\" disabled={busy} onClick={() => { void act(() => api.stop(card!.id)) }}>\n {t('stop')}\n </button>\n )}\n {card.status !== 'running' && card.status !== 'refining' && card.status !== 'merging' && (\n <button type=\"button\" className=\"kb-btn kb-btn-danger\" disabled={busy} onClick={() => { void remove() }}>\n {t('delete')}\n </button>\n )}\n <button type=\"button\" className=\"kb-btn\" onClick={onClose}>{t('cancel')}</button>\n </div>\n </div>\n </div>\n )\n}\n\n/** One plan phase rendered as a card with goal / sessions / conclusion sections. */\nfunction PhaseCard({ phase, phaseIndex, card, t, onOpenSession }: {\n phase: PlanPhase\n phaseIndex: number\n card: KanbanCard\n t: (key: string, params?: Record<string, unknown>) => string\n onOpenSession: (sessionId: string) => void\n}): JSX.Element {\n const attempts = card.sessions.phases.filter((a) => a.phaseIndex === phaseIndex)\n const summaries = attempts\n .filter((a) => a.summary !== undefined && a.summary.trim() !== '')\n .map((a) => a.summary as string)\n const isCurrent = phaseIndex === card.currentPhase && card.status === 'running'\n // A phase with at least one concluded attempt counts as completed.\n const isCompleted = summaries.length > 0\n const cls = 'kb-plan-phase'\n + (isCurrent ? ' kb-plan-phase-current' : '')\n + (isCompleted ? ' kb-plan-phase-completed' : '')\n const planTitle = card.plan?.title ?? ''\n return (\n <li className={cls}>\n <div className=\"kb-plan-phase-head\">\n <span className=\"kb-plan-phase-title\">{phase.title}</span>\n <span className=\"kb-plan-phase-id\">{phase.id}</span>\n </div>\n <div className=\"kb-plan-phase-body\">\n <div className=\"kb-plan-phase-section\">\n <div className=\"kb-plan-phase-section-title\">{t('phaseGoal')}</div>\n <div className=\"kb-plan-phase-goal\">{phase.goal}</div>\n </div>\n {attempts.length > 0 && (\n <div className=\"kb-plan-phase-section\">\n <div className=\"kb-plan-phase-section-title\">{t('phaseSessions')}</div>\n <PhaseSessions attempts={attempts} planTitle={planTitle} phaseIndex={phaseIndex} t={t} onOpenSession={onOpenSession} />\n </div>\n )}\n {summaries.length > 0 && (\n <div className=\"kb-plan-phase-section\">\n <div className=\"kb-plan-phase-section-title\">{t('phaseConclusion')}</div>\n <div className=\"kb-plan-phase-conclusion\">\n {summaries.map((s, idx) => (\n <div key={idx} className=\"kb-plan-phase-conclusion-item\">{s}</div>\n ))}\n </div>\n </div>\n )}\n </div>\n </li>\n )\n}\n\n/**\n * Open-session buttons for one phase's attempts (retries append more). The\n * button labels the session with the deterministic \"{plan title} · phase N\"\n * format (the requested fallback when the host never auto-generated a title);\n * the full session id stays available as the native tooltip.\n */\nfunction PhaseSessions({ attempts, planTitle, phaseIndex, t, onOpenSession }: {\n attempts: PhaseAttempt[]\n planTitle: string\n phaseIndex: number\n t: (key: string, params?: Record<string, unknown>) => string\n onOpenSession: (sessionId: string) => void\n}): JSX.Element {\n return (\n <div className=\"kb-plan-phase-sessions\">\n {attempts.map((a) => (\n <button key={a.sessionId} type=\"button\" className=\"kb-btn kb-btn-small\" title={a.sessionId} onClick={() => onOpenSession(a.sessionId)}>\n {t('openSession')} · {planTitle !== '' ? `${planTitle} · ${t('phaseNum', { n: phaseIndex + 1 })}` : a.sessionId.slice(0, 13)}\n </button>\n ))}\n </div>\n )\n}","import * as primitives from '@deepseek-ai/dsh-client-ui-primitives'\n\n/** Props every shell icon accepts: square edge in px, color rides currentColor. */\nexport interface IconProps {\n size?: number\n className?: string\n}\n\n/** One shell icon component. */\nexport type IconComponent = (props: IconProps) => JSX.Element\n\n/** The runtime export table, read by capability instead of by one hard-coded name. */\nconst table = primitives as unknown as Record<string, IconComponent | undefined>\n\n/** First name the runtime actually provides (a missing export is `undefined`). */\nfunction firstDefined(...names: string[]): IconComponent | undefined {\n for (const name of names) {\n let candidate: IconComponent | undefined\n try {\n candidate = table[name]\n } catch {\n // Some module namespaces reject unknown names instead of yielding\n // `undefined`; a rename must never throw on its way to the fallback.\n continue\n }\n if (typeof candidate === 'function') return candidate\n }\n return undefined\n}\n\n/**\n * Self-contained 16×16 checklist glyph (the 0.1.7 artwork) used when the shell\n * primitives expose none of the names above: the footer entry must render\n * something even on a runtime whose icon exports we do not know.\n */\nfunction ChecklistFallback({ size = 14, className }: IconProps): JSX.Element {\n return (\n <svg\n width={size}\n height={size}\n className={className}\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n strokeWidth={1}\n >\n <path d=\"M3.75 6.25C4.7165 6.25 5.5 5.4665 5.5 4.5C5.5 3.5335 4.7165 2.75 3.75 2.75C2.7835 2.75 2 3.5335 2 4.5C2 5.4665 2.7835 6.25 3.75 6.25Z\" stroke=\"currentColor\" />\n <path d=\"M7.5 4.5H13.5\" stroke=\"currentColor\" />\n <path d=\"M3.75 13.25C4.7165 13.25 5.5 12.4665 5.5 11.5C5.5 10.5335 4.7165 9.75 3.75 9.75C2.7835 9.75 2 10.5335 2 11.5C2 12.4665 2.7835 13.25 3.75 13.25Z\" stroke=\"currentColor\" />\n <path d=\"M7.5 11.5H13.5\" stroke=\"currentColor\" />\n </svg>\n )\n}\n\n/**\n * The checklist icon used by the sidebar panel row entry.\n *\n * DSH 0.1.7 renamed every size-suffixed shell icon (`IconChecklistOutline14`)\n * into stroke-weight variants (`…Regular` 1px / `…Medium` 1.3px) inside\n * `@deepseek-ai/dsh-client-ui-primitives`. A stale named import resolves to\n * `undefined` and React then throws \"Element type is invalid\" while rendering\n * the slot entry — the entry is dropped and the button silently disappears\n * (exactly what upgrading to 0.1.7 did to this plugin). Resolve the name at\n * runtime, newest first, so the next rename degrades to another shipped\n * variant instead of taking the button down with it.\n */\nexport const ChecklistIcon: IconComponent =\n firstDefined(\n 'IconChecklistOutlineRegular',\n 'IconChecklistOutlineMedium',\n 'IconChecklistOutline',\n 'IconChecklistOutline14',\n ) ?? ChecklistFallback\n","import { useCallback, useEffect, useRef, useState } from 'react'\nimport { ChecklistIcon } from './icons'\nimport { api, type PresetOption, type ReasoningOptions } from './api'\nimport type { KanbanSettingsShape, ModelOption } from '../shared/card'\n\n/**\n * Sidebar panel-row glyph for the board (`sidebar.panellist`).\n *\n * The shell owns the row: it renders the button, its label, tooltip and active\n * state, and calls `layout.selectPanel(id)` on click. The entry contributes\n * only the glyph, so this component must stay purely presentational.\n */\nexport function KanbanPanelIcon({ size }: { size: number }): JSX.Element {\n return <ChecklistIcon size={size} />\n}\n\ninterface SettingsSectionProps {\n t?: (key: string) => string\n}\n\n/** Fallback shape while settings load (server always resolves the full set). */\nconst EMPTY_SETTINGS: KanbanSettingsShape = {\n maxParallelWorkers: 1,\n refinementModel: '',\n refinementProvider: '',\n refinementReasoningEffort: '',\n refinementPreset: '',\n phaseModel: '',\n phaseProvider: '',\n phaseReasoningEffort: '',\n phasePreset: '',\n}\n\n/** The setting field names of one session-type group. */\nconst GROUP_FIELDS = {\n refinement: {\n model: 'refinementModel',\n provider: 'refinementProvider',\n effort: 'refinementReasoningEffort',\n preset: 'refinementPreset',\n },\n phase: {\n model: 'phaseModel',\n provider: 'phaseProvider',\n effort: 'phaseReasoningEffort',\n preset: 'phasePreset',\n },\n} as const\n\ntype SessionGroupKey = keyof typeof GROUP_FIELDS\n\ninterface SessionDefaultsGroupProps {\n kind: SessionGroupKey\n settings: KanbanSettingsShape\n models: ModelOption[]\n presets: PresetOption[]\n /** Reasoning efforts already fetched, keyed `provider/model`. */\n efforts: Record<string, ReasoningOptions>\n tr: (key: string) => string\n onCommit: (patch: Partial<KanbanSettingsShape>) => Promise<void>\n loadEfforts: (provider: string, model: string) => void\n}\n\n/** Model / reasoning effort / agent preset defaults for one session type. */\nfunction SessionDefaultsGroup({ kind, settings, models, presets, efforts, tr, onCommit, loadEfforts }: SessionDefaultsGroupProps): JSX.Element {\n const fields = GROUP_FIELDS[kind]\n const model = settings[fields.model]\n const provider = settings[fields.provider]\n const effort = settings[fields.effort]\n const preset = settings[fields.preset]\n const effortKey = model !== '' && provider !== '' ? `${provider}/${model}` : ''\n const effortOptions = effortKey !== '' ? (efforts[effortKey]?.efforts ?? []) : []\n\n const handleModel = (id: string): void => {\n const found = models.find((m) => m.id === id)\n const nextProvider = found?.provider ?? ''\n void onCommit({ [fields.model]: id, [fields.provider]: nextProvider } as Partial<KanbanSettingsShape>)\n if (id !== '' && nextProvider !== '') loadEfforts(nextProvider, id)\n }\n\n return (\n <div className=\"kb-settings-group\">\n <div className=\"kb-settings-group-title\">{tr(kind === 'refinement' ? 'settingsRefineGroup' : 'settingsPhaseGroup')}</div>\n <label className=\"kb-field kb-field-row\">\n <span>{tr('model')}</span>\n <select className=\"kb-input\" value={model} onChange={(e) => handleModel(e.target.value)}>\n <option value=\"\">{tr('defaultOption')}</option>\n {models.map((m) => (\n <option key={m.provider + '/' + m.id} value={m.id}>{m.name ?? m.id}</option>\n ))}\n </select>\n </label>\n <label className=\"kb-field kb-field-row\">\n <span>{tr('reasoningEffort')}</span>\n <select\n className=\"kb-input\"\n value={effort}\n disabled={model === ''}\n onChange={(e) => void onCommit({ [fields.effort]: e.target.value } as Partial<KanbanSettingsShape>)}\n >\n <option value=\"\">{tr('defaultOption')}</option>\n {effortOptions.map((o) => (\n <option key={o.id} value={o.id}>{o.name}</option>\n ))}\n </select>\n </label>\n <label className=\"kb-field kb-field-row\">\n <span>{tr('agentPreset')}</span>\n <select\n className=\"kb-input\"\n value={preset}\n onChange={(e) => void onCommit({ [fields.preset]: e.target.value } as Partial<KanbanSettingsShape>)}\n >\n <option value=\"\">{tr('defaultOption')}</option>\n {presets.map((p) => (\n <option key={p.id} value={p.id}>{p.name ?? p.id}</option>\n ))}\n </select>\n </label>\n </div>\n )\n}\n\n/** Plugin settings page (设置 -> 侧栏「任务看板」): workers + default model + per-session-type defaults. */\nexport function KanbanSettingsSection({ t }: SettingsSectionProps): JSX.Element {\n const tr = t ?? ((key: string) => key)\n const [settings, setSettings] = useState<KanbanSettingsShape>(EMPTY_SETTINGS)\n const [models, setModels] = useState<ModelOption[]>([])\n const [presets, setPresets] = useState<PresetOption[]>([])\n const [efforts, setEfforts] = useState<Record<string, ReasoningOptions>>({})\n const [saved, setSaved] = useState(false)\n\n const commit = useCallback(async (patch: Partial<KanbanSettingsShape>): Promise<void> => {\n try {\n const next = await api.settingsSet(patch)\n setSettings(next)\n setSaved(true)\n window.setTimeout(() => setSaved(false), 1500)\n } catch {\n // keep the previous value\n }\n }, [])\n\n // Refs so async callbacks (effort fetch) read the freshest settings/commit.\n const settingsRef = useRef(settings)\n settingsRef.current = settings\n const commitRef = useRef(commit)\n commitRef.current = commit\n\n useEffect(() => {\n let alive = true\n api.settingsGet()\n .then((s) => { if (alive) setSettings(s) })\n .catch(() => undefined)\n api.models()\n .then((list) => { if (alive) setModels(list) })\n .catch(() => undefined)\n api.presets()\n .then((list) => { if (alive) setPresets(list) })\n .catch(() => undefined)\n return () => { alive = false }\n }, [])\n\n /** Reasoning routes already requested (dedupe across mount + model changes). */\n const effortFetched = useRef<Set<string>>(new Set())\n\n /**\n * Fetch the reasoning efforts of one model route (once per route). A failed\n * fetch is marked `failed` so the stored effort is preserved (see the reset\n * effect below) instead of being wiped by a transient error.\n */\n const loadEfforts = useCallback((provider: string, model: string): void => {\n const key = `${provider}/${model}`\n if (effortFetched.current.has(key)) return\n effortFetched.current.add(key)\n api.reasoningOptions(provider, model)\n .then((opts) => setEfforts((prev) => ({ ...prev, [key]: opts })))\n .catch(() => setEfforts((prev) => ({ ...prev, [key]: { efforts: [], failed: true } })))\n }, [])\n\n // Fetch efforts for the models already configured in settings, so the\n // effort selects show their levels (and validate the stored effort) as soon\n // as settings load — the fetch previously ran only on model change.\n useEffect(() => {\n for (const kind of ['refinement', 'phase'] as const) {\n const fields = GROUP_FIELDS[kind]\n const model = settings[fields.model]\n const provider = settings[fields.provider]\n if (model !== '' && provider !== '') loadEfforts(provider, model)\n }\n }, [settings.refinementModel, settings.refinementProvider, settings.phaseModel, settings.phaseProvider, loadEfforts])\n\n // When the efforts of the selected model arrive, drop a stored effort the\n // model no longer offers (e.g. the user switched the model). A failed fetch\n // (`failed: true`) keeps the stored effort: the model may just be\n // temporarily unresolvable, and wiping the user's choice would be worse.\n useEffect(() => {\n const s = settingsRef.current\n for (const kind of ['refinement', 'phase'] as const) {\n const fields = GROUP_FIELDS[kind]\n const model = s[fields.model]\n const provider = s[fields.provider]\n const effort = s[fields.effort]\n if (effort === '' || model === '' || provider === '') continue\n const opts = efforts[`${provider}/${model}`]\n if (opts === undefined || opts.failed === true) continue\n if (!opts.efforts.some((o) => o.id === effort)) {\n void commitRef.current({ [fields.effort]: '' } as Partial<KanbanSettingsShape>)\n }\n }\n }, [efforts])\n\n return (\n <div className=\"kb-page\">\n <header className=\"kb-page-head\">\n <h3 className=\"kb-page-title\">{tr('settingsTitle')}</h3>\n <p className=\"kb-page-sub\">{tr('sectionSub')}</p>\n </header>\n <div className=\"kb-settings\">\n <label className=\"kb-field kb-field-row\">\n <span>{tr('maxParallelWorkers')}</span>\n <input\n className=\"kb-input kb-input-number\"\n type=\"number\"\n min={1}\n step={1}\n value={settings.maxParallelWorkers}\n onChange={(e) => {\n const value = Math.max(1, Math.floor(Number(e.target.value) || 1))\n setSettings((s) => ({ ...s, maxParallelWorkers: value }))\n void commit({ maxParallelWorkers: value })\n }}\n />\n </label>\n <SessionDefaultsGroup\n kind=\"refinement\"\n settings={settings}\n models={models}\n presets={presets}\n efforts={efforts}\n tr={tr}\n onCommit={commit}\n loadEfforts={loadEfforts}\n />\n <SessionDefaultsGroup\n kind=\"phase\"\n settings={settings}\n models={models}\n presets={presets}\n efforts={efforts}\n tr={tr}\n onCommit={commit}\n loadEfforts={loadEfforts}\n />\n {saved && <div className=\"kb-settings-saved\">{tr('saved')}</div>}\n </div>\n </div>\n )\n}","import type { Context } from '@deepseek-ai/cordis'\ntype ClientContext = Context\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport { BoardRoot } from './board'\nimport { KanbanPanelIcon, KanbanSettingsSection } from './sections'\nimport { LOCALE_NS, zh, en } from './locales'\nimport { KANBAN_PANEL_ID, setClient } from './kanban-state'\n\n/** The settings sidebar entry this plugin's page owns (must stay stable). */\nconst SETTINGS_SECTION_ID = 'task-kanban'\n\n/** Services required before mounting (provided by the client runtime). */\nexport const inject = ['slots', 'sessions', 'workspaces', 'locale']\n\n/** Client plugin body. */\nexport function apply(ctx: ClientContext): void {\n setClient(ctx)\n // Session opening goes through the shell's own view owner\n // (`ctx.uiWorkspace.openSession`, see kanban-state.ts), which already returns\n // the center column to the Conversation — no wrapper to install here.\n\n // Live translate bound to the active locale (labels re-read it per call).\n const t = ctx.locale.bind(LOCALE_NS)\n ctx.effect(() => {\n const off = ctx.locale.register(LOCALE_NS, { zh, en })\n return () => off()\n }, 'task-kanban: dictionaries')\n\n // The board is a GLOBAL MAIN PANEL: the shell renders one `main` entry per\n // selected panel id, so keying it (instead of replacing `main.conversation`)\n // keeps the Conversation mounted and selectable beside the board, and the\n // sidebar's own panel row owns the button that selects it (the\n // `sidebar.panellist` entry below). Nothing of this plugin sits in the\n // sidebar foot any more, so the Settings trigger can never be squeezed.\n ctx.slots.inject('main', () =>\n ctx.slots.register({\n name: 'main',\n key: KANBAN_PANEL_ID,\n locale: LOCALE_NS,\n } as never, BoardRoot as never),\n )\n\n // Sidebar panel row: the shell renders the row (label, tooltip, active state)\n // and calls `layout.selectPanel(id)` on click; the entry supplies the glyph.\n ctx.slots.inject('sidebar.panellist', () =>\n ctx.slots.register({\n name: 'sidebar.panellist',\n id: KANBAN_PANEL_ID,\n order: 20,\n label: () => t('kanban'),\n locale: LOCALE_NS,\n }, KanbanPanelIcon as never),\n )\n\n // The plugin's own settings page (设置 -> 侧栏「任务看板」) rides the\n // task-kanban settings namespace and registers into the settings.section\n // list slot, which gives it one entry in the settings sidebar and renders\n // its content into the panel's content column.\n ctx.slots.inject('settings.section', () =>\n ctx.slots.register({\n name: 'settings.section',\n id: SETTINGS_SECTION_ID,\n order: 200,\n label: () => t('settingsTitle'),\n locale: LOCALE_NS,\n }, KanbanSettingsSection as never),\n )\n}\n"],"mappings":"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"}
|
package/lib/index.js
CHANGED
|
@@ -10,6 +10,14 @@ import { isSkillName, isUserInvocable, renderSkillContent } from "@deepseek-ai/d
|
|
|
10
10
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
11
11
|
//#region src/server/settings.ts
|
|
12
12
|
const KANBAN_SETTINGS_NS = "task-kanban";
|
|
13
|
+
/**
|
|
14
|
+
* The entry config schema. Deliberately UNANNOTATED: schemastery >= 3.18.3
|
|
15
|
+
* added a third `Mode` type parameter, so `.default().volatile()` fields have
|
|
16
|
+
* output type `Volatile<T>` and an explicit `z<KanbanSettingsShape>` no longer
|
|
17
|
+
* compiles (the schema's `meta.default` then carries `Volatile<number>`, which
|
|
18
|
+
* is not a `number`). The plain `KanbanSettingsShape` interface stays the
|
|
19
|
+
* plugin-facing shape: `registerSettings` unwraps the live cells below.
|
|
20
|
+
*/
|
|
13
21
|
const KanbanSettingsSchema = z.object({
|
|
14
22
|
maxParallelWorkers: z.natural().min(1).default(1).volatile(),
|
|
15
23
|
refinementModel: z.string().default("").volatile(),
|
|
@@ -668,7 +676,7 @@ var KanbanRunner = class {
|
|
|
668
676
|
recovered = false;
|
|
669
677
|
/** Agents that already got their scoped kanban tools (event idempotency). */
|
|
670
678
|
toolScopedAgents = /* @__PURE__ */ new WeakSet();
|
|
671
|
-
|
|
679
|
+
agentCreatedOff;
|
|
672
680
|
constructor(ctx, store, settings) {
|
|
673
681
|
this.ctx = ctx;
|
|
674
682
|
this.store = store;
|
|
@@ -681,16 +689,16 @@ var KanbanRunner = class {
|
|
|
681
689
|
this.pumpTimer = setInterval(() => {
|
|
682
690
|
this.pump();
|
|
683
691
|
}, 2500);
|
|
684
|
-
this.
|
|
685
|
-
this.
|
|
692
|
+
this.agentCreatedOff = this.ctx.on("agent/created", (payload) => {
|
|
693
|
+
this.onAgentCreated(payload.agent);
|
|
686
694
|
});
|
|
687
695
|
}
|
|
688
696
|
stop() {
|
|
689
697
|
if (this.pumpTimer !== void 0) clearInterval(this.pumpTimer);
|
|
690
698
|
this.pumpTimer = void 0;
|
|
691
|
-
if (this.
|
|
692
|
-
this.
|
|
693
|
-
this.
|
|
699
|
+
if (this.agentCreatedOff !== void 0) {
|
|
700
|
+
this.agentCreatedOff();
|
|
701
|
+
this.agentCreatedOff = void 0;
|
|
694
702
|
}
|
|
695
703
|
}
|
|
696
704
|
newSessionId() {
|
|
@@ -796,12 +804,13 @@ var KanbanRunner = class {
|
|
|
796
804
|
}
|
|
797
805
|
}
|
|
798
806
|
/**
|
|
799
|
-
* The single place kanban tools get registered.
|
|
800
|
-
* for BOTH `agents.create` (startup) and
|
|
801
|
-
* a restart)
|
|
807
|
+
* The single place kanban tools get registered. Subscribed to
|
|
808
|
+
* `agent/created`, which fires for BOTH `agents.create` (startup) and
|
|
809
|
+
* `agents.resume` (host re-open after a restart) and carries the path in its
|
|
810
|
+
* `source`, so a resumed refinement/phase/merge session keeps its scoped
|
|
802
811
|
* tools. Idempotent per agent object; non-kanban sessions are skipped.
|
|
803
812
|
*/
|
|
804
|
-
async
|
|
813
|
+
async onAgentCreated(agent) {
|
|
805
814
|
if (agent === void 0 || agent.id === void 0 || agent.ctx === void 0) return;
|
|
806
815
|
if (this.toolScopedAgents.has(agent)) return;
|
|
807
816
|
let cardId = this.sessionCards.get(agent.id);
|
package/lib/types/index.d.ts
CHANGED
|
@@ -10,5 +10,25 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
10
10
|
import type { KanbanSettingsShape } from './shared/card.js';
|
|
11
11
|
export declare const name = "@fonlan/dsh-task-kanban";
|
|
12
12
|
export declare const inject: string[];
|
|
13
|
-
export declare const Config: import("@deepseek-ai/schemastery").default<
|
|
13
|
+
export declare const Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
|
|
14
|
+
maxParallelWorkers: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
15
|
+
refinementModel: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
16
|
+
refinementProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
17
|
+
refinementReasoningEffort: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
18
|
+
refinementPreset: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
19
|
+
phaseModel: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
20
|
+
phaseProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
21
|
+
phaseReasoningEffort: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
22
|
+
phasePreset: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
23
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
24
|
+
maxParallelWorkers: import("@deepseek-ai/schemastery").default<number, number, "volatile-defined">;
|
|
25
|
+
refinementModel: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
26
|
+
refinementProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
27
|
+
refinementReasoningEffort: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
28
|
+
refinementPreset: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
29
|
+
phaseModel: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
30
|
+
phaseProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
31
|
+
phaseReasoningEffort: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
32
|
+
phasePreset: import("@deepseek-ai/schemastery").default<string, string, "volatile-defined">;
|
|
33
|
+
}>>, "plain">;
|
|
14
34
|
export declare function apply(ctx: Context, config: KanbanSettingsShape): void;
|
|
@@ -21,7 +21,7 @@ export declare class KanbanRunner implements KanbanToolResolver {
|
|
|
21
21
|
private recovered;
|
|
22
22
|
/** Agents that already got their scoped kanban tools (event idempotency). */
|
|
23
23
|
private toolScopedAgents;
|
|
24
|
-
private
|
|
24
|
+
private agentCreatedOff;
|
|
25
25
|
constructor(ctx: Context, store: TaskStore, settings: KanbanSettingsFace);
|
|
26
26
|
register(ctx: Context): void;
|
|
27
27
|
start(): void;
|
|
@@ -45,12 +45,13 @@ export declare class KanbanRunner implements KanbanToolResolver {
|
|
|
45
45
|
/** Fallback lookup when a session starts before indexSessions ran. */
|
|
46
46
|
private lookupCardOfSession;
|
|
47
47
|
/**
|
|
48
|
-
* The single place kanban tools get registered.
|
|
49
|
-
* for BOTH `agents.create` (startup) and
|
|
50
|
-
* a restart)
|
|
48
|
+
* The single place kanban tools get registered. Subscribed to
|
|
49
|
+
* `agent/created`, which fires for BOTH `agents.create` (startup) and
|
|
50
|
+
* `agents.resume` (host re-open after a restart) and carries the path in its
|
|
51
|
+
* `source`, so a resumed refinement/phase/merge session keeps its scoped
|
|
51
52
|
* tools. Idempotent per agent object; non-kanban sessions are skipped.
|
|
52
53
|
*/
|
|
53
|
-
private
|
|
54
|
+
private onAgentCreated;
|
|
54
55
|
listCards(workspacePath: string): Promise<KanbanCard[]>;
|
|
55
56
|
createTask(input: {
|
|
56
57
|
workspacePath: string;
|
|
@@ -9,7 +9,35 @@ import type { Context } from '@deepseek-ai/cordis';
|
|
|
9
9
|
import z from '@deepseek-ai/schemastery';
|
|
10
10
|
import type { KanbanSessionKind, KanbanSettingsShape } from '../shared/card.js';
|
|
11
11
|
export declare const KANBAN_SETTINGS_NS = "task-kanban";
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The entry config schema. Deliberately UNANNOTATED: schemastery >= 3.18.3
|
|
14
|
+
* added a third `Mode` type parameter, so `.default().volatile()` fields have
|
|
15
|
+
* output type `Volatile<T>` and an explicit `z<KanbanSettingsShape>` no longer
|
|
16
|
+
* compiles (the schema's `meta.default` then carries `Volatile<number>`, which
|
|
17
|
+
* is not a `number`). The plain `KanbanSettingsShape` interface stays the
|
|
18
|
+
* plugin-facing shape: `registerSettings` unwraps the live cells below.
|
|
19
|
+
*/
|
|
20
|
+
export declare const KanbanSettingsSchema: z<Schemastery.ObjectS<NoInfer<{
|
|
21
|
+
maxParallelWorkers: z<number, number, "volatile-defined">;
|
|
22
|
+
refinementModel: z<string, string, "volatile-defined">;
|
|
23
|
+
refinementProvider: z<string, string, "volatile-defined">;
|
|
24
|
+
refinementReasoningEffort: z<string, string, "volatile-defined">;
|
|
25
|
+
refinementPreset: z<string, string, "volatile-defined">;
|
|
26
|
+
phaseModel: z<string, string, "volatile-defined">;
|
|
27
|
+
phaseProvider: z<string, string, "volatile-defined">;
|
|
28
|
+
phaseReasoningEffort: z<string, string, "volatile-defined">;
|
|
29
|
+
phasePreset: z<string, string, "volatile-defined">;
|
|
30
|
+
}>>, Schemastery.ObjectT<NoInfer<{
|
|
31
|
+
maxParallelWorkers: z<number, number, "volatile-defined">;
|
|
32
|
+
refinementModel: z<string, string, "volatile-defined">;
|
|
33
|
+
refinementProvider: z<string, string, "volatile-defined">;
|
|
34
|
+
refinementReasoningEffort: z<string, string, "volatile-defined">;
|
|
35
|
+
refinementPreset: z<string, string, "volatile-defined">;
|
|
36
|
+
phaseModel: z<string, string, "volatile-defined">;
|
|
37
|
+
phaseProvider: z<string, string, "volatile-defined">;
|
|
38
|
+
phaseReasoningEffort: z<string, string, "volatile-defined">;
|
|
39
|
+
phasePreset: z<string, string, "volatile-defined">;
|
|
40
|
+
}>>, "plain">;
|
|
13
41
|
/** The per-session-type default slice a session kind resolves. */
|
|
14
42
|
export interface KanbanSessionDefaults {
|
|
15
43
|
model: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fonlan/dsh-task-kanban",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "DSH web plugin: a task kanban that refines requirements into phased plans, auto-executes them in git worktrees (one session per phase), and auto-merges into the main branch on completion.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -78,49 +78,50 @@
|
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
80
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
81
|
-
"@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
|
|
82
|
-
"@deepseek-ai/dsh-agent-
|
|
83
|
-
"@deepseek-ai/dsh-api-session-controller": "^0.1.5-rc.2",
|
|
84
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
|
|
85
|
-
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.2",
|
|
86
|
-
"@deepseek-ai/dsh-client-ui-layout": "^0.1.5-rc.2",
|
|
87
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
|
|
88
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
|
|
89
|
-
"@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.2",
|
|
90
|
-
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.2",
|
|
91
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
|
|
92
|
-
"@deepseek-ai/dsh-client-ui-workspace": "^0.1.5-rc.2",
|
|
93
|
-
"@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
|
|
94
|
-
"@deepseek-ai/dsh-session": "^0.1.5-rc.2",
|
|
95
|
-
"@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
|
|
96
|
-
"@deepseek-ai/dsh-skill": "^0.1.5-rc.2",
|
|
97
|
-
"@deepseek-ai/dsh-tools": "^0.1.5-rc.2",
|
|
81
|
+
"@deepseek-ai/dsh-agent": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
82
|
+
"@deepseek-ai/dsh-agent-preset-registry": "^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
83
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
84
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-layout": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
87
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
89
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
90
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
91
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
92
|
+
"@deepseek-ai/dsh-client-ui-workspace": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
93
|
+
"@deepseek-ai/dsh-llm": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
94
|
+
"@deepseek-ai/dsh-session": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
95
|
+
"@deepseek-ai/dsh-settings": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
96
|
+
"@deepseek-ai/dsh-skill": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
97
|
+
"@deepseek-ai/dsh-tools": "^0.1.5-rc.2 || ^0.2.0-rc.1 || ^0.2.1-alpha.1",
|
|
98
98
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
99
99
|
"react": "^18.2.0",
|
|
100
100
|
"react-dom": "^18.2.0"
|
|
101
101
|
},
|
|
102
102
|
"devDependencies": {
|
|
103
|
-
"@deepseek-ai/dsh-agent": "
|
|
104
|
-
"@deepseek-ai/dsh-agent-
|
|
105
|
-
"@deepseek-ai/dsh-
|
|
106
|
-
"@deepseek-ai/dsh-
|
|
107
|
-
"@deepseek-ai/dsh-client-
|
|
108
|
-
"@deepseek-ai/dsh-client-ui-
|
|
109
|
-
"@deepseek-ai/dsh-client-ui-
|
|
110
|
-
"@deepseek-ai/dsh-client-ui-
|
|
111
|
-
"@deepseek-ai/dsh-client-ui-
|
|
112
|
-
"@deepseek-ai/dsh-client-ui-
|
|
113
|
-
"@deepseek-ai/dsh-client-ui-settings
|
|
114
|
-
"@deepseek-ai/dsh-client-ui-
|
|
115
|
-
"@deepseek-ai/dsh-client-ui-
|
|
116
|
-
"@deepseek-ai/dsh-client-ui-
|
|
117
|
-
"@deepseek-ai/dsh-
|
|
118
|
-
"@deepseek-ai/dsh-
|
|
119
|
-
"@deepseek-ai/dsh-
|
|
120
|
-
"@deepseek-ai/dsh-
|
|
121
|
-
"@deepseek-ai/dsh-
|
|
122
|
-
"@deepseek-ai/dsh-
|
|
123
|
-
"@deepseek-ai/
|
|
103
|
+
"@deepseek-ai/dsh-agent": "0.2.1-alpha.1",
|
|
104
|
+
"@deepseek-ai/dsh-agent-preset": "0.2.1-alpha.1",
|
|
105
|
+
"@deepseek-ai/dsh-agent-preset-registry": "0.2.1-alpha.1",
|
|
106
|
+
"@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.1",
|
|
107
|
+
"@deepseek-ai/dsh-client-locale": "0.2.1-alpha.1",
|
|
108
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.2.1-alpha.1",
|
|
109
|
+
"@deepseek-ai/dsh-client-ui-layout": "0.2.1-alpha.1",
|
|
110
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.1",
|
|
111
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.1",
|
|
112
|
+
"@deepseek-ai/dsh-client-ui-session": "0.2.1-alpha.1",
|
|
113
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.1-alpha.1",
|
|
114
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "0.2.1-alpha.1",
|
|
115
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "0.2.1-alpha.1",
|
|
116
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.1",
|
|
117
|
+
"@deepseek-ai/dsh-client-ui-workspace": "0.2.1-alpha.1",
|
|
118
|
+
"@deepseek-ai/dsh-host-webserver": "0.2.1-alpha.1",
|
|
119
|
+
"@deepseek-ai/dsh-llm": "0.2.1-alpha.1",
|
|
120
|
+
"@deepseek-ai/dsh-session": "0.2.1-alpha.1",
|
|
121
|
+
"@deepseek-ai/dsh-settings": "0.2.1-alpha.1",
|
|
122
|
+
"@deepseek-ai/dsh-skill": "0.2.1-alpha.1",
|
|
123
|
+
"@deepseek-ai/dsh-tools": "0.2.1-alpha.1",
|
|
124
|
+
"@deepseek-ai/schemastery": "3.18.5-alpha.1",
|
|
124
125
|
"@types/node": "^22.0.0",
|
|
125
126
|
"@types/react": "~18.3.1",
|
|
126
127
|
"@types/react-dom": "~18.3.0",
|
package/src/client/board.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
|
2
2
|
import type { KanbanCard, Lane, PhaseAttempt, PlanPhase } from '../shared/card'
|
|
3
3
|
import { DROP_RULES, laneOf } from '../shared/lanes'
|
|
4
4
|
import { api, gatewaySkillList, type SkillOption } from './api'
|
|
5
|
-
import {
|
|
5
|
+
import { openSessionView } from './kanban-state'
|
|
6
6
|
import { en, zh } from './locales'
|
|
7
7
|
import './board.css'
|
|
8
8
|
|
|
@@ -157,17 +157,10 @@ export function BoardRoot(props: BoardProps): JSX.Element {
|
|
|
157
157
|
}, [cards, refresh, showToast, t])
|
|
158
158
|
|
|
159
159
|
const openSession = useCallback((sessionId: string) => {
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
leaveKanbanPanel(ctx)
|
|
165
|
-
if (ctx === null) return
|
|
166
|
-
try {
|
|
167
|
-
;(ctx.sessions as { open(id: string): void }).open(sessionId)
|
|
168
|
-
} catch {
|
|
169
|
-
showToast(t('sessionNotListed'))
|
|
170
|
-
}
|
|
160
|
+
// The shell's view owner retains the session and reveals the Conversation
|
|
161
|
+
// in one action, so the board itself needs no panel teardown; a runtime
|
|
162
|
+
// without that face cannot show the session at all.
|
|
163
|
+
if (!openSessionView(sessionId)) showToast(t('sessionNotListed'))
|
|
171
164
|
}, [showToast, t])
|
|
172
165
|
|
|
173
166
|
if (items.length === 0) {
|
package/src/client/index.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import type {} from '@deepseek-ai/dsh-client-ui-settings/client'
|
|
|
8
8
|
import { BoardRoot } from './board'
|
|
9
9
|
import { KanbanPanelIcon, KanbanSettingsSection } from './sections'
|
|
10
10
|
import { LOCALE_NS, zh, en } from './locales'
|
|
11
|
-
import { KANBAN_PANEL_ID, setClient
|
|
11
|
+
import { KANBAN_PANEL_ID, setClient } from './kanban-state'
|
|
12
12
|
|
|
13
13
|
/** The settings sidebar entry this plugin's page owns (must stay stable). */
|
|
14
14
|
const SETTINGS_SECTION_ID = 'task-kanban'
|
|
@@ -19,8 +19,9 @@ export const inject = ['slots', 'sessions', 'workspaces', 'locale']
|
|
|
19
19
|
/** Client plugin body. */
|
|
20
20
|
export function apply(ctx: ClientContext): void {
|
|
21
21
|
setClient(ctx)
|
|
22
|
-
//
|
|
23
|
-
|
|
22
|
+
// Session opening goes through the shell's own view owner
|
|
23
|
+
// (`ctx.uiWorkspace.openSession`, see kanban-state.ts), which already returns
|
|
24
|
+
// the center column to the Conversation — no wrapper to install here.
|
|
24
25
|
|
|
25
26
|
// Live translate bound to the active locale (labels re-read it per call).
|
|
26
27
|
const t = ctx.locale.bind(LOCALE_NS)
|
|
@@ -17,6 +17,16 @@ interface LayoutLike {
|
|
|
17
17
|
selectPanel(panelId: string | null): void
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Structural view of the client shell's workspace-navigation face
|
|
22
|
+
* (`ctx.uiWorkspace`, dsh-client-ui-workspace). Declared locally (no value
|
|
23
|
+
* import) so the plugin keeps working on a runtime whose workspace types differ.
|
|
24
|
+
*/
|
|
25
|
+
interface UiWorkspaceLike {
|
|
26
|
+
/** Select a Session (id, or a direct-parent subagent address) and show it. */
|
|
27
|
+
openSession(target: string): void
|
|
28
|
+
}
|
|
29
|
+
|
|
20
30
|
/**
|
|
21
31
|
* HMR-safe owner context, keyed on the global symbol registry.
|
|
22
32
|
*
|
|
@@ -78,41 +88,37 @@ export function leaveKanbanPanel(ctx: ClientContext | null = getClient()): void
|
|
|
78
88
|
}
|
|
79
89
|
|
|
80
90
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* Conversation, so the session view renders.
|
|
91
|
+
* Open one session — or a durable direct-parent subagent address — in the
|
|
92
|
+
* center column, leaving the kanban panel.
|
|
84
93
|
*
|
|
85
|
-
*
|
|
86
|
-
* (
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
94
|
+
* Navigation is a view-owner concern, not a session-controller one: `ctx.sessions`
|
|
95
|
+
* (`ISessions`) exposes retention (`retain`/`using`/`retainInfo`), creation,
|
|
96
|
+
* fork, search and catalog reads, and has no `open`/`openSubagent` at all
|
|
97
|
+
* ("navigation belongs to view owners"). The view owner the shell itself uses is
|
|
98
|
+
* `ctx.uiWorkspace.openSession(target)` (dsh-client-ui-workspace): it retains the
|
|
99
|
+
* session AND reveals the Conversation panel as ONE navigation action. Routing
|
|
100
|
+
* every open through it therefore replaces the old wrapper around
|
|
101
|
+
* `sessions.open`/`openSubagent`, which existed only to hand the center column
|
|
102
|
+
* back when a sidebar click selected a session while the board was showing.
|
|
91
103
|
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
104
|
+
* @param target - session id (or subagent address) to show.
|
|
105
|
+
* @returns whether the runtime provided the navigation face; `false` means the
|
|
106
|
+
* session could not be shown (a client without ui-workspace).
|
|
94
107
|
*/
|
|
95
|
-
export function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
leaveKanbanPanel(ctx)
|
|
103
|
-
return open.call(sessions, id)
|
|
104
|
-
}) as typeof open
|
|
105
|
-
;(bound as unknown as { kbBound?: boolean }).kbBound = true
|
|
106
|
-
sessions.open = bound
|
|
108
|
+
export function openSessionView(target: string, ctx: ClientContext | null = getClient()): boolean {
|
|
109
|
+
if (ctx === null) return false
|
|
110
|
+
let uiWorkspace: UiWorkspaceLike | undefined
|
|
111
|
+
try {
|
|
112
|
+
uiWorkspace = (ctx as unknown as { uiWorkspace?: UiWorkspaceLike }).uiWorkspace
|
|
113
|
+
} catch {
|
|
114
|
+
return false
|
|
107
115
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
;(bound as unknown as { kbBound?: boolean }).kbBound = true
|
|
116
|
-
sessions.openSubagent = bound
|
|
116
|
+
if (typeof uiWorkspace?.openSession !== 'function') return false
|
|
117
|
+
try {
|
|
118
|
+
uiWorkspace.openSession(target)
|
|
119
|
+
return true
|
|
120
|
+
} catch (error) {
|
|
121
|
+
console.error('[@fonlan/dsh-task-kanban] cannot open the session view:', error)
|
|
122
|
+
return false
|
|
117
123
|
}
|
|
118
124
|
}
|
package/src/server/runner.ts
CHANGED
|
@@ -19,7 +19,14 @@ import { currentBranch, detectBaseRef, hasStashMessage, isGitRepo, isTreeDirty,
|
|
|
19
19
|
import { interactiveRefinementPrompt, mergePrompt, phasePrompt, refinementPrompt } from './prompts.js'
|
|
20
20
|
import { loadCardSkill, parseSkillGesture, skillInvocationMessage } from './skills.js'
|
|
21
21
|
import { registerKanbanTools, type KanbanToolResolver } from './tools.js'
|
|
22
|
-
|
|
22
|
+
// Declaration-merge only (erased at runtime): loads the `agentPresets` service
|
|
23
|
+
// augmentation for `ctx.get('agentPresets')` below. The 0.1.x name
|
|
24
|
+
// `@deepseek-ai/dsh-agent-presets` stopped shipping after 0.1.5-rc.3 and does
|
|
25
|
+
// not exist at 0.2.0-rc.1; `@deepseek-ai/dsh-agent-preset` ships on both live
|
|
26
|
+
// runtimes and carries the same `agentPresets` Context augmentation (it depends
|
|
27
|
+
// on `@deepseek-ai/dsh-agent-preset-registry`, which owns the service and the
|
|
28
|
+
// augmentation).
|
|
29
|
+
import type {} from '@deepseek-ai/dsh-agent-preset'
|
|
23
30
|
|
|
24
31
|
const WORKTREES_REL = ['.dsh', 'worktrees']
|
|
25
32
|
|
|
@@ -59,7 +66,7 @@ export class KanbanRunner implements KanbanToolResolver {
|
|
|
59
66
|
private recovered = false
|
|
60
67
|
/** Agents that already got their scoped kanban tools (event idempotency). */
|
|
61
68
|
private toolScopedAgents = new WeakSet<object>()
|
|
62
|
-
private
|
|
69
|
+
private agentCreatedOff: (() => void) | undefined
|
|
63
70
|
|
|
64
71
|
constructor(ctx: Context, store: TaskStore, settings: KanbanSettingsFace) {
|
|
65
72
|
this.ctx = ctx
|
|
@@ -73,21 +80,28 @@ export class KanbanRunner implements KanbanToolResolver {
|
|
|
73
80
|
|
|
74
81
|
start(): void {
|
|
75
82
|
this.pumpTimer = setInterval(() => { void this.pump() }, 2500)
|
|
76
|
-
// `agent/
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
// `agent/created` is the lifecycle edge for an agent that entered the
|
|
84
|
+
// registry — dsh announces every creation path through it, including
|
|
85
|
+
// `agents.resume` (the factory re-opens a persisted session's agent after a
|
|
86
|
+
// host restart), and its `source` records which path that was. The previous
|
|
87
|
+
// subscription used `agent/session-start`, which existed in dsh 0.1.5 only
|
|
88
|
+
// and was already gone by 0.1.7 — so resumed refinement/phase/merge
|
|
89
|
+
// sessions silently lost their scoped kanban tools. `agent/created` exists
|
|
90
|
+
// in 0.1.7-rc.2 and 0.2.0-rc.1 with the same `{ agent, source }` payload.
|
|
91
|
+
// It dispatches as a serial listener, so this callback must not reject:
|
|
92
|
+
// tool registration is best-effort and a lookup failure must not fail the
|
|
93
|
+
// host's agent creation (hence the floating promise).
|
|
94
|
+
this.agentCreatedOff = this.ctx.on('agent/created', (payload: { agent?: { id?: string; ctx?: Context } }): undefined => {
|
|
95
|
+
void this.onAgentCreated(payload.agent)
|
|
82
96
|
})
|
|
83
97
|
}
|
|
84
98
|
|
|
85
99
|
stop(): void {
|
|
86
100
|
if (this.pumpTimer !== undefined) clearInterval(this.pumpTimer)
|
|
87
101
|
this.pumpTimer = undefined
|
|
88
|
-
if (this.
|
|
89
|
-
this.
|
|
90
|
-
this.
|
|
102
|
+
if (this.agentCreatedOff !== undefined) {
|
|
103
|
+
this.agentCreatedOff()
|
|
104
|
+
this.agentCreatedOff = undefined
|
|
91
105
|
}
|
|
92
106
|
}
|
|
93
107
|
|
|
@@ -217,12 +231,13 @@ export class KanbanRunner implements KanbanToolResolver {
|
|
|
217
231
|
}
|
|
218
232
|
|
|
219
233
|
/**
|
|
220
|
-
* The single place kanban tools get registered.
|
|
221
|
-
* for BOTH `agents.create` (startup) and
|
|
222
|
-
* a restart)
|
|
234
|
+
* The single place kanban tools get registered. Subscribed to
|
|
235
|
+
* `agent/created`, which fires for BOTH `agents.create` (startup) and
|
|
236
|
+
* `agents.resume` (host re-open after a restart) and carries the path in its
|
|
237
|
+
* `source`, so a resumed refinement/phase/merge session keeps its scoped
|
|
223
238
|
* tools. Idempotent per agent object; non-kanban sessions are skipped.
|
|
224
239
|
*/
|
|
225
|
-
private async
|
|
240
|
+
private async onAgentCreated(agent: { id?: string; ctx?: Context } | undefined): Promise<void> {
|
|
226
241
|
if (agent === undefined || agent.id === undefined || agent.ctx === undefined) return
|
|
227
242
|
if (this.toolScopedAgents.has(agent)) return
|
|
228
243
|
let cardId = this.sessionCards.get(agent.id)
|
|
@@ -611,9 +626,9 @@ export class KanbanRunner implements KanbanToolResolver {
|
|
|
611
626
|
sessionId,
|
|
612
627
|
meta: { cwd, ...(presetId !== undefined ? { agentPreset: presetId } : {}) },
|
|
613
628
|
...(agentOptions !== undefined ? { agentOptions } : {}),
|
|
614
|
-
// Kanban tools are NOT registered here: `agent/
|
|
615
|
-
//
|
|
616
|
-
//
|
|
629
|
+
// Kanban tools are NOT registered here: `agent/created` (see start())
|
|
630
|
+
// fires for both create and resume, keeping them available even when the
|
|
631
|
+
// host re-opens a persisted kanban session.
|
|
617
632
|
setup: async (agentCtx) => {
|
|
618
633
|
const agentPresets = agentCtx.get('agentPresets') as
|
|
619
634
|
| { mount(agentCtx: unknown, id?: string): Promise<unknown> }
|
package/src/server/settings.ts
CHANGED
|
@@ -11,7 +11,15 @@ import type { KanbanSessionKind, KanbanSettingsShape } from '../shared/card.js'
|
|
|
11
11
|
|
|
12
12
|
export const KANBAN_SETTINGS_NS = 'task-kanban'
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The entry config schema. Deliberately UNANNOTATED: schemastery >= 3.18.3
|
|
16
|
+
* added a third `Mode` type parameter, so `.default().volatile()` fields have
|
|
17
|
+
* output type `Volatile<T>` and an explicit `z<KanbanSettingsShape>` no longer
|
|
18
|
+
* compiles (the schema's `meta.default` then carries `Volatile<number>`, which
|
|
19
|
+
* is not a `number`). The plain `KanbanSettingsShape` interface stays the
|
|
20
|
+
* plugin-facing shape: `registerSettings` unwraps the live cells below.
|
|
21
|
+
*/
|
|
22
|
+
export const KanbanSettingsSchema = z.object({
|
|
15
23
|
// Volatile: the settings plane (settings page / settings.update) only
|
|
16
24
|
// writes volatile-marked fields on dsh >= 0.1.7.
|
|
17
25
|
maxParallelWorkers: z.natural().min(1).default(1).volatile(),
|