open-multi-agent-kit 0.80.8 → 0.90.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/README.md +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/core/automation/index.d.ts +359 -0
- package/dist/core/automation/index.d.ts.map +1 -0
- package/dist/core/automation/index.js +665 -0
- package/dist/core/automation/index.js.map +1 -0
- package/dist/core/browser-use.d.ts +8 -0
- package/dist/core/browser-use.d.ts.map +1 -1
- package/dist/core/browser-use.js +61 -23
- package/dist/core/browser-use.js.map +1 -1
- package/dist/core/diagnostics.d.ts +2 -0
- package/dist/core/diagnostics.d.ts.map +1 -1
- package/dist/core/diagnostics.js.map +1 -1
- package/dist/core/extensions/builtin/todo-checklist.d.ts.map +1 -1
- package/dist/core/extensions/builtin/todo-checklist.js +3 -0
- package/dist/core/extensions/builtin/todo-checklist.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +3 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +4 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +1 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/pi-package-intake-candidates.d.ts +21 -0
- package/dist/core/pi-package-intake-candidates.d.ts.map +1 -0
- package/dist/core/pi-package-intake-candidates.js +109 -0
- package/dist/core/pi-package-intake-candidates.js.map +1 -0
- package/dist/core/pi-package-intake.d.ts +43 -0
- package/dist/core/pi-package-intake.d.ts.map +1 -0
- package/dist/core/pi-package-intake.js +122 -0
- package/dist/core/pi-package-intake.js.map +1 -0
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/provider-display-names.js +1 -0
- package/dist/core/provider-display-names.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +1 -0
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/skills.d.ts +4 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +115 -43
- package/dist/core/skills.js.map +1 -1
- package/dist/core/todo-runtime-state.d.ts +5 -0
- package/dist/core/todo-runtime-state.d.ts.map +1 -0
- package/dist/core/todo-runtime-state.js +12 -0
- package/dist/core/todo-runtime-state.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/modes/interactive/components/control-panel-layout.d.ts +8 -1
- package/dist/modes/interactive/components/control-panel-layout.d.ts.map +1 -1
- package/dist/modes/interactive/components/control-panel-layout.js +48 -16
- package/dist/modes/interactive/components/control-panel-layout.js.map +1 -1
- package/dist/modes/interactive/components/control-panel-runtime-status.d.ts +2 -1
- package/dist/modes/interactive/components/control-panel-runtime-status.d.ts.map +1 -1
- package/dist/modes/interactive/components/control-panel-runtime-status.js +10 -1
- package/dist/modes/interactive/components/control-panel-runtime-status.js.map +1 -1
- package/dist/modes/interactive/components/control-panel.d.ts +1 -0
- package/dist/modes/interactive/components/control-panel.d.ts.map +1 -1
- package/dist/modes/interactive/components/control-panel.js +15 -3
- package/dist/modes/interactive/components/control-panel.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +6 -0
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +7 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +3 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/docs/providers.md +12 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { PiPackageIntakeSummary } from "../../../core/pi-package-intake.ts";
|
|
2
|
+
import { type TodoState } from "../../../core/todo-state.ts";
|
|
3
|
+
export declare const CONTROL_PANEL_ASCII_ART: string[];
|
|
1
4
|
export interface ControlPanelContent {
|
|
2
5
|
appName: string;
|
|
3
6
|
version: string;
|
|
@@ -18,6 +21,10 @@ export interface ControlPanelStatusSnapshot {
|
|
|
18
21
|
readonly optimizerPolicy?: string;
|
|
19
22
|
readonly skillCount?: number;
|
|
20
23
|
readonly mcpCount?: number;
|
|
24
|
+
readonly packageIntake?: PiPackageIntakeSummary;
|
|
25
|
+
readonly cwdLabel?: string;
|
|
26
|
+
readonly gitBranch?: string | null;
|
|
27
|
+
readonly todoState?: TodoState;
|
|
21
28
|
}
|
|
22
|
-
export declare function renderControlPanelLayout(content: ControlPanelContent, expanded: boolean, width: number): string[];
|
|
29
|
+
export declare function renderControlPanelLayout(content: ControlPanelContent, expanded: boolean, width: number, bannerFrame?: string[]): string[];
|
|
23
30
|
//# sourceMappingURL=control-panel-layout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-panel-layout.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-layout.ts"],"names":[],"mappings":"AA6BA,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,MAAM,CAAC;IAClC,oBAAoB,EAAE,MAAM,MAAM,CAAC;IACnC,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,UAAU,EAAE,MAAM,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,0BAA0B,CAAC;CAClD;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAGjH","sourcesContent":["import { visibleWidth } from \"omk-tui\";\nimport { getHeadroomRuntimeStatus } from \"../../../core/context-budget-headroom.ts\";\nimport { loadMcpInventory } from \"../../../core/mcp-inventory.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport {\n\tboxBlankLine,\n\tboxBottom,\n\tboxCenteredLine,\n\tboxTextLine,\n\tboxTop,\n\tcenterLine,\n\tcomposeColumns,\n\tdivider,\n\tfitLine,\n\tsidebarRule,\n\ttextLine,\n} from \"./control-panel-box.ts\";\n\nconst ASCII = [\n\t\" ____ __ __ __ __\",\n\t\" / __ \\\\ / |/ / / //_/\",\n\t\" / /_/ // /|_/ / / ,< \",\n\t\" \\\\____//_/ /_/ /_/|_| \",\n];\nconst META_WIDTH = 31;\nconst DECK_MIN_WIDTH = 112;\nconst SIDEBAR_WIDTH = 38;\nconst GAP_WIDTH = 2;\n\nexport interface ControlPanelContent {\n\tappName: string;\n\tversion: string;\n\tcompactInstructions: () => string;\n\texpandedInstructions: () => string;\n\tcompactOnboarding: () => string;\n\tonboarding: () => string;\n\tstatusSnapshot?: () => ControlPanelStatusSnapshot;\n}\n\nexport interface ControlPanelStatusSnapshot {\n\treadonly modelId?: string;\n\treadonly modelProvider?: string;\n\treadonly thinkingLevel?: string;\n\treadonly contextPercent?: number | null;\n\treadonly contextWindowTokens?: number;\n\treadonly contextTokens?: number | null;\n\treadonly headroomStatus?: string;\n\treadonly optimizerPolicy?: string;\n\treadonly skillCount?: number;\n\treadonly mcpCount?: number;\n}\n\nexport function renderControlPanelLayout(content: ControlPanelContent, expanded: boolean, width: number): string[] {\n\tif (width <= 0) return [];\n\treturn expanded ? renderExpanded(content, width) : renderCompact(content, width);\n}\n\nfunction renderCompact(content: ControlPanelContent, width: number): string[] {\n\tif (width >= DECK_MIN_WIDTH) {\n\t\tconst deck = renderDeck(content, width);\n\t\tif (deck.length > 0) return deck;\n\t}\n\treturn [\n\t\tdivider(width, \"OMK//CONTROL PANEL\", \"accent\"),\n\t\tstatusLine(content, width),\n\t\ttextLine(width, content.compactInstructions()),\n\t\ttextLine(width, content.compactOnboarding(), \"dim\"),\n\t];\n}\n\nfunction renderDeck(content: ControlPanelContent, width: number): string[] {\n\tconst sidebarWidth = Math.min(SIDEBAR_WIDTH, Math.max(34, Math.floor(width * 0.28)));\n\tconst leftWidth = width - GAP_WIDTH - sidebarWidth;\n\tif (leftWidth < 72) return [];\n\tconst lines = composeColumns(\n\t\theroPanel(content, leftWidth),\n\t\tleftWidth,\n\t\tsidebarPanel(content, sidebarWidth),\n\t\tsidebarWidth,\n\t\tGAP_WIDTH,\n\t\twidth,\n\t);\n\tlines.push(controlStripLine(content, width));\n\tlines.push(centerLine(width, content.compactOnboarding(), \"dim\"));\n\treturn lines;\n}\n\nfunction heroPanel(content: ControlPanelContent, width: number): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\tconst modelLabel = modelStatusLabel(statusSnapshot(content));\n\treturn [\n\t\tboxTop(width, `omk v${content.version} · OMK//CONTROL`),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK\"))),\n\t\tboxCenteredLine(width, theme.fg(\"muted\", \"route · verify · loop · control\")),\n\t\tboxBlankLine(width),\n\t\t...ASCII.map((line, index) => {\n\t\t\tconst color: ThemeColor =\n\t\t\t\tindex === 0 ? \"accent\" : index === 1 ? \"warning\" : index === 2 ? \"success\" : \"mdCode\";\n\t\t\treturn boxCenteredLine(width, theme.fg(color, line));\n\t\t}),\n\t\tboxBlankLine(width),\n\t\tboxCenteredLine(width, `${theme.fg(\"warning\", \"*\")} ${theme.bold(\"OMK\")} / ${theme.fg(\"success\", modelLabel)}`),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"mdCode\", \"<>\")} omk-control · ${theme.fg(\"accent\", \"route\")} · ${theme.fg(\"warning\", \"verify\")} · ${theme.fg(\"success\", \"loop\")} · ${theme.fg(\"mdCode\", themeName)}`,\n\t\t),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction sidebarPanel(content: ControlPanelContent, width: number): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst headroomLabel = snapshot.headroomStatus ?? snapshot.optimizerPolicy ?? getHeadroomRuntimeStatus().policyId;\n\tconst mcpCount = snapshot.mcpCount ?? loadMcpInventory().entries.length;\n\tconst skillCount = snapshot.skillCount ?? 0;\n\treturn [\n\t\tsidebarTabs(width),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK://CONTROL\"))),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"warning\", \"CYBERPUNK OPS CORE\"))),\n\t\tboxCenteredLine(width, `${theme.fg(\"accent\", \"MATRIX RAIN\")} // ${theme.fg(\"success\", \"NEON GRID ONLINE\")}`),\n\t\tboxCenteredLine(width, theme.fg(\"mdCode\", \"NIGHT-CITY-MATRIX-V3\")),\n\t\tsidebarRule(width, \"STATUS\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"state:\")} ${theme.fg(\"success\", \"* ready\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} route · evidence · loop · control`),\n\t\tsidebarRule(width, \"TODO\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"next:\")} add branch TODOs with /todos`),\n\t\tsidebarRule(width, \"MODEL / CTX\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"model:\")} ${modelStatusLabel(snapshot)}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"think:\")} ${snapshot.thinkingLevel ?? \"off\"}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"ctx:\")} ${theme.fg(\"success\", contextStatusLabel(snapshot))}`),\n\t\tsidebarRule(width, \"RUNTIME / MCP / SKILLS\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"headroom:\")} ${headroomLabel}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"omk:\")} DAG:omk-parallel-orchestrator`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"res:\")} MCP:${mcpCount} skills:${skillCount}`),\n\t\tsidebarRule(width, \"CONTROL\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${theme.fg(\"success\", \"armed\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"verify:\")} ${theme.fg(\"success\", \"evidence gated\")}`),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction renderExpanded(content: ControlPanelContent, width: number): string[] {\n\tconst lines = [divider(width, \"OMK//CONTROL PANEL\", \"accent\"), statusLine(content, width)];\n\tif (width >= 32) lines.push(...brandLines(width));\n\tlines.push(divider(width, \"SYSTEM MAP\", \"mdHeading\"));\n\tfor (const instruction of content.expandedInstructions().split(\"\\n\")) lines.push(textLine(width, instruction));\n\tlines.push(divider(width, \"STARTUP LINK\", \"success\"));\n\tfor (const onboardingLine of content.onboarding().split(\"\\n\")) lines.push(textLine(width, onboardingLine, \"dim\"));\n\tlines.push(divider(width, \"END\", \"borderMuted\"));\n\treturn lines;\n}\n\nfunction statusSnapshot(content: ControlPanelContent): ControlPanelStatusSnapshot {\n\treturn content.statusSnapshot?.() ?? {};\n}\n\nfunction modelStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\treturn snapshot.modelProvider ? `${snapshot.modelProvider}/${snapshot.modelId}` : snapshot.modelId;\n}\n\nfunction contextStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst windowTokens = snapshot.contextWindowTokens ?? 0;\n\tconst windowLabel = windowTokens > 0 ? formatTokens(windowTokens) : \"?\";\n\treturn snapshot.contextPercent === null || snapshot.contextPercent === undefined\n\t\t? `?/${windowLabel}`\n\t\t: `${snapshot.contextPercent.toFixed(1)}%/${windowLabel}`;\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nfunction sidebarTabs(width: number): string {\n\treturn boxTextLine(\n\t\twidth,\n\t\tfitLine(\n\t\t\t`${theme.bold(theme.fg(\"accent\", \"1:CONTROL\"))} ${theme.fg(\"muted\", \"2:HISTORY\")}`,\n\t\t\tMath.max(0, width - 4),\n\t\t),\n\t);\n}\n\nfunction controlStripLine(content: ControlPanelContent, width: number): string {\n\treturn centerLine(\n\t\twidth,\n\t\t`${theme.bold(theme.fg(\"accent\", \"OMK//CONTROL READ\"))} ${theme.fg(\"accent\", \"route/verify/loop/control\")} · ${content.compactInstructions()}`,\n\t);\n}\n\nfunction brandLines(width: number): string[] {\n\tconst leftWidth = Math.max(...ASCII.map((line) => visibleWidth(line)));\n\tconst minWideWidth = visibleWidth(\"| \") + leftWidth + visibleWidth(\" | \") + META_WIDTH;\n\tif (width < minWideWidth) return ASCII.map((line) => textLine(width, theme.fg(\"accent\", line)));\n\treturn ASCII.map((line, index) =>\n\t\ttextLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"accent\", line.padEnd(leftWidth))}${theme.fg(\"borderMuted\", \" | \")}${metadataLines()[index] ?? \"\"}`,\n\t\t),\n\t);\n}\n\nfunction metadataLines(): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\treturn [\n\t\t`${theme.fg(\"mdCode\", \"PANEL\")} ${theme.fg(\"success\", \"ONLINE\")}`,\n\t\t`${theme.fg(\"mdCode\", \"THEME\")} ${theme.fg(\"accent\", themeName)}`,\n\t\t`${theme.fg(\"mdCode\", \"STARTUP\")} ${theme.fg(\"warning\", \"ARMED\")}`,\n\t\t`${theme.fg(\"mdCode\", \"LINK\")} ${theme.fg(\"success\", \"READY\")}`,\n\t];\n}\n\nfunction statusLine(content: ControlPanelContent, width: number): string {\n\tconst segments = [\n\t\ttheme.bold(theme.fg(\"accent\", `${content.appName.toUpperCase()} v${content.version}`)),\n\t\ttheme.fg(\"success\", \"CORE:READY\"),\n\t\ttheme.fg(\"mdCode\", \"ANSI:ON\"),\n\t\ttheme.fg(\"warning\", \"ASCII:ARMED\"),\n\t\ttheme.fg(\"muted\", \"THEME:LIVE\"),\n\t];\n\treturn textLine(width, segments.join(theme.fg(\"borderMuted\", \" | \")));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"control-panel-layout.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-layout.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAkB,KAAK,SAAS,EAA0B,MAAM,6BAA6B,CAAC;AAgBrG,eAAO,MAAM,uBAAuB,UAKnC,CAAC;AAMF,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,MAAM,CAAC;IAClC,oBAAoB,EAAE,MAAM,MAAM,CAAC;IACnC,iBAAiB,EAAE,MAAM,MAAM,CAAC;IAChC,UAAU,EAAE,MAAM,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,0BAA0B,CAAC;CAClD;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,sBAAsB,CAAC;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC;CAC/B;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EAAE,GACpB,MAAM,EAAE,CAGV","sourcesContent":["import { visibleWidth } from \"omk-tui\";\nimport { getHeadroomRuntimeStatus } from \"../../../core/context-budget-headroom.ts\";\nimport { loadMcpInventory } from \"../../../core/mcp-inventory.ts\";\nimport type { PiPackageIntakeSummary } from \"../../../core/pi-package-intake.ts\";\nimport { nextActiveTodo, type TodoState, summary as todoSummary } from \"../../../core/todo-state.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport {\n\tboxBlankLine,\n\tboxBottom,\n\tboxCenteredLine,\n\tboxTextLine,\n\tboxTop,\n\tcenterLine,\n\tcomposeColumns,\n\tdivider,\n\tfitLine,\n\tsidebarRule,\n\ttextLine,\n} from \"./control-panel-box.ts\";\n\nexport const CONTROL_PANEL_ASCII_ART = [\n\t\" ____ __ __ __ __\",\n\t\" / __ \\\\ / |/ / / //_/\",\n\t\" / /_/ // /|_/ / / ,< \",\n\t\" \\\\____//_/ /_/ /_/|_| \",\n];\nconst META_WIDTH = 31;\nconst DECK_MIN_WIDTH = 112;\nconst SIDEBAR_WIDTH = 38;\nconst GAP_WIDTH = 2;\n\nexport interface ControlPanelContent {\n\tappName: string;\n\tversion: string;\n\tcompactInstructions: () => string;\n\texpandedInstructions: () => string;\n\tcompactOnboarding: () => string;\n\tonboarding: () => string;\n\tstatusSnapshot?: () => ControlPanelStatusSnapshot;\n}\n\nexport interface ControlPanelStatusSnapshot {\n\treadonly modelId?: string;\n\treadonly modelProvider?: string;\n\treadonly thinkingLevel?: string;\n\treadonly contextPercent?: number | null;\n\treadonly contextWindowTokens?: number;\n\treadonly contextTokens?: number | null;\n\treadonly headroomStatus?: string;\n\treadonly optimizerPolicy?: string;\n\treadonly skillCount?: number;\n\treadonly mcpCount?: number;\n\treadonly packageIntake?: PiPackageIntakeSummary;\n\treadonly cwdLabel?: string;\n\treadonly gitBranch?: string | null;\n\treadonly todoState?: TodoState;\n}\n\nexport function renderControlPanelLayout(\n\tcontent: ControlPanelContent,\n\texpanded: boolean,\n\twidth: number,\n\tbannerFrame?: string[],\n): string[] {\n\tif (width <= 0) return [];\n\treturn expanded ? renderExpanded(content, width, bannerFrame) : renderCompact(content, width, bannerFrame);\n}\n\nfunction renderCompact(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tif (width >= DECK_MIN_WIDTH) {\n\t\tconst deck = renderDeck(content, width, bannerFrame);\n\t\tif (deck.length > 0) return deck;\n\t}\n\treturn [\n\t\tdivider(width, \"OMK//CONTROL PANEL\", \"accent\"),\n\t\tstatusLine(content, width),\n\t\ttextLine(width, content.compactInstructions()),\n\t\ttextLine(width, content.compactOnboarding(), \"dim\"),\n\t];\n}\n\nfunction renderDeck(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tconst sidebarWidth = Math.min(SIDEBAR_WIDTH, Math.max(34, Math.floor(width * 0.28)));\n\tconst leftWidth = width - GAP_WIDTH - sidebarWidth;\n\tif (leftWidth < 72) return [];\n\tconst lines = composeColumns(\n\t\theroPanel(content, leftWidth, bannerFrame),\n\t\tleftWidth,\n\t\tsidebarPanel(content, sidebarWidth),\n\t\tsidebarWidth,\n\t\tGAP_WIDTH,\n\t\twidth,\n\t);\n\tlines.push(controlStripLine(content, width));\n\tlines.push(centerLine(width, content.compactOnboarding(), \"dim\"));\n\treturn lines;\n}\n\nfunction heroPanel(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\tconst modelLabel = modelStatusLabel(statusSnapshot(content));\n\treturn [\n\t\tboxTop(width, `omk v${content.version} · OMK//CONTROL`),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK\"))),\n\t\tboxCenteredLine(width, theme.fg(\"muted\", \"route · verify · loop · control\")),\n\t\tboxBlankLine(width),\n\t\t...(bannerFrame ?? CONTROL_PANEL_ASCII_ART).map((line, index) => {\n\t\t\tif (bannerFrame) return boxCenteredLine(width, line);\n\t\t\tconst color: ThemeColor =\n\t\t\t\tindex === 0 ? \"accent\" : index === 1 ? \"warning\" : index === 2 ? \"success\" : \"mdCode\";\n\t\t\treturn boxCenteredLine(width, theme.fg(color, line));\n\t\t}),\n\t\tboxBlankLine(width),\n\t\tboxCenteredLine(width, `${theme.fg(\"warning\", \"*\")} ${theme.bold(\"OMK\")} / ${theme.fg(\"success\", modelLabel)}`),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"mdCode\", \"<>\")} omk-control · ${theme.fg(\"accent\", \"route\")} · ${theme.fg(\"warning\", \"verify\")} · ${theme.fg(\"success\", \"loop\")} · ${theme.fg(\"mdCode\", themeName)}`,\n\t\t),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction sidebarPanel(content: ControlPanelContent, width: number): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst headroomLabel = snapshot.headroomStatus ?? snapshot.optimizerPolicy ?? getHeadroomRuntimeStatus().policyId;\n\tconst mcpCount = snapshot.mcpCount ?? loadMcpInventory().entries.length;\n\tconst skillCount = snapshot.skillCount ?? 0;\n\tconst packageIntakeLabel = packageIntakeStatusLabel(snapshot.packageIntake);\n\treturn [\n\t\tsidebarTabs(width),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK://CONTROL\"))),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"warning\", \"CYBERPUNK OPS CORE\"))),\n\t\tboxCenteredLine(width, `${theme.fg(\"accent\", \"MATRIX RAIN\")} // ${theme.fg(\"success\", \"NEON GRID ONLINE\")}`),\n\t\tboxCenteredLine(width, theme.fg(\"mdCode\", \"NIGHT-CITY-MATRIX-V3\")),\n\t\tsidebarRule(width, \"STATUS\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"state:\")} ${theme.fg(\"success\", \"* ready\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} route · evidence · loop · control`),\n\t\tsidebarRule(width, \"TODO\"),\n\t\t...todoSidebarLines(snapshot.todoState, width),\n\t\tsidebarRule(width, \"SESSION\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"cwd:\")} ${snapshot.cwdLabel ?? \"?\"}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"git:\")} ${snapshot.gitBranch ?? \"?\"}`),\n\t\tsidebarRule(width, \"MODEL / CTX\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"model:\")} ${modelStatusLabel(snapshot)}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"think:\")} ${snapshot.thinkingLevel ?? \"off\"}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"ctx:\")} ${theme.fg(\"success\", contextStatusLabel(snapshot))}`),\n\t\tsidebarRule(width, \"RUNTIME / MCP / SKILLS\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"headroom:\")} ${headroomLabel}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"omk:\")} DAG:omk-parallel-orchestrator`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"res:\")} MCP:${mcpCount} skills:${skillCount}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"pkg:\")} ${packageIntakeLabel}`),\n\t\tsidebarRule(width, \"CONTROL\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${theme.fg(\"success\", \"armed\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"verify:\")} ${theme.fg(\"success\", \"evidence gated\")}`),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction todoSidebarLines(state: TodoState | undefined, width: number): string[] {\n\tif (!state || state.items.length === 0) {\n\t\treturn [\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"todo:\")} empty`),\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"next:\")} no active todos`),\n\t\t];\n\t}\n\tconst counts = todoSummary(state);\n\tconst next = nextActiveTodo(state);\n\treturn [\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"todo:\")} ${counts.done}/${counts.total} done`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"next:\")} ${next?.label ?? \"complete\"}`),\n\t];\n}\n\nfunction packageIntakeStatusLabel(summary: PiPackageIntakeSummary | undefined): string {\n\tif (!summary) return \"ports:pending\";\n\tconst ready = summary.acceptedNative + summary.acceptedReference + summary.acceptedMeasurement;\n\tconst review = summary.deferred + summary.reject;\n\treturn `ports:${ready}/${summary.total} review:${review} block:${summary.hardForkBlocked}`;\n}\n\nfunction renderExpanded(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tconst lines = [divider(width, \"OMK//CONTROL PANEL\", \"accent\"), statusLine(content, width)];\n\tif (width >= 32) lines.push(...brandLines(width, bannerFrame));\n\tlines.push(divider(width, \"SYSTEM MAP\", \"mdHeading\"));\n\tfor (const instruction of content.expandedInstructions().split(\"\\n\")) lines.push(textLine(width, instruction));\n\tlines.push(divider(width, \"STARTUP LINK\", \"success\"));\n\tfor (const onboardingLine of content.onboarding().split(\"\\n\")) lines.push(textLine(width, onboardingLine, \"dim\"));\n\tlines.push(divider(width, \"END\", \"borderMuted\"));\n\treturn lines;\n}\n\nfunction statusSnapshot(content: ControlPanelContent): ControlPanelStatusSnapshot {\n\treturn content.statusSnapshot?.() ?? {};\n}\n\nfunction modelStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\treturn snapshot.modelProvider ? `${snapshot.modelProvider}/${snapshot.modelId}` : snapshot.modelId;\n}\n\nfunction contextStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst windowTokens = snapshot.contextWindowTokens ?? 0;\n\tconst windowLabel = windowTokens > 0 ? formatTokens(windowTokens) : \"?\";\n\treturn snapshot.contextPercent === null || snapshot.contextPercent === undefined\n\t\t? `?/${windowLabel}`\n\t\t: `${snapshot.contextPercent.toFixed(1)}%/${windowLabel}`;\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nfunction sidebarTabs(width: number): string {\n\treturn boxTextLine(\n\t\twidth,\n\t\tfitLine(\n\t\t\t`${theme.bold(theme.fg(\"accent\", \"1:CONTROL\"))} ${theme.fg(\"muted\", \"2:HISTORY\")}`,\n\t\t\tMath.max(0, width - 4),\n\t\t),\n\t);\n}\n\nfunction controlStripLine(content: ControlPanelContent, width: number): string {\n\treturn centerLine(\n\t\twidth,\n\t\t`${theme.bold(theme.fg(\"accent\", \"OMK//CONTROL READ\"))} ${theme.fg(\"accent\", \"route/verify/loop/control\")} · ${content.compactInstructions()}`,\n\t);\n}\n\nfunction brandLines(width: number, bannerFrame?: string[]): string[] {\n\tconst art = bannerFrame ?? CONTROL_PANEL_ASCII_ART;\n\tif (bannerFrame) return art.map((line) => textLine(width, line));\n\tconst leftWidth = Math.max(...art.map((line) => visibleWidth(line)));\n\tconst minWideWidth = visibleWidth(\"| \") + leftWidth + visibleWidth(\" | \") + META_WIDTH;\n\tif (width < minWideWidth) return art.map((line) => textLine(width, theme.fg(\"accent\", line)));\n\treturn art.map((line, index) =>\n\t\ttextLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"accent\", line.padEnd(leftWidth))}${theme.fg(\"borderMuted\", \" | \")}${metadataLines()[index] ?? \"\"}`,\n\t\t),\n\t);\n}\n\nfunction metadataLines(): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\treturn [\n\t\t`${theme.fg(\"mdCode\", \"PANEL\")} ${theme.fg(\"success\", \"ONLINE\")}`,\n\t\t`${theme.fg(\"mdCode\", \"THEME\")} ${theme.fg(\"accent\", themeName)}`,\n\t\t`${theme.fg(\"mdCode\", \"STARTUP\")} ${theme.fg(\"warning\", \"ARMED\")}`,\n\t\t`${theme.fg(\"mdCode\", \"LINK\")} ${theme.fg(\"success\", \"READY\")}`,\n\t];\n}\n\nfunction statusLine(content: ControlPanelContent, width: number): string {\n\tconst segments = [\n\t\ttheme.bold(theme.fg(\"accent\", `${content.appName.toUpperCase()} v${content.version}`)),\n\t\ttheme.fg(\"success\", \"CORE:READY\"),\n\t\ttheme.fg(\"mdCode\", \"ANSI:ON\"),\n\t\ttheme.fg(\"warning\", \"ASCII:ARMED\"),\n\t\ttheme.fg(\"muted\", \"THEME:LIVE\"),\n\t];\n\treturn textLine(width, segments.join(theme.fg(\"borderMuted\", \" | \")));\n}\n"]}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { visibleWidth } from "omk-tui";
|
|
2
2
|
import { getHeadroomRuntimeStatus } from "../../../core/context-budget-headroom.js";
|
|
3
3
|
import { loadMcpInventory } from "../../../core/mcp-inventory.js";
|
|
4
|
+
import { nextActiveTodo, summary as todoSummary } from "../../../core/todo-state.js";
|
|
4
5
|
import { theme } from "../theme/theme.js";
|
|
5
6
|
import { boxBlankLine, boxBottom, boxCenteredLine, boxTextLine, boxTop, centerLine, composeColumns, divider, fitLine, sidebarRule, textLine, } from "./control-panel-box.js";
|
|
6
|
-
const
|
|
7
|
+
export const CONTROL_PANEL_ASCII_ART = [
|
|
7
8
|
" ____ __ __ __ __",
|
|
8
9
|
" / __ \\ / |/ / / //_/",
|
|
9
10
|
" / /_/ // /|_/ / / ,< ",
|
|
@@ -13,14 +14,14 @@ const META_WIDTH = 31;
|
|
|
13
14
|
const DECK_MIN_WIDTH = 112;
|
|
14
15
|
const SIDEBAR_WIDTH = 38;
|
|
15
16
|
const GAP_WIDTH = 2;
|
|
16
|
-
export function renderControlPanelLayout(content, expanded, width) {
|
|
17
|
+
export function renderControlPanelLayout(content, expanded, width, bannerFrame) {
|
|
17
18
|
if (width <= 0)
|
|
18
19
|
return [];
|
|
19
|
-
return expanded ? renderExpanded(content, width) : renderCompact(content, width);
|
|
20
|
+
return expanded ? renderExpanded(content, width, bannerFrame) : renderCompact(content, width, bannerFrame);
|
|
20
21
|
}
|
|
21
|
-
function renderCompact(content, width) {
|
|
22
|
+
function renderCompact(content, width, bannerFrame) {
|
|
22
23
|
if (width >= DECK_MIN_WIDTH) {
|
|
23
|
-
const deck = renderDeck(content, width);
|
|
24
|
+
const deck = renderDeck(content, width, bannerFrame);
|
|
24
25
|
if (deck.length > 0)
|
|
25
26
|
return deck;
|
|
26
27
|
}
|
|
@@ -31,17 +32,17 @@ function renderCompact(content, width) {
|
|
|
31
32
|
textLine(width, content.compactOnboarding(), "dim"),
|
|
32
33
|
];
|
|
33
34
|
}
|
|
34
|
-
function renderDeck(content, width) {
|
|
35
|
+
function renderDeck(content, width, bannerFrame) {
|
|
35
36
|
const sidebarWidth = Math.min(SIDEBAR_WIDTH, Math.max(34, Math.floor(width * 0.28)));
|
|
36
37
|
const leftWidth = width - GAP_WIDTH - sidebarWidth;
|
|
37
38
|
if (leftWidth < 72)
|
|
38
39
|
return [];
|
|
39
|
-
const lines = composeColumns(heroPanel(content, leftWidth), leftWidth, sidebarPanel(content, sidebarWidth), sidebarWidth, GAP_WIDTH, width);
|
|
40
|
+
const lines = composeColumns(heroPanel(content, leftWidth, bannerFrame), leftWidth, sidebarPanel(content, sidebarWidth), sidebarWidth, GAP_WIDTH, width);
|
|
40
41
|
lines.push(controlStripLine(content, width));
|
|
41
42
|
lines.push(centerLine(width, content.compactOnboarding(), "dim"));
|
|
42
43
|
return lines;
|
|
43
44
|
}
|
|
44
|
-
function heroPanel(content, width) {
|
|
45
|
+
function heroPanel(content, width, bannerFrame) {
|
|
45
46
|
const rawThemeName = theme.name ?? "live";
|
|
46
47
|
const themeName = (rawThemeName.startsWith("omk-") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();
|
|
47
48
|
const modelLabel = modelStatusLabel(statusSnapshot(content));
|
|
@@ -50,7 +51,9 @@ function heroPanel(content, width) {
|
|
|
50
51
|
boxCenteredLine(width, theme.bold(theme.fg("accent", "OMK"))),
|
|
51
52
|
boxCenteredLine(width, theme.fg("muted", "route · verify · loop · control")),
|
|
52
53
|
boxBlankLine(width),
|
|
53
|
-
...
|
|
54
|
+
...(bannerFrame ?? CONTROL_PANEL_ASCII_ART).map((line, index) => {
|
|
55
|
+
if (bannerFrame)
|
|
56
|
+
return boxCenteredLine(width, line);
|
|
54
57
|
const color = index === 0 ? "accent" : index === 1 ? "warning" : index === 2 ? "success" : "mdCode";
|
|
55
58
|
return boxCenteredLine(width, theme.fg(color, line));
|
|
56
59
|
}),
|
|
@@ -65,6 +68,7 @@ function sidebarPanel(content, width) {
|
|
|
65
68
|
const headroomLabel = snapshot.headroomStatus ?? snapshot.optimizerPolicy ?? getHeadroomRuntimeStatus().policyId;
|
|
66
69
|
const mcpCount = snapshot.mcpCount ?? loadMcpInventory().entries.length;
|
|
67
70
|
const skillCount = snapshot.skillCount ?? 0;
|
|
71
|
+
const packageIntakeLabel = packageIntakeStatusLabel(snapshot.packageIntake);
|
|
68
72
|
return [
|
|
69
73
|
sidebarTabs(width),
|
|
70
74
|
boxCenteredLine(width, theme.bold(theme.fg("accent", "OMK://CONTROL"))),
|
|
@@ -75,7 +79,10 @@ function sidebarPanel(content, width) {
|
|
|
75
79
|
boxTextLine(width, `${theme.fg("muted", "state:")} ${theme.fg("success", "* ready")}`),
|
|
76
80
|
boxTextLine(width, `${theme.fg("muted", "route:")} route · evidence · loop · control`),
|
|
77
81
|
sidebarRule(width, "TODO"),
|
|
78
|
-
|
|
82
|
+
...todoSidebarLines(snapshot.todoState, width),
|
|
83
|
+
sidebarRule(width, "SESSION"),
|
|
84
|
+
boxTextLine(width, `${theme.fg("muted", "cwd:")} ${snapshot.cwdLabel ?? "?"}`),
|
|
85
|
+
boxTextLine(width, `${theme.fg("muted", "git:")} ${snapshot.gitBranch ?? "?"}`),
|
|
79
86
|
sidebarRule(width, "MODEL / CTX"),
|
|
80
87
|
boxTextLine(width, `${theme.fg("muted", "model:")} ${modelStatusLabel(snapshot)}`),
|
|
81
88
|
boxTextLine(width, `${theme.fg("muted", "think:")} ${snapshot.thinkingLevel ?? "off"}`),
|
|
@@ -84,16 +91,38 @@ function sidebarPanel(content, width) {
|
|
|
84
91
|
boxTextLine(width, `${theme.fg("muted", "headroom:")} ${headroomLabel}`),
|
|
85
92
|
boxTextLine(width, `${theme.fg("muted", "omk:")} DAG:omk-parallel-orchestrator`),
|
|
86
93
|
boxTextLine(width, `${theme.fg("muted", "res:")} MCP:${mcpCount} skills:${skillCount}`),
|
|
94
|
+
boxTextLine(width, `${theme.fg("muted", "pkg:")} ${packageIntakeLabel}`),
|
|
87
95
|
sidebarRule(width, "CONTROL"),
|
|
88
96
|
boxTextLine(width, `${theme.fg("muted", "route:")} ${theme.fg("success", "armed")}`),
|
|
89
97
|
boxTextLine(width, `${theme.fg("muted", "verify:")} ${theme.fg("success", "evidence gated")}`),
|
|
90
98
|
boxBottom(width),
|
|
91
99
|
];
|
|
92
100
|
}
|
|
93
|
-
function
|
|
101
|
+
function todoSidebarLines(state, width) {
|
|
102
|
+
if (!state || state.items.length === 0) {
|
|
103
|
+
return [
|
|
104
|
+
boxTextLine(width, `${theme.fg("muted", "todo:")} empty`),
|
|
105
|
+
boxTextLine(width, `${theme.fg("muted", "next:")} no active todos`),
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
const counts = todoSummary(state);
|
|
109
|
+
const next = nextActiveTodo(state);
|
|
110
|
+
return [
|
|
111
|
+
boxTextLine(width, `${theme.fg("muted", "todo:")} ${counts.done}/${counts.total} done`),
|
|
112
|
+
boxTextLine(width, `${theme.fg("muted", "next:")} ${next?.label ?? "complete"}`),
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
function packageIntakeStatusLabel(summary) {
|
|
116
|
+
if (!summary)
|
|
117
|
+
return "ports:pending";
|
|
118
|
+
const ready = summary.acceptedNative + summary.acceptedReference + summary.acceptedMeasurement;
|
|
119
|
+
const review = summary.deferred + summary.reject;
|
|
120
|
+
return `ports:${ready}/${summary.total} review:${review} block:${summary.hardForkBlocked}`;
|
|
121
|
+
}
|
|
122
|
+
function renderExpanded(content, width, bannerFrame) {
|
|
94
123
|
const lines = [divider(width, "OMK//CONTROL PANEL", "accent"), statusLine(content, width)];
|
|
95
124
|
if (width >= 32)
|
|
96
|
-
lines.push(...brandLines(width));
|
|
125
|
+
lines.push(...brandLines(width, bannerFrame));
|
|
97
126
|
lines.push(divider(width, "SYSTEM MAP", "mdHeading"));
|
|
98
127
|
for (const instruction of content.expandedInstructions().split("\n"))
|
|
99
128
|
lines.push(textLine(width, instruction));
|
|
@@ -135,12 +164,15 @@ function sidebarTabs(width) {
|
|
|
135
164
|
function controlStripLine(content, width) {
|
|
136
165
|
return centerLine(width, `${theme.bold(theme.fg("accent", "OMK//CONTROL READ"))} ${theme.fg("accent", "route/verify/loop/control")} · ${content.compactInstructions()}`);
|
|
137
166
|
}
|
|
138
|
-
function brandLines(width) {
|
|
139
|
-
const
|
|
167
|
+
function brandLines(width, bannerFrame) {
|
|
168
|
+
const art = bannerFrame ?? CONTROL_PANEL_ASCII_ART;
|
|
169
|
+
if (bannerFrame)
|
|
170
|
+
return art.map((line) => textLine(width, line));
|
|
171
|
+
const leftWidth = Math.max(...art.map((line) => visibleWidth(line)));
|
|
140
172
|
const minWideWidth = visibleWidth("| ") + leftWidth + visibleWidth(" | ") + META_WIDTH;
|
|
141
173
|
if (width < minWideWidth)
|
|
142
|
-
return
|
|
143
|
-
return
|
|
174
|
+
return art.map((line) => textLine(width, theme.fg("accent", line)));
|
|
175
|
+
return art.map((line, index) => textLine(width, `${theme.fg("accent", line.padEnd(leftWidth))}${theme.fg("borderMuted", " | ")}${metadataLines()[index] ?? ""}`));
|
|
144
176
|
}
|
|
145
177
|
function metadataLines() {
|
|
146
178
|
const rawThemeName = theme.name ?? "live";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-panel-layout.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { visibleWidth } from \"omk-tui\";\nimport { getHeadroomRuntimeStatus } from \"../../../core/context-budget-headroom.ts\";\nimport { loadMcpInventory } from \"../../../core/mcp-inventory.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport {\n\tboxBlankLine,\n\tboxBottom,\n\tboxCenteredLine,\n\tboxTextLine,\n\tboxTop,\n\tcenterLine,\n\tcomposeColumns,\n\tdivider,\n\tfitLine,\n\tsidebarRule,\n\ttextLine,\n} from \"./control-panel-box.ts\";\n\nconst ASCII = [\n\t\" ____ __ __ __ __\",\n\t\" / __ \\\\ / |/ / / //_/\",\n\t\" / /_/ // /|_/ / / ,< \",\n\t\" \\\\____//_/ /_/ /_/|_| \",\n];\nconst META_WIDTH = 31;\nconst DECK_MIN_WIDTH = 112;\nconst SIDEBAR_WIDTH = 38;\nconst GAP_WIDTH = 2;\n\nexport interface ControlPanelContent {\n\tappName: string;\n\tversion: string;\n\tcompactInstructions: () => string;\n\texpandedInstructions: () => string;\n\tcompactOnboarding: () => string;\n\tonboarding: () => string;\n\tstatusSnapshot?: () => ControlPanelStatusSnapshot;\n}\n\nexport interface ControlPanelStatusSnapshot {\n\treadonly modelId?: string;\n\treadonly modelProvider?: string;\n\treadonly thinkingLevel?: string;\n\treadonly contextPercent?: number | null;\n\treadonly contextWindowTokens?: number;\n\treadonly contextTokens?: number | null;\n\treadonly headroomStatus?: string;\n\treadonly optimizerPolicy?: string;\n\treadonly skillCount?: number;\n\treadonly mcpCount?: number;\n}\n\nexport function renderControlPanelLayout(content: ControlPanelContent, expanded: boolean, width: number): string[] {\n\tif (width <= 0) return [];\n\treturn expanded ? renderExpanded(content, width) : renderCompact(content, width);\n}\n\nfunction renderCompact(content: ControlPanelContent, width: number): string[] {\n\tif (width >= DECK_MIN_WIDTH) {\n\t\tconst deck = renderDeck(content, width);\n\t\tif (deck.length > 0) return deck;\n\t}\n\treturn [\n\t\tdivider(width, \"OMK//CONTROL PANEL\", \"accent\"),\n\t\tstatusLine(content, width),\n\t\ttextLine(width, content.compactInstructions()),\n\t\ttextLine(width, content.compactOnboarding(), \"dim\"),\n\t];\n}\n\nfunction renderDeck(content: ControlPanelContent, width: number): string[] {\n\tconst sidebarWidth = Math.min(SIDEBAR_WIDTH, Math.max(34, Math.floor(width * 0.28)));\n\tconst leftWidth = width - GAP_WIDTH - sidebarWidth;\n\tif (leftWidth < 72) return [];\n\tconst lines = composeColumns(\n\t\theroPanel(content, leftWidth),\n\t\tleftWidth,\n\t\tsidebarPanel(content, sidebarWidth),\n\t\tsidebarWidth,\n\t\tGAP_WIDTH,\n\t\twidth,\n\t);\n\tlines.push(controlStripLine(content, width));\n\tlines.push(centerLine(width, content.compactOnboarding(), \"dim\"));\n\treturn lines;\n}\n\nfunction heroPanel(content: ControlPanelContent, width: number): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\tconst modelLabel = modelStatusLabel(statusSnapshot(content));\n\treturn [\n\t\tboxTop(width, `omk v${content.version} · OMK//CONTROL`),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK\"))),\n\t\tboxCenteredLine(width, theme.fg(\"muted\", \"route · verify · loop · control\")),\n\t\tboxBlankLine(width),\n\t\t...ASCII.map((line, index) => {\n\t\t\tconst color: ThemeColor =\n\t\t\t\tindex === 0 ? \"accent\" : index === 1 ? \"warning\" : index === 2 ? \"success\" : \"mdCode\";\n\t\t\treturn boxCenteredLine(width, theme.fg(color, line));\n\t\t}),\n\t\tboxBlankLine(width),\n\t\tboxCenteredLine(width, `${theme.fg(\"warning\", \"*\")} ${theme.bold(\"OMK\")} / ${theme.fg(\"success\", modelLabel)}`),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"mdCode\", \"<>\")} omk-control · ${theme.fg(\"accent\", \"route\")} · ${theme.fg(\"warning\", \"verify\")} · ${theme.fg(\"success\", \"loop\")} · ${theme.fg(\"mdCode\", themeName)}`,\n\t\t),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction sidebarPanel(content: ControlPanelContent, width: number): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst headroomLabel = snapshot.headroomStatus ?? snapshot.optimizerPolicy ?? getHeadroomRuntimeStatus().policyId;\n\tconst mcpCount = snapshot.mcpCount ?? loadMcpInventory().entries.length;\n\tconst skillCount = snapshot.skillCount ?? 0;\n\treturn [\n\t\tsidebarTabs(width),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK://CONTROL\"))),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"warning\", \"CYBERPUNK OPS CORE\"))),\n\t\tboxCenteredLine(width, `${theme.fg(\"accent\", \"MATRIX RAIN\")} // ${theme.fg(\"success\", \"NEON GRID ONLINE\")}`),\n\t\tboxCenteredLine(width, theme.fg(\"mdCode\", \"NIGHT-CITY-MATRIX-V3\")),\n\t\tsidebarRule(width, \"STATUS\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"state:\")} ${theme.fg(\"success\", \"* ready\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} route · evidence · loop · control`),\n\t\tsidebarRule(width, \"TODO\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"next:\")} add branch TODOs with /todos`),\n\t\tsidebarRule(width, \"MODEL / CTX\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"model:\")} ${modelStatusLabel(snapshot)}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"think:\")} ${snapshot.thinkingLevel ?? \"off\"}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"ctx:\")} ${theme.fg(\"success\", contextStatusLabel(snapshot))}`),\n\t\tsidebarRule(width, \"RUNTIME / MCP / SKILLS\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"headroom:\")} ${headroomLabel}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"omk:\")} DAG:omk-parallel-orchestrator`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"res:\")} MCP:${mcpCount} skills:${skillCount}`),\n\t\tsidebarRule(width, \"CONTROL\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${theme.fg(\"success\", \"armed\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"verify:\")} ${theme.fg(\"success\", \"evidence gated\")}`),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction renderExpanded(content: ControlPanelContent, width: number): string[] {\n\tconst lines = [divider(width, \"OMK//CONTROL PANEL\", \"accent\"), statusLine(content, width)];\n\tif (width >= 32) lines.push(...brandLines(width));\n\tlines.push(divider(width, \"SYSTEM MAP\", \"mdHeading\"));\n\tfor (const instruction of content.expandedInstructions().split(\"\\n\")) lines.push(textLine(width, instruction));\n\tlines.push(divider(width, \"STARTUP LINK\", \"success\"));\n\tfor (const onboardingLine of content.onboarding().split(\"\\n\")) lines.push(textLine(width, onboardingLine, \"dim\"));\n\tlines.push(divider(width, \"END\", \"borderMuted\"));\n\treturn lines;\n}\n\nfunction statusSnapshot(content: ControlPanelContent): ControlPanelStatusSnapshot {\n\treturn content.statusSnapshot?.() ?? {};\n}\n\nfunction modelStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\treturn snapshot.modelProvider ? `${snapshot.modelProvider}/${snapshot.modelId}` : snapshot.modelId;\n}\n\nfunction contextStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst windowTokens = snapshot.contextWindowTokens ?? 0;\n\tconst windowLabel = windowTokens > 0 ? formatTokens(windowTokens) : \"?\";\n\treturn snapshot.contextPercent === null || snapshot.contextPercent === undefined\n\t\t? `?/${windowLabel}`\n\t\t: `${snapshot.contextPercent.toFixed(1)}%/${windowLabel}`;\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nfunction sidebarTabs(width: number): string {\n\treturn boxTextLine(\n\t\twidth,\n\t\tfitLine(\n\t\t\t`${theme.bold(theme.fg(\"accent\", \"1:CONTROL\"))} ${theme.fg(\"muted\", \"2:HISTORY\")}`,\n\t\t\tMath.max(0, width - 4),\n\t\t),\n\t);\n}\n\nfunction controlStripLine(content: ControlPanelContent, width: number): string {\n\treturn centerLine(\n\t\twidth,\n\t\t`${theme.bold(theme.fg(\"accent\", \"OMK//CONTROL READ\"))} ${theme.fg(\"accent\", \"route/verify/loop/control\")} · ${content.compactInstructions()}`,\n\t);\n}\n\nfunction brandLines(width: number): string[] {\n\tconst leftWidth = Math.max(...ASCII.map((line) => visibleWidth(line)));\n\tconst minWideWidth = visibleWidth(\"| \") + leftWidth + visibleWidth(\" | \") + META_WIDTH;\n\tif (width < minWideWidth) return ASCII.map((line) => textLine(width, theme.fg(\"accent\", line)));\n\treturn ASCII.map((line, index) =>\n\t\ttextLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"accent\", line.padEnd(leftWidth))}${theme.fg(\"borderMuted\", \" | \")}${metadataLines()[index] ?? \"\"}`,\n\t\t),\n\t);\n}\n\nfunction metadataLines(): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\treturn [\n\t\t`${theme.fg(\"mdCode\", \"PANEL\")} ${theme.fg(\"success\", \"ONLINE\")}`,\n\t\t`${theme.fg(\"mdCode\", \"THEME\")} ${theme.fg(\"accent\", themeName)}`,\n\t\t`${theme.fg(\"mdCode\", \"STARTUP\")} ${theme.fg(\"warning\", \"ARMED\")}`,\n\t\t`${theme.fg(\"mdCode\", \"LINK\")} ${theme.fg(\"success\", \"READY\")}`,\n\t];\n}\n\nfunction statusLine(content: ControlPanelContent, width: number): string {\n\tconst segments = [\n\t\ttheme.bold(theme.fg(\"accent\", `${content.appName.toUpperCase()} v${content.version}`)),\n\t\ttheme.fg(\"success\", \"CORE:READY\"),\n\t\ttheme.fg(\"mdCode\", \"ANSI:ON\"),\n\t\ttheme.fg(\"warning\", \"ASCII:ARMED\"),\n\t\ttheme.fg(\"muted\", \"THEME:LIVE\"),\n\t];\n\treturn textLine(width, segments.join(theme.fg(\"borderMuted\", \" | \")));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"control-panel-layout.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE,OAAO,EAAE,cAAc,EAAkB,OAAO,IAAI,WAAW,EAAE,MAAM,6BAA6B,CAAC;AACrG,OAAO,EAAmB,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EACN,YAAY,EACZ,SAAS,EACT,eAAe,EACf,WAAW,EACX,MAAM,EACN,UAAU,EACV,cAAc,EACd,OAAO,EACP,OAAO,EACP,WAAW,EACX,QAAQ,GACR,MAAM,wBAAwB,CAAC;AAEhC,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACtC,yBAAyB;IACzB,0BAA0B;IAC1B,0BAA0B;IAC1B,2BAA2B;CAC3B,CAAC;AACF,MAAM,UAAU,GAAG,EAAE,CAAC;AACtB,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,SAAS,GAAG,CAAC,CAAC;AA6BpB,MAAM,UAAU,wBAAwB,CACvC,OAA4B,EAC5B,QAAiB,EACjB,KAAa,EACb,WAAsB,EACX;IACX,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1B,OAAO,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;AAAA,CAC3G;AAED,SAAS,aAAa,CAAC,OAA4B,EAAE,KAAa,EAAE,WAAsB,EAAY;IACrG,IAAI,KAAK,IAAI,cAAc,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QACrD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IAClC,CAAC;IACD,OAAO;QACN,OAAO,CAAC,KAAK,EAAE,oBAAoB,EAAE,QAAQ,CAAC;QAC9C,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC;QAC1B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,mBAAmB,EAAE,CAAC;QAC9C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,EAAE,EAAE,KAAK,CAAC;KACnD,CAAC;AAAA,CACF;AAED,SAAS,UAAU,CAAC,OAA4B,EAAE,KAAa,EAAE,WAAsB,EAAY;IAClG,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACrF,MAAM,SAAS,GAAG,KAAK,GAAG,SAAS,GAAG,YAAY,CAAC;IACnD,IAAI,SAAS,GAAG,EAAE;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,KAAK,GAAG,cAAc,CAC3B,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC,EAC1C,SAAS,EACT,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,EACnC,YAAY,EACZ,SAAS,EACT,KAAK,CACL,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC7C,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC;IAClE,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,SAAS,CAAC,OAA4B,EAAE,KAAa,EAAE,WAAsB,EAAY;IACjG,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC;IAC1C,MAAM,SAAS,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IACzG,MAAM,UAAU,GAAG,gBAAgB,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7D,OAAO;QACN,MAAM,CAAC,KAAK,EAAE,QAAQ,OAAO,CAAC,OAAO,kBAAiB,CAAC;QACvD,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;QAC7D,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,oCAAiC,CAAC,CAAC;QAC5E,YAAY,CAAC,KAAK,CAAC;QACnB,GAAG,CAAC,WAAW,IAAI,uBAAuB,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAChE,IAAI,WAAW;gBAAE,OAAO,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACrD,MAAM,KAAK,GACV,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;YACvF,OAAO,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QAAA,CACrD,CAAC;QACF,YAAY,CAAC,KAAK,CAAC;QACnB,eAAe,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,CAAC;QAChH,eAAe,CACd,KAAK,EACL,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,mBAAkB,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAM,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,OAAM,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,OAAM,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,CACjL;QACD,SAAS,CAAC,KAAK,CAAC;KAChB,CAAC;AAAA,CACF;AAED,SAAS,YAAY,CAAC,OAA4B,EAAE,KAAa,EAAY;IAC5E,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,aAAa,GAAG,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,eAAe,IAAI,wBAAwB,EAAE,CAAC,QAAQ,CAAC;IACjH,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,IAAI,gBAAgB,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IACxE,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,IAAI,CAAC,CAAC;IAC5C,MAAM,kBAAkB,GAAG,wBAAwB,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC5E,OAAO;QACN,WAAW,CAAC,KAAK,CAAC;QAClB,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC,CAAC;QACvE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC,CAAC;QAC7E,eAAe,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,CAAC;QAC5G,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;QAClE,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC;QAC5B,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC;QACtF,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,uCAAoC,CAAC;QACtF,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC;QAC1B,GAAG,gBAAgB,CAAC,QAAQ,CAAC,SAAS,EAAE,KAAK,CAAC;QAC9C,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC;QAC7B,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,IAAI,GAAG,EAAE,CAAC;QAC9E,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,QAAQ,CAAC,SAAS,IAAI,GAAG,EAAE,CAAC;QAC/E,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC;QACjC,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClF,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,QAAQ,CAAC,aAAa,IAAI,KAAK,EAAE,CAAC;QACvF,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QACvG,WAAW,CAAC,KAAK,EAAE,wBAAwB,CAAC;QAC5C,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,IAAI,aAAa,EAAE,CAAC;QACxE,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,gCAAgC,CAAC;QAChF,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,QAAQ,WAAW,UAAU,EAAE,CAAC;QACvF,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,kBAAkB,EAAE,CAAC;QACxE,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC;QAC7B,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,CAAC;QACpF,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,gBAAgB,CAAC,EAAE,CAAC;QAC9F,SAAS,CAAC,KAAK,CAAC;KAChB,CAAC;AAAA,CACF;AAED,SAAS,gBAAgB,CAAC,KAA4B,EAAE,KAAa,EAAY;IAChF,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxC,OAAO;YACN,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC;YACzD,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,kBAAkB,CAAC;SACnE,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO;QACN,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,KAAK,OAAO,CAAC;QACvF,WAAW,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE,KAAK,IAAI,UAAU,EAAE,CAAC;KAChF,CAAC;AAAA,CACF;AAED,SAAS,wBAAwB,CAAC,OAA2C,EAAU;IACtF,IAAI,CAAC,OAAO;QAAE,OAAO,eAAe,CAAC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,cAAc,GAAG,OAAO,CAAC,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAC/F,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC;IACjD,OAAO,SAAS,KAAK,IAAI,OAAO,CAAC,KAAK,WAAW,MAAM,UAAU,OAAO,CAAC,eAAe,EAAE,CAAC;AAAA,CAC3F;AAED,SAAS,cAAc,CAAC,OAA4B,EAAE,KAAa,EAAE,WAAsB,EAAY;IACtG,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC3F,IAAI,KAAK,IAAI,EAAE;QAAE,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;IAC/D,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;IACtD,KAAK,MAAM,WAAW,IAAI,OAAO,CAAC,oBAAoB,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC;IAC/G,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC;IACtD,KAAK,MAAM,cAAc,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,CAAC,CAAC,CAAC;IAClH,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC;IACjD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,SAAS,cAAc,CAAC,OAA4B,EAA8B;IACjF,OAAO,OAAO,CAAC,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC;AAAA,CACxC;AAED,SAAS,gBAAgB,CAAC,QAAoC,EAAU;IACvE,IAAI,CAAC,QAAQ,CAAC,OAAO;QAAE,OAAO,UAAU,CAAC;IACzC,OAAO,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,aAAa,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;AAAA,CACnG;AAED,SAAS,kBAAkB,CAAC,QAAoC,EAAU;IACzE,MAAM,YAAY,GAAG,QAAQ,CAAC,mBAAmB,IAAI,CAAC,CAAC;IACvD,MAAM,WAAW,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACxE,OAAO,QAAQ,CAAC,cAAc,KAAK,IAAI,IAAI,QAAQ,CAAC,cAAc,KAAK,SAAS;QAC/E,CAAC,CAAC,KAAK,WAAW,EAAE;QACpB,CAAC,CAAC,GAAG,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,EAAE,CAAC;AAAA,CAC3D;AAED,SAAS,YAAY,CAAC,KAAa,EAAU;IAC5C,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1C,IAAI,KAAK,GAAG,KAAK;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1D,IAAI,KAAK,GAAG,OAAO;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC;IAC3D,IAAI,KAAK,GAAG,QAAQ;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAChE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC;AAAA,CACzC;AAED,SAAS,WAAW,CAAC,KAAa,EAAU;IAC3C,OAAO,WAAW,CACjB,KAAK,EACL,OAAO,CACN,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,EACrF,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CACtB,CACD,CAAC;AAAA,CACF;AAED,SAAS,gBAAgB,CAAC,OAA4B,EAAE,KAAa,EAAU;IAC9E,OAAO,UAAU,CAChB,KAAK,EACL,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,2BAA2B,CAAC,OAAM,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAC9I,CAAC;AAAA,CACF;AAED,SAAS,UAAU,CAAC,KAAa,EAAE,WAAsB,EAAY;IACpE,MAAM,GAAG,GAAG,WAAW,IAAI,uBAAuB,CAAC;IACnD,IAAI,WAAW;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,UAAU,CAAC;IACvF,IAAI,KAAK,GAAG,YAAY;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9F,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC9B,QAAQ,CACP,KAAK,EACL,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,aAAa,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAC/G,CACD,CAAC;AAAA,CACF;AAED,SAAS,aAAa,GAAa;IAClC,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC;IAC1C,MAAM,SAAS,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,WAAW,EAAE,CAAC;IACzG,OAAO;QACN,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,EAAE;QACjE,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE;QACjE,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE;QAClE,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE;KAC/D,CAAC;AAAA,CACF;AAED,SAAS,UAAU,CAAC,OAA4B,EAAE,KAAa,EAAU;IACxE,MAAM,QAAQ,GAAG;QAChB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACtF,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,YAAY,CAAC;QACjC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;QAC7B,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,CAAC;QAClC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC;KAC/B,CAAC;IACF,OAAO,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAAA,CACtE","sourcesContent":["import { visibleWidth } from \"omk-tui\";\nimport { getHeadroomRuntimeStatus } from \"../../../core/context-budget-headroom.ts\";\nimport { loadMcpInventory } from \"../../../core/mcp-inventory.ts\";\nimport type { PiPackageIntakeSummary } from \"../../../core/pi-package-intake.ts\";\nimport { nextActiveTodo, type TodoState, summary as todoSummary } from \"../../../core/todo-state.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport {\n\tboxBlankLine,\n\tboxBottom,\n\tboxCenteredLine,\n\tboxTextLine,\n\tboxTop,\n\tcenterLine,\n\tcomposeColumns,\n\tdivider,\n\tfitLine,\n\tsidebarRule,\n\ttextLine,\n} from \"./control-panel-box.ts\";\n\nexport const CONTROL_PANEL_ASCII_ART = [\n\t\" ____ __ __ __ __\",\n\t\" / __ \\\\ / |/ / / //_/\",\n\t\" / /_/ // /|_/ / / ,< \",\n\t\" \\\\____//_/ /_/ /_/|_| \",\n];\nconst META_WIDTH = 31;\nconst DECK_MIN_WIDTH = 112;\nconst SIDEBAR_WIDTH = 38;\nconst GAP_WIDTH = 2;\n\nexport interface ControlPanelContent {\n\tappName: string;\n\tversion: string;\n\tcompactInstructions: () => string;\n\texpandedInstructions: () => string;\n\tcompactOnboarding: () => string;\n\tonboarding: () => string;\n\tstatusSnapshot?: () => ControlPanelStatusSnapshot;\n}\n\nexport interface ControlPanelStatusSnapshot {\n\treadonly modelId?: string;\n\treadonly modelProvider?: string;\n\treadonly thinkingLevel?: string;\n\treadonly contextPercent?: number | null;\n\treadonly contextWindowTokens?: number;\n\treadonly contextTokens?: number | null;\n\treadonly headroomStatus?: string;\n\treadonly optimizerPolicy?: string;\n\treadonly skillCount?: number;\n\treadonly mcpCount?: number;\n\treadonly packageIntake?: PiPackageIntakeSummary;\n\treadonly cwdLabel?: string;\n\treadonly gitBranch?: string | null;\n\treadonly todoState?: TodoState;\n}\n\nexport function renderControlPanelLayout(\n\tcontent: ControlPanelContent,\n\texpanded: boolean,\n\twidth: number,\n\tbannerFrame?: string[],\n): string[] {\n\tif (width <= 0) return [];\n\treturn expanded ? renderExpanded(content, width, bannerFrame) : renderCompact(content, width, bannerFrame);\n}\n\nfunction renderCompact(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tif (width >= DECK_MIN_WIDTH) {\n\t\tconst deck = renderDeck(content, width, bannerFrame);\n\t\tif (deck.length > 0) return deck;\n\t}\n\treturn [\n\t\tdivider(width, \"OMK//CONTROL PANEL\", \"accent\"),\n\t\tstatusLine(content, width),\n\t\ttextLine(width, content.compactInstructions()),\n\t\ttextLine(width, content.compactOnboarding(), \"dim\"),\n\t];\n}\n\nfunction renderDeck(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tconst sidebarWidth = Math.min(SIDEBAR_WIDTH, Math.max(34, Math.floor(width * 0.28)));\n\tconst leftWidth = width - GAP_WIDTH - sidebarWidth;\n\tif (leftWidth < 72) return [];\n\tconst lines = composeColumns(\n\t\theroPanel(content, leftWidth, bannerFrame),\n\t\tleftWidth,\n\t\tsidebarPanel(content, sidebarWidth),\n\t\tsidebarWidth,\n\t\tGAP_WIDTH,\n\t\twidth,\n\t);\n\tlines.push(controlStripLine(content, width));\n\tlines.push(centerLine(width, content.compactOnboarding(), \"dim\"));\n\treturn lines;\n}\n\nfunction heroPanel(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\tconst modelLabel = modelStatusLabel(statusSnapshot(content));\n\treturn [\n\t\tboxTop(width, `omk v${content.version} · OMK//CONTROL`),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK\"))),\n\t\tboxCenteredLine(width, theme.fg(\"muted\", \"route · verify · loop · control\")),\n\t\tboxBlankLine(width),\n\t\t...(bannerFrame ?? CONTROL_PANEL_ASCII_ART).map((line, index) => {\n\t\t\tif (bannerFrame) return boxCenteredLine(width, line);\n\t\t\tconst color: ThemeColor =\n\t\t\t\tindex === 0 ? \"accent\" : index === 1 ? \"warning\" : index === 2 ? \"success\" : \"mdCode\";\n\t\t\treturn boxCenteredLine(width, theme.fg(color, line));\n\t\t}),\n\t\tboxBlankLine(width),\n\t\tboxCenteredLine(width, `${theme.fg(\"warning\", \"*\")} ${theme.bold(\"OMK\")} / ${theme.fg(\"success\", modelLabel)}`),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"mdCode\", \"<>\")} omk-control · ${theme.fg(\"accent\", \"route\")} · ${theme.fg(\"warning\", \"verify\")} · ${theme.fg(\"success\", \"loop\")} · ${theme.fg(\"mdCode\", themeName)}`,\n\t\t),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction sidebarPanel(content: ControlPanelContent, width: number): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst headroomLabel = snapshot.headroomStatus ?? snapshot.optimizerPolicy ?? getHeadroomRuntimeStatus().policyId;\n\tconst mcpCount = snapshot.mcpCount ?? loadMcpInventory().entries.length;\n\tconst skillCount = snapshot.skillCount ?? 0;\n\tconst packageIntakeLabel = packageIntakeStatusLabel(snapshot.packageIntake);\n\treturn [\n\t\tsidebarTabs(width),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"accent\", \"OMK://CONTROL\"))),\n\t\tboxCenteredLine(width, theme.bold(theme.fg(\"warning\", \"CYBERPUNK OPS CORE\"))),\n\t\tboxCenteredLine(width, `${theme.fg(\"accent\", \"MATRIX RAIN\")} // ${theme.fg(\"success\", \"NEON GRID ONLINE\")}`),\n\t\tboxCenteredLine(width, theme.fg(\"mdCode\", \"NIGHT-CITY-MATRIX-V3\")),\n\t\tsidebarRule(width, \"STATUS\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"state:\")} ${theme.fg(\"success\", \"* ready\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} route · evidence · loop · control`),\n\t\tsidebarRule(width, \"TODO\"),\n\t\t...todoSidebarLines(snapshot.todoState, width),\n\t\tsidebarRule(width, \"SESSION\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"cwd:\")} ${snapshot.cwdLabel ?? \"?\"}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"git:\")} ${snapshot.gitBranch ?? \"?\"}`),\n\t\tsidebarRule(width, \"MODEL / CTX\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"model:\")} ${modelStatusLabel(snapshot)}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"think:\")} ${snapshot.thinkingLevel ?? \"off\"}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"ctx:\")} ${theme.fg(\"success\", contextStatusLabel(snapshot))}`),\n\t\tsidebarRule(width, \"RUNTIME / MCP / SKILLS\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"headroom:\")} ${headroomLabel}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"omk:\")} DAG:omk-parallel-orchestrator`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"res:\")} MCP:${mcpCount} skills:${skillCount}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"pkg:\")} ${packageIntakeLabel}`),\n\t\tsidebarRule(width, \"CONTROL\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${theme.fg(\"success\", \"armed\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"verify:\")} ${theme.fg(\"success\", \"evidence gated\")}`),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction todoSidebarLines(state: TodoState | undefined, width: number): string[] {\n\tif (!state || state.items.length === 0) {\n\t\treturn [\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"todo:\")} empty`),\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"next:\")} no active todos`),\n\t\t];\n\t}\n\tconst counts = todoSummary(state);\n\tconst next = nextActiveTodo(state);\n\treturn [\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"todo:\")} ${counts.done}/${counts.total} done`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"next:\")} ${next?.label ?? \"complete\"}`),\n\t];\n}\n\nfunction packageIntakeStatusLabel(summary: PiPackageIntakeSummary | undefined): string {\n\tif (!summary) return \"ports:pending\";\n\tconst ready = summary.acceptedNative + summary.acceptedReference + summary.acceptedMeasurement;\n\tconst review = summary.deferred + summary.reject;\n\treturn `ports:${ready}/${summary.total} review:${review} block:${summary.hardForkBlocked}`;\n}\n\nfunction renderExpanded(content: ControlPanelContent, width: number, bannerFrame?: string[]): string[] {\n\tconst lines = [divider(width, \"OMK//CONTROL PANEL\", \"accent\"), statusLine(content, width)];\n\tif (width >= 32) lines.push(...brandLines(width, bannerFrame));\n\tlines.push(divider(width, \"SYSTEM MAP\", \"mdHeading\"));\n\tfor (const instruction of content.expandedInstructions().split(\"\\n\")) lines.push(textLine(width, instruction));\n\tlines.push(divider(width, \"STARTUP LINK\", \"success\"));\n\tfor (const onboardingLine of content.onboarding().split(\"\\n\")) lines.push(textLine(width, onboardingLine, \"dim\"));\n\tlines.push(divider(width, \"END\", \"borderMuted\"));\n\treturn lines;\n}\n\nfunction statusSnapshot(content: ControlPanelContent): ControlPanelStatusSnapshot {\n\treturn content.statusSnapshot?.() ?? {};\n}\n\nfunction modelStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\treturn snapshot.modelProvider ? `${snapshot.modelProvider}/${snapshot.modelId}` : snapshot.modelId;\n}\n\nfunction contextStatusLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst windowTokens = snapshot.contextWindowTokens ?? 0;\n\tconst windowLabel = windowTokens > 0 ? formatTokens(windowTokens) : \"?\";\n\treturn snapshot.contextPercent === null || snapshot.contextPercent === undefined\n\t\t? `?/${windowLabel}`\n\t\t: `${snapshot.contextPercent.toFixed(1)}%/${windowLabel}`;\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nfunction sidebarTabs(width: number): string {\n\treturn boxTextLine(\n\t\twidth,\n\t\tfitLine(\n\t\t\t`${theme.bold(theme.fg(\"accent\", \"1:CONTROL\"))} ${theme.fg(\"muted\", \"2:HISTORY\")}`,\n\t\t\tMath.max(0, width - 4),\n\t\t),\n\t);\n}\n\nfunction controlStripLine(content: ControlPanelContent, width: number): string {\n\treturn centerLine(\n\t\twidth,\n\t\t`${theme.bold(theme.fg(\"accent\", \"OMK//CONTROL READ\"))} ${theme.fg(\"accent\", \"route/verify/loop/control\")} · ${content.compactInstructions()}`,\n\t);\n}\n\nfunction brandLines(width: number, bannerFrame?: string[]): string[] {\n\tconst art = bannerFrame ?? CONTROL_PANEL_ASCII_ART;\n\tif (bannerFrame) return art.map((line) => textLine(width, line));\n\tconst leftWidth = Math.max(...art.map((line) => visibleWidth(line)));\n\tconst minWideWidth = visibleWidth(\"| \") + leftWidth + visibleWidth(\" | \") + META_WIDTH;\n\tif (width < minWideWidth) return art.map((line) => textLine(width, theme.fg(\"accent\", line)));\n\treturn art.map((line, index) =>\n\t\ttextLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"accent\", line.padEnd(leftWidth))}${theme.fg(\"borderMuted\", \" | \")}${metadataLines()[index] ?? \"\"}`,\n\t\t),\n\t);\n}\n\nfunction metadataLines(): string[] {\n\tconst rawThemeName = theme.name ?? \"live\";\n\tconst themeName = (rawThemeName.startsWith(\"omk-\") ? rawThemeName.slice(4) : rawThemeName).toUpperCase();\n\treturn [\n\t\t`${theme.fg(\"mdCode\", \"PANEL\")} ${theme.fg(\"success\", \"ONLINE\")}`,\n\t\t`${theme.fg(\"mdCode\", \"THEME\")} ${theme.fg(\"accent\", themeName)}`,\n\t\t`${theme.fg(\"mdCode\", \"STARTUP\")} ${theme.fg(\"warning\", \"ARMED\")}`,\n\t\t`${theme.fg(\"mdCode\", \"LINK\")} ${theme.fg(\"success\", \"READY\")}`,\n\t];\n}\n\nfunction statusLine(content: ControlPanelContent, width: number): string {\n\tconst segments = [\n\t\ttheme.bold(theme.fg(\"accent\", `${content.appName.toUpperCase()} v${content.version}`)),\n\t\ttheme.fg(\"success\", \"CORE:READY\"),\n\t\ttheme.fg(\"mdCode\", \"ANSI:ON\"),\n\t\ttheme.fg(\"warning\", \"ASCII:ARMED\"),\n\t\ttheme.fg(\"muted\", \"THEME:LIVE\"),\n\t];\n\treturn textLine(width, segments.join(theme.fg(\"borderMuted\", \" | \")));\n}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentSession } from "../../../core/agent-session.ts";
|
|
2
|
+
import type { ReadonlyFooterDataProvider } from "../../../core/footer-data-provider.ts";
|
|
2
3
|
import type { SessionManager } from "../../../core/session-manager.ts";
|
|
3
4
|
import type { ControlPanelStatusSnapshot } from "./control-panel-layout.ts";
|
|
4
|
-
export declare function createControlPanelStatusSnapshot(session: AgentSession, sessionManager: SessionManager): ControlPanelStatusSnapshot;
|
|
5
|
+
export declare function createControlPanelStatusSnapshot(session: AgentSession, sessionManager: SessionManager, footerData?: ReadonlyFooterDataProvider): ControlPanelStatusSnapshot;
|
|
5
6
|
//# sourceMappingURL=control-panel-runtime-status.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-panel-runtime-status.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-runtime-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"control-panel-runtime-status.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-runtime-status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAGxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAEvE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAG5E,wBAAgB,gCAAgC,CAC/C,OAAO,EAAE,YAAY,EACrB,cAAc,EAAE,cAAc,EAC9B,UAAU,CAAC,EAAE,0BAA0B,GACrC,0BAA0B,CAsB5B","sourcesContent":["import type { AgentSession } from \"../../../core/agent-session.ts\";\nimport { getHeadroomRuntimeStatus } from \"../../../core/context-budget-headroom.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { loadMcpInventory } from \"../../../core/mcp-inventory.ts\";\nimport { evaluatePiPackageIntake } from \"../../../core/pi-package-intake.ts\";\nimport type { SessionManager } from \"../../../core/session-manager.ts\";\nimport { getCurrentTodoState } from \"../../../core/todo-runtime-state.ts\";\nimport type { ControlPanelStatusSnapshot } from \"./control-panel-layout.ts\";\nimport { formatCwdForFooter } from \"./footer.ts\";\n\nexport function createControlPanelStatusSnapshot(\n\tsession: AgentSession,\n\tsessionManager: SessionManager,\n\tfooterData?: ReadonlyFooterDataProvider,\n): ControlPanelStatusSnapshot {\n\tconst contextUsage = session.getContextUsage();\n\tconst contextWindow = contextUsage?.contextWindow ?? session.state.model?.contextWindow ?? 0;\n\tconst headroom = getHeadroomRuntimeStatus();\n\tconst packageIntake = evaluatePiPackageIntake().summary;\n\tconst cwdLabel = formatCwdForFooter(sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\treturn {\n\t\tmodelId: session.state.model?.id,\n\t\tmodelProvider: session.state.model?.provider,\n\t\tthinkingLevel: session.state.thinkingLevel ?? \"off\",\n\t\tcontextPercent: contextUsage?.percent ?? null,\n\t\tcontextWindowTokens: contextWindow,\n\t\tcontextTokens: contextUsage?.tokens ?? null,\n\t\theadroomStatus: headroom.policyId,\n\t\toptimizerPolicy: headroom.selector,\n\t\tmcpCount: loadMcpInventory(sessionManager.getCwd()).entries.length,\n\t\tskillCount: session.resourceLoader.getSkills().skills.length,\n\t\tpackageIntake,\n\t\tcwdLabel,\n\t\tgitBranch: footerData?.getGitBranch(),\n\t\ttodoState: getCurrentTodoState(),\n\t};\n}\n"]}
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { getHeadroomRuntimeStatus } from "../../../core/context-budget-headroom.js";
|
|
2
2
|
import { loadMcpInventory } from "../../../core/mcp-inventory.js";
|
|
3
|
-
|
|
3
|
+
import { evaluatePiPackageIntake } from "../../../core/pi-package-intake.js";
|
|
4
|
+
import { getCurrentTodoState } from "../../../core/todo-runtime-state.js";
|
|
5
|
+
import { formatCwdForFooter } from "./footer.js";
|
|
6
|
+
export function createControlPanelStatusSnapshot(session, sessionManager, footerData) {
|
|
4
7
|
const contextUsage = session.getContextUsage();
|
|
5
8
|
const contextWindow = contextUsage?.contextWindow ?? session.state.model?.contextWindow ?? 0;
|
|
6
9
|
const headroom = getHeadroomRuntimeStatus();
|
|
10
|
+
const packageIntake = evaluatePiPackageIntake().summary;
|
|
11
|
+
const cwdLabel = formatCwdForFooter(sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);
|
|
7
12
|
return {
|
|
8
13
|
modelId: session.state.model?.id,
|
|
9
14
|
modelProvider: session.state.model?.provider,
|
|
@@ -15,6 +20,10 @@ export function createControlPanelStatusSnapshot(session, sessionManager) {
|
|
|
15
20
|
optimizerPolicy: headroom.selector,
|
|
16
21
|
mcpCount: loadMcpInventory(sessionManager.getCwd()).entries.length,
|
|
17
22
|
skillCount: session.resourceLoader.getSkills().skills.length,
|
|
23
|
+
packageIntake,
|
|
24
|
+
cwdLabel,
|
|
25
|
+
gitBranch: footerData?.getGitBranch(),
|
|
26
|
+
todoState: getCurrentTodoState(),
|
|
18
27
|
};
|
|
19
28
|
}
|
|
20
29
|
//# sourceMappingURL=control-panel-runtime-status.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-panel-runtime-status.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-runtime-status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"control-panel-runtime-status.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-runtime-status.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAEpF,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAE7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAE1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,MAAM,UAAU,gCAAgC,CAC/C,OAAqB,EACrB,cAA8B,EAC9B,UAAuC,EACV;IAC7B,MAAM,YAAY,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAC/C,MAAM,aAAa,GAAG,YAAY,EAAE,aAAa,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,aAAa,IAAI,CAAC,CAAC;IAC7F,MAAM,QAAQ,GAAG,wBAAwB,EAAE,CAAC;IAC5C,MAAM,aAAa,GAAG,uBAAuB,EAAE,CAAC,OAAO,CAAC;IACxD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,cAAc,CAAC,MAAM,EAAE,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC1G,OAAO;QACN,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE;QAChC,aAAa,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ;QAC5C,aAAa,EAAE,OAAO,CAAC,KAAK,CAAC,aAAa,IAAI,KAAK;QACnD,cAAc,EAAE,YAAY,EAAE,OAAO,IAAI,IAAI;QAC7C,mBAAmB,EAAE,aAAa;QAClC,aAAa,EAAE,YAAY,EAAE,MAAM,IAAI,IAAI;QAC3C,cAAc,EAAE,QAAQ,CAAC,QAAQ;QACjC,eAAe,EAAE,QAAQ,CAAC,QAAQ;QAClC,QAAQ,EAAE,gBAAgB,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM;QAClE,UAAU,EAAE,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,MAAM;QAC5D,aAAa;QACb,QAAQ;QACR,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE;QACrC,SAAS,EAAE,mBAAmB,EAAE;KAChC,CAAC;AAAA,CACF","sourcesContent":["import type { AgentSession } from \"../../../core/agent-session.ts\";\nimport { getHeadroomRuntimeStatus } from \"../../../core/context-budget-headroom.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { loadMcpInventory } from \"../../../core/mcp-inventory.ts\";\nimport { evaluatePiPackageIntake } from \"../../../core/pi-package-intake.ts\";\nimport type { SessionManager } from \"../../../core/session-manager.ts\";\nimport { getCurrentTodoState } from \"../../../core/todo-runtime-state.ts\";\nimport type { ControlPanelStatusSnapshot } from \"./control-panel-layout.ts\";\nimport { formatCwdForFooter } from \"./footer.ts\";\n\nexport function createControlPanelStatusSnapshot(\n\tsession: AgentSession,\n\tsessionManager: SessionManager,\n\tfooterData?: ReadonlyFooterDataProvider,\n): ControlPanelStatusSnapshot {\n\tconst contextUsage = session.getContextUsage();\n\tconst contextWindow = contextUsage?.contextWindow ?? session.state.model?.contextWindow ?? 0;\n\tconst headroom = getHeadroomRuntimeStatus();\n\tconst packageIntake = evaluatePiPackageIntake().summary;\n\tconst cwdLabel = formatCwdForFooter(sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\treturn {\n\t\tmodelId: session.state.model?.id,\n\t\tmodelProvider: session.state.model?.provider,\n\t\tthinkingLevel: session.state.thinkingLevel ?? \"off\",\n\t\tcontextPercent: contextUsage?.percent ?? null,\n\t\tcontextWindowTokens: contextWindow,\n\t\tcontextTokens: contextUsage?.tokens ?? null,\n\t\theadroomStatus: headroom.policyId,\n\t\toptimizerPolicy: headroom.selector,\n\t\tmcpCount: loadMcpInventory(sessionManager.getCwd()).entries.length,\n\t\tskillCount: session.resourceLoader.getSkills().skills.length,\n\t\tpackageIntake,\n\t\tcwdLabel,\n\t\tgitBranch: footerData?.getGitBranch(),\n\t\ttodoState: getCurrentTodoState(),\n\t};\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-panel.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"control-panel.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAUzC,OAAO,EAA2B,KAAK,mBAAmB,EAA4B,MAAM,2BAA2B,CAAC;AAExH,YAAY,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAEjG,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,MAAM,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,OAAO,CAAC;IACrB,eAAe,EAAE,MAAM,OAAO,CAAC;IAC/B,kBAAkB,EAAE,MAAM,OAAO,CAAC;IAClC,mBAAmB,EAAE,MAAM,OAAO,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAID,qBAAa,qBAAsB,YAAW,SAAS;IACtD,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAC9C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IACtE,OAAO,CAAC,WAAW,CAA+B;IAClD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAA6C;IAClE,OAAO,CAAC,eAAe,CAAK;IAE5B,YAAY,OAAO,EAAE,mBAAmB,EAAE,aAAa,CAAC,EAAE,yBAAyB,EAGlF;IAED,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAQnC;IAED,UAAU,IAAI,IAAI,CAAG;IAErB,OAAO,IAAI,IAAI,CAOd;IAED,UAAU,IAAI,IAAI,CAMjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAG9B;IAED,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,IAAI;IAuBZ,OAAO,CAAC,kBAAkB;IAQ1B,OAAO,CAAC,UAAU;CAgBlB","sourcesContent":["import type { Component } from \"omk-tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { MIN_BANNER_WIDTH } from \"./control-panel-gradient.ts\";\nimport {\n\tcomposeIdleBanner,\n\tcomposeIntroBanner,\n\tIDLE_MS,\n\tINTRO_MS,\n\tshouldAnimate,\n} from \"./control-panel-gradient-motion.ts\";\nimport { CONTROL_PANEL_ASCII_ART, type ControlPanelContent, renderControlPanelLayout } from \"./control-panel-layout.ts\";\n\nexport type { ControlPanelContent, ControlPanelStatusSnapshot } from \"./control-panel-layout.ts\";\n\nexport interface ControlPanelMotionOptions {\n\trequestRender: () => void;\n\tisTTY: () => boolean;\n\tisReducedMotion: () => boolean;\n\tisIdleDriftEnabled: () => boolean;\n\tisHeaderVisibleHint: () => boolean;\n\tgetRenderWidth?: () => number;\n\tnow?: () => number;\n}\n\ntype BannerMotionPhase = \"intro\" | \"idle\" | \"static\";\n\nexport class ControlPanelComponent implements Component {\n\tprivate expanded = false;\n\tprivate readonly content: ControlPanelContent;\n\tprivate readonly motionOptions: ControlPanelMotionOptions | undefined;\n\tprivate motionPhase: BannerMotionPhase = \"static\";\n\tprivate motionStartMs = 0;\n\tprivate motionTimerId: ReturnType<typeof setInterval> | undefined;\n\tprivate lastRenderWidth = 0;\n\n\tconstructor(content: ControlPanelContent, motionOptions?: ControlPanelMotionOptions) {\n\t\tthis.content = content;\n\t\tthis.motionOptions = motionOptions;\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tconst wasExpanded = this.expanded;\n\t\tthis.expanded = expanded;\n\t\tif (!wasExpanded && expanded) {\n\t\t\tthis.startMotion();\n\t\t} else if (wasExpanded && !expanded) {\n\t\t\tthis.stopMotionToStatic();\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\tdispose(): void {\n\t\tif (this.motionTimerId !== undefined) {\n\t\t\tclearInterval(this.motionTimerId);\n\t\t\tthis.motionTimerId = undefined;\n\t\t}\n\t\tthis.motionPhase = \"static\";\n\t\tthis.motionStartMs = 0;\n\t}\n\n\tstopMotion(): void {\n\t\tconst hadTimer = this.motionTimerId !== undefined;\n\t\tthis.dispose();\n\t\tif (hadTimer) {\n\t\t\tthis.motionOptions?.requestRender();\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tthis.lastRenderWidth = width;\n\t\treturn renderControlPanelLayout(this.content, this.expanded, width, this.currentBannerFrame());\n\t}\n\n\tprivate currentMotionWidth(): number {\n\t\tconst width = this.motionOptions?.getRenderWidth?.() ?? this.lastRenderWidth;\n\t\treturn width > 0 ? width : MIN_BANNER_WIDTH;\n\t}\n\n\tprivate startMotion(): void {\n\t\tconst opts = this.motionOptions;\n\t\tif (!opts || this.motionTimerId !== undefined || !this.canAnimate(\"intro\")) return;\n\n\t\tthis.motionPhase = \"intro\";\n\t\tthis.motionStartMs = (opts.now ?? Date.now)();\n\t\tthis.motionTimerId = setInterval(() => this.tick(), 100);\n\t\tif (this.motionTimerId && typeof this.motionTimerId === \"object\" && \"unref\" in this.motionTimerId) {\n\t\t\tthis.motionTimerId.unref();\n\t\t}\n\t\topts.requestRender();\n\t}\n\n\tprivate currentBannerFrame(): string[] | undefined {\n\t\tconst opts = this.motionOptions;\n\t\tif (!opts || this.motionPhase === \"static\") return undefined;\n\t\tconst elapsedMs = Math.max(0, (opts.now ?? Date.now)() - this.motionStartMs);\n\t\tconst mode = theme.getColorMode();\n\t\tconst noColor = process.env.NO_COLOR !== undefined;\n\t\tif (this.motionPhase === \"idle\") {\n\t\t\treturn composeIdleBanner(CONTROL_PANEL_ASCII_ART, mode, noColor, elapsedMs);\n\t\t}\n\t\treturn composeIntroBanner(CONTROL_PANEL_ASCII_ART, mode, noColor, elapsedMs);\n\t}\n\n\tprivate tick(): void {\n\t\tconst opts = this.motionOptions;\n\t\tif (!opts || !this.canAnimate(this.motionPhase === \"idle\" ? \"idle\" : \"intro\")) {\n\t\t\tthis.stopMotionToStatic();\n\t\t\treturn;\n\t\t}\n\n\t\tconst now = (opts.now ?? Date.now)();\n\t\tif (this.motionPhase === \"intro\" && now - this.motionStartMs >= INTRO_MS) {\n\t\t\tif (!opts.isIdleDriftEnabled()) {\n\t\t\t\tthis.stopMotionToStatic();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.motionPhase = \"idle\";\n\t\t\tthis.motionStartMs = now;\n\t\t}\n\t\tif (this.motionPhase === \"idle\" && now - this.motionStartMs >= IDLE_MS) {\n\t\t\tthis.stopMotionToStatic();\n\t\t\treturn;\n\t\t}\n\t\topts.requestRender();\n\t}\n\n\tprivate stopMotionToStatic(): void {\n\t\tconst hadTimer = this.motionTimerId !== undefined;\n\t\tthis.dispose();\n\t\tif (hadTimer) {\n\t\t\tthis.motionOptions?.requestRender();\n\t\t}\n\t}\n\n\tprivate canAnimate(phase: \"intro\" | \"idle\"): boolean {\n\t\tconst opts = this.motionOptions;\n\t\tif (!opts) return false;\n\t\treturn shouldAnimate({\n\t\t\tphase,\n\t\t\tisTTY: opts.isTTY(),\n\t\t\tnoColor: process.env.NO_COLOR !== undefined,\n\t\t\tcolorMode: theme.getColorMode(),\n\t\t\texpanded: this.expanded,\n\t\t\twidth: this.currentMotionWidth(),\n\t\t\treducedMotion: opts.isReducedMotion(),\n\t\t\tbusy: false,\n\t\t\theaderVisibleHint: opts.isHeaderVisibleHint(),\n\t\t\tidleDriftEnabled: opts.isIdleDriftEnabled(),\n\t\t});\n\t}\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { theme } from "../theme/theme.js";
|
|
2
2
|
import { MIN_BANNER_WIDTH } from "./control-panel-gradient.js";
|
|
3
|
-
import { IDLE_MS, INTRO_MS, shouldAnimate } from "./control-panel-gradient-motion.js";
|
|
4
|
-
import { renderControlPanelLayout } from "./control-panel-layout.js";
|
|
3
|
+
import { composeIdleBanner, composeIntroBanner, IDLE_MS, INTRO_MS, shouldAnimate, } from "./control-panel-gradient-motion.js";
|
|
4
|
+
import { CONTROL_PANEL_ASCII_ART, renderControlPanelLayout } from "./control-panel-layout.js";
|
|
5
5
|
export class ControlPanelComponent {
|
|
6
6
|
expanded = false;
|
|
7
7
|
content;
|
|
@@ -42,7 +42,7 @@ export class ControlPanelComponent {
|
|
|
42
42
|
}
|
|
43
43
|
render(width) {
|
|
44
44
|
this.lastRenderWidth = width;
|
|
45
|
-
return renderControlPanelLayout(this.content, this.expanded, width);
|
|
45
|
+
return renderControlPanelLayout(this.content, this.expanded, width, this.currentBannerFrame());
|
|
46
46
|
}
|
|
47
47
|
currentMotionWidth() {
|
|
48
48
|
const width = this.motionOptions?.getRenderWidth?.() ?? this.lastRenderWidth;
|
|
@@ -60,6 +60,18 @@ export class ControlPanelComponent {
|
|
|
60
60
|
}
|
|
61
61
|
opts.requestRender();
|
|
62
62
|
}
|
|
63
|
+
currentBannerFrame() {
|
|
64
|
+
const opts = this.motionOptions;
|
|
65
|
+
if (!opts || this.motionPhase === "static")
|
|
66
|
+
return undefined;
|
|
67
|
+
const elapsedMs = Math.max(0, (opts.now ?? Date.now)() - this.motionStartMs);
|
|
68
|
+
const mode = theme.getColorMode();
|
|
69
|
+
const noColor = process.env.NO_COLOR !== undefined;
|
|
70
|
+
if (this.motionPhase === "idle") {
|
|
71
|
+
return composeIdleBanner(CONTROL_PANEL_ASCII_ART, mode, noColor, elapsedMs);
|
|
72
|
+
}
|
|
73
|
+
return composeIntroBanner(CONTROL_PANEL_ASCII_ART, mode, noColor, elapsedMs);
|
|
74
|
+
}
|
|
63
75
|
tick() {
|
|
64
76
|
const opts = this.motionOptions;
|
|
65
77
|
if (!opts || !this.canAnimate(this.motionPhase === "idle" ? "idle" : "intro")) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-panel.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,
|
|
1
|
+
{"version":3,"file":"control-panel.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from \"omk-tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { MIN_BANNER_WIDTH } from \"./control-panel-gradient.ts\";\nimport {\n\tcomposeIdleBanner,\n\tcomposeIntroBanner,\n\tIDLE_MS,\n\tINTRO_MS,\n\tshouldAnimate,\n} from \"./control-panel-gradient-motion.ts\";\nimport { CONTROL_PANEL_ASCII_ART, type ControlPanelContent, renderControlPanelLayout } from \"./control-panel-layout.ts\";\n\nexport type { ControlPanelContent, ControlPanelStatusSnapshot } from \"./control-panel-layout.ts\";\n\nexport interface ControlPanelMotionOptions {\n\trequestRender: () => void;\n\tisTTY: () => boolean;\n\tisReducedMotion: () => boolean;\n\tisIdleDriftEnabled: () => boolean;\n\tisHeaderVisibleHint: () => boolean;\n\tgetRenderWidth?: () => number;\n\tnow?: () => number;\n}\n\ntype BannerMotionPhase = \"intro\" | \"idle\" | \"static\";\n\nexport class ControlPanelComponent implements Component {\n\tprivate expanded = false;\n\tprivate readonly content: ControlPanelContent;\n\tprivate readonly motionOptions: ControlPanelMotionOptions | undefined;\n\tprivate motionPhase: BannerMotionPhase = \"static\";\n\tprivate motionStartMs = 0;\n\tprivate motionTimerId: ReturnType<typeof setInterval> | undefined;\n\tprivate lastRenderWidth = 0;\n\n\tconstructor(content: ControlPanelContent, motionOptions?: ControlPanelMotionOptions) {\n\t\tthis.content = content;\n\t\tthis.motionOptions = motionOptions;\n\t}\n\n\tsetExpanded(expanded: boolean): void {\n\t\tconst wasExpanded = this.expanded;\n\t\tthis.expanded = expanded;\n\t\tif (!wasExpanded && expanded) {\n\t\t\tthis.startMotion();\n\t\t} else if (wasExpanded && !expanded) {\n\t\t\tthis.stopMotionToStatic();\n\t\t}\n\t}\n\n\tinvalidate(): void {}\n\n\tdispose(): void {\n\t\tif (this.motionTimerId !== undefined) {\n\t\t\tclearInterval(this.motionTimerId);\n\t\t\tthis.motionTimerId = undefined;\n\t\t}\n\t\tthis.motionPhase = \"static\";\n\t\tthis.motionStartMs = 0;\n\t}\n\n\tstopMotion(): void {\n\t\tconst hadTimer = this.motionTimerId !== undefined;\n\t\tthis.dispose();\n\t\tif (hadTimer) {\n\t\t\tthis.motionOptions?.requestRender();\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tthis.lastRenderWidth = width;\n\t\treturn renderControlPanelLayout(this.content, this.expanded, width, this.currentBannerFrame());\n\t}\n\n\tprivate currentMotionWidth(): number {\n\t\tconst width = this.motionOptions?.getRenderWidth?.() ?? this.lastRenderWidth;\n\t\treturn width > 0 ? width : MIN_BANNER_WIDTH;\n\t}\n\n\tprivate startMotion(): void {\n\t\tconst opts = this.motionOptions;\n\t\tif (!opts || this.motionTimerId !== undefined || !this.canAnimate(\"intro\")) return;\n\n\t\tthis.motionPhase = \"intro\";\n\t\tthis.motionStartMs = (opts.now ?? Date.now)();\n\t\tthis.motionTimerId = setInterval(() => this.tick(), 100);\n\t\tif (this.motionTimerId && typeof this.motionTimerId === \"object\" && \"unref\" in this.motionTimerId) {\n\t\t\tthis.motionTimerId.unref();\n\t\t}\n\t\topts.requestRender();\n\t}\n\n\tprivate currentBannerFrame(): string[] | undefined {\n\t\tconst opts = this.motionOptions;\n\t\tif (!opts || this.motionPhase === \"static\") return undefined;\n\t\tconst elapsedMs = Math.max(0, (opts.now ?? Date.now)() - this.motionStartMs);\n\t\tconst mode = theme.getColorMode();\n\t\tconst noColor = process.env.NO_COLOR !== undefined;\n\t\tif (this.motionPhase === \"idle\") {\n\t\t\treturn composeIdleBanner(CONTROL_PANEL_ASCII_ART, mode, noColor, elapsedMs);\n\t\t}\n\t\treturn composeIntroBanner(CONTROL_PANEL_ASCII_ART, mode, noColor, elapsedMs);\n\t}\n\n\tprivate tick(): void {\n\t\tconst opts = this.motionOptions;\n\t\tif (!opts || !this.canAnimate(this.motionPhase === \"idle\" ? \"idle\" : \"intro\")) {\n\t\t\tthis.stopMotionToStatic();\n\t\t\treturn;\n\t\t}\n\n\t\tconst now = (opts.now ?? Date.now)();\n\t\tif (this.motionPhase === \"intro\" && now - this.motionStartMs >= INTRO_MS) {\n\t\t\tif (!opts.isIdleDriftEnabled()) {\n\t\t\t\tthis.stopMotionToStatic();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.motionPhase = \"idle\";\n\t\t\tthis.motionStartMs = now;\n\t\t}\n\t\tif (this.motionPhase === \"idle\" && now - this.motionStartMs >= IDLE_MS) {\n\t\t\tthis.stopMotionToStatic();\n\t\t\treturn;\n\t\t}\n\t\topts.requestRender();\n\t}\n\n\tprivate stopMotionToStatic(): void {\n\t\tconst hadTimer = this.motionTimerId !== undefined;\n\t\tthis.dispose();\n\t\tif (hadTimer) {\n\t\t\tthis.motionOptions?.requestRender();\n\t\t}\n\t}\n\n\tprivate canAnimate(phase: \"intro\" | \"idle\"): boolean {\n\t\tconst opts = this.motionOptions;\n\t\tif (!opts) return false;\n\t\treturn shouldAnimate({\n\t\t\tphase,\n\t\t\tisTTY: opts.isTTY(),\n\t\t\tnoColor: process.env.NO_COLOR !== undefined,\n\t\t\tcolorMode: theme.getColorMode(),\n\t\t\texpanded: this.expanded,\n\t\t\twidth: this.currentMotionWidth(),\n\t\t\treducedMotion: opts.isReducedMotion(),\n\t\t\tbusy: false,\n\t\t\theaderVisibleHint: opts.isHeaderVisibleHint(),\n\t\t\tidleDriftEnabled: opts.isIdleDriftEnabled(),\n\t\t});\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,SAAS,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAoCxF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAYhF;AAED;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAA6B;IAE/C,YAAY,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,0BAA0B,EAGxE;IAED,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAEtC;IAED,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IA2B3B;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA4J9B;CACD","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"omk-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts for compact footer display.\n */\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\n/**\n * Format byte counts for compact footer display.\n */\nfunction formatBytes(bytes: number): string {\n\tif (bytes < 1024) return `${bytes}B`;\n\tif (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)}K`;\n\tif (bytes < 1024 * 1024 * 1024) return `${Math.round(bytes / (1024 * 1024))}M`;\n\treturn `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}G`;\n}\n\nexport function formatCwdForFooter(cwd: string, home: string | undefined): string {\n\tif (!home) return cwd;\n\n\tconst resolvedCwd = resolve(cwd);\n\tconst resolvedHome = resolve(home);\n\tconst relativeToHome = relative(resolvedHome, resolvedCwd);\n\tconst isInsideHome =\n\t\trelativeToHome === \"\" ||\n\t\t(relativeToHome !== \"..\" && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));\n\n\tif (!isInsideHome) return cwd;\n\treturn relativeToHome === \"\" ? \"~\" : `~${sep}${relativeToHome}`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate session: AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\n\tconstructor(session: AgentSession, footerData: ReadonlyFooterDataProvider) {\n\t\tthis.session = session;\n\t\tthis.footerData = footerData;\n\t}\n\n\tsetSession(session: AgentSession): void {\n\t\tthis.session = session;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Build a compact CPU/memory metrics segment for the footer.\n\t * Degrades gracefully to shorter forms (or is omitted entirely) when the\n\t * terminal is very narrow so the rest of the footer can still render.\n\t */\n\tprivate buildMetricsSegment(width: number): string | undefined {\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tconst mem = this.footerData.getMemoryRssBytes();\n\t\tif (cpu === null || mem === null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst memWarningThreshold = 1024 * 1024 * 1024; // 1 GiB\n\t\tconst memErrorThreshold = 1.5 * 1024 * 1024 * 1024; // 1.5 GiB\n\t\tconst color: \"error\" | \"warning\" | \"dim\" =\n\t\t\tcpu >= 90 || mem >= memErrorThreshold ? \"error\" : cpu >= 70 || mem >= memWarningThreshold ? \"warning\" : \"dim\";\n\n\t\tconst cpuStr = `${Math.round(cpu)}%`;\n\t\tconst memStr = formatBytes(mem);\n\n\t\tif (width >= 48) {\n\t\t\treturn theme.fg(color, `CPU ${cpuStr} MEM ${memStr}`);\n\t\t}\n\t\tif (width >= 30) {\n\t\t\treturn theme.fg(color, `${cpuStr} ${memStr}`);\n\t\t}\n\t\tif (width >= 18) {\n\t\t\treturn theme.fg(color, cpuStr);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Add CPU/memory metrics; degrade gracefully when horizontal space is tight.\n\t\tconst metricsSegment = this.buildMetricsSegment(width);\n\t\tif (metricsSegment) {\n\t\t\tstatsParts.push(metricsSegment);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst pwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\tconst lines = [pwdLine, dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,SAAS,CAAC;AACxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AA2CxF,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAYhF;AAED;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAA6B;IAE/C,YAAY,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,0BAA0B,EAGxE;IAED,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAEtC;IAED,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IA2B3B;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA6J9B;CACD","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"omk-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport type { PiPackageIntakeSummary } from \"../../../core/pi-package-intake.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts for compact footer display.\n */\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\n/**\n * Format byte counts for compact footer display.\n */\nfunction formatBytes(bytes: number): string {\n\tif (bytes < 1024) return `${bytes}B`;\n\tif (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)}K`;\n\tif (bytes < 1024 * 1024 * 1024) return `${Math.round(bytes / (1024 * 1024))}M`;\n\treturn `${(bytes / (1024 * 1024 * 1024)).toFixed(1)}G`;\n}\n\nfunction formatPackageIntake(summary: PiPackageIntakeSummary): string {\n\tconst ready = summary.acceptedNative + summary.acceptedReference + summary.acceptedMeasurement;\n\tconst review = summary.deferred + summary.reject;\n\treturn `PKG ${ready}/${summary.total} R${review} B${summary.hardForkBlocked}`;\n}\n\nexport function formatCwdForFooter(cwd: string, home: string | undefined): string {\n\tif (!home) return cwd;\n\n\tconst resolvedCwd = resolve(cwd);\n\tconst resolvedHome = resolve(home);\n\tconst relativeToHome = relative(resolvedHome, resolvedCwd);\n\tconst isInsideHome =\n\t\trelativeToHome === \"\" ||\n\t\t(relativeToHome !== \"..\" && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));\n\n\tif (!isInsideHome) return cwd;\n\treturn relativeToHome === \"\" ? \"~\" : `~${sep}${relativeToHome}`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate session: AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\n\tconstructor(session: AgentSession, footerData: ReadonlyFooterDataProvider) {\n\t\tthis.session = session;\n\t\tthis.footerData = footerData;\n\t}\n\n\tsetSession(session: AgentSession): void {\n\t\tthis.session = session;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Build a compact CPU/memory metrics segment for the footer.\n\t * Degrades gracefully to shorter forms (or is omitted entirely) when the\n\t * terminal is very narrow so the rest of the footer can still render.\n\t */\n\tprivate buildMetricsSegment(width: number): string | undefined {\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tconst mem = this.footerData.getMemoryRssBytes();\n\t\tif (cpu === null || mem === null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst memWarningThreshold = 1024 * 1024 * 1024; // 1 GiB\n\t\tconst memErrorThreshold = 1.5 * 1024 * 1024 * 1024; // 1.5 GiB\n\t\tconst color: \"error\" | \"warning\" | \"dim\" =\n\t\t\tcpu >= 90 || mem >= memErrorThreshold ? \"error\" : cpu >= 70 || mem >= memWarningThreshold ? \"warning\" : \"dim\";\n\n\t\tconst cpuStr = `${Math.round(cpu)}%`;\n\t\tconst memStr = formatBytes(mem);\n\n\t\tif (width >= 48) {\n\t\t\treturn theme.fg(color, `CPU ${cpuStr} MEM ${memStr}`);\n\t\t}\n\t\tif (width >= 30) {\n\t\t\treturn theme.fg(color, `${cpuStr} ${memStr}`);\n\t\t}\n\t\tif (width >= 18) {\n\t\t\treturn theme.fg(color, cpuStr);\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(width: number): string[] {\n\t\tconst state = this.session.state;\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = state.model ? this.session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Add CPU/memory metrics; degrade gracefully when horizontal space is tight.\n\t\tconst metricsSegment = this.buildMetricsSegment(width);\n\t\tif (metricsSegment) {\n\t\t\tstatsParts.push(metricsSegment);\n\t\t}\n\t\tstatsParts.push(formatPackageIntake(this.footerData.getPackageIntakeSummary()));\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst pwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\tconst lines = [pwdLine, dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
|