open-multi-agent-kit 0.90.0 → 0.90.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +20 -31
  3. package/dist/cli/args.d.ts +0 -2
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +1 -7
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-session.d.ts +2 -0
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +41 -5
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/bang-skill-invocation.d.ts +26 -0
  12. package/dist/core/bang-skill-invocation.d.ts.map +1 -0
  13. package/dist/core/bang-skill-invocation.js +59 -0
  14. package/dist/core/bang-skill-invocation.js.map +1 -0
  15. package/dist/core/compactor.d.ts.map +1 -1
  16. package/dist/core/compactor.js +0 -3
  17. package/dist/core/compactor.js.map +1 -1
  18. package/dist/core/context-budget-governor-v2.d.ts +2 -1
  19. package/dist/core/context-budget-governor-v2.d.ts.map +1 -1
  20. package/dist/core/context-budget-governor-v2.js +1 -0
  21. package/dist/core/context-budget-governor-v2.js.map +1 -1
  22. package/dist/core/context-budget-headroom-types.d.ts +8 -0
  23. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  24. package/dist/core/context-budget-headroom-types.js.map +1 -1
  25. package/dist/core/context-budget-headroom.d.ts +1 -1
  26. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  27. package/dist/core/context-budget-headroom.js +17 -4
  28. package/dist/core/context-budget-headroom.js.map +1 -1
  29. package/dist/core/context-budget-system-prompt.d.ts +2 -1
  30. package/dist/core/context-budget-system-prompt.d.ts.map +1 -1
  31. package/dist/core/context-budget-system-prompt.js +6 -1
  32. package/dist/core/context-budget-system-prompt.js.map +1 -1
  33. package/dist/core/context-budget-v2-cache-hash.d.ts +3 -0
  34. package/dist/core/context-budget-v2-cache-hash.d.ts.map +1 -0
  35. package/dist/core/context-budget-v2-cache-hash.js +16 -0
  36. package/dist/core/context-budget-v2-cache-hash.js.map +1 -0
  37. package/dist/core/context-budget-v2-cache-keys.d.ts +58 -0
  38. package/dist/core/context-budget-v2-cache-keys.d.ts.map +1 -0
  39. package/dist/core/context-budget-v2-cache-keys.js +128 -0
  40. package/dist/core/context-budget-v2-cache-keys.js.map +1 -0
  41. package/dist/core/context-budget-v2-cache-provider.d.ts +23 -0
  42. package/dist/core/context-budget-v2-cache-provider.d.ts.map +1 -0
  43. package/dist/core/context-budget-v2-cache-provider.js +29 -0
  44. package/dist/core/context-budget-v2-cache-provider.js.map +1 -0
  45. package/dist/core/context-budget-v2-cache-telemetry.d.ts +36 -0
  46. package/dist/core/context-budget-v2-cache-telemetry.d.ts.map +1 -0
  47. package/dist/core/context-budget-v2-cache-telemetry.js +64 -0
  48. package/dist/core/context-budget-v2-cache-telemetry.js.map +1 -0
  49. package/dist/core/context-budget-v2-cache-validation.d.ts +36 -0
  50. package/dist/core/context-budget-v2-cache-validation.d.ts.map +1 -0
  51. package/dist/core/context-budget-v2-cache-validation.js +98 -0
  52. package/dist/core/context-budget-v2-cache-validation.js.map +1 -0
  53. package/dist/core/context-budget-v2-cache.d.ts +36 -0
  54. package/dist/core/context-budget-v2-cache.d.ts.map +1 -0
  55. package/dist/core/context-budget-v2-cache.js +50 -0
  56. package/dist/core/context-budget-v2-cache.js.map +1 -0
  57. package/dist/core/context-budget-v2-plan-cache-keys.d.ts +16 -0
  58. package/dist/core/context-budget-v2-plan-cache-keys.d.ts.map +1 -0
  59. package/dist/core/context-budget-v2-plan-cache-keys.js +49 -0
  60. package/dist/core/context-budget-v2-plan-cache-keys.js.map +1 -0
  61. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  62. package/dist/core/context-budget-v2-planner.js +55 -2
  63. package/dist/core/context-budget-v2-planner.js.map +1 -1
  64. package/dist/core/context-budget-v2-representation-cache.d.ts +27 -0
  65. package/dist/core/context-budget-v2-representation-cache.d.ts.map +1 -0
  66. package/dist/core/context-budget-v2-representation-cache.js +173 -0
  67. package/dist/core/context-budget-v2-representation-cache.js.map +1 -0
  68. package/dist/core/context-budget-v2-selection.d.ts +2 -0
  69. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  70. package/dist/core/context-budget-v2-selection.js +12 -2
  71. package/dist/core/context-budget-v2-selection.js.map +1 -1
  72. package/dist/core/context-budget-v2-types.d.ts +107 -1
  73. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  74. package/dist/core/context-budget-v2-types.js.map +1 -1
  75. package/dist/core/headroom.d.ts.map +1 -1
  76. package/dist/core/headroom.js +0 -4
  77. package/dist/core/headroom.js.map +1 -1
  78. package/dist/core/hook-inventory.d.ts.map +1 -1
  79. package/dist/core/hook-inventory.js +6 -0
  80. package/dist/core/hook-inventory.js.map +1 -1
  81. package/dist/core/mcp-computer-use-presets.d.ts +64 -0
  82. package/dist/core/mcp-computer-use-presets.d.ts.map +1 -0
  83. package/dist/core/mcp-computer-use-presets.js +158 -0
  84. package/dist/core/mcp-computer-use-presets.js.map +1 -0
  85. package/dist/core/mcp-public-presets.d.ts +578 -0
  86. package/dist/core/mcp-public-presets.d.ts.map +1 -0
  87. package/dist/core/mcp-public-presets.js +647 -0
  88. package/dist/core/mcp-public-presets.js.map +1 -0
  89. package/dist/core/model-registry.d.ts.map +1 -1
  90. package/dist/core/model-registry.js +1 -0
  91. package/dist/core/model-registry.js.map +1 -1
  92. package/dist/core/pi-package-intake-candidates.d.ts +14 -1
  93. package/dist/core/pi-package-intake-candidates.d.ts.map +1 -1
  94. package/dist/core/pi-package-intake-candidates.js +182 -0
  95. package/dist/core/pi-package-intake-candidates.js.map +1 -1
  96. package/dist/core/pi-package-intake.d.ts +8 -1
  97. package/dist/core/pi-package-intake.d.ts.map +1 -1
  98. package/dist/core/pi-package-intake.js +14 -4
  99. package/dist/core/pi-package-intake.js.map +1 -1
  100. package/dist/core/reverse-skill.d.ts +2 -0
  101. package/dist/core/reverse-skill.d.ts.map +1 -0
  102. package/dist/core/reverse-skill.js +7 -0
  103. package/dist/core/reverse-skill.js.map +1 -0
  104. package/dist/core/settings-manager.d.ts +3 -3
  105. package/dist/core/settings-manager.d.ts.map +1 -1
  106. package/dist/core/settings-manager.js.map +1 -1
  107. package/dist/core/system-prompt.d.ts +2 -0
  108. package/dist/core/system-prompt.d.ts.map +1 -1
  109. package/dist/core/system-prompt.js +58 -5
  110. package/dist/core/system-prompt.js.map +1 -1
  111. package/dist/index.d.ts +1 -0
  112. package/dist/index.d.ts.map +1 -1
  113. package/dist/index.js +1 -0
  114. package/dist/index.js.map +1 -1
  115. package/dist/migrations.d.ts +19 -0
  116. package/dist/migrations.d.ts.map +1 -1
  117. package/dist/migrations.js +39 -15
  118. package/dist/migrations.js.map +1 -1
  119. package/dist/modes/interactive/components/control-panel-box.d.ts +3 -3
  120. package/dist/modes/interactive/components/control-panel-box.d.ts.map +1 -1
  121. package/dist/modes/interactive/components/control-panel-box.js +10 -10
  122. package/dist/modes/interactive/components/control-panel-box.js.map +1 -1
  123. package/dist/modes/interactive/components/control-panel-gradient-motion.d.ts.map +1 -1
  124. package/dist/modes/interactive/components/control-panel-gradient-motion.js +2 -1
  125. package/dist/modes/interactive/components/control-panel-gradient-motion.js.map +1 -1
  126. package/dist/modes/interactive/components/control-panel-layout.d.ts +15 -1
  127. package/dist/modes/interactive/components/control-panel-layout.d.ts.map +1 -1
  128. package/dist/modes/interactive/components/control-panel-layout.js +212 -68
  129. package/dist/modes/interactive/components/control-panel-layout.js.map +1 -1
  130. package/dist/modes/interactive/components/control-panel-runtime-status.d.ts.map +1 -1
  131. package/dist/modes/interactive/components/control-panel-runtime-status.js +10 -0
  132. package/dist/modes/interactive/components/control-panel-runtime-status.js.map +1 -1
  133. package/dist/modes/interactive/components/control-panel-sparkles.d.ts +27 -0
  134. package/dist/modes/interactive/components/control-panel-sparkles.d.ts.map +1 -0
  135. package/dist/modes/interactive/components/control-panel-sparkles.js +42 -0
  136. package/dist/modes/interactive/components/control-panel-sparkles.js.map +1 -0
  137. package/dist/modes/interactive/components/control-panel.d.ts +8 -0
  138. package/dist/modes/interactive/components/control-panel.d.ts.map +1 -1
  139. package/dist/modes/interactive/components/control-panel.js +40 -8
  140. package/dist/modes/interactive/components/control-panel.js.map +1 -1
  141. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  142. package/dist/modes/interactive/components/settings-selector.js +2 -1
  143. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  144. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  145. package/dist/modes/interactive/components/thinking-selector.js +2 -1
  146. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  147. package/dist/modes/interactive/interactive-mode.d.ts +2 -0
  148. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  149. package/dist/modes/interactive/interactive-mode.js +63 -14
  150. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  151. package/dist/modes/interactive/theme/theme.d.ts +1 -1
  152. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  153. package/dist/modes/interactive/theme/theme.js +2 -0
  154. package/dist/modes/interactive/theme/theme.js.map +1 -1
  155. package/docs/skills.md +8 -4
  156. package/docs/usage.md +4 -3
  157. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  158. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  159. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  160. package/examples/extensions/gondolin/package-lock.json +2 -2
  161. package/examples/extensions/gondolin/package.json +1 -1
  162. package/examples/extensions/preset.ts +2 -2
  163. package/examples/extensions/sandbox/package-lock.json +2 -2
  164. package/examples/extensions/sandbox/package.json +1 -1
  165. package/examples/extensions/with-deps/package-lock.json +2 -2
  166. package/examples/extensions/with-deps/package.json +1 -1
  167. package/npm-shrinkwrap.json +27 -34
  168. package/package.json +5 -5
