centaurus-cli 3.1.2 → 3.1.4

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 (142) hide show
  1. package/dist/cli-adapter.js +689 -155
  2. package/dist/cli-adapter.js.map +1 -1
  3. package/dist/config/defaultConfig.js +1 -4
  4. package/dist/config/defaultConfig.js.map +1 -1
  5. package/dist/config/models.js +6 -0
  6. package/dist/config/models.js.map +1 -1
  7. package/dist/config/slash-commands.js +66 -2
  8. package/dist/config/slash-commands.js.map +1 -1
  9. package/dist/config/types.js +4 -4
  10. package/dist/config/types.js.map +1 -1
  11. package/dist/index.js +36 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/services/ai-context-injector.js +109 -0
  14. package/dist/services/ai-context-injector.js.map +1 -1
  15. package/dist/services/ai-service-client.js +3 -2
  16. package/dist/services/ai-service-client.js.map +1 -1
  17. package/dist/services/api-client.js.map +1 -1
  18. package/dist/services/background-task-manager.js +59 -0
  19. package/dist/services/background-task-manager.js.map +1 -1
  20. package/dist/services/local-chat-storage.js +2 -0
  21. package/dist/services/local-chat-storage.js.map +1 -1
  22. package/dist/services/skill-storage.js +141 -0
  23. package/dist/services/skill-storage.js.map +1 -0
  24. package/dist/services/sub-agent-manager.js +49 -8
  25. package/dist/services/sub-agent-manager.js.map +1 -1
  26. package/dist/services/warpify-detector.js +17 -5
  27. package/dist/services/warpify-detector.js.map +1 -1
  28. package/dist/tools/background-command.js +5 -2
  29. package/dist/tools/background-command.js.map +1 -1
  30. package/dist/tools/command.js +367 -109
  31. package/dist/tools/command.js.map +1 -1
  32. package/dist/tools/file-ops.js +23 -6
  33. package/dist/tools/file-ops.js.map +1 -1
  34. package/dist/tools/plan-mode.js +184 -336
  35. package/dist/tools/plan-mode.js.map +1 -1
  36. package/dist/tools/sub-agent.js +24 -5
  37. package/dist/tools/sub-agent.js.map +1 -1
  38. package/dist/tools/todo-list.js +157 -0
  39. package/dist/tools/todo-list.js.map +1 -0
  40. package/dist/types/skill.js +30 -0
  41. package/dist/types/skill.js.map +1 -0
  42. package/dist/ui/components/App.js +956 -162
  43. package/dist/ui/components/App.js.map +1 -1
  44. package/dist/ui/components/AuthScreen.js +3 -1
  45. package/dist/ui/components/AuthScreen.js.map +1 -1
  46. package/dist/ui/components/AuthWelcomeScreen.js +3 -1
  47. package/dist/ui/components/AuthWelcomeScreen.js.map +1 -1
  48. package/dist/ui/components/CodeBlock.js +3 -1
  49. package/dist/ui/components/CodeBlock.js.map +1 -1
  50. package/dist/ui/components/CompactShellPreview.js +44 -0
  51. package/dist/ui/components/CompactShellPreview.js.map +1 -0
  52. package/dist/ui/components/ConfigViewer.js +3 -1
  53. package/dist/ui/components/ConfigViewer.js.map +1 -1
  54. package/dist/ui/components/ConfirmPrompt.js +3 -1
  55. package/dist/ui/components/ConfirmPrompt.js.map +1 -1
  56. package/dist/ui/components/ConnectionStatusMessage.js +3 -1
  57. package/dist/ui/components/ConnectionStatusMessage.js.map +1 -1
  58. package/dist/ui/components/DetailedPlanReviewScreen.js +84 -74
  59. package/dist/ui/components/DetailedPlanReviewScreen.js.map +1 -1
  60. package/dist/ui/components/DiffViewer.js +6 -3
  61. package/dist/ui/components/DiffViewer.js.map +1 -1
  62. package/dist/ui/components/FileCreationPreview.js.map +1 -1
  63. package/dist/ui/components/FileTagAutocomplete.js +4 -2
  64. package/dist/ui/components/FileTagAutocomplete.js.map +1 -1
  65. package/dist/ui/components/InputBox.js +243 -40
  66. package/dist/ui/components/InputBox.js.map +1 -1
  67. package/dist/ui/components/InteractiveShell.js +5 -3
  68. package/dist/ui/components/InteractiveShell.js.map +1 -1
  69. package/dist/ui/components/KeyboardHelp.js +4 -1
  70. package/dist/ui/components/KeyboardHelp.js.map +1 -1
  71. package/dist/ui/components/LoadingIndicator.js +3 -1
  72. package/dist/ui/components/LoadingIndicator.js.map +1 -1
  73. package/dist/ui/components/MCPAddScreen.js +63 -13
  74. package/dist/ui/components/MCPAddScreen.js.map +1 -1
  75. package/dist/ui/components/MarkdownRenderer.js +3 -1
  76. package/dist/ui/components/MarkdownRenderer.js.map +1 -1
  77. package/dist/ui/components/MessageDisplay.js +9 -7
  78. package/dist/ui/components/MessageDisplay.js.map +1 -1
  79. package/dist/ui/components/ModelPicker.js +170 -0
  80. package/dist/ui/components/ModelPicker.js.map +1 -0
  81. package/dist/ui/components/MonitorModeAIPanel.js +3 -1
  82. package/dist/ui/components/MonitorModeAIPanel.js.map +1 -1
  83. package/dist/ui/components/PlanAcceptedMessage.js +12 -6
  84. package/dist/ui/components/PlanAcceptedMessage.js.map +1 -1
  85. package/dist/ui/components/PlanQuestionMessage.js +37 -0
  86. package/dist/ui/components/PlanQuestionMessage.js.map +1 -0
  87. package/dist/ui/components/PlanQuestionScreen.js +138 -0
  88. package/dist/ui/components/PlanQuestionScreen.js.map +1 -0
  89. package/dist/ui/components/PlanReviewScreen.js +7 -9
  90. package/dist/ui/components/PlanReviewScreen.js.map +1 -1
  91. package/dist/ui/components/RulesEditorScreen.js +65 -28
  92. package/dist/ui/components/RulesEditorScreen.js.map +1 -1
  93. package/dist/ui/components/SelectPrompt.js +3 -1
  94. package/dist/ui/components/SelectPrompt.js.map +1 -1
  95. package/dist/ui/components/SkillCreatorScreen.js +217 -0
  96. package/dist/ui/components/SkillCreatorScreen.js.map +1 -0
  97. package/dist/ui/components/SlashCommandAutocomplete.js +4 -2
  98. package/dist/ui/components/SlashCommandAutocomplete.js.map +1 -1
  99. package/dist/ui/components/StatusBar.js +4 -2
  100. package/dist/ui/components/StatusBar.js.map +1 -1
  101. package/dist/ui/components/StreamingMessageDisplay.js +5 -3
  102. package/dist/ui/components/StreamingMessageDisplay.js.map +1 -1
  103. package/dist/ui/components/SubAgentListScreen.js +65 -0
  104. package/dist/ui/components/SubAgentListScreen.js.map +1 -0
  105. package/dist/ui/components/SubAgentViewScreen.js +123 -0
  106. package/dist/ui/components/SubAgentViewScreen.js.map +1 -0
  107. package/dist/ui/components/TaskCompletedMessage.js +40 -8
  108. package/dist/ui/components/TaskCompletedMessage.js.map +1 -1
  109. package/dist/ui/components/TaskProgressIndicator.js +6 -4
  110. package/dist/ui/components/TaskProgressIndicator.js.map +1 -1
  111. package/dist/ui/components/TextEditor.js +297 -0
  112. package/dist/ui/components/TextEditor.js.map +1 -0
  113. package/dist/ui/components/TodoListMessage.js +59 -0
  114. package/dist/ui/components/TodoListMessage.js.map +1 -0
  115. package/dist/ui/components/ToolExecutionMessage.js +134 -84
  116. package/dist/ui/components/ToolExecutionMessage.js.map +1 -1
  117. package/dist/ui/components/ToolExecutionStatus.js +3 -1
  118. package/dist/ui/components/ToolExecutionStatus.js.map +1 -1
  119. package/dist/ui/components/WelcomeBanner.js +33 -33
  120. package/dist/ui/components/WelcomeBanner.js.map +1 -1
  121. package/dist/ui/components/WorkflowCreatorScreen.js +5 -3
  122. package/dist/ui/components/WorkflowCreatorScreen.js.map +1 -1
  123. package/dist/ui/theme.js +97 -0
  124. package/dist/ui/theme.js.map +1 -0
  125. package/dist/ui/utils/chat-history-limit.js +247 -0
  126. package/dist/ui/utils/chat-history-limit.js.map +1 -0
  127. package/dist/utils/chat-formatter.js +22 -9
  128. package/dist/utils/chat-formatter.js.map +1 -1
  129. package/dist/utils/git-stats.js +7 -5
  130. package/dist/utils/git-stats.js.map +1 -1
  131. package/dist/utils/input-classifier.js +11 -1
  132. package/dist/utils/input-classifier.js.map +1 -1
  133. package/dist/utils/output-truncation.js +175 -0
  134. package/dist/utils/output-truncation.js.map +1 -0
  135. package/dist/utils/rule-reference-resolver.js +3 -3
  136. package/dist/utils/rule-reference-resolver.js.map +1 -1
  137. package/dist/utils/tunnel-commands-manager.js +134 -0
  138. package/dist/utils/tunnel-commands-manager.js.map +1 -0
  139. package/package.json +91 -90
  140. package/postinstall.js +4 -11
  141. package/dist/ui/components/MultiLineInput.js +0 -255
  142. package/dist/ui/components/MultiLineInput.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
2
  import { Box, Text } from "ink";
3
3
  import Spinner from "ink-spinner";
4
+ import { useTheme } from "../theme.js";
4
5
  const TOOL_LABELS = {
5
6
  read_file: { verb: "Reading file", emoji: "\u{1F4D6}" },
6
7
  write_file: { verb: "Writing file", emoji: "\u270D\uFE0F" },
@@ -40,11 +41,12 @@ const ToolExecutionStatus = React.memo(({
40
41
  error
41
42
  }) => {
42
43
  const toolInfo = getToolDisplayInfo(toolName);
44
+ const theme = useTheme();
43
45
  if (status === "pending") {
44
46
  return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "#666666", dimColor: true }, "\u23F3 ", toolInfo.verb, "..."));
45
47
  }
46
48
  if (status === "executing") {
47
- return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "#00ccff" }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React.createElement(Text, { color: "#00ccff", bold: true }, " ", toolInfo.emoji, " ", toolInfo.verb, "..."));
49
+ return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: theme.accent }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React.createElement(Text, { color: theme.accent, bold: true }, " ", toolInfo.emoji, " ", toolInfo.verb, "..."));
48
50
  }
