@mariozechner/pi-coding-agent 0.7.29 → 0.8.2

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 (63) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +74 -4
  3. package/dist/main.d.ts.map +1 -1
  4. package/dist/main.js +7 -4
  5. package/dist/main.js.map +1 -1
  6. package/dist/settings-manager.d.ts +3 -0
  7. package/dist/settings-manager.d.ts.map +1 -1
  8. package/dist/settings-manager.js +7 -0
  9. package/dist/settings-manager.js.map +1 -1
  10. package/dist/theme/dark.json +70 -0
  11. package/dist/theme/light.json +69 -0
  12. package/dist/theme/theme-schema.json +246 -0
  13. package/dist/theme/theme.d.ts +33 -0
  14. package/dist/theme/theme.d.ts.map +1 -0
  15. package/dist/theme/theme.js +470 -0
  16. package/dist/theme/theme.js.map +1 -0
  17. package/dist/tui/assistant-message.d.ts.map +1 -1
  18. package/dist/tui/assistant-message.js +7 -7
  19. package/dist/tui/assistant-message.js.map +1 -1
  20. package/dist/tui/dynamic-border.d.ts +1 -0
  21. package/dist/tui/dynamic-border.d.ts.map +1 -1
  22. package/dist/tui/dynamic-border.js +5 -2
  23. package/dist/tui/dynamic-border.js.map +1 -1
  24. package/dist/tui/footer.d.ts +3 -1
  25. package/dist/tui/footer.d.ts.map +1 -1
  26. package/dist/tui/footer.js +20 -5
  27. package/dist/tui/footer.js.map +1 -1
  28. package/dist/tui/model-selector.d.ts.map +1 -1
  29. package/dist/tui/model-selector.js +14 -13
  30. package/dist/tui/model-selector.js.map +1 -1
  31. package/dist/tui/oauth-selector.d.ts.map +1 -1
  32. package/dist/tui/oauth-selector.js +9 -8
  33. package/dist/tui/oauth-selector.js.map +1 -1
  34. package/dist/tui/queue-mode-selector.d.ts.map +1 -1
  35. package/dist/tui/queue-mode-selector.js +3 -10
  36. package/dist/tui/queue-mode-selector.js.map +1 -1
  37. package/dist/tui/session-selector.d.ts +1 -0
  38. package/dist/tui/session-selector.d.ts.map +1 -1
  39. package/dist/tui/session-selector.js +11 -15
  40. package/dist/tui/session-selector.js.map +1 -1
  41. package/dist/tui/theme-selector.d.ts +11 -0
  42. package/dist/tui/theme-selector.d.ts.map +1 -0
  43. package/dist/tui/theme-selector.js +46 -0
  44. package/dist/tui/theme-selector.js.map +1 -0
  45. package/dist/tui/thinking-selector.d.ts.map +1 -1
  46. package/dist/tui/thinking-selector.js +3 -10
  47. package/dist/tui/thinking-selector.js.map +1 -1
  48. package/dist/tui/tool-execution.d.ts.map +1 -1
  49. package/dist/tui/tool-execution.js +30 -111
  50. package/dist/tui/tool-execution.js.map +1 -1
  51. package/dist/tui/tui-renderer.d.ts +3 -1
  52. package/dist/tui/tui-renderer.d.ts.map +1 -1
  53. package/dist/tui/tui-renderer.js +146 -94
  54. package/dist/tui/tui-renderer.js.map +1 -1
  55. package/dist/tui/user-message-selector.d.ts +1 -0
  56. package/dist/tui/user-message-selector.d.ts.map +1 -1
  57. package/dist/tui/user-message-selector.js +12 -20
  58. package/dist/tui/user-message-selector.js.map +1 -1
  59. package/dist/tui/user-message.d.ts +0 -1
  60. package/dist/tui/user-message.d.ts.map +1 -1
  61. package/dist/tui/user-message.js +5 -4
  62. package/dist/tui/user-message.js.map +1 -1
  63. package/package.json +6 -4