@@ -3,21 +3,21 @@ import { theme } from "../theme/theme.js";
3
3
  export function composeColumns(leftLines, leftWidth, rightLines, rightWidth, gapWidth, width) {
4
4
  return Array.from({ length: Math.max(leftLines.length, rightLines.length) }, (_, index) => clipLine(`${fitLine(leftLines[index] ?? "", leftWidth)}${" ".repeat(gapWidth)}${fitLine(rightLines[index] ?? "", rightWidth)}`, width));
5
5
  }
6
- export function sidebarRule(width, label) {
6
+ export function sidebarRule(width, label, borderColor = "borderAccent") {
7
7
  const bodyWidth = Math.max(0, width - 2);
8
8
  const labelText = ` ${label} `;
9
9
  const fill = Math.max(0, bodyWidth - visibleWidth(labelText));
10
10
  const left = Math.floor(fill / 2);
11
11
  const right = fill - left;
12
- return clipLine(`${theme.fg("border", "|")}${theme.fg("borderMuted", "-".repeat(left))}${theme.bold(theme.fg("accent", labelText))}${theme.fg("borderMuted", "-".repeat(right))}${theme.fg("border", "|")}`, width);
12
+ return clipLine(`${theme.fg(borderColor, "\u2502")}${theme.fg("borderMuted", "\u2500".repeat(left))}${theme.bold(theme.fg("accent", labelText))}${theme.fg("borderMuted", "\u2500".repeat(right))}${theme.fg(borderColor, "\u2502")}`, width);
13
13
  }
14
- export function boxTop(width, label) {
14
+ export function boxTop(width, label, borderColor = "borderAccent") {
15
15
  const text = ` ${label} `;
16
16
  const fillWidth = Math.max(0, width - visibleWidth("+") - visibleWidth(text) - visibleWidth("+"));
17
- return clipLine(`${theme.fg("border", "+")}${theme.bold(theme.fg("accent", text))}${theme.fg("border", "-".repeat(fillWidth))}${theme.fg("border", "+")}`, width);
17
+ return clipLine(`${theme.fg(borderColor, "\u256d")}${theme.bold(theme.fg("accent", text))}${theme.fg(borderColor, "\u2500".repeat(fillWidth))}${theme.fg(borderColor, "\u256e")}`, width);
18
18
  }
19
- export function boxBottom(width) {
20
- return clipLine(theme.fg("border", `+${"-".repeat(Math.max(0, width - 2))}+`), width);
19
+ export function boxBottom(width, borderColor = "borderAccent") {
20
+ return clipLine(theme.fg(borderColor, `\u2570${"\u2500".repeat(Math.max(0, width - 2))}\u256f`), width);
21
21
  }
22
22
  export function boxBlankLine(width) {
23
23
  return boxTextLine(width, "");
@@ -27,7 +27,7 @@ export function boxCenteredLine(width, text, color) {
27
27
  }
28
28
  export function boxTextLine(width, text, color) {
29
29
  const body = color ? theme.fg(color, text) : text;
30
- return clipLine(`${theme.fg("border", "| ")}${fitLine(body, Math.max(0, width - 4))}${theme.fg("border", " |")}`, width);
30
+ return clipLine(`${theme.fg("borderAccent", "\u2502 ")}${fitLine(body, Math.max(0, width - 4))}${theme.fg("borderAccent", " \u2502")}`, width);
31
31
  }
32
32
  export function centerLine(width, text, color) {
33
33
  return clipLine(centerText(width, color ? theme.fg(color, text) : text), width);
@@ -38,12 +38,12 @@ export function centerText(width, text) {
38
38
  return `${" ".repeat(Math.floor(remaining / 2))}${fitted}${" ".repeat(Math.ceil(remaining / 2))}`;
39
39
  }
40
40
  export function divider(width, label, color) {
41
- const fillWidth = Math.max(0, width - visibleWidth("+-- ") - visibleWidth(label) - 1);
42
- return clipLine(`${theme.fg("border", "+-- ")}${theme.bold(theme.fg(color, label))}${theme.fg("border", ` ${"-".repeat(fillWidth)}`)}`, width);
41
+ const fillWidth = Math.max(0, width - visibleWidth("+-- ") - visibleWidth(label) - visibleWidth(" +"));
42
+ return clipLine(`${theme.fg("border", "+-- ")}${theme.bold(theme.fg(color, label))}${theme.fg("border", ` ${"-".repeat(fillWidth)}+`)}`, width);
43
43
  }
44
44
  export function textLine(width, text, color) {
45
45
  const body = color ? theme.fg(color, text) : text;
46
- return clipLine(`${theme.fg("borderMuted", "| ")}${body}`, width);
46
+ return clipLine(`${theme.fg("borderMuted", "\u2502 ")}${fitLine(body, Math.max(0, width - 4))}${theme.fg("borderMuted", " \u2502")}`, width);
47
47
  }
48
48
  export function fitLine(line, width) {
49
49
  const clipped = truncateToWidth(line, width, "");
@@ -1 +1 @@
1
- {"version":3,"file":"control-panel-box.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-box.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth, visibleWidth } from \"omk-tui\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\n\nexport function composeColumns(\n\tleftLines: string[],\n\tleftWidth: number,\n\trightLines: string[],\n\trightWidth: number,\n\tgapWidth: number,\n\twidth: number,\n): string[] {\n\treturn Array.from({ length: Math.max(leftLines.length, rightLines.length) }, (_, index) =>\n\t\tclipLine(\n\t\t\t`${fitLine(leftLines[index] ?? \"\", leftWidth)}${\" \".repeat(gapWidth)}${fitLine(rightLines[index] ?? \"\", rightWidth)}`,\n\t\t\twidth,\n\t\t),\n\t);\n}\n\nexport function sidebarRule(width: number, label: string): string {\n\tconst bodyWidth = Math.max(0, width - 2);\n\tconst labelText = ` ${label} `;\n\tconst fill = Math.max(0, bodyWidth - visibleWidth(labelText));\n\tconst left = Math.floor(fill / 2);\n\tconst right = fill - left;\n\treturn clipLine(\n\t\t`${theme.fg(\"border\", \"|\")}${theme.fg(\"borderMuted\", \"-\".repeat(left))}${theme.bold(theme.fg(\"accent\", labelText))}${theme.fg(\"borderMuted\", \"-\".repeat(right))}${theme.fg(\"border\", \"|\")}`,\n\t\twidth,\n\t);\n}\n\nexport function boxTop(width: number, label: string): string {\n\tconst text = ` ${label} `;\n\tconst fillWidth = Math.max(0, width - visibleWidth(\"+\") - visibleWidth(text) - visibleWidth(\"+\"));\n\treturn clipLine(\n\t\t`${theme.fg(\"border\", \"+\")}${theme.bold(theme.fg(\"accent\", text))}${theme.fg(\"border\", \"-\".repeat(fillWidth))}${theme.fg(\"border\", \"+\")}`,\n\t\twidth,\n\t);\n}\n\nexport function boxBottom(width: number): string {\n\treturn clipLine(theme.fg(\"border\", `+${\"-\".repeat(Math.max(0, width - 2))}+`), width);\n}\n\nexport function boxBlankLine(width: number): string {\n\treturn boxTextLine(width, \"\");\n}\n\nexport function boxCenteredLine(width: number, text: string, color?: ThemeColor): string {\n\treturn boxTextLine(width, centerText(Math.max(0, width - 4), color ? theme.fg(color, text) : text));\n}\n\nexport function boxTextLine(width: number, text: string, color?: ThemeColor): string {\n\tconst body = color ? theme.fg(color, text) : text;\n\treturn clipLine(\n\t\t`${theme.fg(\"border\", \"| \")}${fitLine(body, Math.max(0, width - 4))}${theme.fg(\"border\", \" |\")}`,\n\t\twidth,\n\t);\n}\n\nexport function centerLine(width: number, text: string, color?: ThemeColor): string {\n\treturn clipLine(centerText(width, color ? theme.fg(color, text) : text), width);\n}\n\nexport function centerText(width: number, text: string): string {\n\tconst fitted = truncateToWidth(text, width, \"\");\n\tconst remaining = Math.max(0, width - visibleWidth(fitted));\n\treturn `${\" \".repeat(Math.floor(remaining / 2))}${fitted}${\" \".repeat(Math.ceil(remaining / 2))}`;\n}\n\nexport function divider(width: number, label: string, color: ThemeColor): string {\n\tconst fillWidth = Math.max(0, width - visibleWidth(\"+-- \") - visibleWidth(label) - 1);\n\treturn clipLine(\n\t\t`${theme.fg(\"border\", \"+-- \")}${theme.bold(theme.fg(color, label))}${theme.fg(\"border\", ` ${\"-\".repeat(fillWidth)}`)}`,\n\t\twidth,\n\t);\n}\n\nexport function textLine(width: number, text: string, color?: ThemeColor): string {\n\tconst body = color ? theme.fg(color, text) : text;\n\treturn clipLine(`${theme.fg(\"borderMuted\", \"| \")}${body}`, width);\n}\n\nexport function fitLine(line: string, width: number): string {\n\tconst clipped = truncateToWidth(line, width, \"\");\n\treturn `${clipped}${\" \".repeat(Math.max(0, width - visibleWidth(clipped)))}`;\n}\n\nexport function clipLine(line: string, width: number): string {\n\treturn visibleWidth(line) <= width ? line : truncateToWidth(line, width, \"\");\n}\n"]}
1
+ {"version":3,"file":"control-panel-box.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-box.ts"],"names":[],"mappings":"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","sourcesContent":["import { truncateToWidth, visibleWidth } from \"omk-tui\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\n\nexport function composeColumns(\n\tleftLines: string[],\n\tleftWidth: number,\n\trightLines: string[],\n\trightWidth: number,\n\tgapWidth: number,\n\twidth: number,\n): string[] {\n\treturn Array.from({ length: Math.max(leftLines.length, rightLines.length) }, (_, index) =>\n\t\tclipLine(\n\t\t\t`${fitLine(leftLines[index] ?? \"\", leftWidth)}${\" \".repeat(gapWidth)}${fitLine(rightLines[index] ?? \"\", rightWidth)}`,\n\t\t\twidth,\n\t\t),\n\t);\n}\n\nexport function sidebarRule(width: number, label: string, borderColor: ThemeColor = \"borderAccent\"): string {\n\tconst bodyWidth = Math.max(0, width - 2);\n\tconst labelText = ` ${label} `;\n\tconst fill = Math.max(0, bodyWidth - visibleWidth(labelText));\n\tconst left = Math.floor(fill / 2);\n\tconst right = fill - left;\n\treturn clipLine(\n\t\t`${theme.fg(borderColor, \"\\u2502\")}${theme.fg(\"borderMuted\", \"\\u2500\".repeat(left))}${theme.bold(theme.fg(\"accent\", labelText))}${theme.fg(\"borderMuted\", \"\\u2500\".repeat(right))}${theme.fg(borderColor, \"\\u2502\")}`,\n\t\twidth,\n\t);\n}\n\nexport function boxTop(width: number, label: string, borderColor: ThemeColor = \"borderAccent\"): string {\n\tconst text = ` ${label} `;\n\tconst fillWidth = Math.max(0, width - visibleWidth(\"+\") - visibleWidth(text) - visibleWidth(\"+\"));\n\treturn clipLine(\n\t\t`${theme.fg(borderColor, \"\\u256d\")}${theme.bold(theme.fg(\"accent\", text))}${theme.fg(borderColor, \"\\u2500\".repeat(fillWidth))}${theme.fg(borderColor, \"\\u256e\")}`,\n\t\twidth,\n\t);\n}\n\nexport function boxBottom(width: number, borderColor: ThemeColor = \"borderAccent\"): string {\n\treturn clipLine(theme.fg(borderColor, `\\u2570${\"\\u2500\".repeat(Math.max(0, width - 2))}\\u256f`), width);\n}\n\nexport function boxBlankLine(width: number): string {\n\treturn boxTextLine(width, \"\");\n}\n\nexport function boxCenteredLine(width: number, text: string, color?: ThemeColor): string {\n\treturn boxTextLine(width, centerText(Math.max(0, width - 4), color ? theme.fg(color, text) : text));\n}\n\nexport function boxTextLine(width: number, text: string, color?: ThemeColor): string {\n\tconst body = color ? theme.fg(color, text) : text;\n\treturn clipLine(\n\t\t`${theme.fg(\"borderAccent\", \"\\u2502 \")}${fitLine(body, Math.max(0, width - 4))}${theme.fg(\"borderAccent\", \" \\u2502\")}`,\n\t\twidth,\n\t);\n}\n\nexport function centerLine(width: number, text: string, color?: ThemeColor): string {\n\treturn clipLine(centerText(width, color ? theme.fg(color, text) : text), width);\n}\n\nexport function centerText(width: number, text: string): string {\n\tconst fitted = truncateToWidth(text, width, \"\");\n\tconst remaining = Math.max(0, width - visibleWidth(fitted));\n\treturn `${\" \".repeat(Math.floor(remaining / 2))}${fitted}${\" \".repeat(Math.ceil(remaining / 2))}`;\n}\n\nexport function divider(width: number, label: string, color: ThemeColor): string {\n\tconst fillWidth = Math.max(0, width - visibleWidth(\"+-- \") - visibleWidth(label) - visibleWidth(\" +\"));\n\treturn clipLine(\n\t\t`${theme.fg(\"border\", \"+-- \")}${theme.bold(theme.fg(color, label))}${theme.fg(\"border\", ` ${\"-\".repeat(fillWidth)}+`)}`,\n\t\twidth,\n\t);\n}\n\nexport function textLine(width: number, text: string, color?: ThemeColor): string {\n\tconst body = color ? theme.fg(color, text) : text;\n\treturn clipLine(\n\t\t`${theme.fg(\"borderMuted\", \"\\u2502 \")}${fitLine(body, Math.max(0, width - 4))}${theme.fg(\"borderMuted\", \" \\u2502\")}`,\n\t\twidth,\n\t);\n}\n\nexport function fitLine(line: string, width: number): string {\n\tconst clipped = truncateToWidth(line, width, \"\");\n\treturn `${clipped}${\" \".repeat(Math.max(0, width - visibleWidth(clipped)))}`;\n}\n\nexport function clipLine(line: string, width: number): string {\n\treturn visibleWidth(line) <= width ? line : truncateToWidth(line, width, \"\");\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"control-panel-gradient-motion.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-gradient-motion.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,iBAAiB,EAItB,MAAM,6BAA6B,CAAC;AAErC,eAAO,MAAM,QAAQ,MAAM,CAAC;AAC5B,eAAO,MAAM,OAAO,OAAO,CAAC;AAuC5B;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,MAAM,EAAE,EACb,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GACf,MAAM,EAAE,CAsCV;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAChC,GAAG,EAAE,MAAM,EAAE,EACb,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GACf,MAAM,EAAE,CAmBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACtC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;CAC1B,GAAG,OAAO,CAKV","sourcesContent":["import {\n\tcolorAt,\n\tcomposeStaticBanner,\n\ttype GradientColorMode,\n\ttype GradientGeom,\n\tpaintGlyph,\n\tshouldGradient,\n} from \"./control-panel-gradient.ts\";\n\nexport const INTRO_MS = 900;\nexport const IDLE_MS = 6000;\nconst IDLE_PERIOD_MS = 4200;\nconst REVEAL_BAND = 0.16;\nconst DEFAULT_SHEAR = 2.2;\n\nfunction clamp01(value: number): number {\n\tif (value < 0) return 0;\n\tif (value > 1) return 1;\n\treturn value;\n}\n\nfunction easeOutCubic(t: number): number {\n\tconst u = clamp01(t);\n\treturn 1 - (1 - u) * (1 - u) * (1 - u);\n}\n\nfunction diagonalPosition(x: number, y: number, geom: GradientGeom): number {\n\tconst span = geom.cols - 1 + (geom.rows - 1) * geom.shear;\n\tif (span <= 0) return 0;\n\treturn clamp01((x + (geom.rows - 1 - y) * geom.shear) / span);\n}\n\nconst SCRAMBLE_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%&*+=<>[]{}|/\\\\~^\";\nconst SCRAMBLE_LEN = SCRAMBLE_CHARS.length;\n\nfunction scrambleGlyph(x: number, y: number, seed: number): string {\n\t// Deterministic scramble from position + seed (no Math.random)\n\tconst hash = ((x * 374761393 + y * 668265263 + seed * 1274126177) >>> 0) % SCRAMBLE_LEN;\n\treturn SCRAMBLE_CHARS[hash]!;\n}\n\nfunction dimRgb(r: number, g: number, b: number, factor: number): { r: number; g: number; b: number } {\n\treturn {\n\t\tr: Math.round(r * factor),\n\t\tg: Math.round(g * factor),\n\t\tb: Math.round(b * factor),\n\t};\n}\n\n/**\n * Phase 2: one-shot intro reveal. Diagonal wipe using easeOutCubic.\n * At elapsed >= INTRO_MS the output is byte-identical to composeStaticBanner.\n */\nexport function composeIntroBanner(\n\tart: string[],\n\tmode: GradientColorMode,\n\tnoColor: boolean,\n\telapsedMs: number,\n): string[] {\n\tif (elapsedMs >= INTRO_MS) {\n\t\treturn composeStaticBanner(art, mode, noColor);\n\t}\n\n\tconst geom: GradientGeom = {\n\t\tcols: Math.max(0, ...art.map((line) => Array.from(line).length)),\n\t\trows: art.length,\n\t\tshear: DEFAULT_SHEAR,\n\t};\n\n\tconst p = easeOutCubic(elapsedMs / INTRO_MS);\n\tconst seed = Math.floor(elapsedMs / 100); // changes every 100ms tick\n\n\treturn art.map((line, y) => {\n\t\tconst glyphs = Array.from(line);\n\t\tlet rendered = \"\";\n\t\tfor (let x = 0; x < glyphs.length; x++) {\n\t\t\tconst glyph = glyphs[x]!;\n\t\t\tif (glyph === \" \") {\n\t\t\t\trendered += \" \";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst d = diagonalPosition(x, y, geom);\n\t\t\tconst cell = clamp01((p - d + REVEAL_BAND) / REVEAL_BAND);\n\t\t\tconst { r, g, b } = colorAt(x, y, 0, geom);\n\t\t\tif (cell >= 1) {\n\t\t\t\trendered += paintGlyph(glyph, r, g, b, mode, noColor);\n\t\t\t} else {\n\t\t\t\t// Scramble with dimmed color\n\t\t\t\tconst scramble = scrambleGlyph(x, y, seed);\n\t\t\t\tconst dimFactor = 0.3 + 0.7 * cell;\n\t\t\t\tconst { r: dr, g: dg, b: db } = dimRgb(r, g, b, dimFactor);\n\t\t\t\trendered += paintGlyph(scramble, dr, dg, db, mode, noColor);\n\t\t\t}\n\t\t}\n\t\treturn rendered;\n\t});\n}\n\n/**\n * Phase 3: opt-in idle drift. Reuses colorAt(x,y,t) DRIFT math.\n */\nexport function composeIdleBanner(\n\tart: string[],\n\tmode: GradientColorMode,\n\tnoColor: boolean,\n\telapsedMs: number,\n): string[] {\n\tconst geom: GradientGeom = {\n\t\tcols: Math.max(0, ...art.map((line) => Array.from(line).length)),\n\t\trows: art.length,\n\t\tshear: DEFAULT_SHEAR,\n\t};\n\n\tconst t = (elapsedMs % IDLE_PERIOD_MS) / IDLE_PERIOD_MS;\n\n\treturn art.map((line, y) => {\n\t\tconst glyphs = Array.from(line);\n\t\tlet rendered = \"\";\n\t\tfor (let x = 0; x < glyphs.length; x++) {\n\t\t\tconst glyph = glyphs[x]!;\n\t\t\tconst { r, g, b } = colorAt(x, y, t, geom);\n\t\t\trendered += paintGlyph(glyph, r, g, b, mode, noColor);\n\t\t}\n\t\treturn rendered;\n\t});\n}\n\n/**\n * Gate: should we animate the banner for this phase?\n */\nexport function shouldAnimate(options: {\n\tphase: \"intro\" | \"idle\";\n\tisTTY: boolean;\n\tnoColor: boolean;\n\tcolorMode: GradientColorMode;\n\texpanded: boolean;\n\twidth: number;\n\treducedMotion: boolean;\n\tbusy: boolean;\n\theaderVisibleHint: boolean;\n\tidleDriftEnabled: boolean;\n}): boolean {\n\tif (!shouldGradient(options)) return false;\n\tif (options.reducedMotion || options.busy || !options.headerVisibleHint) return false;\n\tif (options.phase === \"idle\" && !options.idleDriftEnabled) return false;\n\treturn true;\n}\n"]}
1
+ {"version":3,"file":"control-panel-gradient-motion.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-gradient-motion.ts"],"names":[],"mappings":"AAAA,OAAO,EAGN,KAAK,iBAAiB,EAItB,MAAM,6BAA6B,CAAC;AAErC,eAAO,MAAM,QAAQ,MAAM,CAAC;AAC5B,eAAO,MAAM,OAAO,OAAO,CAAC;AAuC5B;;;GAGG;AACH,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,MAAM,EAAE,EACb,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GACf,MAAM,EAAE,CAsCV;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAChC,GAAG,EAAE,MAAM,EAAE,EACb,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GACf,MAAM,EAAE,CAmBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACtC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,OAAO,CAAC;IACvB,IAAI,EAAE,OAAO,CAAC;IACd,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;CAC1B,GAAG,OAAO,CAMV","sourcesContent":["import {\n\tcolorAt,\n\tcomposeStaticBanner,\n\ttype GradientColorMode,\n\ttype GradientGeom,\n\tpaintGlyph,\n\tshouldGradient,\n} from \"./control-panel-gradient.ts\";\n\nexport const INTRO_MS = 900;\nexport const IDLE_MS = 6000;\nconst IDLE_PERIOD_MS = 4200;\nconst REVEAL_BAND = 0.16;\nconst DEFAULT_SHEAR = 2.2;\n\nfunction clamp01(value: number): number {\n\tif (value < 0) return 0;\n\tif (value > 1) return 1;\n\treturn value;\n}\n\nfunction easeOutCubic(t: number): number {\n\tconst u = clamp01(t);\n\treturn 1 - (1 - u) * (1 - u) * (1 - u);\n}\n\nfunction diagonalPosition(x: number, y: number, geom: GradientGeom): number {\n\tconst span = geom.cols - 1 + (geom.rows - 1) * geom.shear;\n\tif (span <= 0) return 0;\n\treturn clamp01((x + (geom.rows - 1 - y) * geom.shear) / span);\n}\n\nconst SCRAMBLE_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%&*+=<>[]{}|/\\\\~^\";\nconst SCRAMBLE_LEN = SCRAMBLE_CHARS.length;\n\nfunction scrambleGlyph(x: number, y: number, seed: number): string {\n\t// Deterministic scramble from position + seed (no Math.random)\n\tconst hash = ((x * 374761393 + y * 668265263 + seed * 1274126177) >>> 0) % SCRAMBLE_LEN;\n\treturn SCRAMBLE_CHARS[hash]!;\n}\n\nfunction dimRgb(r: number, g: number, b: number, factor: number): { r: number; g: number; b: number } {\n\treturn {\n\t\tr: Math.round(r * factor),\n\t\tg: Math.round(g * factor),\n\t\tb: Math.round(b * factor),\n\t};\n}\n\n/**\n * Phase 2: one-shot intro reveal. Diagonal wipe using easeOutCubic.\n * At elapsed >= INTRO_MS the output is byte-identical to composeStaticBanner.\n */\nexport function composeIntroBanner(\n\tart: string[],\n\tmode: GradientColorMode,\n\tnoColor: boolean,\n\telapsedMs: number,\n): string[] {\n\tif (elapsedMs >= INTRO_MS) {\n\t\treturn composeStaticBanner(art, mode, noColor);\n\t}\n\n\tconst geom: GradientGeom = {\n\t\tcols: Math.max(0, ...art.map((line) => Array.from(line).length)),\n\t\trows: art.length,\n\t\tshear: DEFAULT_SHEAR,\n\t};\n\n\tconst p = easeOutCubic(elapsedMs / INTRO_MS);\n\tconst seed = Math.floor(elapsedMs / 100); // changes every 100ms tick\n\n\treturn art.map((line, y) => {\n\t\tconst glyphs = Array.from(line);\n\t\tlet rendered = \"\";\n\t\tfor (let x = 0; x < glyphs.length; x++) {\n\t\t\tconst glyph = glyphs[x]!;\n\t\t\tif (glyph === \" \") {\n\t\t\t\trendered += \" \";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst d = diagonalPosition(x, y, geom);\n\t\t\tconst cell = clamp01((p - d + REVEAL_BAND) / REVEAL_BAND);\n\t\t\tconst { r, g, b } = colorAt(x, y, 0, geom);\n\t\t\tif (cell >= 1) {\n\t\t\t\trendered += paintGlyph(glyph, r, g, b, mode, noColor);\n\t\t\t} else {\n\t\t\t\t// Scramble with dimmed color\n\t\t\t\tconst scramble = scrambleGlyph(x, y, seed);\n\t\t\t\tconst dimFactor = 0.3 + 0.7 * cell;\n\t\t\t\tconst { r: dr, g: dg, b: db } = dimRgb(r, g, b, dimFactor);\n\t\t\t\trendered += paintGlyph(scramble, dr, dg, db, mode, noColor);\n\t\t\t}\n\t\t}\n\t\treturn rendered;\n\t});\n}\n\n/**\n * Phase 3: opt-in idle drift. Reuses colorAt(x,y,t) DRIFT math.\n */\nexport function composeIdleBanner(\n\tart: string[],\n\tmode: GradientColorMode,\n\tnoColor: boolean,\n\telapsedMs: number,\n): string[] {\n\tconst geom: GradientGeom = {\n\t\tcols: Math.max(0, ...art.map((line) => Array.from(line).length)),\n\t\trows: art.length,\n\t\tshear: DEFAULT_SHEAR,\n\t};\n\n\tconst t = (elapsedMs % IDLE_PERIOD_MS) / IDLE_PERIOD_MS;\n\n\treturn art.map((line, y) => {\n\t\tconst glyphs = Array.from(line);\n\t\tlet rendered = \"\";\n\t\tfor (let x = 0; x < glyphs.length; x++) {\n\t\t\tconst glyph = glyphs[x]!;\n\t\t\tconst { r, g, b } = colorAt(x, y, t, geom);\n\t\t\trendered += paintGlyph(glyph, r, g, b, mode, noColor);\n\t\t}\n\t\treturn rendered;\n\t});\n}\n\n/**\n * Gate: should we animate the banner for this phase?\n */\nexport function shouldAnimate(options: {\n\tphase: \"intro\" | \"idle\";\n\tisTTY: boolean;\n\tnoColor: boolean;\n\tcolorMode: GradientColorMode;\n\texpanded: boolean;\n\twidth: number;\n\treducedMotion: boolean;\n\tbusy: boolean;\n\theaderVisibleHint: boolean;\n\tidleDriftEnabled: boolean;\n}): boolean {\n\tconst forceColor = process.env.FORCE_COLOR !== undefined;\n\tif (!shouldGradient({ ...options, isTTY: options.isTTY || forceColor })) return false;\n\tif (options.reducedMotion || options.busy || !options.headerVisibleHint) return false;\n\tif (options.phase === \"idle\" && !options.idleDriftEnabled) return false;\n\treturn true;\n}\n"]}
@@ -101,7 +101,8 @@ export function composeIdleBanner(art, mode, noColor, elapsedMs) {
101
101
  * Gate: should we animate the banner for this phase?
102
102
  */
103
103
  export function shouldAnimate(options) {
104
- if (!shouldGradient(options))
104
+ const forceColor = process.env.FORCE_COLOR !== undefined;
105
+ if (!shouldGradient({ ...options, isTTY: options.isTTY || forceColor }))
105
106
  return false;
106
107
  if (options.reducedMotion || options.busy || !options.headerVisibleHint)
107
108
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"control-panel-gradient-motion.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-gradient-motion.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tcolorAt,\n\tcomposeStaticBanner,\n\ttype GradientColorMode,\n\ttype GradientGeom,\n\tpaintGlyph,\n\tshouldGradient,\n} from \"./control-panel-gradient.ts\";\n\nexport const INTRO_MS = 900;\nexport const IDLE_MS = 6000;\nconst IDLE_PERIOD_MS = 4200;\nconst REVEAL_BAND = 0.16;\nconst DEFAULT_SHEAR = 2.2;\n\nfunction clamp01(value: number): number {\n\tif (value < 0) return 0;\n\tif (value > 1) return 1;\n\treturn value;\n}\n\nfunction easeOutCubic(t: number): number {\n\tconst u = clamp01(t);\n\treturn 1 - (1 - u) * (1 - u) * (1 - u);\n}\n\nfunction diagonalPosition(x: number, y: number, geom: GradientGeom): number {\n\tconst span = geom.cols - 1 + (geom.rows - 1) * geom.shear;\n\tif (span <= 0) return 0;\n\treturn clamp01((x + (geom.rows - 1 - y) * geom.shear) / span);\n}\n\nconst SCRAMBLE_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%&*+=<>[]{}|/\\\\~^\";\nconst SCRAMBLE_LEN = SCRAMBLE_CHARS.length;\n\nfunction scrambleGlyph(x: number, y: number, seed: number): string {\n\t// Deterministic scramble from position + seed (no Math.random)\n\tconst hash = ((x * 374761393 + y * 668265263 + seed * 1274126177) >>> 0) % SCRAMBLE_LEN;\n\treturn SCRAMBLE_CHARS[hash]!;\n}\n\nfunction dimRgb(r: number, g: number, b: number, factor: number): { r: number; g: number; b: number } {\n\treturn {\n\t\tr: Math.round(r * factor),\n\t\tg: Math.round(g * factor),\n\t\tb: Math.round(b * factor),\n\t};\n}\n\n/**\n * Phase 2: one-shot intro reveal. Diagonal wipe using easeOutCubic.\n * At elapsed >= INTRO_MS the output is byte-identical to composeStaticBanner.\n */\nexport function composeIntroBanner(\n\tart: string[],\n\tmode: GradientColorMode,\n\tnoColor: boolean,\n\telapsedMs: number,\n): string[] {\n\tif (elapsedMs >= INTRO_MS) {\n\t\treturn composeStaticBanner(art, mode, noColor);\n\t}\n\n\tconst geom: GradientGeom = {\n\t\tcols: Math.max(0, ...art.map((line) => Array.from(line).length)),\n\t\trows: art.length,\n\t\tshear: DEFAULT_SHEAR,\n\t};\n\n\tconst p = easeOutCubic(elapsedMs / INTRO_MS);\n\tconst seed = Math.floor(elapsedMs / 100); // changes every 100ms tick\n\n\treturn art.map((line, y) => {\n\t\tconst glyphs = Array.from(line);\n\t\tlet rendered = \"\";\n\t\tfor (let x = 0; x < glyphs.length; x++) {\n\t\t\tconst glyph = glyphs[x]!;\n\t\t\tif (glyph === \" \") {\n\t\t\t\trendered += \" \";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst d = diagonalPosition(x, y, geom);\n\t\t\tconst cell = clamp01((p - d + REVEAL_BAND) / REVEAL_BAND);\n\t\t\tconst { r, g, b } = colorAt(x, y, 0, geom);\n\t\t\tif (cell >= 1) {\n\t\t\t\trendered += paintGlyph(glyph, r, g, b, mode, noColor);\n\t\t\t} else {\n\t\t\t\t// Scramble with dimmed color\n\t\t\t\tconst scramble = scrambleGlyph(x, y, seed);\n\t\t\t\tconst dimFactor = 0.3 + 0.7 * cell;\n\t\t\t\tconst { r: dr, g: dg, b: db } = dimRgb(r, g, b, dimFactor);\n\t\t\t\trendered += paintGlyph(scramble, dr, dg, db, mode, noColor);\n\t\t\t}\n\t\t}\n\t\treturn rendered;\n\t});\n}\n\n/**\n * Phase 3: opt-in idle drift. Reuses colorAt(x,y,t) DRIFT math.\n */\nexport function composeIdleBanner(\n\tart: string[],\n\tmode: GradientColorMode,\n\tnoColor: boolean,\n\telapsedMs: number,\n): string[] {\n\tconst geom: GradientGeom = {\n\t\tcols: Math.max(0, ...art.map((line) => Array.from(line).length)),\n\t\trows: art.length,\n\t\tshear: DEFAULT_SHEAR,\n\t};\n\n\tconst t = (elapsedMs % IDLE_PERIOD_MS) / IDLE_PERIOD_MS;\n\n\treturn art.map((line, y) => {\n\t\tconst glyphs = Array.from(line);\n\t\tlet rendered = \"\";\n\t\tfor (let x = 0; x < glyphs.length; x++) {\n\t\t\tconst glyph = glyphs[x]!;\n\t\t\tconst { r, g, b } = colorAt(x, y, t, geom);\n\t\t\trendered += paintGlyph(glyph, r, g, b, mode, noColor);\n\t\t}\n\t\treturn rendered;\n\t});\n}\n\n/**\n * Gate: should we animate the banner for this phase?\n */\nexport function shouldAnimate(options: {\n\tphase: \"intro\" | \"idle\";\n\tisTTY: boolean;\n\tnoColor: boolean;\n\tcolorMode: GradientColorMode;\n\texpanded: boolean;\n\twidth: number;\n\treducedMotion: boolean;\n\tbusy: boolean;\n\theaderVisibleHint: boolean;\n\tidleDriftEnabled: boolean;\n}): boolean {\n\tif (!shouldGradient(options)) return false;\n\tif (options.reducedMotion || options.busy || !options.headerVisibleHint) return false;\n\tif (options.phase === \"idle\" && !options.idleDriftEnabled) return false;\n\treturn true;\n}\n"]}
1
+ {"version":3,"file":"control-panel-gradient-motion.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-gradient-motion.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tcolorAt,\n\tcomposeStaticBanner,\n\ttype GradientColorMode,\n\ttype GradientGeom,\n\tpaintGlyph,\n\tshouldGradient,\n} from \"./control-panel-gradient.ts\";\n\nexport const INTRO_MS = 900;\nexport const IDLE_MS = 6000;\nconst IDLE_PERIOD_MS = 4200;\nconst REVEAL_BAND = 0.16;\nconst DEFAULT_SHEAR = 2.2;\n\nfunction clamp01(value: number): number {\n\tif (value < 0) return 0;\n\tif (value > 1) return 1;\n\treturn value;\n}\n\nfunction easeOutCubic(t: number): number {\n\tconst u = clamp01(t);\n\treturn 1 - (1 - u) * (1 - u) * (1 - u);\n}\n\nfunction diagonalPosition(x: number, y: number, geom: GradientGeom): number {\n\tconst span = geom.cols - 1 + (geom.rows - 1) * geom.shear;\n\tif (span <= 0) return 0;\n\treturn clamp01((x + (geom.rows - 1 - y) * geom.shear) / span);\n}\n\nconst SCRAMBLE_CHARS = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@#$%&*+=<>[]{}|/\\\\~^\";\nconst SCRAMBLE_LEN = SCRAMBLE_CHARS.length;\n\nfunction scrambleGlyph(x: number, y: number, seed: number): string {\n\t// Deterministic scramble from position + seed (no Math.random)\n\tconst hash = ((x * 374761393 + y * 668265263 + seed * 1274126177) >>> 0) % SCRAMBLE_LEN;\n\treturn SCRAMBLE_CHARS[hash]!;\n}\n\nfunction dimRgb(r: number, g: number, b: number, factor: number): { r: number; g: number; b: number } {\n\treturn {\n\t\tr: Math.round(r * factor),\n\t\tg: Math.round(g * factor),\n\t\tb: Math.round(b * factor),\n\t};\n}\n\n/**\n * Phase 2: one-shot intro reveal. Diagonal wipe using easeOutCubic.\n * At elapsed >= INTRO_MS the output is byte-identical to composeStaticBanner.\n */\nexport function composeIntroBanner(\n\tart: string[],\n\tmode: GradientColorMode,\n\tnoColor: boolean,\n\telapsedMs: number,\n): string[] {\n\tif (elapsedMs >= INTRO_MS) {\n\t\treturn composeStaticBanner(art, mode, noColor);\n\t}\n\n\tconst geom: GradientGeom = {\n\t\tcols: Math.max(0, ...art.map((line) => Array.from(line).length)),\n\t\trows: art.length,\n\t\tshear: DEFAULT_SHEAR,\n\t};\n\n\tconst p = easeOutCubic(elapsedMs / INTRO_MS);\n\tconst seed = Math.floor(elapsedMs / 100); // changes every 100ms tick\n\n\treturn art.map((line, y) => {\n\t\tconst glyphs = Array.from(line);\n\t\tlet rendered = \"\";\n\t\tfor (let x = 0; x < glyphs.length; x++) {\n\t\t\tconst glyph = glyphs[x]!;\n\t\t\tif (glyph === \" \") {\n\t\t\t\trendered += \" \";\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst d = diagonalPosition(x, y, geom);\n\t\t\tconst cell = clamp01((p - d + REVEAL_BAND) / REVEAL_BAND);\n\t\t\tconst { r, g, b } = colorAt(x, y, 0, geom);\n\t\t\tif (cell >= 1) {\n\t\t\t\trendered += paintGlyph(glyph, r, g, b, mode, noColor);\n\t\t\t} else {\n\t\t\t\t// Scramble with dimmed color\n\t\t\t\tconst scramble = scrambleGlyph(x, y, seed);\n\t\t\t\tconst dimFactor = 0.3 + 0.7 * cell;\n\t\t\t\tconst { r: dr, g: dg, b: db } = dimRgb(r, g, b, dimFactor);\n\t\t\t\trendered += paintGlyph(scramble, dr, dg, db, mode, noColor);\n\t\t\t}\n\t\t}\n\t\treturn rendered;\n\t});\n}\n\n/**\n * Phase 3: opt-in idle drift. Reuses colorAt(x,y,t) DRIFT math.\n */\nexport function composeIdleBanner(\n\tart: string[],\n\tmode: GradientColorMode,\n\tnoColor: boolean,\n\telapsedMs: number,\n): string[] {\n\tconst geom: GradientGeom = {\n\t\tcols: Math.max(0, ...art.map((line) => Array.from(line).length)),\n\t\trows: art.length,\n\t\tshear: DEFAULT_SHEAR,\n\t};\n\n\tconst t = (elapsedMs % IDLE_PERIOD_MS) / IDLE_PERIOD_MS;\n\n\treturn art.map((line, y) => {\n\t\tconst glyphs = Array.from(line);\n\t\tlet rendered = \"\";\n\t\tfor (let x = 0; x < glyphs.length; x++) {\n\t\t\tconst glyph = glyphs[x]!;\n\t\t\tconst { r, g, b } = colorAt(x, y, t, geom);\n\t\t\trendered += paintGlyph(glyph, r, g, b, mode, noColor);\n\t\t}\n\t\treturn rendered;\n\t});\n}\n\n/**\n * Gate: should we animate the banner for this phase?\n */\nexport function shouldAnimate(options: {\n\tphase: \"intro\" | \"idle\";\n\tisTTY: boolean;\n\tnoColor: boolean;\n\tcolorMode: GradientColorMode;\n\texpanded: boolean;\n\twidth: number;\n\treducedMotion: boolean;\n\tbusy: boolean;\n\theaderVisibleHint: boolean;\n\tidleDriftEnabled: boolean;\n}): boolean {\n\tconst forceColor = process.env.FORCE_COLOR !== undefined;\n\tif (!shouldGradient({ ...options, isTTY: options.isTTY || forceColor })) return false;\n\tif (options.reducedMotion || options.busy || !options.headerVisibleHint) return false;\n\tif (options.phase === \"idle\" && !options.idleDriftEnabled) return false;\n\treturn true;\n}\n"]}
@@ -1,6 +1,10 @@
1
1
  import type { PiPackageIntakeSummary } from "../../../core/pi-package-intake.ts";
2
2
  import { type TodoState } from "../../../core/todo-state.ts";
3
3
  export declare const CONTROL_PANEL_ASCII_ART: string[];
4
+ export declare const CONTROL_PANEL_OVERLAY_MIN_WIDTH = 112;
5
+ export declare const CONTROL_PANEL_DECK_MIN_WIDTH: number;
6
+ export declare const CONTROL_PANEL_SIDEBAR_WIDTH = 38;
7
+ export declare const CONTROL_PANEL_GAP_WIDTH = 2;
4
8
  export interface ControlPanelContent {
5
9
  appName: string;
6
10
  version: string;
@@ -25,6 +29,16 @@ export interface ControlPanelStatusSnapshot {
25
29
  readonly cwdLabel?: string;
26
30
  readonly gitBranch?: string | null;
27
31
  readonly todoState?: TodoState;
32
+ readonly runtimeState?: string;
33
+ readonly routeState?: string;
34
+ readonly evidenceState?: string;
35
+ readonly controlState?: string;
36
+ readonly dagOrchestrationState?: string;
37
+ readonly ansiColorState?: string;
38
+ readonly startupState?: string;
39
+ readonly linkState?: string;
40
+ readonly sidebarState?: string;
28
41
  }
29
- export declare function renderControlPanelLayout(content: ControlPanelContent, expanded: boolean, width: number, bannerFrame?: string[]): string[];
42
+ export declare function renderControlPanelLayout(content: ControlPanelContent, expanded: boolean, width: number, bannerFrame?: string[], sparkleMs?: number): string[];
43
+ export declare function renderControlPanelRightPane(content: ControlPanelContent, width: number): string[];
30
44
  //# 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":"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
+ {"version":3,"file":"control-panel-layout.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/control-panel-layout.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,EAAkB,KAAK,SAAS,EAA0B,MAAM,6BAA6B,CAAC;AAgBrG,eAAO,MAAM,uBAAuB,UAQnC,CAAC;AAEF,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,eAAO,MAAM,4BAA4B,QAAsC,CAAC;AAChF,eAAO,MAAM,2BAA2B,KAAK,CAAC;AAC9C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAKzC,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;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,wBAAgB,wBAAwB,CACvC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,MAAM,EACb,WAAW,CAAC,EAAE,MAAM,EAAE,EACtB,SAAS,SAAI,GACX,MAAM,EAAE,CAKV;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAGjG","sourcesContent":["import { truncateToWidth, visibleWidth } from \"omk-tui\";\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\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\";\nimport { SPARKLE_ROW_BOTTOM, SPARKLE_ROW_TOP, sparkleRow } from \"./control-panel-sparkles.ts\";\n\nexport const CONTROL_PANEL_ASCII_ART = [\n\t\" ███████ ██ ██ ██ ██\",\n\t\"██ ██ ███ ███ ██ ██ \",\n\t\"██ ██ ████ ████ ██ ██ \",\n\t\"██ ██ ██ ██ ██ ██ █████ \",\n\t\"██ ██ ██ ███ ██ ██ ██ \",\n\t\"██ ██ ██ █ ██ ██ ██ \",\n\t\" ███████ ██ ██ ██ ██\",\n];\nconst META_WIDTH = 31;\nexport const CONTROL_PANEL_OVERLAY_MIN_WIDTH = 112;\nexport const CONTROL_PANEL_DECK_MIN_WIDTH = CONTROL_PANEL_OVERLAY_MIN_WIDTH + 1;\nexport const CONTROL_PANEL_SIDEBAR_WIDTH = 38;\nexport const CONTROL_PANEL_GAP_WIDTH = 2;\nconst CONTEXT_METER_CELLS = 12;\nconst SPARKLINE_WIDTH = 16;\nconst SPARKLINE_GLYPHS = \"▁▂▃▄▅▆▇█\";\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\treadonly runtimeState?: string;\n\treadonly routeState?: string;\n\treadonly evidenceState?: string;\n\treadonly controlState?: string;\n\treadonly dagOrchestrationState?: string;\n\treadonly ansiColorState?: string;\n\treadonly startupState?: string;\n\treadonly linkState?: string;\n\treadonly sidebarState?: string;\n}\n\nexport function renderControlPanelLayout(\n\tcontent: ControlPanelContent,\n\texpanded: boolean,\n\twidth: number,\n\tbannerFrame?: string[],\n\tsparkleMs = 0,\n): string[] {\n\tif (width <= 0) return [];\n\treturn expanded\n\t\t? renderExpanded(content, width, bannerFrame, sparkleMs)\n\t\t: renderCompact(content, width, bannerFrame, sparkleMs);\n}\n\nexport function renderControlPanelRightPane(content: ControlPanelContent, width: number): string[] {\n\tif (width <= 0) return [];\n\treturn sidebarPanel(content, width);\n}\n\nfunction renderCompact(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tif (width >= CONTROL_PANEL_DECK_MIN_WIDTH) {\n\t\tconst deck = renderDeck(content, width, bannerFrame, sparkleMs);\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[], sparkleMs = 0): string[] {\n\tconst { leftWidth, sidebarWidth } = deckWidths(width);\n\tif (leftWidth < 72) return [];\n\tconst lines = composeColumns(\n\t\theroPanel(content, leftWidth, bannerFrame, sparkleMs),\n\t\tleftWidth,\n\t\tsidebarPanel(content, sidebarWidth),\n\t\tsidebarWidth,\n\t\tCONTROL_PANEL_GAP_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 deckWidths(width: number): { leftWidth: number; sidebarWidth: number } {\n\tconst sidebarWidth = Math.min(CONTROL_PANEL_SIDEBAR_WIDTH, Math.max(34, Math.floor(width * 0.28)));\n\treturn { leftWidth: width - CONTROL_PANEL_GAP_WIDTH - sidebarWidth, sidebarWidth };\n}\n\nfunction heroPanel(content: ControlPanelContent, width: number, bannerFrame?: string[], sparkleMs = 0): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst innerWidth = Math.max(0, width - 4);\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\tboxTextLine(width, sparkleRow(innerWidth, SPARKLE_ROW_TOP, sparkleMs)),\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 = index < 2 ? \"mdCode\" : index < 4 ? \"accent\" : index < 6 ? \"success\" : \"warning\";\n\t\t\treturn boxCenteredLine(width, theme.fg(color, line));\n\t\t}),\n\t\tboxTextLine(width, sparkleRow(innerWidth, SPARKLE_ROW_BOTTOM, sparkleMs)),\n\t\tboxCenteredLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"warning\", \"●\")} ${theme.bold(\"OMK\")} / ${theme.fg(\"success\", heroModelLabel(snapshot))}`,\n\t\t),\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\", \"control\")}`,\n\t\t),\n\t\tboxBottom(width),\n\t];\n}\n\nfunction heroModelLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tif (!snapshot.modelId) return \"no-model\";\n\tconst think = snapshot.thinkingLevel;\n\treturn think && think !== \"off\" ? `${snapshot.modelId}:${think}` : snapshot.modelId;\n}\n\nfunction sidebarPanel(content: ControlPanelContent, width: number): string[] {\n\tconst snapshot = statusSnapshot(content);\n\tconst headroomLabel = snapshot.headroomStatus ?? snapshot.optimizerPolicy ?? \"unknown\";\n\tconst mcpCount = snapshot.mcpCount;\n\tconst skillCount = snapshot.skillCount;\n\tconst packageIntakeLabel = packageIntakeStatusLabel(snapshot.packageIntake);\n\tconst contextMeter = contextMeterLabel(snapshot);\n\tconst activitySparkline = activitySparklineLabel(snapshot);\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(\"mdCode\", \"MATRIX RAIN\")} // ${theme.fg(\"success\", \"NEON GRID ONLINE\")}`),\n\t\tboxCenteredLine(width, theme.fg(\"text\", \"NIGHT-CITY-MATRIX-V3\")),\n\t\tsidebarRule(width, \"STATUS\", \"accent\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"state:\")} ${coloredStatus(snapshot.runtimeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${coloredStatus(snapshot.routeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"evidence:\")} ${coloredStatus(snapshot.evidenceState ?? \"unknown\")}`),\n\t\tsidebarRule(width, \"TODO\", \"accent\"),\n\t\t...todoSidebarLines(snapshot.todoState, width),\n\t\tsidebarRule(width, \"SESSION\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"cwd\", snapshot.cwdLabel ?? \"?\", \"end\"),\n\t\tsemanticBoxTextLine(width, \"git\", snapshot.gitBranch ?? \"?\", \"start\"),\n\t\tsidebarRule(width, \"MODEL / CTX\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"model\", modelStatusLabel(snapshot), \"end\"),\n\t\tsemanticBoxTextLine(width, \"think\", snapshot.thinkingLevel ?? \"off\", \"start\"),\n\t\tboxTextLine(\n\t\t\twidth,\n\t\t\t`${theme.fg(\"muted\", \"ctx:\")} ${theme.fg(statusColor(snapshot.contextPercent === undefined || snapshot.contextPercent === null ? \"unknown\" : \"ready\"), contextStatusLabel(snapshot))}`,\n\t\t),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"meter:\")} ${contextMeter}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"pulse:\")} ${activitySparkline}`),\n\t\tsidebarRule(width, \"RUNTIME / MCP / SKILLS\", \"accent\"),\n\t\tsemanticBoxTextLine(width, \"headroom\", headroomLabel, \"end\"),\n\t\tsemanticBoxTextLine(width, \"omk\", snapshot.dagOrchestrationState ?? \"unknown\", \"end\"),\n\t\tsemanticBoxTextLine(width, \"sidebar\", snapshot.sidebarState ?? \"unknown\", \"start\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"res:\")} MCP:${mcpCount ?? \"?\"} skills:${skillCount ?? \"?\"}`),\n\t\tsemanticBoxTextLine(width, \"pkg\", packageIntakeLabel, \"end\"),\n\t\tsidebarRule(width, \"CONTROL\", \"accent\"),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"route:\")} ${coloredStatus(snapshot.routeState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"verify:\")} ${coloredStatus(snapshot.evidenceState ?? \"unknown\")}`),\n\t\tboxTextLine(width, `${theme.fg(\"muted\", \"control:\")} ${coloredStatus(snapshot.controlState ?? \"unknown\")}`),\n\t\tboxBottom(width, \"accent\"),\n\t];\n}\n\nfunction semanticBoxTextLine(\n\twidth: number,\n\tlabel: string,\n\tvalue: string,\n\tpreserve: \"start\" | \"middle\" | \"end\",\n): string {\n\tconst prefix = `${theme.fg(\"muted\", `${label}:`)} `;\n\tconst availableWidth = Math.max(0, width - 4 - visibleWidth(prefix));\n\treturn boxTextLine(width, `${prefix}${semanticTruncate(value, availableWidth, preserve)}`);\n}\n\nfunction semanticTruncate(value: string, maxWidth: number, preserve: \"start\" | \"middle\" | \"end\"): string {\n\tif (visibleWidth(value) <= maxWidth) return value;\n\tif (maxWidth <= 0) return \"\";\n\tif (preserve === \"start\") return truncateToWidth(value, maxWidth, \"…\");\n\tconst ellipsis = \"…\";\n\tconst targetWidth = maxWidth - visibleWidth(ellipsis);\n\tif (targetWidth <= 0) return truncateToWidth(ellipsis, maxWidth, \"\");\n\tif (preserve === \"middle\") {\n\t\tconst headWidth = Math.max(1, Math.floor(targetWidth / 2));\n\t\tconst tailWidth = Math.max(1, targetWidth - headWidth);\n\t\treturn `${takeStart(value, headWidth)}${ellipsis}${takeEnd(value, tailWidth)}`;\n\t}\n\treturn `${ellipsis}${takeEnd(value, targetWidth)}`;\n}\n\nfunction takeStart(value: string, maxWidth: number): string {\n\tlet prefix = \"\";\n\tfor (const char of Array.from(value)) {\n\t\tif (visibleWidth(prefix + char) > maxWidth) break;\n\t\tprefix += char;\n\t}\n\treturn prefix;\n}\n\nfunction takeEnd(value: string, maxWidth: number): string {\n\tlet suffix = \"\";\n\tfor (const char of Array.from(value).reverse()) {\n\t\tif (visibleWidth(char + suffix) > maxWidth) break;\n\t\tsuffix = char + suffix;\n\t}\n\treturn suffix;\n}\n\nfunction statusToken(label: string, value: string | undefined): string {\n\tconst state = value ?? \"unknown\";\n\treturn theme.fg(statusColor(state), `${label}:${state.toUpperCase()}`);\n}\n\nfunction coloredStatus(value: string): string {\n\treturn theme.fg(statusColor(value), value);\n}\n\nfunction statusColor(value: string): ThemeColor {\n\tconst normalized = value.toLowerCase();\n\tif ([\"ready\", \"on\", \"active\", \"available\", \"linked\", \"tracking\", \"pinned\"].includes(normalized)) return \"success\";\n\tif ([\"degraded\", \"limited\", \"blocked\", \"off\"].includes(normalized)) return \"warning\";\n\treturn \"muted\";\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\tsemanticBoxTextLine(width, \"next\", next?.label ?? \"complete\", \"middle\"),\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[], sparkleMs = 0): string[] {\n\tif (width >= CONTROL_PANEL_DECK_MIN_WIDTH) {\n\t\tconst { leftWidth, sidebarWidth } = deckWidths(width);\n\t\tconst resourceLines = [\"\", ...content.onboarding().split(\"\\n\")];\n\t\tconst rightRail = blankSidebarRail(sidebarWidth, resourceLines.length);\n\t\tconst lines = [...renderDeck(content, width, bannerFrame, sparkleMs)];\n\t\tlines.push(...composeColumns(resourceLines, leftWidth, rightRail, sidebarWidth, CONTROL_PANEL_GAP_WIDTH, width));\n\t\treturn lines;\n\t}\n\n\tconst lines = [divider(width, \"OMK//CONTROL PANEL\", \"accent\"), statusLine(content, width)];\n\tif (width >= 32) lines.push(...brandLines(content, 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 blankSidebarRail(width: number, lineCount: number): string[] {\n\treturn Array.from({ length: lineCount }, () => boxTextLine(width, \"\"));\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 contextMeterLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst percent = normalizePercent(snapshot.contextPercent);\n\tif (percent === undefined) {\n\t\treturn `${theme.fg(\"borderMuted\", \"░\".repeat(CONTEXT_METER_CELLS))} ${theme.fg(\"muted\", \"??%\")}`;\n\t}\n\tconst filled = Math.max(0, Math.min(CONTEXT_METER_CELLS, Math.round((percent / 100) * CONTEXT_METER_CELLS)));\n\tconst color: ThemeColor = percent >= 85 ? \"warning\" : percent >= 65 ? \"mdCode\" : \"success\";\n\treturn `${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(CONTEXT_METER_CELLS - filled))} ${theme.fg(color, `${Math.round(percent)}%`)}`;\n}\n\nfunction activitySparklineLabel(snapshot: ControlPanelStatusSnapshot): string {\n\tconst seed = hashSnapshot(snapshot);\n\tconst glyphs = Array.from(SPARKLINE_GLYPHS);\n\tconst parts: string[] = [];\n\tfor (let index = 0; index < SPARKLINE_WIDTH; index++) {\n\t\tconst glyph = glyphs[(seed + index * 5 + index * index) % glyphs.length] ?? \"▁\";\n\t\tconst color: ThemeColor = index % 5 === 0 ? \"accent\" : index % 3 === 0 ? \"warning\" : \"success\";\n\t\tparts.push(theme.fg(color, glyph));\n\t}\n\treturn parts.join(\"\");\n}\n\nfunction normalizePercent(value: number | null | undefined): number | undefined {\n\tif (value === null || value === undefined || !Number.isFinite(value)) return undefined;\n\tif (value < 0) return 0;\n\tif (value > 100) return 100;\n\treturn value;\n}\n\nfunction hashSnapshot(snapshot: ControlPanelStatusSnapshot): number {\n\tconst source = [\n\t\tsnapshot.modelProvider,\n\t\tsnapshot.modelId,\n\t\tsnapshot.contextPercent?.toFixed(1),\n\t\tsnapshot.contextTokens,\n\t\tsnapshot.contextWindowTokens,\n\t\tsnapshot.cwdLabel,\n\t\tsnapshot.gitBranch,\n\t].join(\"|\");\n\tlet hash = 2166136261;\n\tfor (let index = 0; index < source.length; index++) {\n\t\thash ^= source.charCodeAt(index);\n\t\thash = Math.imul(hash, 16777619);\n\t}\n\treturn hash >>> 0;\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(content: ControlPanelContent, 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\tconst snapshot = statusSnapshot(content);\n\treturn art.map((line, index) =>\n\t\ttextLine(\n\t\t\twidth,\n\t\t\t`${fitLine(theme.fg(\"accent\", line), leftWidth)}${theme.fg(\"borderMuted\", \" | \")}${metadataLines(snapshot)[index] ?? \"\"}`,\n\t\t),\n\t);\n}\n\nfunction metadataLines(snapshot: ControlPanelStatusSnapshot): 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\")} ${coloredStatus(snapshot.runtimeState ?? \"unknown\")}`,\n\t\t`${theme.fg(\"mdCode\", \"THEME\")} ${theme.fg(\"accent\", themeName)}`,\n\t\t`${theme.fg(\"mdCode\", \"STARTUP\")} ${coloredStatus(snapshot.startupState ?? \"unknown\")}`,\n\t\t`${theme.fg(\"mdCode\", \"LINK\")} ${coloredStatus(snapshot.linkState ?? \"unknown\")}`,\n\t];\n}\n\nfunction statusLine(content: ControlPanelContent, width: number): string {\n\tconst snapshot = statusSnapshot(content);\n\tconst segments = [\n\t\ttheme.bold(theme.fg(\"accent\", `${content.appName.toUpperCase()} v${content.version}`)),\n\t\tstatusToken(\"CORE\", snapshot.runtimeState),\n\t\tstatusToken(\"ANSI\", snapshot.ansiColorState),\n\t\tstatusToken(\"STARTUP\", snapshot.startupState),\n\t\tstatusToken(\"LINK\", snapshot.linkState),\n\t\ttheme.fg(\"muted\", \"THEME:LIVE\"),\n\t];\n\treturn textLine(width, segments.join(theme.fg(\"borderMuted\", \" | \")));\n}\n"]}