49
51
  if (status === "success") {
50
52
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "#00cc66", bold: true }, "\u2713 ", toolInfo.emoji, " ", toolInfo.verb)), result && result.length < 300 && /* @__PURE__ */ React.createElement(Box, { marginLeft: 2 }, /* @__PURE__ */ React.createElement(Text, { color: "#666666" }, result)));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/components/ToolExecutionStatus.tsx"],"sourcesContent":["import React, { useState, useEffect } from 'react';\r\nimport { Box, Text } from 'ink';\r\nimport Spinner from 'ink-spinner';\r\n\r\ninterface ToolExecutionStatusProps {\r\n toolName: string;\r\n status: 'pending' | 'executing' | 'success' | 'error';\r\n result?: string;\r\n error?: string;\r\n}\r\n\r\nconst TOOL_LABELS: Record<string, { verb: string; emoji: string }> = {\r\n read_file: { verb: 'Reading file', emoji: '📖' },\r\n write_file: { verb: 'Writing file', emoji: '✍️' },\r\n edit_file: { verb: 'Editing file', emoji: '✏️' },\r\n list_directory: { verb: 'Listing directory', emoji: '📁' },\r\n execute_command: { verb: 'Executing command', emoji: '⚡' },\r\n grep_search: { verb: 'Searching files', emoji: '🔍' },\r\n create_image: { verb: 'Generating image', emoji: '🎨' },\r\n add_mcp: { verb: 'Adding MCP server', emoji: '🔌' },\r\n};\r\n\r\n/**\r\n * Parse MCP tool name to extract server name and tool name\r\n * MCP tools are named: mcp_{serverName}_{toolName}\r\n */\r\nfunction parseMCPToolName(toolName: string): { serverName: string; mcpToolName: string } | null {\r\n if (!toolName.startsWith('mcp_')) return null;\r\n const withoutPrefix = toolName.slice(4);\r\n const firstUnderscoreIdx = withoutPrefix.indexOf('_');\r\n if (firstUnderscoreIdx === -1) {\r\n return { serverName: withoutPrefix, mcpToolName: 'unknown' };\r\n }\r\n return {\r\n serverName: withoutPrefix.slice(0, firstUnderscoreIdx),\r\n mcpToolName: withoutPrefix.slice(firstUnderscoreIdx + 1)\r\n };\r\n}\r\n\r\nfunction getToolDisplayInfo(toolName: string): { verb: string; emoji: string } {\r\n const mcpInfo = parseMCPToolName(toolName);\r\n if (mcpInfo) {\r\n return {\r\n verb: `MCP ${mcpInfo.serverName} ${mcpInfo.mcpToolName}`,\r\n emoji: '⚡'\r\n };\r\n }\r\n return TOOL_LABELS[toolName] || { verb: 'Executing', emoji: '🔧' };\r\n}\r\n\r\nexport const ToolExecutionStatus: React.FC<ToolExecutionStatusProps> = React.memo(({\r\n toolName,\r\n status,\r\n result,\r\n error,\r\n}) => {\r\n const toolInfo = getToolDisplayInfo(toolName);\r\n\r\n if (status === 'pending') {\r\n return (\r\n <Box>\r\n <Text color=\"#666666\" dimColor>⏳ {toolInfo.verb}...</Text>\r\n </Box>\r\n );\r\n }\r\n\r\n if (status === 'executing') {\r\n return (\r\n <Box>\r\n <Text color=\"#00ccff\">\r\n <Spinner type=\"dots\" />\r\n </Text>\r\n <Text color=\"#00ccff\" bold> {toolInfo.emoji} {toolInfo.verb}...</Text>\r\n </Box>\r\n );\r\n }\r\n\r\n if (status === 'success') {\r\n return (\r\n <Box flexDirection=\"column\">\r\n <Box>\r\n <Text color=\"#00cc66\" bold>✓ {toolInfo.emoji} {toolInfo.verb}</Text>\r\n </Box>\r\n {result && result.length < 300 && (\r\n <Box marginLeft={2}>\r\n <Text color=\"#666666\">{result}</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n }\r\n\r\n if (status === 'error') {\r\n return (\r\n <Box flexDirection=\"column\">\r\n <Box>\r\n <Text color=\"#ff3366\" bold>✗ {toolInfo.emoji} {toolInfo.verb} failed</Text>\r\n </Box>\r\n {error && (\r\n <Box marginLeft={2}>\r\n <Text color=\"#ff3366\">{error.slice(0, 200)}{error.length > 200 ? '...' : ''}</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n }\r\n\r\n return null;\r\n}, (prevProps, nextProps) => {\r\n // Only re-render if props actually changed\r\n return prevProps.toolName === nextProps.toolName &&\r\n prevProps.status === nextProps.status &&\r\n prevProps.result === nextProps.result &&\r\n prevProps.error === nextProps.error;\r\n});\r\n"],"mappings":"AAAA,OAAO,WAAoC;AAC3C,SAAS,KAAK,YAAY;AAC1B,OAAO,aAAa;AASpB,MAAM,cAA+D;AAAA,EACnE,WAAW,EAAE,MAAM,gBAAgB,OAAO,YAAK;AAAA,EAC/C,YAAY,EAAE,MAAM,gBAAgB,OAAO,eAAK;AAAA,EAChD,WAAW,EAAE,MAAM,gBAAgB,OAAO,eAAK;AAAA,EAC/C,gBAAgB,EAAE,MAAM,qBAAqB,OAAO,YAAK;AAAA,EACzD,iBAAiB,EAAE,MAAM,qBAAqB,OAAO,SAAI;AAAA,EACzD,aAAa,EAAE,MAAM,mBAAmB,OAAO,YAAK;AAAA,EACpD,cAAc,EAAE,MAAM,oBAAoB,OAAO,YAAK;AAAA,EACtD,SAAS,EAAE,MAAM,qBAAqB,OAAO,YAAK;AACpD;AAMA,SAAS,iBAAiB,UAAsE;AAC9F,MAAI,CAAC,SAAS,WAAW,MAAM,EAAG,QAAO;AACzC,QAAM,gBAAgB,SAAS,MAAM,CAAC;AACtC,QAAM,qBAAqB,cAAc,QAAQ,GAAG;AACpD,MAAI,uBAAuB,IAAI;AAC7B,WAAO,EAAE,YAAY,eAAe,aAAa,UAAU;AAAA,EAC7D;AACA,SAAO;AAAA,IACL,YAAY,cAAc,MAAM,GAAG,kBAAkB;AAAA,IACrD,aAAa,cAAc,MAAM,qBAAqB,CAAC;AAAA,EACzD;AACF;AAEA,SAAS,mBAAmB,UAAmD;AAC7E,QAAM,UAAU,iBAAiB,QAAQ;AACzC,MAAI,SAAS;AACX,WAAO;AAAA,MACL,MAAM,OAAO,QAAQ,UAAU,IAAI,QAAQ,WAAW;AAAA,MACtD,OAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,YAAY,QAAQ,KAAK,EAAE,MAAM,aAAa,OAAO,YAAK;AACnE;AAEO,MAAM,sBAA0D,MAAM,KAAK,CAAC;AAAA,EACjF;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,WAAW,mBAAmB,QAAQ;AAE5C,MAAI,WAAW,WAAW;AACxB,WACE,oCAAC,WACC,oCAAC,QAAK,OAAM,WAAU,UAAQ,QAAC,WAAG,SAAS,MAAK,KAAG,CACrD;AAAA,EAEJ;AAEA,MAAI,WAAW,aAAa;AAC1B,WACE,oCAAC,WACC,oCAAC,QAAK,OAAM,aACV,oCAAC,WAAQ,MAAK,QAAO,CACvB,GACA,oCAAC,QAAK,OAAM,WAAU,MAAI,QAAC,KAAE,SAAS,OAAM,KAAE,SAAS,MAAK,KAAG,CACjE;AAAA,EAEJ;AAEA,MAAI,WAAW,WAAW;AACxB,WACE,oCAAC,OAAI,eAAc,YACjB,oCAAC,WACC,oCAAC,QAAK,OAAM,WAAU,MAAI,QAAC,WAAG,SAAS,OAAM,KAAE,SAAS,IAAK,CAC/D,GACC,UAAU,OAAO,SAAS,OACzB,oCAAC,OAAI,YAAY,KACf,oCAAC,QAAK,OAAM,aAAW,MAAO,CAChC,CAEJ;AAAA,EAEJ;AAEA,MAAI,WAAW,SAAS;AACtB,WACE,oCAAC,OAAI,eAAc,YACjB,oCAAC,WACC,oCAAC,QAAK,OAAM,WAAU,MAAI,QAAC,WAAG,SAAS,OAAM,KAAE,SAAS,MAAK,SAAO,CACtE,GACC,SACC,oCAAC,OAAI,YAAY,KACf,oCAAC,QAAK,OAAM,aAAW,MAAM,MAAM,GAAG,GAAG,GAAG,MAAM,SAAS,MAAM,QAAQ,EAAG,CAC9E,CAEJ;AAAA,EAEJ;AAEA,SAAO;AACT,GAAG,CAAC,WAAW,cAAc;AAE3B,SAAO,UAAU,aAAa,UAAU,YACtC,UAAU,WAAW,UAAU,UAC/B,UAAU,WAAW,UAAU,UAC/B,UAAU,UAAU,UAAU;AAClC,CAAC;","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/components/ToolExecutionStatus.tsx"],"sourcesContent":["import React, { useState, useEffect } from 'react';\r\nimport { Box, Text } from 'ink';\r\nimport Spinner from 'ink-spinner';\r\nimport { useTheme } from '../theme.js';\r\n\r\ninterface ToolExecutionStatusProps {\r\n toolName: string;\r\n status: 'pending' | 'executing' | 'success' | 'error';\r\n result?: string;\r\n error?: string;\r\n}\r\n\r\nconst TOOL_LABELS: Record<string, { verb: string; emoji: string }> = {\r\n read_file: { verb: 'Reading file', emoji: '📖' },\r\n write_file: { verb: 'Writing file', emoji: '✍️' },\r\n edit_file: { verb: 'Editing file', emoji: '✏️' },\r\n list_directory: { verb: 'Listing directory', emoji: '📁' },\r\n execute_command: { verb: 'Executing command', emoji: '⚡' },\r\n grep_search: { verb: 'Searching files', emoji: '🔍' },\r\n create_image: { verb: 'Generating image', emoji: '🎨' },\r\n add_mcp: { verb: 'Adding MCP server', emoji: '🔌' },\r\n};\r\n\r\n/**\r\n * Parse MCP tool name to extract server name and tool name\r\n * MCP tools are named: mcp_{serverName}_{toolName}\r\n */\r\nfunction parseMCPToolName(toolName: string): { serverName: string; mcpToolName: string } | null {\r\n if (!toolName.startsWith('mcp_')) return null;\r\n const withoutPrefix = toolName.slice(4);\r\n const firstUnderscoreIdx = withoutPrefix.indexOf('_');\r\n if (firstUnderscoreIdx === -1) {\r\n return { serverName: withoutPrefix, mcpToolName: 'unknown' };\r\n }\r\n return {\r\n serverName: withoutPrefix.slice(0, firstUnderscoreIdx),\r\n mcpToolName: withoutPrefix.slice(firstUnderscoreIdx + 1)\r\n };\r\n}\r\n\r\nfunction getToolDisplayInfo(toolName: string): { verb: string; emoji: string } {\r\n const mcpInfo = parseMCPToolName(toolName);\r\n if (mcpInfo) {\r\n return {\r\n verb: `MCP ${mcpInfo.serverName} ${mcpInfo.mcpToolName}`,\r\n emoji: '⚡'\r\n };\r\n }\r\n return TOOL_LABELS[toolName] || { verb: 'Executing', emoji: '🔧' };\r\n}\r\n\r\nexport const ToolExecutionStatus: React.FC<ToolExecutionStatusProps> = React.memo(({\r\n toolName,\r\n status,\r\n result,\r\n error,\r\n}) => {\r\n const toolInfo = getToolDisplayInfo(toolName);\r\n const theme = useTheme();\r\n\r\n if (status === 'pending') {\r\n return (\r\n <Box>\r\n <Text color=\"#666666\" dimColor>⏳ {toolInfo.verb}...</Text>\r\n </Box>\r\n );\r\n }\r\n\r\n if (status === 'executing') {\r\n return (\r\n <Box>\r\n <Text color={theme.accent}>\r\n <Spinner type=\"dots\" />\r\n </Text>\r\n <Text color={theme.accent} bold> {toolInfo.emoji} {toolInfo.verb}...</Text>\r\n </Box>\r\n );\r\n }\r\n\r\n if (status === 'success') {\r\n return (\r\n <Box flexDirection=\"column\">\r\n <Box>\r\n <Text color=\"#00cc66\" bold>✓ {toolInfo.emoji} {toolInfo.verb}</Text>\r\n </Box>\r\n {result && result.length < 300 && (\r\n <Box marginLeft={2}>\r\n <Text color=\"#666666\">{result}</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n }\r\n\r\n if (status === 'error') {\r\n return (\r\n <Box flexDirection=\"column\">\r\n <Box>\r\n <Text color=\"#ff3366\" bold>✗ {toolInfo.emoji} {toolInfo.verb} failed</Text>\r\n </Box>\r\n {error && (\r\n <Box marginLeft={2}>\r\n <Text color=\"#ff3366\">{error.slice(0, 200)}{error.length > 200 ? '...' : ''}</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n }\r\n\r\n return null;\r\n}, (prevProps, nextProps) => {\r\n // Only re-render if props actually changed\r\n return prevProps.toolName === nextProps.toolName &&\r\n prevProps.status === nextProps.status &&\r\n prevProps.result === nextProps.result &&\r\n prevProps.error === nextProps.error;\r\n});\r\n"],"mappings":"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","names":[]}
@@ -10,6 +10,7 @@ import { apiClient } from "../../services/api-client.js";
10
10
  import { rulesStorage } from "../../services/rules-storage.js";
11
11
  import { ConfigManager } from "../../config/manager.js";
12
12
  import { MCPConfigManager } from "../../config/mcp-config-manager.js";
13
+ import { useTheme } from "../../ui/theme.js";
13
14
  function tryDecodeUserFromToken() {
14
15
  try {
15
16
  const sessionPath = path.join(os.homedir(), ".centaurus", "session.json");
@@ -30,6 +31,7 @@ const WelcomeBanner = React.memo(() => {
30
31
  const version = getCurrentVersion();
31
32
  const recentChats = localChatStorage.listChats().slice(0, 3);
32
33
  const cols = process.stdout.columns || 120;
34
+ const theme = useTheme();
33
35
  const isAuth = apiClient.isAuthenticated();
34
36
  const cachedUser = apiClient.getCachedUser();
35
37
  const userInfo = isAuth ? cachedUser ? { name: cachedUser.fullName, email: cachedUser.email } : tryDecodeUserFromToken() : null;
@@ -61,35 +63,33 @@ const WelcomeBanner = React.memo(() => {
61
63
  } catch {
62
64
  }
63
65
  const contentW = cols - 4;
64
- const rightC = cols - 91;
66
+ const rightC = cols - 70;
65
67
  const showRight = rightC >= 22;
66
68
  const tipsCount = rightC >= 40 ? 5 : rightC >= 34 ? 4 : rightC >= 28 ? 3 : 2;
67
69
  const chatsCount = rightC >= 36 ? 3 : rightC >= 26 ? 2 : 1;
68
70
  const titleLen = Math.max(8, rightC - 12);
69
71
  const sepLen = Math.max(10, rightC);
70
- const showLogo = cols >= 86;
72
+ const showIcon = cols >= 60;
71
73
  const titleStr = " CENTAURUS CLI ";
72
74
  const verStr = ` v${version} `;
73
75
  const innerW = cols - 2;
74
76
  const fillLen = Math.max(2, innerW - titleStr.length - verStr.length - 2);
75
77
  const topBorder = `\u256D\u2500${titleStr}${"\u2500".repeat(fillLen)}${verStr}\u2500\u256E`;
76
78
  const bottomBorder = `\u2570${"\u2500".repeat(innerW)}\u256F`;
77
- const centaurusLines = [
78
- " \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
79
- "\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D",
80
- "\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557",
81
- "\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551",
82
- "\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551",
83
- " \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
84
- ];
85
- const cliLines = [
86
- " \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
87
- " \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551",
88
- " \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551",
89
- " \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551",
90
- " \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551",
91
- " \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D"
79
+ const iconLines = [
80
+ [{ t: " " }, { t: "\u2584\u2584\u2584\u2584", c: "#3A6A8A" }, { t: " " }, { t: "\u2584\u2584\u2584\u2584\u2584\u2584\u2584\u2584", c: "#3A6A8A" }, { t: " " }, { t: "\u2584", c: "#00CCFF" }],
81
+ [{ t: " " }, { t: "\u2588\u2588\u2580\u2580\u2588", c: "#3A6A8A" }, { t: "\u2580", c: "#00CCFF" }, { t: "\u2588\u2588\u2588\u2580\u2580\u2580", c: "#3A6A8A" }, { t: "\u2588\u2588", c: "#00CCFF" }, { t: "\u2580\u2580\u2580\u2580\u2584", c: "#3A6A8A" }],
82
+ [{ t: " " }, { t: "\u2588\u2580\u2588\u2588\u2584\u2584\u2580\u2580", c: "#3A6A8A" }, { t: "\u2580", c: "#88BBDD" }, { t: "\u2588", c: "#3A6A8A" }, { t: "\u2580\u2580", c: "#00CCFF" }, { t: "\u2580", c: "#3A6A8A" }, { t: "\u2588\u2588\u2588\u2588\u2588\u2588", c: "#F86464" }, { t: "\u2588\u2584", c: "#3A6A8A" }],
83
+ [{ t: " " }, { t: "\u2580", c: "#3A6A8A" }, { t: "\u2580", c: "#00CCFF" }, { t: "\u2580\u2580\u2580", c: "#3A6A8A" }, { t: "\u2580\u2580\u2588", c: "#88BBDD" }, { t: "\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588", c: "#F86464" }, { t: "\u2588", c: "#3A6A8A" }, { t: " " }, { t: "\u2588\u2588\u2584", c: "#00CCFF" }],
84
+ [{ t: " " }, { t: "\u2580\u2588", c: "#3A6A8A" }, { t: "\u2580", c: "#88BBDD" }, { t: "\u2588\u2588\u2580", c: "#3A6A8A" }, { t: "\u2580\u2580\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588", c: "#F86464" }, { t: "\u2588\u2588\u2584\u2584", c: "#3A6A8A" }, { t: " " }, { t: "\u2580", c: "#00CCFF" }],
85
+ [{ t: " " }, { t: "\u2588", c: "#3A6A8A" }, { t: "\u2580", c: "#00CCFF" }, { t: "\u2588\u2588\u2588", c: "#3A6A8A" }, { t: "\u2580", c: "#00CCFF" }, { t: "\u2588\u2588", c: "#3A6A8A" }, { t: "\u2580\u2580\u2588\u2588\u2588\u2588\u2588", c: "#F86464" }, { t: "\u2588\u2580\u2588\u2588\u2588", c: "#3A6A8A" }],
86
+ [{ t: " " }, { t: "\u2580\u2588", c: "#3A6A8A" }, { t: "\u2580\u2588", c: "#88BBDD" }, { t: "\u2580\u2580\u2580\u2588", c: "#3A6A8A" }, { t: "\u2580", c: "#88BBDD" }, { t: "\u2580", c: "#3A6A8A" }, { t: "\u2580", c: "#00CCFF" }, { t: "\u2580\u2588", c: "#3A6A8A" }, { t: "\u2580", c: "#F86464" }, { t: "\u2588\u2588\u2584\u2588", c: "#3A6A8A" }, { t: "\u2580", c: "#00CCFF" }, { t: "\u2580\u2588", c: "#3A6A8A" }],
87
+ [{ t: " " }, { t: "\u2580\u2588", c: "#3A6A8A" }, { t: "\u2580\u2580", c: "#88BBDD" }, { t: "\u2580\u2588\u2588\u2588", c: "#F86464" }, { t: "\u2580\u2588", c: "#3A6A8A" }, { t: "\u2580", c: "#00CCFF" }, { t: "\u2588", c: "#3A6A8A" }, { t: "\u2580\u2580\u2580\u2580\u2580", c: "#88BBDD" }, { t: "\u2588\u2588", c: "#3A6A8A" }],
88
+ [{ t: " " }, { t: "\u2580\u2580\u2580\u2580\u2580\u2580\u2580", c: "#3A6A8A" }]
92
89
  ];
90
+ const renderIconRow = (segments, key) => /* @__PURE__ */ React.createElement(Text, { key }, segments.map(
91
+ (seg, j) => seg.c ? /* @__PURE__ */ React.createElement(Text, { key: j, color: seg.c }, seg.t) : /* @__PURE__ */ React.createElement(Text, { key: j }, seg.t)
92
+ ));
93
93
  const allTips = [
94
94
  { key: "/help", desc: "List all commands" },
95
95
  { key: "/clean-ui", desc: "Refresh the chat UI" },
@@ -102,7 +102,7 @@ const WelcomeBanner = React.memo(() => {
102
102
  const fmtDate = (iso) => new Date(iso).toLocaleDateString("en-US", { month: "short", day: "numeric" });
103
103
  const trunc = (s, n) => s.length > n ? s.slice(0, n - 1) + "\u2026" : s;
104
104
  const InfoRow = ({ label, children }) => /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "#555" }, label.padEnd(10)), children);
105
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "#00ccff" }, topBorder), /* @__PURE__ */ React.createElement(
105
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.accent }, topBorder), /* @__PURE__ */ React.createElement(
106
106
  Box,
107
107
  {
108
108
  borderStyle: "round",
@@ -110,67 +110,67 @@ const WelcomeBanner = React.memo(() => {
110
110
  borderBottom: false,
111
111
  borderLeft: true,
112
112
  borderRight: true,
113
- borderColor: "#00ccff",
113
+ borderColor: theme.accent,
114
114
  flexDirection: "column",
115
115
  paddingX: 1,
116
116
  width: cols
117
117
  },
118
- /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginRight: 3 }, showLogo ? /* @__PURE__ */ React.createElement(React.Fragment, null, centaurusLines.map((line, i) => /* @__PURE__ */ React.createElement(Text, { key: `c${i}`, color: "#00ccff" }, line)), cliLines.map((line, i) => /* @__PURE__ */ React.createElement(Text, { key: `l${i}`, color: "#00ccff" }, line))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Text, { color: "#00ccff", bold: true }, "CENTAURUS"), /* @__PURE__ */ React.createElement(Text, { color: "#00ccff", bold: true }, "CLI"))), showRight && /* @__PURE__ */ React.createElement(
118
+ /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginTop: 1 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginRight: 3 }, showIcon && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginRight: 2 }, iconLines.map((segments, i) => renderIconRow(segments, `icon${i}`))), /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", justifyContent: "center" }, /* @__PURE__ */ React.createElement(Text, { color: theme.accent, bold: true, dimColor: false }, "CENTAURUS"), /* @__PURE__ */ React.createElement(Text, { color: theme.accent, bold: true }, "CLI"), /* @__PURE__ */ React.createElement(Text, null, " "), /* @__PURE__ */ React.createElement(Text, { color: "#555" }, "v", version), /* @__PURE__ */ React.createElement(Text, { color: "#444" }, "Your AI-powered terminal"))), showRight && /* @__PURE__ */ React.createElement(
119
119
  Box,
120
120
  {
121
121
  borderStyle: "single",
122
- borderColor: "#1a5a7a",
122
+ borderColor: theme.borderDim,
123
123
  flexDirection: "column",
124
124
  paddingX: 1,
125
125
  flexGrow: 1
126
126
  },
127
- /* @__PURE__ */ React.createElement(Text, { color: "#00ccff", bold: true }, "Beginner Tips"),
127
+ /* @__PURE__ */ React.createElement(Text, { color: theme.accent, bold: true }, "Beginner Tips"),
128
128
  /* @__PURE__ */ React.createElement(Text, null, " "),
129
- tips.map((t, i) => /* @__PURE__ */ React.createElement(Box, { key: i }, /* @__PURE__ */ React.createElement(Text, { color: "#00aacc" }, t.key.padEnd(8)), /* @__PURE__ */ React.createElement(Text, { color: "#666" }, t.desc))),
130
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "#1a5a7a" }, "\u2500".repeat(sepLen))),
131
- /* @__PURE__ */ React.createElement(Text, { color: "#00ccff", bold: true }, "Recent Activity"),
129
+ tips.map((t, i) => /* @__PURE__ */ React.createElement(Box, { key: i }, /* @__PURE__ */ React.createElement(Text, { color: theme.accentMuted }, t.key.padEnd(8)), /* @__PURE__ */ React.createElement(Text, { color: "#666" }, t.desc))),
130
+ /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.borderDim }, "\u2500".repeat(sepLen))),
131
+ /* @__PURE__ */ React.createElement(Text, { color: theme.accent, bold: true }, "Recent Activity"),
132
132
  /* @__PURE__ */ React.createElement(Text, null, " "),