@@ -1,10 +1,10 @@
1
1
  import { CombinedAutocompleteProvider, Container, Input, Loader, Markdown, ProcessTerminal, Spacer, Text, TruncatedText, TUI, } from "@mariozechner/pi-tui";
2
- import chalk from "chalk";
3
2
  import { exec } from "child_process";
4
3
  import { getChangelogPath, parseChangelog } from "../changelog.js";
5
4
  import { exportSessionToHtml } from "../export-html.js";
6
5
  import { getApiKeyForModel, getAvailableModels } from "../model-config.js";
7
6
  import { listOAuthProviders, login, logout } from "../oauth/index.js";
7
+ import { getEditorTheme, getMarkdownTheme, onThemeChange, setTheme, theme } from "../theme/theme.js";
8
8
  import { AssistantMessageComponent } from "./assistant-message.js";
9
9
  import { CustomEditor } from "./custom-editor.js";
10
10
  import { DynamicBorder } from "./dynamic-border.js";
@@ -12,6 +12,7 @@ import { FooterComponent } from "./footer.js";
12
12
  import { ModelSelectorComponent } from "./model-selector.js";
13
13
  import { OAuthSelectorComponent } from "./oauth-selector.js";
14
14
  import { QueueModeSelectorComponent } from "./queue-mode-selector.js";
15
+ import { ThemeSelectorComponent } from "./theme-selector.js";
15
16
  import { ThinkingSelectorComponent } from "./thinking-selector.js";
16
17
  import { ToolExecutionComponent } from "./tool-execution.js";
17
18
  import { UserMessageComponent } from "./user-message.js";
@@ -48,6 +49,8 @@ export class TuiRenderer {
48
49
  thinkingSelector = null;
49
50
  // Queue mode selector
50
51
  queueModeSelector = null;
52
+ // Theme selector
53
+ themeSelector = null;
51
54
  // Model selector
52
55
  modelSelector = null;
53
56
  // User message selector (for branching)
@@ -72,7 +75,7 @@ export class TuiRenderer {
72
75
  this.chatContainer = new Container();
73
76
  this.pendingMessagesContainer = new Container();
74
77
  this.statusContainer = new Container();
75
- this.editor = new CustomEditor();
78
+ this.editor = new CustomEditor(getEditorTheme());
76
79
  this.editorContainer = new Container(); // Container to hold editor or selector
77
80
  this.editorContainer.addChild(this.editor); // Start with editor
78
81
  this.footer = new FooterComponent(agent.state);
@@ -113,10 +116,15 @@ export class TuiRenderer {
113
116
  name: "queue",
114
117
  description: "Select message queue mode (opens selector UI)",
115
118
  };
119
+ const themeCommand = {
120
+ name: "theme",
121
+ description: "Select color theme (opens selector UI)",
122
+ };
116
123
  // Setup autocomplete for file paths and slash commands
117
124
  const autocompleteProvider = new CombinedAutocompleteProvider([
118
125
  thinkingCommand,
119
126
  modelCommand,
127
+ themeCommand,
120
128
  exportCommand,
121
129
  sessionCommand,
122
130
  changelogCommand,
@@ -131,33 +139,33 @@ export class TuiRenderer {
131
139
  if (this.isInitialized)
132
140
  return;
133
141
  // Add header with logo and instructions
134
- const logo = chalk.bold.cyan("pi") + chalk.dim(` v${this.version}`);
135
- const instructions = chalk.dim("esc") +
136
- chalk.gray(" to interrupt") +
142
+ const logo = theme.bold(theme.fg("accent", "pi")) + theme.fg("dim", ` v${this.version}`);
143
+ const instructions = theme.fg("dim", "esc") +
144
+ theme.fg("muted", " to interrupt") +
137
145
  "\n" +
138
- chalk.dim("ctrl+c") +
139
- chalk.gray(" to clear") +
146
+ theme.fg("dim", "ctrl+c") +
147
+ theme.fg("muted", " to clear") +
140
148
  "\n" +
141
- chalk.dim("ctrl+c twice") +
142
- chalk.gray(" to exit") +
149
+ theme.fg("dim", "ctrl+c twice") +
150
+ theme.fg("muted", " to exit") +
143
151
  "\n" +
144
- chalk.dim("ctrl+k") +
145
- chalk.gray(" to delete line") +
152
+ theme.fg("dim", "ctrl+k") +
153
+ theme.fg("muted", " to delete line") +
146
154
  "\n" +
147
- chalk.dim("shift+tab") +
148
- chalk.gray(" to cycle thinking") +
155
+ theme.fg("dim", "shift+tab") +
156
+ theme.fg("muted", " to cycle thinking") +
149
157
  "\n" +
150
- chalk.dim("ctrl+p") +
151
- chalk.gray(" to cycle models") +
158
+ theme.fg("dim", "ctrl+p") +
159
+ theme.fg("muted", " to cycle models") +
152
160
  "\n" +
153
- chalk.dim("ctrl+o") +
154
- chalk.gray(" to expand tools") +
161
+ theme.fg("dim", "ctrl+o") +
162
+ theme.fg("muted", " to expand tools") +
155
163
  "\n" +
156
- chalk.dim("/") +
157
- chalk.gray(" for commands") +
164
+ theme.fg("dim", "/") +
165
+ theme.fg("muted", " for commands") +
158
166
  "\n" +
159
- chalk.dim("drop files") +
160
- chalk.gray(" to attach");
167
+ theme.fg("dim", "drop files") +
168
+ theme.fg("muted", " to attach");
161
169
  const header = new Text(logo + "\n" + instructions, 1, 0);
162
170
  // Setup UI layout
163
171
  this.ui.addChild(new Spacer(1));
@@ -165,21 +173,21 @@ export class TuiRenderer {
165
173
  this.ui.addChild(new Spacer(1));
166
174
  // Add new version notification if available
167
175
  if (this.newVersion) {
168
- this.ui.addChild(new DynamicBorder(chalk.yellow));
169
- this.ui.addChild(new Text(chalk.bold.yellow("Update Available") +
176
+ this.ui.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
177
+ this.ui.addChild(new Text(theme.bold(theme.fg("warning", "Update Available")) +
170
178
  "\n" +
171
- chalk.gray(`New version ${this.newVersion} is available. Run: `) +
172
- chalk.cyan("npm install -g @mariozechner/pi-coding-agent"), 1, 0));
173
- this.ui.addChild(new DynamicBorder(chalk.yellow));
179
+ theme.fg("muted", `New version ${this.newVersion} is available. Run: `) +
180
+ theme.fg("accent", "npm install -g @mariozechner/pi-coding-agent"), 1, 0));
181
+ this.ui.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
174
182
  }
175
183
  // Add changelog if provided
176
184
  if (this.changelogMarkdown) {
177
- this.ui.addChild(new DynamicBorder(chalk.cyan));
178
- this.ui.addChild(new Text(chalk.bold.cyan("What's New"), 1, 0));
185
+ this.ui.addChild(new DynamicBorder());
186
+ this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
179
187
  this.ui.addChild(new Spacer(1));
180
- this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0));
188
+ this.ui.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, getMarkdownTheme()));
181
189
  this.ui.addChild(new Spacer(1));
182
- this.ui.addChild(new DynamicBorder(chalk.cyan));
190
+ this.ui.addChild(new DynamicBorder());
183
191
  }
184
192
  this.ui.addChild(this.chatContainer);
185
193
  this.ui.addChild(this.pendingMessagesContainer);
@@ -282,6 +290,12 @@ export class TuiRenderer {
282
290
  this.editor.setText("");
283
291
  return;
284
292
  }
293
+ // Check for /theme command
294
+ if (text === "/theme") {
295
+ this.showThemeSelector();
296
+ this.editor.setText("");
297
+ return;
298
+ }
285
299
  // Normal message submission - validate model and API key first
286
300
  const currentModel = this.agent.state.model;
287
301
  if (!currentModel) {
@@ -323,6 +337,12 @@ export class TuiRenderer {
323
337
  // Start the UI
324
338
  this.ui.start();
325
339
  this.isInitialized = true;
340
+ // Set up theme file watcher for live reload
341
+ onThemeChange(() => {
342
+ this.ui.invalidate();
343
+ this.updateEditorBorderColor();
344
+ this.ui.requestRender();
345
+ });
326
346
  }
327
347
  async handleEvent(event, state) {
328
348
  if (!this.isInitialized) {
@@ -339,7 +359,7 @@ export class TuiRenderer {
339
359
  this.loadingAnimation.stop();
340
360
  }
341
361
  this.statusContainer.clear();
342
- this.loadingAnimation = new Loader(this.ui, "Working... (esc to interrupt)");
362
+ this.loadingAnimation = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), "Working... (esc to interrupt)");
343
363
  this.statusContainer.addChild(this.loadingAnimation);
344
364
  this.ui.requestRender();
345
365
  break;
@@ -580,34 +600,16 @@ export class TuiRenderer {
580
600
  this.lastSigintTime = now;
581
601
  }
582
602
  }
583
- getThinkingBorderColor(level) {
584
- // More thinking = more color (gray → dim colors → bright colors)
585
- switch (level) {
586
- case "off":
587
- return chalk.gray;
588
- case "minimal":
589
- return chalk.dim.blue;
590
- case "low":
591
- return chalk.blue;
592
- case "medium":
593
- return chalk.cyan;
594
- case "high":
595
- return chalk.magenta;
596
- default:
597
- return chalk.gray;
598
- }
599
- }
600
603
  updateEditorBorderColor() {
601
604
  const level = this.agent.state.thinkingLevel || "off";
602
- const color = this.getThinkingBorderColor(level);
603
- this.editor.borderColor = color;
605
+ this.editor.borderColor = theme.getThinkingBorderColor(level);
604
606
  this.ui.requestRender();
605
607
  }
606
608
  cycleThinkingLevel() {
607
609
  // Only cycle if model supports thinking
608
610
  if (!this.agent.state.model?.reasoning) {
609
611
  this.chatContainer.addChild(new Spacer(1));
610
- this.chatContainer.addChild(new Text(chalk.dim("Current model does not support thinking"), 1, 0));
612
+ this.chatContainer.addChild(new Text(theme.fg("dim", "Current model does not support thinking"), 1, 0));
611
613
  this.ui.requestRender();
612
614
  return;
613
615
  }
@@ -624,7 +626,7 @@ export class TuiRenderer {
624
626
  this.updateEditorBorderColor();
625
627
  // Show brief notification
626
628
  this.chatContainer.addChild(new Spacer(1));
627
- this.chatContainer.addChild(new Text(chalk.dim(`Thinking level: ${nextLevel}`), 1, 0));
629
+ this.chatContainer.addChild(new Text(theme.fg("dim", `Thinking level: ${nextLevel}`), 1, 0));
628
630
  this.ui.requestRender();
629
631
  }
630
632
  async cycleModel() {
@@ -647,7 +649,7 @@ export class TuiRenderer {
647
649
  }
648
650
  if (modelsToUse.length === 1) {
649
651
  this.chatContainer.addChild(new Spacer(1));
650
- this.chatContainer.addChild(new Text(chalk.dim("Only one model in scope"), 1, 0));
652
+ this.chatContainer.addChild(new Text(theme.fg("dim", "Only one model in scope"), 1, 0));
651
653
  this.ui.requestRender();
652
654
  return;
653
655
  }
@@ -669,7 +671,7 @@ export class TuiRenderer {
669
671
  this.agent.setModel(nextModel);
670
672
  // Show notification
671
673
  this.chatContainer.addChild(new Spacer(1));
672
- this.chatContainer.addChild(new Text(chalk.dim(`Switched to ${nextModel.name || nextModel.id}`), 1, 0));
674
+ this.chatContainer.addChild(new Text(theme.fg("dim", `Switched to ${nextModel.name || nextModel.id}`), 1, 0));
673
675
  this.ui.requestRender();
674
676
  }
675
677
  toggleToolOutputExpansion() {
@@ -689,13 +691,13 @@ export class TuiRenderer {
689
691
  showError(errorMessage) {
690
692
  // Show error message in the chat
691
693
  this.chatContainer.addChild(new Spacer(1));
692
- this.chatContainer.addChild(new Text(chalk.red(`Error: ${errorMessage}`), 1, 0));
694
+ this.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage}`), 1, 0));
693
695
  this.ui.requestRender();
694
696
  }
695
697
  showWarning(warningMessage) {
696
698
  // Show warning message in the chat
697
699
  this.chatContainer.addChild(new Spacer(1));
698
- this.chatContainer.addChild(new Text(chalk.yellow(`Warning: ${warningMessage}`), 1, 0));
700
+ this.chatContainer.addChild(new Text(theme.fg("warning", `Warning: ${warningMessage}`), 1, 0));
699
701
  this.ui.requestRender();
700
702
  }
701
703
  showThinkingSelector() {
@@ -709,7 +711,7 @@ export class TuiRenderer {
709
711
  this.updateEditorBorderColor();
710
712
  // Show confirmation message with proper spacing
711
713
  this.chatContainer.addChild(new Spacer(1));
712
- const confirmText = new Text(chalk.dim(`Thinking level: ${level}`), 1, 0);
714
+ const confirmText = new Text(theme.fg("dim", `Thinking level: ${level}`), 1, 0);
713
715
  this.chatContainer.addChild(confirmText);
714
716
  // Hide selector and show editor again
715
717
  this.hideThinkingSelector();
@@ -741,7 +743,7 @@ export class TuiRenderer {
741
743
  this.settingsManager.setQueueMode(mode);
742
744
  // Show confirmation message with proper spacing
743
745
  this.chatContainer.addChild(new Spacer(1));
744
- const confirmText = new Text(chalk.dim(`Queue mode: ${mode}`), 1, 0);
746
+ const confirmText = new Text(theme.fg("dim", `Queue mode: ${mode}`), 1, 0);
745
747
  this.chatContainer.addChild(confirmText);
746
748
  // Hide selector and show editor again
747
749
  this.hideQueueModeSelector();
@@ -764,6 +766,56 @@ export class TuiRenderer {
764
766
  this.queueModeSelector = null;
765
767
  this.ui.setFocus(this.editor);
766
768
  }
769
+ showThemeSelector() {
770
+ // Get current theme from settings
771
+ const currentTheme = this.settingsManager.getTheme() || "dark";
772
+ // Create theme selector
773
+ this.themeSelector = new ThemeSelectorComponent(currentTheme, (themeName) => {
774
+ // Apply the selected theme
775
+ const result = setTheme(themeName);
776
+ // Save theme to settings
777
+ this.settingsManager.setTheme(themeName);
778
+ // Invalidate all components to clear cached rendering
779
+ this.ui.invalidate();
780
+ // Show confirmation or error message
781
+ this.chatContainer.addChild(new Spacer(1));
782
+ if (result.success) {
783
+ const confirmText = new Text(theme.fg("dim", `Theme: ${themeName}`), 1, 0);
784
+ this.chatContainer.addChild(confirmText);
785
+ }
786
+ else {
787
+ const errorText = new Text(theme.fg("error", `Failed to load theme "${themeName}": ${result.error}\nFell back to dark theme.`), 1, 0);
788
+ this.chatContainer.addChild(errorText);
789
+ }
790
+ // Hide selector and show editor again
791
+ this.hideThemeSelector();
792
+ this.ui.requestRender();
793
+ }, () => {
794
+ // Just hide the selector
795
+ this.hideThemeSelector();
796
+ this.ui.requestRender();
797
+ }, (themeName) => {
798
+ // Preview theme on selection change
799
+ const result = setTheme(themeName);
800
+ if (result.success) {
801
+ this.ui.invalidate();
802
+ this.ui.requestRender();
803
+ }
804
+ // If failed, theme already fell back to dark, just don't re-render
805
+ });
806
+ // Replace editor with selector
807
+ this.editorContainer.clear();
808
+ this.editorContainer.addChild(this.themeSelector);
809
+ this.ui.setFocus(this.themeSelector.getSelectList());
810
+ this.ui.requestRender();
811
+ }
812
+ hideThemeSelector() {
813
+ // Replace selector with editor in the container
814
+ this.editorContainer.clear();
815
+ this.editorContainer.addChild(this.editor);
816
+ this.themeSelector = null;
817
+ this.ui.setFocus(this.editor);
818
+ }
767
819
  showModelSelector() {
768
820
  // Create model selector with current model
769
821
  this.modelSelector = new ModelSelectorComponent(this.ui, this.agent.state.model, this.settingsManager, (model) => {
@@ -773,7 +825,7 @@ export class TuiRenderer {
773
825
  this.sessionManager.saveModelChange(model.provider, model.id);
774
826
  // Show confirmation message with proper spacing
775
827
  this.chatContainer.addChild(new Spacer(1));
776
- const confirmText = new Text(chalk.dim(`Model: ${model.id}`), 1, 0);
828
+ const confirmText = new Text(theme.fg("dim", `Model: ${model.id}`), 1, 0);
777
829
  this.chatContainer.addChild(confirmText);
778
830
  // Hide selector and show editor again
779
831
  this.hideModelSelector();
@@ -813,7 +865,7 @@ export class TuiRenderer {
813
865
  // Don't show selector if there are no messages or only one message
814
866
  if (userMessages.length <= 1) {
815
867
  this.chatContainer.addChild(new Spacer(1));
816
- this.chatContainer.addChild(new Text(chalk.dim("No messages to branch from"), 1, 0));
868
+ this.chatContainer.addChild(new Text(theme.fg("dim", "No messages to branch from"), 1, 0));
817
869
  this.ui.requestRender();
818
870
  return;
819
871
  }
@@ -837,7 +889,7 @@ export class TuiRenderer {
837
889
  this.renderInitialMessages(this.agent.state);
838
890
  // Show confirmation message
839
891
  this.chatContainer.addChild(new Spacer(1));
840
- this.chatContainer.addChild(new Text(chalk.dim(`Branched to new session from message ${messageIndex}`), 1, 0));
892
+ this.chatContainer.addChild(new Text(theme.fg("dim", `Branched to new session from message ${messageIndex}`), 1, 0));
841
893
  // Put the selected message in the editor
842
894
  this.editor.setText(selectedText);
843
895
  // Hide selector and show editor again
@@ -868,7 +920,7 @@ export class TuiRenderer {
868
920
  const loggedInProviders = listOAuthProviders();
869
921
  if (loggedInProviders.length === 0) {
870
922
  this.chatContainer.addChild(new Spacer(1));
871
- this.chatContainer.addChild(new Text(chalk.dim("No OAuth providers logged in. Use /login first."), 1, 0));
923
+ this.chatContainer.addChild(new Text(theme.fg("dim", "No OAuth providers logged in. Use /login first."), 1, 0));
872
924
  this.ui.requestRender();
873
925
  return;
874
926
  }
@@ -881,16 +933,16 @@ export class TuiRenderer {
881
933
  if (mode === "login") {
882
934
  // Handle login
883
935
  this.chatContainer.addChild(new Spacer(1));
884
- this.chatContainer.addChild(new Text(chalk.dim(`Logging in to ${providerId}...`), 1, 0));
936
+ this.chatContainer.addChild(new Text(theme.fg("dim", `Logging in to ${providerId}...`), 1, 0));
885
937
  this.ui.requestRender();
886
938
  try {
887
939
  await login(providerId, (url) => {
888
940
  // Show auth URL to user
889
941
  this.chatContainer.addChild(new Spacer(1));
890
- this.chatContainer.addChild(new Text(chalk.cyan("Opening browser to:"), 1, 0));
891
- this.chatContainer.addChild(new Text(chalk.cyan(url), 1, 0));
942
+ this.chatContainer.addChild(new Text(theme.fg("accent", "Opening browser to:"), 1, 0));
943
+ this.chatContainer.addChild(new Text(theme.fg("accent", url), 1, 0));
892
944
  this.chatContainer.addChild(new Spacer(1));
893
- this.chatContainer.addChild(new Text(chalk.yellow("Paste the authorization code below:"), 1, 0));
945
+ this.chatContainer.addChild(new Text(theme.fg("warning", "Paste the authorization code below:"), 1, 0));
894
946
  this.ui.requestRender();
895
947
  // Open URL in browser
896
948
  const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
@@ -915,8 +967,8 @@ export class TuiRenderer {
915
967
  });
916
968
  // Success
917
969
  this.chatContainer.addChild(new Spacer(1));
918
- this.chatContainer.addChild(new Text(chalk.green(`✓ Successfully logged in to ${providerId}`), 1, 0));
919
- this.chatContainer.addChild(new Text(chalk.dim(`Tokens saved to ~/.pi/agent/oauth.json`), 1, 0));
970
+ this.chatContainer.addChild(new Text(theme.fg("success", `✓ Successfully logged in to ${providerId}`), 1, 0));
971
+ this.chatContainer.addChild(new Text(theme.fg("dim", `Tokens saved to ~/.pi/agent/oauth.json`), 1, 0));
920
972
  this.ui.requestRender();
921
973
  }
922
974
  catch (error) {
@@ -928,8 +980,8 @@ export class TuiRenderer {
928
980
  try {
929
981
  await logout(providerId);
930
982
  this.chatContainer.addChild(new Spacer(1));
931
- this.chatContainer.addChild(new Text(chalk.green(`✓ Successfully logged out of ${providerId}`), 1, 0));
932
- this.chatContainer.addChild(new Text(chalk.dim(`Credentials removed from ~/.pi/agent/oauth.json`), 1, 0));
983
+ this.chatContainer.addChild(new Text(theme.fg("success", `✓ Successfully logged out of ${providerId}`), 1, 0));
984
+ this.chatContainer.addChild(new Text(theme.fg("dim", `Credentials removed from ~/.pi/agent/oauth.json`), 1, 0));
933
985
  this.ui.requestRender();
934
986
  }
935
987
  catch (error) {
@@ -963,13 +1015,13 @@ export class TuiRenderer {
963
1015
  const filePath = exportSessionToHtml(this.sessionManager, this.agent.state, outputPath);
964
1016
  // Show success message in chat - matching thinking level style
965
1017
  this.chatContainer.addChild(new Spacer(1));
966
- this.chatContainer.addChild(new Text(chalk.dim(`Session exported to: ${filePath}`), 1, 0));
1018
+ this.chatContainer.addChild(new Text(theme.fg("dim", `Session exported to: ${filePath}`), 1, 0));
967
1019
  this.ui.requestRender();
968
1020
  }
969
1021
  catch (error) {
970
1022
  // Show error message in chat
971
1023
  this.chatContainer.addChild(new Spacer(1));
972
- this.chatContainer.addChild(new Text(chalk.red(`Failed to export session: ${error.message || "Unknown error"}`), 1, 0));
1024
+ this.chatContainer.addChild(new Text(theme.fg("error", `Failed to export session: ${error.message || "Unknown error"}`), 1, 0));
973
1025
  this.ui.requestRender();
974
1026
  }
975
1027
  }
@@ -1008,28 +1060,28 @@ export class TuiRenderer {
1008
1060
  }
1009
1061
  const totalTokens = totalInput + totalOutput + totalCacheRead + totalCacheWrite;
1010
1062
  // Build info text
1011
- let info = `${chalk.bold("Session Info")}\n\n`;
1012
- info += `${chalk.dim("File:")} ${sessionFile}\n`;
1013
- info += `${chalk.dim("ID:")} ${this.sessionManager.getSessionId()}\n\n`;
1014
- info += `${chalk.bold("Messages")}\n`;
1015
- info += `${chalk.dim("User:")} ${userMessages}\n`;
1016
- info += `${chalk.dim("Assistant:")} ${assistantMessages}\n`;
1017
- info += `${chalk.dim("Tool Calls:")} ${toolCalls}\n`;
1018
- info += `${chalk.dim("Tool Results:")} ${toolResults}\n`;
1019
- info += `${chalk.dim("Total:")} ${totalMessages}\n\n`;
1020
- info += `${chalk.bold("Tokens")}\n`;
1021
- info += `${chalk.dim("Input:")} ${totalInput.toLocaleString()}\n`;
1022
- info += `${chalk.dim("Output:")} ${totalOutput.toLocaleString()}\n`;
1063
+ let info = `${theme.bold("Session Info")}\n\n`;
1064
+ info += `${theme.fg("dim", "File:")} ${sessionFile}\n`;
1065
+ info += `${theme.fg("dim", "ID:")} ${this.sessionManager.getSessionId()}\n\n`;
1066
+ info += `${theme.bold("Messages")}\n`;
1067
+ info += `${theme.fg("dim", "User:")} ${userMessages}\n`;
1068
+ info += `${theme.fg("dim", "Assistant:")} ${assistantMessages}\n`;
1069
+ info += `${theme.fg("dim", "Tool Calls:")} ${toolCalls}\n`;
1070
+ info += `${theme.fg("dim", "Tool Results:")} ${toolResults}\n`;
1071
+ info += `${theme.fg("dim", "Total:")} ${totalMessages}\n\n`;
1072
+ info += `${theme.bold("Tokens")}\n`;
1073
+ info += `${theme.fg("dim", "Input:")} ${totalInput.toLocaleString()}\n`;
1074
+ info += `${theme.fg("dim", "Output:")} ${totalOutput.toLocaleString()}\n`;
1023
1075
  if (totalCacheRead > 0) {
1024
- info += `${chalk.dim("Cache Read:")} ${totalCacheRead.toLocaleString()}\n`;
1076
+ info += `${theme.fg("dim", "Cache Read:")} ${totalCacheRead.toLocaleString()}\n`;
1025
1077
  }
1026
1078
  if (totalCacheWrite > 0) {
1027
- info += `${chalk.dim("Cache Write:")} ${totalCacheWrite.toLocaleString()}\n`;
1079
+ info += `${theme.fg("dim", "Cache Write:")} ${totalCacheWrite.toLocaleString()}\n`;
1028
1080
  }
1029
- info += `${chalk.dim("Total:")} ${totalTokens.toLocaleString()}\n`;
1081
+ info += `${theme.fg("dim", "Total:")} ${totalTokens.toLocaleString()}\n`;
1030
1082
  if (totalCost > 0) {
1031
- info += `\n${chalk.bold("Cost")}\n`;
1032
- info += `${chalk.dim("Total:")} ${totalCost.toFixed(4)}`;
1083
+ info += `\n${theme.bold("Cost")}\n`;
1084
+ info += `${theme.fg("dim", "Total:")} ${totalCost.toFixed(4)}`;
1033
1085
  }
1034
1086
  // Show info in chat
1035
1087
  this.chatContainer.addChild(new Spacer(1));
@@ -1048,11 +1100,11 @@ export class TuiRenderer {
1048
1100
  : "No changelog entries found.";
1049
1101
  // Display in chat
1050
1102
  this.chatContainer.addChild(new Spacer(1));
1051
- this.chatContainer.addChild(new DynamicBorder(chalk.cyan));
1052
- this.ui.addChild(new Text(chalk.bold.cyan("What's New"), 1, 0));
1103
+ this.chatContainer.addChild(new DynamicBorder());
1104
+ this.ui.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
1053
1105
  this.ui.addChild(new Spacer(1));
1054
- this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1));
1055
- this.chatContainer.addChild(new DynamicBorder(chalk.cyan));
1106
+ this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, getMarkdownTheme()));
1107
+ this.chatContainer.addChild(new DynamicBorder());
1056
1108
  this.ui.requestRender();
1057
1109
  }
1058
1110
  updatePendingMessagesDisplay() {
@@ -1060,7 +1112,7 @@ export class TuiRenderer {
1060
1112
  if (this.queuedMessages.length > 0) {
1061
1113
  this.pendingMessagesContainer.addChild(new Spacer(1));
1062
1114
  for (const message of this.queuedMessages) {
1063
- const queuedText = chalk.dim("Queued: " + message);
1115
+ const queuedText = theme.fg("dim", "Queued: " + message);
1064
1116
  this.pendingMessagesContainer.addChild(new TruncatedText(queuedText, 1, 0));
1065
1117
  }
1066
1118
  }