133
- displayChats.length === 0 ? /* @__PURE__ */ React.createElement(Text, { color: "#444" }, "No recent chats yet.") : displayChats.map((chat) => /* @__PURE__ */ React.createElement(Box, { key: chat.id }, /* @__PURE__ */ React.createElement(Text, { color: "#00aacc" }, "\u25CF "), /* @__PURE__ */ React.createElement(Text, { color: "white" }, trunc(chat.title, titleLen)), /* @__PURE__ */ React.createElement(Text, { color: "#444" }, " ", fmtDate(chat.updatedAt)))),
133
+ displayChats.length === 0 ? /* @__PURE__ */ React.createElement(Text, { color: "#444" }, "No recent chats yet.") : displayChats.map((chat) => /* @__PURE__ */ React.createElement(Box, { key: chat.id }, /* @__PURE__ */ React.createElement(Text, { color: theme.accentMuted }, "\u25CF "), /* @__PURE__ */ React.createElement(Text, { color: "white" }, trunc(chat.title, titleLen)), /* @__PURE__ */ React.createElement(Text, { color: "#444" }, " ", fmtDate(chat.updatedAt)))),
134
134
  /* @__PURE__ */ React.createElement(Text, null, " "),
135
135
  /* @__PURE__ */ React.createElement(Text, { color: "#444" }, "/chat resume to continue")
136
136
  )),
137
- /* @__PURE__ */ React.createElement(Box, { marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "#1a4a6a" }, "\u2500".repeat(contentW))),
137
+ /* @__PURE__ */ React.createElement(Box, { marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.divider }, "\u2500".repeat(contentW))),
138
138
  /* @__PURE__ */ React.createElement(Box, { flexDirection: "row", marginBottom: 1 }, /* @__PURE__ */ React.createElement(
139
139
  Box,
140
140
  {
141
141
  borderStyle: "single",
142
- borderColor: "#1a5a7a",
142
+ borderColor: theme.borderDim,
143
143
  flexDirection: "column",
144
144
  paddingX: 1,
145
145
  flexGrow: 1,
146
146
  flexBasis: 0,
147
147
  marginRight: 2
148
148
  },
149
- /* @__PURE__ */ React.createElement(Text, { color: "#00ccff", bold: true }, "Subscription & Session Info"),
149
+ /* @__PURE__ */ React.createElement(Text, { color: theme.accent, bold: true }, "Subscription & Session Info"),
150
150
  /* @__PURE__ */ React.createElement(Text, null, " "),
151
151
  userInfo?.name && /* @__PURE__ */ React.createElement(InfoRow, { label: "User" }, /* @__PURE__ */ React.createElement(Text, { color: "white" }, userInfo.name)),
152
152
  userInfo?.email && /* @__PURE__ */ React.createElement(InfoRow, { label: "Email" }, /* @__PURE__ */ React.createElement(Text, { color: "#888" }, userInfo.email)),
153
153
  !userInfo && /* @__PURE__ */ React.createElement(InfoRow, { label: "Account" }, /* @__PURE__ */ React.createElement(Text, { color: isAuth ? "#00dd77" : "#ff5555" }, isAuth ? "\u25CF Signed in" : "\u25CB Not logged in")),
154
- /* @__PURE__ */ React.createElement(InfoRow, { label: "Plan" }, /* @__PURE__ */ React.createElement(Text, { color: plan.toLowerCase() === "pro" ? "#f0c040" : "#00aacc" }, plan.charAt(0).toUpperCase() + plan.slice(1))),
154
+ /* @__PURE__ */ React.createElement(InfoRow, { label: "Plan" }, /* @__PURE__ */ React.createElement(Text, { color: plan.toLowerCase() === "pro" ? "#f0c040" : theme.accentMuted }, plan.charAt(0).toUpperCase() + plan.slice(1))),
155
155
  /* @__PURE__ */ React.createElement(InfoRow, { label: "Messages" }, /* @__PURE__ */ React.createElement(Text, { color: "white" }, msgUsed), /* @__PURE__ */ React.createElement(Text, { color: "#555" }, " / ", maxMsgs, " used "), /* @__PURE__ */ React.createElement(Text, { color: msgLeft < 10 ? "#ffaa00" : "#00dd77" }, "(", msgLeft, " left)")),
156
156
  /* @__PURE__ */ React.createElement(InfoRow, { label: "Window" }, /* @__PURE__ */ React.createElement(Text, { color: "#888" }, sessionHrs, "h session"), timeLeft ? /* @__PURE__ */ React.createElement(Text, { color: "#555" }, " \u2014 ", timeLeft, " remaining") : /* @__PURE__ */ React.createElement(Text, { color: "#444" }, " \u2014 not started"))
157
157
  ), /* @__PURE__ */ React.createElement(
158
158
  Box,
159
159
  {
160
160
  borderStyle: "single",
161
- borderColor: "#1a5a7a",
161
+ borderColor: theme.borderDim,
162
162
  flexDirection: "column",
163
163
  paddingX: 1,
164
164
  flexGrow: 1,
165
165
  flexBasis: 0
166
166
  },
167
- /* @__PURE__ */ React.createElement(Text, { color: "#00ccff", bold: true }, "Current Config"),
167
+ /* @__PURE__ */ React.createElement(Text, { color: theme.accent, bold: true }, "Current Config"),
168
168
  /* @__PURE__ */ React.createElement(Text, null, " "),
169
169
  /* @__PURE__ */ React.createElement(InfoRow, { label: "MCPs" }, mcpTotal === 0 ? /* @__PURE__ */ React.createElement(Text, { color: "#444" }, "None configured") : /* @__PURE__ */ React.createElement(Text, { color: "white" }, mcpEnabled, " enabled", /* @__PURE__ */ React.createElement(Text, { color: "#555" }, " (", mcpTotal, " total)"))),
170
170
  /* @__PURE__ */ React.createElement(InfoRow, { label: "AutoFill" }, /* @__PURE__ */ React.createElement(Text, { color: autoSuggest ? "#00dd77" : "#555" }, autoSuggest ? "On" : "Off")),
171
171
  /* @__PURE__ */ React.createElement(InfoRow, { label: "Rules" }, rulesCount === 0 ? /* @__PURE__ */ React.createElement(Text, { color: "#444" }, "None configured") : /* @__PURE__ */ React.createElement(Text, { color: "white" }, rulesCount, " rule", rulesCount !== 1 ? "s" : "", " configured"))
172
172
  ))
173
- ), /* @__PURE__ */ React.createElement(Text, { color: "#00ccff" }, bottomBorder));
173
+ ), /* @__PURE__ */ React.createElement(Text, { color: theme.accent }, bottomBorder));
174
174
  });
175
175
  export {
176
176
  WelcomeBanner
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/components/WelcomeBanner.tsx"],"sourcesContent":["import React from 'react';\r\nimport { Box, Text } from 'ink';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport * as os from 'os';\r\nimport { getCurrentVersion } from '../../utils/version-checker.js';\r\nimport { localChatStorage } from '../../services/local-chat-storage.js';\r\nimport { sessionQuotaManager } from '../../services/session-quota-manager.js';\r\nimport { apiClient } from '../../services/api-client.js';\r\nimport { rulesStorage } from '../../services/rules-storage.js';\r\nimport { ConfigManager } from '../../config/manager.js';\r\nimport { MCPConfigManager } from '../../config/mcp-config-manager.js';\r\n\r\n/** Attempt to extract name/email from the saved session token without a network call.\r\n * Works when the server issues JWTs; silently returns null for opaque tokens. */\r\nfunction tryDecodeUserFromToken(): { name?: string; email?: string } | null {\r\n try {\r\n const sessionPath = path.join(os.homedir(), '.centaurus', 'session.json');\r\n const raw = fs.readFileSync(sessionPath, 'utf-8');\r\n const { sessionToken } = JSON.parse(raw) as { sessionToken?: string };\r\n if (!sessionToken) return null;\r\n const parts = sessionToken.split('.');\r\n if (parts.length < 2) return null;\r\n const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));\r\n const name = payload.name ?? payload.fullName ?? payload.given_name ?? undefined;\r\n const email = payload.email ?? payload.sub ?? undefined;\r\n return (name || email) ? { name, email } : null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport const WelcomeBanner: React.FC = React.memo(() => {\r\n const version = getCurrentVersion();\r\n const recentChats = localChatStorage.listChats().slice(0, 3);\r\n const cols = process.stdout.columns || 120;\r\n\r\n // ── Session / account data ───────────────────────────────────────\r\n const isAuth = apiClient.isAuthenticated();\r\n const cachedUser = apiClient.getCachedUser();\r\n const userInfo = isAuth\r\n ? (cachedUser\r\n ? { name: cachedUser.fullName, email: cachedUser.email }\r\n : tryDecodeUserFromToken())\r\n : null;\r\n const plan = sessionQuotaManager.getPlan();\r\n const msgUsed = sessionQuotaManager.getMessagesUsed();\r\n const msgLeft = sessionQuotaManager.getRemainingMessages();\r\n const quota = sessionQuotaManager.getCurrentConfig();\r\n const maxMsgs = quota.maxMessagesPerSession;\r\n const sessionHrs = Math.round(quota.sessionDurationMs / (1000 * 60 * 60));\r\n const sessStart = sessionQuotaManager.getSessionStartTime();\r\n const timeLeft = sessStart > 0 ? sessionQuotaManager.getFormattedTimeRemaining() : null;\r\n\r\n // ── Config data ──────────────────────────────────────────────────\r\n let mcpEnabled = 0;\r\n let mcpTotal = 0;\r\n let autoSuggest = false;\r\n let rulesCount = 0;\r\n try {\r\n const cfg = new ConfigManager().load();\r\n autoSuggest = cfg.aiAutoSuggest ?? false;\r\n } catch { /* use defaults */ }\r\n try {\r\n const mcpMgr = new MCPConfigManager();\r\n mcpEnabled = mcpMgr.getServers().length;\r\n mcpTotal = mcpMgr.getAllServers().length;\r\n } catch { /* show 0 */ }\r\n try { rulesCount = rulesStorage.list().length; } catch { /* show 0 */ }\r\n\r\n // ── Layout math ──────────────────────────────────────────────────\r\n const contentW = cols - 4; // outer Box: border(2) + paddingX(2) consumed\r\n const rightC = cols - 91; // content cols available inside right info panel\r\n const showRight = rightC >= 22;\r\n const tipsCount = rightC >= 40 ? 5 : rightC >= 34 ? 4 : rightC >= 28 ? 3 : 2;\r\n const chatsCount = rightC >= 36 ? 3 : rightC >= 26 ? 2 : 1;\r\n const titleLen = Math.max(8, rightC - 12);\r\n const sepLen = Math.max(10, rightC);\r\n\r\n // Show the 80-char block logo only when the terminal is wide enough.\r\n // Below 86 cols the art would wrap; fall back to a plain-text header instead.\r\n const showLogo = cols >= 86;\r\n\r\n // ── Custom border strings ────────────────────────────────────────\r\n const titleStr = ' CENTAURUS CLI ';\r\n const verStr = ` v${version} `;\r\n const innerW = cols - 2;\r\n const fillLen = Math.max(2, innerW - titleStr.length - verStr.length - 2);\r\n const topBorder = `╭─${titleStr}${'─'.repeat(fillLen)}${verStr}─╮`;\r\n const bottomBorder = `╰${'─'.repeat(innerW)}╯`;\r\n\r\n // ── ASCII art — correct 6-row ANSI Shadow, SINGLE layer ──────────\r\n // The old 5-row version was missing Row 4 entirely. That row contains\r\n // the crucial ╚════██║ segment that forms the lower curve of S (and the\r\n // corresponding intermediate strokes for E, N, U, R). Without it every\r\n // letter that has a middle horizontal looked truncated and S was broken.\r\n const centaurusLines = [\r\n ' ██████╗███████╗███╗ ██╗████████╗ █████╗ ██╗ ██╗██████╗ ██╗ ██╗███████╗',\r\n '██╔════╝██╔════╝████╗ ██║╚══██╔══╝██╔══██╗██║ ██║██╔══██╗██║ ██║██╔════╝',\r\n '██║ █████╗ ██╔██╗ ██║ ██║ ███████║██║ ██║██████╔╝██║ ██║███████╗',\r\n '██║ ██╔══╝ ██║╚██╗██║ ██║ ██╔══██║██║ ██║██╔══██╗██║ ██║╚════██║',\r\n '╚██████╗███████╗██║ ╚████║ ██║ ██║ ██║╚██████╔╝██║ ██║╚██████╔╝███████║',\r\n ' ╚═════╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝',\r\n ];\r\n // CLI — correct 6-row version (rows 3 & 4 identical for C, L, I — correct)\r\n const cliLines = [\r\n ' ██████╗██╗ ██╗',\r\n ' ██╔════╝██║ ██║',\r\n ' ██║ ██║ ██║',\r\n ' ██║ ██║ ██║',\r\n ' ╚██████╗███████╗██║',\r\n ' ╚═════╝╚══════╝╚═╝',\r\n ];\r\n\r\n // ── Helpers ──────────────────────────────────────────────────────\r\n const allTips = [\r\n { key: '/help', desc: 'List all commands' },\r\n { key: '/clean-ui', desc: 'Refresh the chat UI' },\r\n { key: '/model', desc: 'Switch AI model' },\r\n { key: '/docs', desc: 'View documentation' },\r\n { key: '/plan', desc: 'Switch to plan mode' },\r\n ];\r\n const tips = allTips.slice(0, tipsCount);\r\n const displayChats = recentChats.slice(0, chatsCount);\r\n\r\n const fmtDate = (iso: string) =>\r\n new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\r\n\r\n const trunc = (s: string, n: number) =>\r\n s.length > n ? s.slice(0, n - 1) + '\\u2026' : s;\r\n\r\n const InfoRow = ({ label, children }: { label: string; children: React.ReactNode }) => (\r\n <Box>\r\n <Text color=\"#555\">{label.padEnd(10)}</Text>\r\n {children}\r\n </Box>\r\n );\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginBottom={1}>\r\n\r\n {/* ══ Custom top border: ╭─ CENTAURUS CLI ──── v{x} ─╮ ══ */}\r\n <Text color=\"#00ccff\">{topBorder}</Text>\r\n\r\n {/* ══ Content — left + right borders only (top/bottom are the Text lines above/below) ══ */}\r\n <Box\r\n borderStyle=\"round\"\r\n borderTop={false}\r\n borderBottom={false}\r\n borderLeft={true}\r\n borderRight={true}\r\n borderColor=\"#00ccff\"\r\n flexDirection=\"column\"\r\n paddingX={1}\r\n width={cols}\r\n >\r\n\r\n {/* ── Top row: logo (left) | Tips + Recent (right) ── */}\r\n <Box flexDirection=\"row\" marginTop={1}>\r\n\r\n {/* Logo column — full block art, or plain-text fallback for narrow terminals */}\r\n <Box flexDirection=\"column\" marginRight={3}>\r\n {showLogo ? (\r\n <>\r\n {centaurusLines.map((line, i) => (\r\n <Text key={`c${i}`} color=\"#00ccff\">{line}</Text>\r\n ))}\r\n {cliLines.map((line, i) => (\r\n <Text key={`l${i}`} color=\"#00ccff\">{line}</Text>\r\n ))}\r\n </>\r\n ) : (\r\n <>\r\n <Text color=\"#00ccff\" bold>CENTAURUS</Text>\r\n <Text color=\"#00ccff\" bold>CLI</Text>\r\n </>\r\n )}\r\n </Box>\r\n\r\n {/* Tips + Recent Activity — hidden when terminal too narrow */}\r\n {showRight && (\r\n <Box\r\n borderStyle=\"single\"\r\n borderColor=\"#1a5a7a\"\r\n flexDirection=\"column\"\r\n paddingX={1}\r\n flexGrow={1}\r\n >\r\n <Text color=\"#00ccff\" bold>Beginner Tips</Text>\r\n <Text> </Text>\r\n {tips.map((t, i) => (\r\n <Box key={i}>\r\n <Text color=\"#00aacc\">{t.key.padEnd(8)}</Text>\r\n <Text color=\"#666\">{t.desc}</Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color=\"#1a5a7a\">{'─'.repeat(sepLen)}</Text>\r\n </Box>\r\n <Text color=\"#00ccff\" bold>Recent Activity</Text>\r\n <Text> </Text>\r\n {displayChats.length === 0 ? (\r\n <Text color=\"#444\">No recent chats yet.</Text>\r\n ) : (\r\n displayChats.map((chat) => (\r\n <Box key={chat.id}>\r\n <Text color=\"#00aacc\">{'● '}</Text>\r\n <Text color=\"white\">{trunc(chat.title, titleLen)}</Text>\r\n <Text color=\"#444\">{' '}{fmtDate(chat.updatedAt)}</Text>\r\n </Box>\r\n ))\r\n )}\r\n <Text> </Text>\r\n <Text color=\"#444\">/chat resume to continue</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n\r\n {/* ── Full-width divider ── */}\r\n <Box marginTop={1} marginBottom={1}>\r\n <Text color=\"#1a4a6a\">{'─'.repeat(contentW)}</Text>\r\n </Box>\r\n\r\n {/* ── Bottom row: Session Info | Current Config ── */}\r\n <Box flexDirection=\"row\" marginBottom={1}>\r\n\r\n {/* Session Info */}\r\n <Box\r\n borderStyle=\"single\"\r\n borderColor=\"#1a5a7a\"\r\n flexDirection=\"column\"\r\n paddingX={1}\r\n flexGrow={1}\r\n flexBasis={0}\r\n marginRight={2}\r\n >\r\n <Text color=\"#00ccff\" bold>Subscription &amp; Session Info</Text>\r\n <Text> </Text>\r\n {userInfo?.name && (\r\n <InfoRow label=\"User\">\r\n <Text color=\"white\">{userInfo.name}</Text>\r\n </InfoRow>\r\n )}\r\n {userInfo?.email && (\r\n <InfoRow label=\"Email\">\r\n <Text color=\"#888\">{userInfo.email}</Text>\r\n </InfoRow>\r\n )}\r\n {!userInfo && (\r\n <InfoRow label=\"Account\">\r\n <Text color={isAuth ? '#00dd77' : '#ff5555'}>\r\n {isAuth ? '● Signed in' : '○ Not logged in'}\r\n </Text>\r\n </InfoRow>\r\n )}\r\n <InfoRow label=\"Plan\">\r\n <Text color={plan.toLowerCase() === 'pro' ? '#f0c040' : '#00aacc'}>\r\n {plan.charAt(0).toUpperCase() + plan.slice(1)}\r\n </Text>\r\n </InfoRow>\r\n <InfoRow label=\"Messages\">\r\n <Text color=\"white\">{msgUsed}</Text>\r\n <Text color=\"#555\"> / {maxMsgs} used </Text>\r\n <Text color={msgLeft < 10 ? '#ffaa00' : '#00dd77'}>({msgLeft} left)</Text>\r\n </InfoRow>\r\n <InfoRow label=\"Window\">\r\n <Text color=\"#888\">{sessionHrs}h session</Text>\r\n {timeLeft\r\n ? <Text color=\"#555\"> — {timeLeft} remaining</Text>\r\n : <Text color=\"#444\"> — not started</Text>\r\n }\r\n </InfoRow>\r\n </Box>\r\n\r\n {/* Current Config */}\r\n <Box\r\n borderStyle=\"single\"\r\n borderColor=\"#1a5a7a\"\r\n flexDirection=\"column\"\r\n paddingX={1}\r\n flexGrow={1}\r\n flexBasis={0}\r\n >\r\n <Text color=\"#00ccff\" bold>Current Config</Text>\r\n <Text> </Text>\r\n <InfoRow label=\"MCPs\">\r\n {mcpTotal === 0\r\n ? <Text color=\"#444\">None configured</Text>\r\n : <Text color=\"white\">{mcpEnabled} enabled<Text color=\"#555\"> ({mcpTotal} total)</Text></Text>\r\n }\r\n </InfoRow>\r\n <InfoRow label=\"AutoFill\">\r\n <Text color={autoSuggest ? '#00dd77' : '#555'}>\r\n {autoSuggest ? 'On' : 'Off'}\r\n </Text>\r\n </InfoRow>\r\n <InfoRow label=\"Rules\">\r\n {rulesCount === 0\r\n ? <Text color=\"#444\">None configured</Text>\r\n : <Text color=\"white\">{rulesCount} rule{rulesCount !== 1 ? 's' : ''} configured</Text>\r\n }\r\n </InfoRow>\r\n </Box>\r\n\r\n </Box>\r\n </Box>\r\n\r\n {/* ══ Custom bottom border: ╰───╯ ══ */}\r\n <Text color=\"#00ccff\">{bottomBorder}</Text>\r\n\r\n </Box>\r\n );\r\n});\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/components/WelcomeBanner.tsx"],"sourcesContent":["import React from 'react';\r\nimport { Box, Text } from 'ink';\r\nimport * as fs from 'fs';\r\nimport * as path from 'path';\r\nimport * as os from 'os';\r\nimport { getCurrentVersion } from '../../utils/version-checker.js';\r\nimport { localChatStorage } from '../../services/local-chat-storage.js';\r\nimport { sessionQuotaManager } from '../../services/session-quota-manager.js';\r\nimport { apiClient } from '../../services/api-client.js';\r\nimport { rulesStorage } from '../../services/rules-storage.js';\r\nimport { ConfigManager } from '../../config/manager.js';\r\nimport { MCPConfigManager } from '../../config/mcp-config-manager.js';\r\nimport { useTheme } from '../../ui/theme.js';\r\n\r\n/** Attempt to extract name/email from the saved session token without a network call.\r\n * Works when the server issues JWTs; silently returns null for opaque tokens. */\r\nfunction tryDecodeUserFromToken(): { name?: string; email?: string } | null {\r\n try {\r\n const sessionPath = path.join(os.homedir(), '.centaurus', 'session.json');\r\n const raw = fs.readFileSync(sessionPath, 'utf-8');\r\n const { sessionToken } = JSON.parse(raw) as { sessionToken?: string };\r\n if (!sessionToken) return null;\r\n const parts = sessionToken.split('.');\r\n if (parts.length < 2) return null;\r\n const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf-8'));\r\n const name = payload.name ?? payload.fullName ?? payload.given_name ?? undefined;\r\n const email = payload.email ?? payload.sub ?? undefined;\r\n return (name || email) ? { name, email } : null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport const WelcomeBanner: React.FC = React.memo(() => {\r\n const version = getCurrentVersion();\r\n const recentChats = localChatStorage.listChats().slice(0, 3);\r\n const cols = process.stdout.columns || 120;\r\n const theme = useTheme();\r\n\r\n // ── Session / account data ───────────────────────────────────────\r\n const isAuth = apiClient.isAuthenticated();\r\n const cachedUser = apiClient.getCachedUser();\r\n const userInfo = isAuth\r\n ? (cachedUser\r\n ? { name: cachedUser.fullName, email: cachedUser.email }\r\n : tryDecodeUserFromToken())\r\n : null;\r\n const plan = sessionQuotaManager.getPlan();\r\n const msgUsed = sessionQuotaManager.getMessagesUsed();\r\n const msgLeft = sessionQuotaManager.getRemainingMessages();\r\n const quota = sessionQuotaManager.getCurrentConfig();\r\n const maxMsgs = quota.maxMessagesPerSession;\r\n const sessionHrs = Math.round(quota.sessionDurationMs / (1000 * 60 * 60));\r\n const sessStart = sessionQuotaManager.getSessionStartTime();\r\n const timeLeft = sessStart > 0 ? sessionQuotaManager.getFormattedTimeRemaining() : null;\r\n\r\n // ── Config data ──────────────────────────────────────────────────\r\n let mcpEnabled = 0;\r\n let mcpTotal = 0;\r\n let autoSuggest = false;\r\n let rulesCount = 0;\r\n try {\r\n const cfg = new ConfigManager().load();\r\n autoSuggest = cfg.aiAutoSuggest ?? false;\r\n } catch { /* use defaults */ }\r\n try {\r\n const mcpMgr = new MCPConfigManager();\r\n mcpEnabled = mcpMgr.getServers().length;\r\n mcpTotal = mcpMgr.getAllServers().length;\r\n } catch { /* show 0 */ }\r\n try { rulesCount = rulesStorage.list().length; } catch { /* show 0 */ }\r\n\r\n // ── Layout math ──────────────────────────────────────────────────\r\n const contentW = cols - 4; // outer Box: border(2) + paddingX(2) consumed\r\n // Icon (~36) + gap(2) + title(~25) + gap(3) + border(4) = ~70 consumed by left side\r\n const rightC = cols - 70; // content cols available inside right info panel\r\n const showRight = rightC >= 22;\r\n const tipsCount = rightC >= 40 ? 5 : rightC >= 34 ? 4 : rightC >= 28 ? 3 : 2;\r\n const chatsCount = rightC >= 36 ? 3 : rightC >= 26 ? 2 : 1;\r\n const titleLen = Math.max(8, rightC - 12);\r\n const sepLen = Math.max(10, rightC);\r\n\r\n // Show the icon when terminal is wide enough (icon is ~36 chars + text)\r\n const showIcon = cols >= 60;\r\n\r\n // ── Custom border strings ────────────────────────────────────────\r\n const titleStr = ' CENTAURUS CLI ';\r\n const verStr = ` v${version} `;\r\n const innerW = cols - 2;\r\n const fillLen = Math.max(2, innerW - titleStr.length - verStr.length - 2);\r\n const topBorder = `╭─${titleStr}${'─'.repeat(fillLen)}${verStr}─╮`;\r\n const bottomBorder = `╰${'─'.repeat(innerW)}╯`;\r\n\r\n // ── Centaurus icon — ASCII art converted from Centaurus-icon.png ──\r\n // Each row is an array of { t: text, c?: color } segments.\r\n // Colors: ring=#3A6A8A, planet=#F86464, star=#00CCFF, highlight=#88BBDD\r\n type IconSegment = { t: string; c?: string };\r\n const iconLines: IconSegment[][] = [\r\n [{ t: ' ' }, { t: '▄▄▄▄', c: '#3A6A8A' }, { t: ' ' }, { t: '▄▄▄▄▄▄▄▄', c: '#3A6A8A' }, { t: ' ' }, { t: '▄', c: '#00CCFF' }],\r\n [{ t: ' ' }, { t: '██▀▀█', c: '#3A6A8A' }, { t: '▀', c: '#00CCFF' }, { t: '███▀▀▀', c: '#3A6A8A' }, { t: '██', c: '#00CCFF' }, { t: '▀▀▀▀▄', c: '#3A6A8A' }],\r\n [{ t: ' ' }, { t: '█▀██▄▄▀▀', c: '#3A6A8A' }, { t: '▀', c: '#88BBDD' }, { t: '█', c: '#3A6A8A' }, { t: '▀▀', c: '#00CCFF' }, { t: '▀', c: '#3A6A8A' }, { t: '██████', c: '#F86464' }, { t: '█▄', c: '#3A6A8A' }],\r\n [{ t: ' ' }, { t: '▀', c: '#3A6A8A' }, { t: '▀', c: '#00CCFF' }, { t: '▀▀▀', c: '#3A6A8A' }, { t: '▀▀█', c: '#88BBDD' }, { t: '███████████', c: '#F86464' }, { t: '█', c: '#3A6A8A' }, { t: ' ' }, { t: '██▄', c: '#00CCFF' }],\r\n [{ t: ' ' }, { t: '▀█', c: '#3A6A8A' }, { t: '▀', c: '#88BBDD' }, { t: '██▀', c: '#3A6A8A' }, { t: '▀▀█████████', c: '#F86464' }, { t: '██▄▄', c: '#3A6A8A' }, { t: ' ' }, { t: '▀', c: '#00CCFF' }],\r\n [{ t: ' ' }, { t: '█', c: '#3A6A8A' }, { t: '▀', c: '#00CCFF' }, { t: '███', c: '#3A6A8A' }, { t: '▀', c: '#00CCFF' }, { t: '██', c: '#3A6A8A' }, { t: '▀▀█████', c: '#F86464' }, { t: '█▀███', c: '#3A6A8A' }],\r\n [{ t: ' ' }, { t: '▀█', c: '#3A6A8A' }, { t: '▀█', c: '#88BBDD' }, { t: '▀▀▀█', c: '#3A6A8A' }, { t: '▀', c: '#88BBDD' }, { t: '▀', c: '#3A6A8A' }, { t: '▀', c: '#00CCFF' }, { t: '▀█', c: '#3A6A8A' }, { t: '▀', c: '#F86464' }, { t: '██▄█', c: '#3A6A8A' }, { t: '▀', c: '#00CCFF' }, { t: '▀█', c: '#3A6A8A' }],\r\n [{ t: ' ' }, { t: '▀█', c: '#3A6A8A' }, { t: '▀▀', c: '#88BBDD' }, { t: '▀███', c: '#F86464' }, { t: '▀█', c: '#3A6A8A' }, { t: '▀', c: '#00CCFF' }, { t: '█', c: '#3A6A8A' }, { t: '▀▀▀▀▀', c: '#88BBDD' }, { t: '██', c: '#3A6A8A' }],\r\n [{ t: ' ' }, { t: '▀▀▀▀▀▀▀', c: '#3A6A8A' }],\r\n ];\r\n\r\n // Helper to render a single icon row as React elements\r\n const renderIconRow = (segments: IconSegment[], key: string) => (\r\n <Text key={key}>\r\n {segments.map((seg, j) =>\r\n seg.c\r\n ? <Text key={j} color={seg.c}>{seg.t}</Text>\r\n : <Text key={j}>{seg.t}</Text>\r\n )}\r\n </Text>\r\n );\r\n\r\n // ── Helpers ──────────────────────────────────────────────────────\r\n const allTips = [\r\n { key: '/help', desc: 'List all commands' },\r\n { key: '/clean-ui', desc: 'Refresh the chat UI' },\r\n { key: '/model', desc: 'Switch AI model' },\r\n { key: '/docs', desc: 'View documentation' },\r\n { key: '/plan', desc: 'Switch to plan mode' },\r\n ];\r\n const tips = allTips.slice(0, tipsCount);\r\n const displayChats = recentChats.slice(0, chatsCount);\r\n\r\n const fmtDate = (iso: string) =>\r\n new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric' });\r\n\r\n const trunc = (s: string, n: number) =>\r\n s.length > n ? s.slice(0, n - 1) + '\\u2026' : s;\r\n\r\n const InfoRow = ({ label, children }: { label: string; children: React.ReactNode }) => (\r\n <Box>\r\n <Text color=\"#555\">{label.padEnd(10)}</Text>\r\n {children}\r\n </Box>\r\n );\r\n\r\n return (\r\n <Box flexDirection=\"column\" marginBottom={1}>\r\n\r\n {/* ══ Custom top border: ╭─ CENTAURUS CLI ──── v{x} ─╮ ══ */}\r\n <Text color={theme.accent}>{topBorder}</Text>\r\n\r\n {/* ══ Content — left + right borders only (top/bottom are the Text lines above/below) ══ */}\r\n <Box\r\n borderStyle=\"round\"\r\n borderTop={false}\r\n borderBottom={false}\r\n borderLeft={true}\r\n borderRight={true}\r\n borderColor={theme.accent}\r\n flexDirection=\"column\"\r\n paddingX={1}\r\n width={cols}\r\n >\r\n\r\n {/* ── Top row: icon + text (left) | Tips + Recent (right) ── */}\r\n <Box flexDirection=\"row\" marginTop={1}>\r\n\r\n {/* Icon + Title column */}\r\n <Box flexDirection=\"row\" marginRight={3}>\r\n {/* Icon — colored ASCII art of Centaurus planet logo */}\r\n {showIcon && (\r\n <Box flexDirection=\"column\" marginRight={2}>\r\n {iconLines.map((segments, i) => renderIconRow(segments, `icon${i}`))}\r\n </Box>\r\n )}\r\n\r\n {/* Title text — normal size, vertically centered alongside icon */}\r\n <Box flexDirection=\"column\" justifyContent=\"center\">\r\n <Text color={theme.accent} bold dimColor={false}>CENTAURUS</Text>\r\n <Text color={theme.accent} bold>CLI</Text>\r\n <Text> </Text>\r\n <Text color=\"#555\">v{version}</Text>\r\n <Text color=\"#444\">Your AI-powered terminal</Text>\r\n </Box>\r\n </Box>\r\n\r\n {/* Tips + Recent Activity — hidden when terminal too narrow */}\r\n {showRight && (\r\n <Box\r\n borderStyle=\"single\"\r\n borderColor={theme.borderDim}\r\n flexDirection=\"column\"\r\n paddingX={1}\r\n flexGrow={1}\r\n >\r\n <Text color={theme.accent} bold>Beginner Tips</Text>\r\n <Text> </Text>\r\n {tips.map((t, i) => (\r\n <Box key={i}>\r\n <Text color={theme.accentMuted}>{t.key.padEnd(8)}</Text>\r\n <Text color=\"#666\">{t.desc}</Text>\r\n </Box>\r\n ))}\r\n <Box marginTop={1}>\r\n <Text color={theme.borderDim}>{'─'.repeat(sepLen)}</Text>\r\n </Box>\r\n <Text color={theme.accent} bold>Recent Activity</Text>\r\n <Text> </Text>\r\n {displayChats.length === 0 ? (\r\n <Text color=\"#444\">No recent chats yet.</Text>\r\n ) : (\r\n displayChats.map((chat) => (\r\n <Box key={chat.id}>\r\n <Text color={theme.accentMuted}>{'● '}</Text>\r\n <Text color=\"white\">{trunc(chat.title, titleLen)}</Text>\r\n <Text color=\"#444\">{' '}{fmtDate(chat.updatedAt)}</Text>\r\n </Box>\r\n ))\r\n )}\r\n <Text> </Text>\r\n <Text color=\"#444\">/chat resume to continue</Text>\r\n </Box>\r\n )}\r\n </Box>\r\n\r\n {/* ── Full-width divider ── */}\r\n <Box marginTop={1} marginBottom={1}>\r\n <Text color={theme.divider}>{'─'.repeat(contentW)}</Text>\r\n </Box>\r\n\r\n {/* ── Bottom row: Session Info | Current Config ── */}\r\n <Box flexDirection=\"row\" marginBottom={1}>\r\n\r\n {/* Session Info */}\r\n <Box\r\n borderStyle=\"single\"\r\n borderColor={theme.borderDim}\r\n flexDirection=\"column\"\r\n paddingX={1}\r\n flexGrow={1}\r\n flexBasis={0}\r\n marginRight={2}\r\n >\r\n <Text color={theme.accent} bold>Subscription &amp; Session Info</Text>\r\n <Text> </Text>\r\n {userInfo?.name && (\r\n <InfoRow label=\"User\">\r\n <Text color=\"white\">{userInfo.name}</Text>\r\n </InfoRow>\r\n )}\r\n {userInfo?.email && (\r\n <InfoRow label=\"Email\">\r\n <Text color=\"#888\">{userInfo.email}</Text>\r\n </InfoRow>\r\n )}\r\n {!userInfo && (\r\n <InfoRow label=\"Account\">\r\n <Text color={isAuth ? '#00dd77' : '#ff5555'}>\r\n {isAuth ? '● Signed in' : '○ Not logged in'}\r\n </Text>\r\n </InfoRow>\r\n )}\r\n <InfoRow label=\"Plan\">\r\n <Text color={plan.toLowerCase() === 'pro' ? '#f0c040' : theme.accentMuted}>\r\n {plan.charAt(0).toUpperCase() + plan.slice(1)}\r\n </Text>\r\n </InfoRow>\r\n <InfoRow label=\"Messages\">\r\n <Text color=\"white\">{msgUsed}</Text>\r\n <Text color=\"#555\"> / {maxMsgs} used </Text>\r\n <Text color={msgLeft < 10 ? '#ffaa00' : '#00dd77'}>({msgLeft} left)</Text>\r\n </InfoRow>\r\n <InfoRow label=\"Window\">\r\n <Text color=\"#888\">{sessionHrs}h session</Text>\r\n {timeLeft\r\n ? <Text color=\"#555\"> — {timeLeft} remaining</Text>\r\n : <Text color=\"#444\"> — not started</Text>\r\n }\r\n </InfoRow>\r\n </Box>\r\n\r\n {/* Current Config */}\r\n <Box\r\n borderStyle=\"single\"\r\n borderColor={theme.borderDim}\r\n flexDirection=\"column\"\r\n paddingX={1}\r\n flexGrow={1}\r\n flexBasis={0}\r\n >\r\n <Text color={theme.accent} bold>Current Config</Text>\r\n <Text> </Text>\r\n <InfoRow label=\"MCPs\">\r\n {mcpTotal === 0\r\n ? <Text color=\"#444\">None configured</Text>\r\n : <Text color=\"white\">{mcpEnabled} enabled<Text color=\"#555\"> ({mcpTotal} total)</Text></Text>\r\n }\r\n </InfoRow>\r\n <InfoRow label=\"AutoFill\">\r\n <Text color={autoSuggest ? '#00dd77' : '#555'}>\r\n {autoSuggest ? 'On' : 'Off'}\r\n </Text>\r\n </InfoRow>\r\n <InfoRow label=\"Rules\">\r\n {rulesCount === 0\r\n ? <Text color=\"#444\">None configured</Text>\r\n : <Text color=\"white\">{rulesCount} rule{rulesCount !== 1 ? 's' : ''} configured</Text>\r\n }\r\n </InfoRow>\r\n </Box>\r\n\r\n </Box>\r\n </Box>\r\n\r\n {/* ══ Custom bottom border: ╰───╯ ══ */}\r\n <Text color={theme.accent}>{bottomBorder}</Text>\r\n\r\n </Box>\r\n );\r\n});\r\n"],"mappings":"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","names":[]}
@@ -1,11 +1,13 @@
1
1
  import React, { useState, useCallback } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
3
  import TextInput from "ink-text-input";
4
+ import { useTheme } from "../theme.js";
4
5
  const WorkflowCreatorScreen = ({
5
6
  onSave,
6
7
  onCancel,
7
8
  initialSteps
8
9
  }) => {
10
+ const theme = useTheme();
9
11
  const [steps, setSteps] = useState(initialSteps || []);
10
12
  const [currentInput, setCurrentInput] = useState("");
11
13
  const [stepType, setStepType] = useState("command");
@@ -65,7 +67,7 @@ const WorkflowCreatorScreen = ({
65
67
  handleRemoveLastStep();
66
68
  }
67
69
  });
68
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React.createElement(Box, { borderStyle: "round", borderColor: "#00ccff", paddingX: 2, paddingY: 1, marginBottom: 1 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: "#00ccff", bold: true }, "\u{1F4CB} Workflow Creator"), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, isLearnedWorkflow ? "Review the steps captured from your learning session. You can edit, add, or remove steps." : "Create a new workflow by adding steps one at a time.")), isLearnedWorkflow && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "#00cc66" }, "\u2728 ", steps.length, " steps captured from learn-workflow mode")), /* @__PURE__ */ React.createElement(Box, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "#ffd700" }, "Ctrl+D"), " Toggle between Command/Instruction mode"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "#ffd700" }, "Ctrl+S"), " Save workflow"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "#ffd700" }, "Ctrl+Z"), " Remove last step"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "#ffd700" }, "ESC"), " Cancel")))), error && /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "red" }, "\u26A0\uFE0F ", error)), steps.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "#00cc66" }, "Steps (", steps.length, "):"), steps.map((step, index) => /* @__PURE__ */ React.createElement(Box, { key: index, marginLeft: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: "#888" }, index + 1, "."), " ", /* @__PURE__ */ React.createElement(Text, { color: step.type === "command" ? "#ffd700" : "#00ccff" }, "[", step.type === "command" ? "\u26A1CMD" : "\u{1F4DD}INS", "]"), " ", /* @__PURE__ */ React.createElement(Text, null, step.content))))), creatorState === "adding-steps" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, null, "Mode: ", /* @__PURE__ */ React.createElement(Text, { color: stepType === "command" ? "#ffd700" : "#00ccff", bold: true }, stepType === "command" ? "\u26A1 COMMAND" : "\u{1F4DD} INSTRUCTION"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " (Ctrl+D to switch)"))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "#888" }, "Step ", steps.length + 1, ": "), /* @__PURE__ */ React.createElement(
70
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", paddingX: 1 }, /* @__PURE__ */ React.createElement(Box, { borderStyle: "round", borderColor: theme.accent, paddingX: 2, paddingY: 1, marginBottom: 1 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: theme.accent, bold: true }, "\u{1F4CB} Workflow Creator"), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, isLearnedWorkflow ? "Review the steps captured from your learning session. You can edit, add, or remove steps." : "Create a new workflow by adding steps one at a time.")), isLearnedWorkflow && /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "#00cc66" }, "\u2728 ", steps.length, " steps captured from learn-workflow mode")), /* @__PURE__ */ React.createElement(Box, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "#ffd700" }, "Ctrl+D"), " Toggle between Command/Instruction mode"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "#ffd700" }, "Ctrl+S"), " Save workflow"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "#ffd700" }, "Ctrl+Z"), " Remove last step"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "#ffd700" }, "ESC"), " Cancel")))), error && /* @__PURE__ */ React.createElement(Box, { marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "red" }, "\u26A0\uFE0F ", error)), steps.length > 0 && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", marginBottom: 1 }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "#00cc66" }, "Steps (", steps.length, "):"), steps.map((step, index) => /* @__PURE__ */ React.createElement(Box, { key: index, marginLeft: 1 }, /* @__PURE__ */ React.createElement(Text, null, /* @__PURE__ */ React.createElement(Text, { color: "#888" }, index + 1, "."), " ", /* @__PURE__ */ React.createElement(Text, { color: step.type === "command" ? "#ffd700" : theme.accent }, "[", step.type === "command" ? "\u26A1CMD" : "\u{1F4DD}INS", "]"), " ", /* @__PURE__ */ React.createElement(Text, null, step.content))))), creatorState === "adding-steps" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, null, "Mode: ", /* @__PURE__ */ React.createElement(Text, { color: stepType === "command" ? "#ffd700" : theme.accent, bold: true }, stepType === "command" ? "\u26A1 COMMAND" : "\u{1F4DD} INSTRUCTION"), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, " (Ctrl+D to switch)"))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: "#888" }, "Step ", steps.length + 1, ": "), /* @__PURE__ */ React.createElement(
69
71
  TextInput,
70
72
  {
71
73
  value: currentInput,
@@ -73,7 +75,7 @@ const WorkflowCreatorScreen = ({
73
75
  onSubmit: handleAddStep,
74
76
  placeholder: stepType === "command" ? "Enter exact command to run..." : "Describe what should happen..."
75
77
  }
76
- ))), creatorState === "naming" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "#00ccff" }, "Save Workflow"), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Workflow name: "), /* @__PURE__ */ React.createElement(
78
+ ))), creatorState === "naming" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.accent }, "Save Workflow"), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Workflow name: "), /* @__PURE__ */ React.createElement(
77
79
  TextInput,
78
80
  {
79
81
  value: workflowName,
@@ -81,7 +83,7 @@ const WorkflowCreatorScreen = ({
81
83
  onSubmit: handleNameSubmit,
82
84
  placeholder: "my-workflow"
83
85
  }
84
- ))), creatorState === "description" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "#00ccff" }, "Save Workflow: ", workflowName), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Description (optional, press Enter to skip): "), /* @__PURE__ */ React.createElement(
86
+ ))), creatorState === "description" && /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.accent }, "Save Workflow: ", workflowName), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, null, "Description (optional, press Enter to skip): "), /* @__PURE__ */ React.createElement(
85
87
  TextInput,
86
88
  {
87
89
  value: workflowDescription,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/ui/components/WorkflowCreatorScreen.tsx"],"sourcesContent":["/**\r\n * Workflow Creator Screen\r\n * \r\n * A screen for creating new workflows with step-by-step input.\r\n * Supports both command (exact) and instruction (interpreted) step types.\r\n * \r\n * Controls:\r\n * - Type step content and press Enter to add\r\n * - Ctrl+D to toggle between Command and Instruction mode\r\n * - Ctrl+S to save the workflow\r\n * - ESC to cancel and exit\r\n */\r\n\r\nimport React, { useState, useCallback } from 'react';\r\nimport { Box, Text, useInput } from 'ink';\r\nimport TextInput from 'ink-text-input';\r\n\r\ninterface WorkflowStep {\r\n type: 'command' | 'instruction';\r\n content: string;\r\n}\r\n\r\ninterface WorkflowCreatorScreenProps {\r\n onSave: (name: string, steps: WorkflowStep[], description?: string) => void;\r\n onCancel: () => void;\r\n initialSteps?: WorkflowStep[]; // Pre-populated steps from learn-workflow mode\r\n}\r\n\r\ntype CreatorState = 'adding-steps' | 'naming' | 'description';\r\n\r\nexport const WorkflowCreatorScreen: React.FC<WorkflowCreatorScreenProps> = ({\r\n onSave,\r\n onCancel,\r\n initialSteps\r\n}) => {\r\n // Initialize steps from initialSteps if provided (learn-workflow mode)\r\n const [steps, setSteps] = useState<WorkflowStep[]>(initialSteps || []);\r\n const [currentInput, setCurrentInput] = useState('');\r\n const [stepType, setStepType] = useState<'command' | 'instruction'>('command');\r\n const [creatorState, setCreatorState] = useState<CreatorState>('adding-steps');\r\n const [workflowName, setWorkflowName] = useState('');\r\n const [workflowDescription, setWorkflowDescription] = useState('');\r\n const [error, setError] = useState<string | null>(null);\r\n const [isLearnedWorkflow] = useState<boolean>(!!initialSteps && initialSteps.length > 0);\r\n\r\n // Handle keyboard shortcuts\r\n useInput((input, key) => {\r\n // ESC to cancel\r\n if (key.escape) {\r\n onCancel();\r\n return;\r\n }\r\n\r\n // Only handle shortcuts in adding-steps mode\r\n if (creatorState === 'adding-steps') {\r\n // Ctrl+D to toggle step type\r\n if (key.ctrl && input === 'd') {\r\n setStepType(prev => prev === 'command' ? 'instruction' : 'command');\r\n return;\r\n }\r\n\r\n // Ctrl+S to save (move to naming state)\r\n if (key.ctrl && input === 's') {\r\n if (steps.length === 0) {\r\n setError('Add at least one step before saving');\r\n setTimeout(() => setError(null), 3000);\r\n return;\r\n }\r\n setCreatorState('naming');\r\n return;\r\n }\r\n }\r\n });\r\n\r\n // Handle adding a step\r\n const handleAddStep = useCallback((content: string) => {\r\n if (!content.trim()) return;\r\n\r\n setSteps(prev => [...prev, { type: stepType, content: content.trim() }]);\r\n setCurrentInput('');\r\n setError(null);\r\n }, [stepType]);\r\n\r\n // Handle workflow name submission\r\n const handleNameSubmit = useCallback((name: string) => {\r\n if (!name.trim()) {\r\n setError('Workflow name is required');\r\n setTimeout(() => setError(null), 3000);\r\n return;\r\n }\r\n setWorkflowName(name.trim());\r\n setCreatorState('description');\r\n }, []);\r\n\r\n // Handle description submission (optional)\r\n const handleDescriptionSubmit = useCallback((description: string) => {\r\n onSave(workflowName, steps, description.trim() || undefined);\r\n }, [workflowName, steps, onSave]);\r\n\r\n // Remove last step\r\n const handleRemoveLastStep = useCallback(() => {\r\n if (steps.length > 0) {\r\n setSteps(prev => prev.slice(0, -1));\r\n }\r\n }, [steps.length]);\r\n\r\n // Handle backspace on empty input to remove last step\r\n useInput((input, key) => {\r\n if (creatorState === 'adding-steps' && key.backspace && currentInput === '' && steps.length > 0) {\r\n // Don't remove step on regular backspace - only with Ctrl+Backspace\r\n }\r\n // Ctrl+Z to remove last step\r\n if (key.ctrl && input === 'z' && creatorState === 'adding-steps' && steps.length > 0) {\r\n handleRemoveLastStep();\r\n }\r\n });\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingX={1}>\r\n {/* Header */}\r\n <Box borderStyle=\"round\" borderColor=\"#00ccff\" paddingX={2} paddingY={1} marginBottom={1}>\r\n <Box flexDirection=\"column\">\r\n <Text color=\"#00ccff\" bold>📋 Workflow Creator</Text>\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {isLearnedWorkflow\r\n ? 'Review the steps captured from your learning session. You can edit, add, or remove steps.'\r\n : 'Create a new workflow by adding steps one at a time.'}\r\n </Text>\r\n </Box>\r\n {isLearnedWorkflow && (\r\n <Box marginTop={1}>\r\n <Text color=\"#00cc66\">✨ {steps.length} steps captured from learn-workflow mode</Text>\r\n </Box>\r\n )}\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text dimColor>\r\n <Text color=\"#ffd700\">Ctrl+D</Text> Toggle between Command/Instruction mode\r\n </Text>\r\n <Text dimColor>\r\n <Text color=\"#ffd700\">Ctrl+S</Text> Save workflow\r\n </Text>\r\n <Text dimColor>\r\n <Text color=\"#ffd700\">Ctrl+Z</Text> Remove last step\r\n </Text>\r\n <Text dimColor>\r\n <Text color=\"#ffd700\">ESC</Text> Cancel\r\n </Text>\r\n </Box>\r\n </Box>\r\n </Box>\r\n\r\n {/* Error message */}\r\n {error && (\r\n <Box marginBottom={1}>\r\n <Text color=\"red\">⚠️ {error}</Text>\r\n </Box>\r\n )}\r\n\r\n {/* Current steps */}\r\n {steps.length > 0 && (\r\n <Box flexDirection=\"column\" marginBottom={1}>\r\n <Text bold color=\"#00cc66\">Steps ({steps.length}):</Text>\r\n {steps.map((step, index) => (\r\n <Box key={index} marginLeft={1}>\r\n <Text>\r\n <Text color=\"#888\">{index + 1}.</Text>{' '}\r\n <Text color={step.type === 'command' ? '#ffd700' : '#00ccff'}>\r\n [{step.type === 'command' ? '⚡CMD' : '📝INS'}]\r\n </Text>{' '}\r\n <Text>{step.content}</Text>\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* Input area */}\r\n {creatorState === 'adding-steps' && (\r\n <Box flexDirection=\"column\">\r\n <Box>\r\n <Text>\r\n Mode: <Text color={stepType === 'command' ? '#ffd700' : '#00ccff'} bold>\r\n {stepType === 'command' ? '⚡ COMMAND' : '📝 INSTRUCTION'}\r\n </Text>\r\n <Text dimColor> (Ctrl+D to switch)</Text>\r\n </Text>\r\n </Box>\r\n <Box marginTop={1}>\r\n <Text color=\"#888\">Step {steps.length + 1}: </Text>\r\n <TextInput\r\n value={currentInput}\r\n onChange={setCurrentInput}\r\n onSubmit={handleAddStep}\r\n placeholder={stepType === 'command'\r\n ? 'Enter exact command to run...'\r\n : 'Describe what should happen...'}\r\n />\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* Naming state */}\r\n {creatorState === 'naming' && (\r\n <Box flexDirection=\"column\">\r\n <Text bold color=\"#00ccff\">Save Workflow</Text>\r\n <Box marginTop={1}>\r\n <Text>Workflow name: </Text>\r\n <TextInput\r\n value={workflowName}\r\n onChange={setWorkflowName}\r\n onSubmit={handleNameSubmit}\r\n placeholder=\"my-workflow\"\r\n />\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* Description state */}\r\n {creatorState === 'description' && (\r\n <Box flexDirection=\"column\">\r\n <Text bold color=\"#00ccff\">Save Workflow: {workflowName}</Text>\r\n <Box marginTop={1}>\r\n <Text>Description (optional, press Enter to skip): </Text>\r\n <TextInput\r\n value={workflowDescription}\r\n onChange={setWorkflowDescription}\r\n onSubmit={handleDescriptionSubmit}\r\n placeholder=\"What does this workflow do?\"\r\n />\r\n </Box>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n};\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/ui/components/WorkflowCreatorScreen.tsx"],"sourcesContent":["/**\r\n * Workflow Creator Screen\r\n * \r\n * A screen for creating new workflows with step-by-step input.\r\n * Supports both command (exact) and instruction (interpreted) step types.\r\n * \r\n * Controls:\r\n * - Type step content and press Enter to add\r\n * - Ctrl+D to toggle between Command and Instruction mode\r\n * - Ctrl+S to save the workflow\r\n * - ESC to cancel and exit\r\n */\r\n\r\nimport React, { useState, useCallback } from 'react';\r\nimport { Box, Text, useInput } from 'ink';\r\nimport TextInput from 'ink-text-input';\r\nimport { useTheme } from '../theme.js';\r\n\r\ninterface WorkflowStep {\r\n type: 'command' | 'instruction';\r\n content: string;\r\n}\r\n\r\ninterface WorkflowCreatorScreenProps {\r\n onSave: (name: string, steps: WorkflowStep[], description?: string) => void;\r\n onCancel: () => void;\r\n initialSteps?: WorkflowStep[]; // Pre-populated steps from learn-workflow mode\r\n}\r\n\r\ntype CreatorState = 'adding-steps' | 'naming' | 'description';\r\n\r\nexport const WorkflowCreatorScreen: React.FC<WorkflowCreatorScreenProps> = ({\r\n onSave,\r\n onCancel,\r\n initialSteps\r\n}) => {\r\n const theme = useTheme();\r\n // Initialize steps from initialSteps if provided (learn-workflow mode)\r\n const [steps, setSteps] = useState<WorkflowStep[]>(initialSteps || []);\r\n const [currentInput, setCurrentInput] = useState('');\r\n const [stepType, setStepType] = useState<'command' | 'instruction'>('command');\r\n const [creatorState, setCreatorState] = useState<CreatorState>('adding-steps');\r\n const [workflowName, setWorkflowName] = useState('');\r\n const [workflowDescription, setWorkflowDescription] = useState('');\r\n const [error, setError] = useState<string | null>(null);\r\n const [isLearnedWorkflow] = useState<boolean>(!!initialSteps && initialSteps.length > 0);\r\n\r\n // Handle keyboard shortcuts\r\n useInput((input, key) => {\r\n // ESC to cancel\r\n if (key.escape) {\r\n onCancel();\r\n return;\r\n }\r\n\r\n // Only handle shortcuts in adding-steps mode\r\n if (creatorState === 'adding-steps') {\r\n // Ctrl+D to toggle step type\r\n if (key.ctrl && input === 'd') {\r\n setStepType(prev => prev === 'command' ? 'instruction' : 'command');\r\n return;\r\n }\r\n\r\n // Ctrl+S to save (move to naming state)\r\n if (key.ctrl && input === 's') {\r\n if (steps.length === 0) {\r\n setError('Add at least one step before saving');\r\n setTimeout(() => setError(null), 3000);\r\n return;\r\n }\r\n setCreatorState('naming');\r\n return;\r\n }\r\n }\r\n });\r\n\r\n // Handle adding a step\r\n const handleAddStep = useCallback((content: string) => {\r\n if (!content.trim()) return;\r\n\r\n setSteps(prev => [...prev, { type: stepType, content: content.trim() }]);\r\n setCurrentInput('');\r\n setError(null);\r\n }, [stepType]);\r\n\r\n // Handle workflow name submission\r\n const handleNameSubmit = useCallback((name: string) => {\r\n if (!name.trim()) {\r\n setError('Workflow name is required');\r\n setTimeout(() => setError(null), 3000);\r\n return;\r\n }\r\n setWorkflowName(name.trim());\r\n setCreatorState('description');\r\n }, []);\r\n\r\n // Handle description submission (optional)\r\n const handleDescriptionSubmit = useCallback((description: string) => {\r\n onSave(workflowName, steps, description.trim() || undefined);\r\n }, [workflowName, steps, onSave]);\r\n\r\n // Remove last step\r\n const handleRemoveLastStep = useCallback(() => {\r\n if (steps.length > 0) {\r\n setSteps(prev => prev.slice(0, -1));\r\n }\r\n }, [steps.length]);\r\n\r\n // Handle backspace on empty input to remove last step\r\n useInput((input, key) => {\r\n if (creatorState === 'adding-steps' && key.backspace && currentInput === '' && steps.length > 0) {\r\n // Don't remove step on regular backspace - only with Ctrl+Backspace\r\n }\r\n // Ctrl+Z to remove last step\r\n if (key.ctrl && input === 'z' && creatorState === 'adding-steps' && steps.length > 0) {\r\n handleRemoveLastStep();\r\n }\r\n });\r\n\r\n return (\r\n <Box flexDirection=\"column\" paddingX={1}>\r\n {/* Header */}\r\n <Box borderStyle=\"round\" borderColor={theme.accent} paddingX={2} paddingY={1} marginBottom={1}>\r\n <Box flexDirection=\"column\">\r\n <Text color={theme.accent} bold>📋 Workflow Creator</Text>\r\n <Box marginTop={1}>\r\n <Text dimColor>\r\n {isLearnedWorkflow\r\n ? 'Review the steps captured from your learning session. You can edit, add, or remove steps.'\r\n : 'Create a new workflow by adding steps one at a time.'}\r\n </Text>\r\n </Box>\r\n {isLearnedWorkflow && (\r\n <Box marginTop={1}>\r\n <Text color=\"#00cc66\">✨ {steps.length} steps captured from learn-workflow mode</Text>\r\n </Box>\r\n )}\r\n <Box marginTop={1} flexDirection=\"column\">\r\n <Text dimColor>\r\n <Text color=\"#ffd700\">Ctrl+D</Text> Toggle between Command/Instruction mode\r\n </Text>\r\n <Text dimColor>\r\n <Text color=\"#ffd700\">Ctrl+S</Text> Save workflow\r\n </Text>\r\n <Text dimColor>\r\n <Text color=\"#ffd700\">Ctrl+Z</Text> Remove last step\r\n </Text>\r\n <Text dimColor>\r\n <Text color=\"#ffd700\">ESC</Text> Cancel\r\n </Text>\r\n </Box>\r\n </Box>\r\n </Box>\r\n\r\n {/* Error message */}\r\n {error && (\r\n <Box marginBottom={1}>\r\n <Text color=\"red\">⚠️ {error}</Text>\r\n </Box>\r\n )}\r\n\r\n {/* Current steps */}\r\n {steps.length > 0 && (\r\n <Box flexDirection=\"column\" marginBottom={1}>\r\n <Text bold color=\"#00cc66\">Steps ({steps.length}):</Text>\r\n {steps.map((step, index) => (\r\n <Box key={index} marginLeft={1}>\r\n <Text>\r\n <Text color=\"#888\">{index + 1}.</Text>{' '}\r\n <Text color={step.type === 'command' ? '#ffd700' : theme.accent}>\r\n [{step.type === 'command' ? '⚡CMD' : '📝INS'}]\r\n </Text>{' '}\r\n <Text>{step.content}</Text>\r\n </Text>\r\n </Box>\r\n ))}\r\n </Box>\r\n )}\r\n\r\n {/* Input area */}\r\n {creatorState === 'adding-steps' && (\r\n <Box flexDirection=\"column\">\r\n <Box>\r\n <Text>\r\n Mode: <Text color={stepType === 'command' ? '#ffd700' : theme.accent} bold>\r\n {stepType === 'command' ? '⚡ COMMAND' : '📝 INSTRUCTION'}\r\n </Text>\r\n <Text dimColor> (Ctrl+D to switch)</Text>\r\n </Text>\r\n </Box>\r\n <Box marginTop={1}>\r\n <Text color=\"#888\">Step {steps.length + 1}: </Text>\r\n <TextInput\r\n value={currentInput}\r\n onChange={setCurrentInput}\r\n onSubmit={handleAddStep}\r\n placeholder={stepType === 'command'\r\n ? 'Enter exact command to run...'\r\n : 'Describe what should happen...'}\r\n />\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* Naming state */}\r\n {creatorState === 'naming' && (\r\n <Box flexDirection=\"column\">\r\n <Text bold color={theme.accent}>Save Workflow</Text>\r\n <Box marginTop={1}>\r\n <Text>Workflow name: </Text>\r\n <TextInput\r\n value={workflowName}\r\n onChange={setWorkflowName}\r\n onSubmit={handleNameSubmit}\r\n placeholder=\"my-workflow\"\r\n />\r\n </Box>\r\n </Box>\r\n )}\r\n\r\n {/* Description state */}\r\n {creatorState === 'description' && (\r\n <Box flexDirection=\"column\">\r\n <Text bold color={theme.accent}>Save Workflow: {workflowName}</Text>\r\n <Box marginTop={1}>\r\n <Text>Description (optional, press Enter to skip): </Text>\r\n <TextInput\r\n value={workflowDescription}\r\n onChange={setWorkflowDescription}\r\n onSubmit={handleDescriptionSubmit}\r\n placeholder=\"What does this workflow do?\"\r\n />\r\n </Box>\r\n </Box>\r\n )}\r\n </Box>\r\n );\r\n};\r\n"],"mappings":"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","names":[]}
@@ -0,0 +1,97 @@
1
+ import React from "react";
2
+ const DEFAULT_ACCENT_COLOR = "#00ccff";
3
+ const THEME_PALETTE = [
4
+ { name: "blue", value: "#00ccff", label: "\u2588\u2588\u2588\u2588 Ocean Blue" },
5
+ { name: "cyan", value: "#00ddbb", label: "\u2588\u2588\u2588\u2588 Cyan Teal" },
6
+ { name: "green", value: "#00cc88", label: "\u2588\u2588\u2588\u2588 Emerald Green" },
7
+ { name: "lime", value: "#88cc00", label: "\u2588\u2588\u2588\u2588 Lime" },
8
+ { name: "yellow", value: "#ffcc00", label: "\u2588\u2588\u2588\u2588 Golden Yellow" },
9
+ { name: "orange", value: "#ff8844", label: "\u2588\u2588\u2588\u2588 Sunset Orange" },
10
+ { name: "red", value: "#ff4466", label: "\u2588\u2588\u2588\u2588 Ruby Red" },
11
+ { name: "pink", value: "#ff66aa", label: "\u2588\u2588\u2588\u2588 Pink" },
12
+ { name: "violet", value: "#aa66ff", label: "\u2588\u2588\u2588\u2588 Violet Purple" },
13
+ { name: "sky", value: "#4499ff", label: "\u2588\u2588\u2588\u2588 Sky Blue" }
14
+ ];
15
+ function hexToHsl(hex) {
16
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
17
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
18
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
19
+ const max = Math.max(r, g, b);
20
+ const min = Math.min(r, g, b);
21
+ const l = (max + min) / 2;
22
+ let h = 0;
23
+ let s = 0;
24
+ if (max !== min) {
25
+ const d = max - min;
26
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
27
+ if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
28
+ else if (max === g) h = ((b - r) / d + 2) / 6;
29
+ else h = ((r - g) / d + 4) / 6;
30
+ }
31
+ return [h * 360, s * 100, l * 100];
32
+ }
33
+ function hslToHex(h, s, l) {
34
+ h /= 360;
35
+ s /= 100;
36
+ l /= 100;
37
+ const hue2rgb = (p, q, t) => {
38
+ if (t < 0) t += 1;
39
+ if (t > 1) t -= 1;
40
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
41
+ if (t < 1 / 2) return q;
42
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
43
+ return p;
44
+ };
45
+ let r, g, b;
46
+ if (s === 0) {
47
+ r = g = b = l;
48
+ } else {
49
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
50
+ const p = 2 * l - q;
51
+ r = hue2rgb(p, q, h + 1 / 3);
52
+ g = hue2rgb(p, q, h);
53
+ b = hue2rgb(p, q, h - 1 / 3);
54
+ }
55
+ const toHex = (n) => Math.round(n * 255).toString(16).padStart(2, "0");
56
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
57
+ }
58
+ function deriveThemeColors(accent) {
59
+ const [h, s, l] = hexToHsl(accent.slice(0, 7));
60
+ return {
61
+ accent,
62
+ accentMuted: hslToHex(h, Math.max(s * 0.85, 30), l * 0.82),
63
+ border: hslToHex(h, Math.max(s * 0.65, 25), Math.min(l * 0.52, 42)),
64
+ borderDim: hslToHex(h, Math.max(s * 0.6, 20), Math.min(l * 0.4, 32)),
65
+ divider: hslToHex(h, Math.max(s * 0.55, 18), Math.min(l * 0.35, 28)),
66
+ bgAccent: hslToHex(h, Math.max(s * 0.7, 25), Math.min(l * 0.25, 20))
67
+ };
68
+ }
69
+ let _accentColor = DEFAULT_ACCENT_COLOR;
70
+ let _themeColors = deriveThemeColors(DEFAULT_ACCENT_COLOR);
71
+ function setGlobalAccentColor(color) {
72
+ _accentColor = color;
73
+ _themeColors = deriveThemeColors(color);
74
+ }
75
+ function getAccentColor() {
76
+ return _accentColor;
77
+ }
78
+ function getThemeColors() {
79
+ return _themeColors;
80
+ }
81
+ const ThemeContext = React.createContext(
82
+ deriveThemeColors(DEFAULT_ACCENT_COLOR)
83
+ );
84
+ function useTheme() {
85
+ return React.useContext(ThemeContext);
86
+ }
87
+ export {
88
+ DEFAULT_ACCENT_COLOR,
89
+ THEME_PALETTE,
90
+ ThemeContext,
91
+ deriveThemeColors,
92
+ getAccentColor,
93
+ getThemeColors,
94
+ setGlobalAccentColor,
95
+ useTheme
96
+ };
97
+ //# sourceMappingURL=theme.js.map