@mariozechner/pi-coding-agent 0.7.28 → 0.8.1

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 (66) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +74 -4
  3. package/dist/main.d.ts.map +1 -1
  4. package/dist/main.js +18 -9
  5. package/dist/main.js.map +1 -1
  6. package/dist/session-manager.d.ts.map +1 -1
  7. package/dist/session-manager.js +1 -1
  8. package/dist/session-manager.js.map +1 -1
  9. package/dist/settings-manager.d.ts +3 -0
  10. package/dist/settings-manager.d.ts.map +1 -1
  11. package/dist/settings-manager.js +7 -0
  12. package/dist/settings-manager.js.map +1 -1
  13. package/dist/theme/dark.json +70 -0
  14. package/dist/theme/light.json +69 -0
  15. package/dist/theme/theme-schema.json +246 -0
  16. package/dist/theme/theme.d.ts +33 -0
  17. package/dist/theme/theme.d.ts.map +1 -0
  18. package/dist/theme/theme.js +470 -0
  19. package/dist/theme/theme.js.map +1 -0
  20. package/dist/tui/assistant-message.d.ts.map +1 -1
  21. package/dist/tui/assistant-message.js +7 -7
  22. package/dist/tui/assistant-message.js.map +1 -1
  23. package/dist/tui/dynamic-border.d.ts +1 -0
  24. package/dist/tui/dynamic-border.d.ts.map +1 -1
  25. package/dist/tui/dynamic-border.js +5 -2
  26. package/dist/tui/dynamic-border.js.map +1 -1
  27. package/dist/tui/footer.d.ts +3 -1
  28. package/dist/tui/footer.d.ts.map +1 -1
  29. package/dist/tui/footer.js +20 -5
  30. package/dist/tui/footer.js.map +1 -1
  31. package/dist/tui/model-selector.d.ts.map +1 -1
  32. package/dist/tui/model-selector.js +14 -13
  33. package/dist/tui/model-selector.js.map +1 -1
  34. package/dist/tui/oauth-selector.d.ts.map +1 -1
  35. package/dist/tui/oauth-selector.js +9 -8
  36. package/dist/tui/oauth-selector.js.map +1 -1
  37. package/dist/tui/queue-mode-selector.d.ts.map +1 -1
  38. package/dist/tui/queue-mode-selector.js +3 -10
  39. package/dist/tui/queue-mode-selector.js.map +1 -1
  40. package/dist/tui/session-selector.d.ts +1 -0
  41. package/dist/tui/session-selector.d.ts.map +1 -1
  42. package/dist/tui/session-selector.js +11 -15
  43. package/dist/tui/session-selector.js.map +1 -1
  44. package/dist/tui/theme-selector.d.ts +11 -0
  45. package/dist/tui/theme-selector.d.ts.map +1 -0
  46. package/dist/tui/theme-selector.js +46 -0
  47. package/dist/tui/theme-selector.js.map +1 -0
  48. package/dist/tui/thinking-selector.d.ts.map +1 -1
  49. package/dist/tui/thinking-selector.js +3 -10
  50. package/dist/tui/thinking-selector.js.map +1 -1
  51. package/dist/tui/tool-execution.d.ts.map +1 -1
  52. package/dist/tui/tool-execution.js +36 -109
  53. package/dist/tui/tool-execution.js.map +1 -1
  54. package/dist/tui/tui-renderer.d.ts +3 -1
  55. package/dist/tui/tui-renderer.d.ts.map +1 -1
  56. package/dist/tui/tui-renderer.js +146 -94
  57. package/dist/tui/tui-renderer.js.map +1 -1
  58. package/dist/tui/user-message-selector.d.ts +1 -0
  59. package/dist/tui/user-message-selector.d.ts.map +1 -1
  60. package/dist/tui/user-message-selector.js +12 -20
  61. package/dist/tui/user-message-selector.js.map +1 -1
  62. package/dist/tui/user-message.d.ts +0 -1
  63. package/dist/tui/user-message.d.ts.map +1 -1
  64. package/dist/tui/user-message.js +5 -4
  65. package/dist/tui/user-message.js.map +1 -1
  66. package/package.json +6 -4
@@ -0,0 +1,70 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/theme-schema.json",
3
+ "name": "dark",
4
+ "vars": {
5
+ "cyan": "#00d7ff",
6
+ "blue": "#5f87ff",
7
+ "green": "#b5bd68",
8
+ "red": "#cc6666",
9
+ "yellow": "#ffff00",
10
+ "gray": "#808080",
11
+ "dimGray": "#666666",
12
+ "darkGray": "#303030",
13
+ "accent": "#8abeb7",
14
+ "userMsgBg": "#343541",
15
+ "toolPendingBg": "#282832",
16
+ "toolSuccessBg": "#283228",
17
+ "toolErrorBg": "#3c2828"
18
+ },
19
+ "colors": {
20
+ "accent": "accent",
21
+ "border": "blue",
22
+ "borderAccent": "cyan",
23
+ "borderMuted": "darkGray",
24
+ "success": "green",
25
+ "error": "red",
26
+ "warning": "yellow",
27
+ "muted": "gray",
28
+ "dim": "dimGray",
29
+ "text": "",
30
+
31
+ "userMessageBg": "userMsgBg",
32
+ "userMessageText": "",
33
+ "toolPendingBg": "toolPendingBg",
34
+ "toolSuccessBg": "toolSuccessBg",
35
+ "toolErrorBg": "toolErrorBg",
36
+ "toolTitle": "",
37
+ "toolOutput": "gray",
38
+
39
+ "mdHeading": "#f0c674",
40
+ "mdLink": "#81a2be",
41
+ "mdLinkUrl": "dimGray",
42
+ "mdCode": "accent",
43
+ "mdCodeBlock": "green",
44
+ "mdCodeBlockBorder": "gray",
45
+ "mdQuote": "gray",
46
+ "mdQuoteBorder": "gray",
47
+ "mdHr": "gray",
48
+ "mdListBullet": "accent",
49
+
50
+ "toolDiffAdded": "green",
51
+ "toolDiffRemoved": "red",
52
+ "toolDiffContext": "gray",
53
+
54
+ "syntaxComment": "gray",
55
+ "syntaxKeyword": "cyan",
56
+ "syntaxFunction": "blue",
57
+ "syntaxVariable": "",
58
+ "syntaxString": "green",
59
+ "syntaxNumber": "yellow",
60
+ "syntaxType": "cyan",
61
+ "syntaxOperator": "",
62
+ "syntaxPunctuation": "gray",
63
+
64
+ "thinkingOff": "darkGray",
65
+ "thinkingMinimal": "#4e4e4e",
66
+ "thinkingLow": "#5f87af",
67
+ "thinkingMedium": "#81a2be",
68
+ "thinkingHigh": "#b294bb"
69
+ }
70
+ }
@@ -0,0 +1,69 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/theme-schema.json",
3
+ "name": "light",
4
+ "vars": {
5
+ "teal": "#5f8787",
6
+ "blue": "#5f87af",
7
+ "green": "#87af87",
8
+ "red": "#af5f5f",
9
+ "yellow": "#d7af5f",
10
+ "mediumGray": "#6c6c6c",
11
+ "dimGray": "#8a8a8a",
12
+ "lightGray": "#b0b0b0",
13
+ "userMsgBg": "#e8e8e8",
14
+ "toolPendingBg": "#e8e8f0",
15
+ "toolSuccessBg": "#e8f0e8",
16
+ "toolErrorBg": "#f0e8e8"
17
+ },
18
+ "colors": {
19
+ "accent": "teal",
20
+ "border": "blue",
21
+ "borderAccent": "teal",
22
+ "borderMuted": "lightGray",
23
+ "success": "green",
24
+ "error": "red",
25
+ "warning": "yellow",
26
+ "muted": "mediumGray",
27
+ "dim": "dimGray",
28
+ "text": "",
29
+
30
+ "userMessageBg": "userMsgBg",
31
+ "userMessageText": "",
32
+ "toolPendingBg": "toolPendingBg",
33
+ "toolSuccessBg": "toolSuccessBg",
34
+ "toolErrorBg": "toolErrorBg",
35
+ "toolTitle": "",
36
+ "toolOutput": "mediumGray",
37
+
38
+ "mdHeading": "yellow",
39
+ "mdLink": "blue",
40
+ "mdLinkUrl": "dimGray",
41
+ "mdCode": "teal",
42
+ "mdCodeBlock": "green",
43
+ "mdCodeBlockBorder": "mediumGray",
44
+ "mdQuote": "mediumGray",
45
+ "mdQuoteBorder": "mediumGray",
46
+ "mdHr": "mediumGray",
47
+ "mdListBullet": "green",
48
+
49
+ "toolDiffAdded": "green",
50
+ "toolDiffRemoved": "red",
51
+ "toolDiffContext": "mediumGray",
52
+
53
+ "syntaxComment": "mediumGray",
54
+ "syntaxKeyword": "teal",
55
+ "syntaxFunction": "blue",
56
+ "syntaxVariable": "",
57
+ "syntaxString": "green",
58
+ "syntaxNumber": "yellow",
59
+ "syntaxType": "teal",
60
+ "syntaxOperator": "",
61
+ "syntaxPunctuation": "mediumGray",
62
+
63
+ "thinkingOff": "lightGray",
64
+ "thinkingMinimal": "#9e9e9e",
65
+ "thinkingLow": "#5f87af",
66
+ "thinkingMedium": "#5f8787",
67
+ "thinkingHigh": "#875f87"
68
+ }
69
+ }
@@ -0,0 +1,246 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "title": "Pi Coding Agent Theme",
4
+ "description": "Theme schema for Pi coding agent",
5
+ "type": "object",
6
+ "required": ["name", "colors"],
7
+ "properties": {
8
+ "$schema": {
9
+ "type": "string",
10
+ "description": "JSON schema reference"
11
+ },
12
+ "name": {
13
+ "type": "string",
14
+ "description": "Theme name"
15
+ },
16
+ "vars": {
17
+ "type": "object",
18
+ "description": "Reusable color variables",
19
+ "additionalProperties": {
20
+ "oneOf": [
21
+ {
22
+ "type": "string",
23
+ "description": "Hex color (#RRGGBB), variable reference, or empty string for terminal default"
24
+ },
25
+ {
26
+ "type": "integer",
27
+ "minimum": 0,
28
+ "maximum": 255,
29
+ "description": "256-color palette index (0-255)"
30
+ }
31
+ ]
32
+ }
33
+ },
34
+ "colors": {
35
+ "type": "object",
36
+ "description": "Theme color definitions (all required)",
37
+ "required": [
38
+ "accent",
39
+ "border",
40
+ "borderAccent",
41
+ "borderMuted",
42
+ "success",
43
+ "error",
44
+ "warning",
45
+ "muted",
46
+ "dim",
47
+ "text",
48
+ "userMessageBg",
49
+ "userMessageText",
50
+ "toolPendingBg",
51
+ "toolSuccessBg",
52
+ "toolErrorBg",
53
+ "toolText",
54
+ "mdHeading",
55
+ "mdLink",
56
+ "mdCode",
57
+ "mdCodeBlock",
58
+ "mdCodeBlockBorder",
59
+ "mdQuote",
60
+ "mdQuoteBorder",
61
+ "mdHr",
62
+ "mdListBullet",
63
+ "toolDiffAdded",
64
+ "toolDiffRemoved",
65
+ "toolDiffContext",
66
+ "syntaxComment",
67
+ "syntaxKeyword",
68
+ "syntaxFunction",
69
+ "syntaxVariable",
70
+ "syntaxString",
71
+ "syntaxNumber",
72
+ "syntaxType",
73
+ "syntaxOperator",
74
+ "syntaxPunctuation"
75
+ ],
76
+ "properties": {
77
+ "accent": {
78
+ "$ref": "#/$defs/colorValue",
79
+ "description": "Primary accent color (logo, selected items, cursor)"
80
+ },
81
+ "border": {
82
+ "$ref": "#/$defs/colorValue",
83
+ "description": "Normal borders"
84
+ },
85
+ "borderAccent": {
86
+ "$ref": "#/$defs/colorValue",
87
+ "description": "Highlighted borders"
88
+ },
89
+ "borderMuted": {
90
+ "$ref": "#/$defs/colorValue",
91
+ "description": "Subtle borders"
92
+ },
93
+ "success": {
94
+ "$ref": "#/$defs/colorValue",
95
+ "description": "Success states"
96
+ },
97
+ "error": {
98
+ "$ref": "#/$defs/colorValue",
99
+ "description": "Error states"
100
+ },
101
+ "warning": {
102
+ "$ref": "#/$defs/colorValue",
103
+ "description": "Warning states"
104
+ },
105
+ "muted": {
106
+ "$ref": "#/$defs/colorValue",
107
+ "description": "Secondary/dimmed text"
108
+ },
109
+ "dim": {
110
+ "$ref": "#/$defs/colorValue",
111
+ "description": "Very dimmed text (more subtle than muted)"
112
+ },
113
+ "text": {
114
+ "$ref": "#/$defs/colorValue",
115
+ "description": "Default text color (usually empty string)"
116
+ },
117
+ "userMessageBg": {
118
+ "$ref": "#/$defs/colorValue",
119
+ "description": "User message background"
120
+ },
121
+ "userMessageText": {
122
+ "$ref": "#/$defs/colorValue",
123
+ "description": "User message text color"
124
+ },
125
+ "toolPendingBg": {
126
+ "$ref": "#/$defs/colorValue",
127
+ "description": "Tool execution box (pending state)"
128
+ },
129
+ "toolSuccessBg": {
130
+ "$ref": "#/$defs/colorValue",
131
+ "description": "Tool execution box (success state)"
132
+ },
133
+ "toolErrorBg": {
134
+ "$ref": "#/$defs/colorValue",
135
+ "description": "Tool execution box (error state)"
136
+ },
137
+ "toolText": {
138
+ "$ref": "#/$defs/colorValue",
139
+ "description": "Tool execution box text color"
140
+ },
141
+ "mdHeading": {
142
+ "$ref": "#/$defs/colorValue",
143
+ "description": "Markdown heading text"
144
+ },
145
+ "mdLink": {
146
+ "$ref": "#/$defs/colorValue",
147
+ "description": "Markdown link text"
148
+ },
149
+ "mdCode": {
150
+ "$ref": "#/$defs/colorValue",
151
+ "description": "Markdown inline code"
152
+ },
153
+ "mdCodeBlock": {
154
+ "$ref": "#/$defs/colorValue",
155
+ "description": "Markdown code block content"
156
+ },
157
+ "mdCodeBlockBorder": {
158
+ "$ref": "#/$defs/colorValue",
159
+ "description": "Markdown code block fences"
160
+ },
161
+ "mdQuote": {
162
+ "$ref": "#/$defs/colorValue",
163
+ "description": "Markdown blockquote text"
164
+ },
165
+ "mdQuoteBorder": {
166
+ "$ref": "#/$defs/colorValue",
167
+ "description": "Markdown blockquote border"
168
+ },
169
+ "mdHr": {
170
+ "$ref": "#/$defs/colorValue",
171
+ "description": "Markdown horizontal rule"
172
+ },
173
+ "mdListBullet": {
174
+ "$ref": "#/$defs/colorValue",
175
+ "description": "Markdown list bullets/numbers"
176
+ },
177
+ "toolDiffAdded": {
178
+ "$ref": "#/$defs/colorValue",
179
+ "description": "Added lines in tool diffs"
180
+ },
181
+ "toolDiffRemoved": {
182
+ "$ref": "#/$defs/colorValue",
183
+ "description": "Removed lines in tool diffs"
184
+ },
185
+ "toolDiffContext": {
186
+ "$ref": "#/$defs/colorValue",
187
+ "description": "Context lines in tool diffs"
188
+ },
189
+ "syntaxComment": {
190
+ "$ref": "#/$defs/colorValue",
191
+ "description": "Syntax highlighting: comments"
192
+ },
193
+ "syntaxKeyword": {
194
+ "$ref": "#/$defs/colorValue",
195
+ "description": "Syntax highlighting: keywords"
196
+ },
197
+ "syntaxFunction": {
198
+ "$ref": "#/$defs/colorValue",
199
+ "description": "Syntax highlighting: function names"
200
+ },
201
+ "syntaxVariable": {
202
+ "$ref": "#/$defs/colorValue",
203
+ "description": "Syntax highlighting: variable names"
204
+ },
205
+ "syntaxString": {
206
+ "$ref": "#/$defs/colorValue",
207
+ "description": "Syntax highlighting: string literals"
208
+ },
209
+ "syntaxNumber": {
210
+ "$ref": "#/$defs/colorValue",
211
+ "description": "Syntax highlighting: number literals"
212
+ },
213
+ "syntaxType": {
214
+ "$ref": "#/$defs/colorValue",
215
+ "description": "Syntax highlighting: type names"
216
+ },
217
+ "syntaxOperator": {
218
+ "$ref": "#/$defs/colorValue",
219
+ "description": "Syntax highlighting: operators"
220
+ },
221
+ "syntaxPunctuation": {
222
+ "$ref": "#/$defs/colorValue",
223
+ "description": "Syntax highlighting: punctuation"
224
+ }
225
+ },
226
+ "additionalProperties": false
227
+ }
228
+ },
229
+ "additionalProperties": false,
230
+ "$defs": {
231
+ "colorValue": {
232
+ "oneOf": [
233
+ {
234
+ "type": "string",
235
+ "description": "Hex color (#RRGGBB), variable reference, or empty string for terminal default"
236
+ },
237
+ {
238
+ "type": "integer",
239
+ "minimum": 0,
240
+ "maximum": 255,
241
+ "description": "256-color palette index (0-255)"
242
+ }
243
+ ]
244
+ }
245
+ }
246
+ }
@@ -0,0 +1,33 @@
1
+ import type { EditorTheme, MarkdownTheme, SelectListTheme } from "@mariozechner/pi-tui";
2
+ export type ThemeColor = "accent" | "border" | "borderAccent" | "borderMuted" | "success" | "error" | "warning" | "muted" | "dim" | "text" | "userMessageText" | "toolTitle" | "toolOutput" | "mdHeading" | "mdLink" | "mdLinkUrl" | "mdCode" | "mdCodeBlock" | "mdCodeBlockBorder" | "mdQuote" | "mdQuoteBorder" | "mdHr" | "mdListBullet" | "toolDiffAdded" | "toolDiffRemoved" | "toolDiffContext" | "syntaxComment" | "syntaxKeyword" | "syntaxFunction" | "syntaxVariable" | "syntaxString" | "syntaxNumber" | "syntaxType" | "syntaxOperator" | "syntaxPunctuation" | "thinkingOff" | "thinkingMinimal" | "thinkingLow" | "thinkingMedium" | "thinkingHigh";
3
+ export type ThemeBg = "userMessageBg" | "toolPendingBg" | "toolSuccessBg" | "toolErrorBg";
4
+ type ColorMode = "truecolor" | "256color";
5
+ export declare class Theme {
6
+ private fgColors;
7
+ private bgColors;
8
+ private mode;
9
+ constructor(fgColors: Record<ThemeColor, string | number>, bgColors: Record<ThemeBg, string | number>, mode: ColorMode);
10
+ fg(color: ThemeColor, text: string): string;
11
+ bg(color: ThemeBg, text: string): string;
12
+ bold(text: string): string;
13
+ italic(text: string): string;
14
+ underline(text: string): string;
15
+ getFgAnsi(color: ThemeColor): string;
16
+ getBgAnsi(color: ThemeBg): string;
17
+ getColorMode(): ColorMode;
18
+ getThinkingBorderColor(level: "off" | "minimal" | "low" | "medium" | "high"): (str: string) => string;
19
+ }
20
+ export declare function getAvailableThemes(): string[];
21
+ export declare let theme: Theme;
22
+ export declare function initTheme(themeName?: string): void;
23
+ export declare function setTheme(name: string): {
24
+ success: boolean;
25
+ error?: string;
26
+ };
27
+ export declare function onThemeChange(callback: () => void): void;
28
+ export declare function stopThemeWatcher(): void;
29
+ export declare function getMarkdownTheme(): MarkdownTheme;
30
+ export declare function getSelectListTheme(): SelectListTheme;
31
+ export declare function getEditorTheme(): EditorTheme;
32
+ export {};
33
+ //# sourceMappingURL=theme.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/theme/theme.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAgFxF,MAAM,MAAM,UAAU,GACnB,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,aAAa,GACb,SAAS,GACT,OAAO,GACP,SAAS,GACT,OAAO,GACP,KAAK,GACL,MAAM,GACN,iBAAiB,GACjB,WAAW,GACX,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,WAAW,GACX,QAAQ,GACR,aAAa,GACb,mBAAmB,GACnB,SAAS,GACT,eAAe,GACf,MAAM,GACN,cAAc,GACd,eAAe,GACf,iBAAiB,GACjB,iBAAiB,GACjB,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,GACd,cAAc,GACd,YAAY,GACZ,gBAAgB,GAChB,mBAAmB,GACnB,aAAa,GACb,iBAAiB,GACjB,aAAa,GACb,gBAAgB,GAChB,cAAc,CAAC;AAElB,MAAM,MAAM,OAAO,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,GAAG,aAAa,CAAC;AAE1F,KAAK,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AA2G1C,qBAAa,KAAK;IACjB,OAAO,CAAC,QAAQ,CAA0B;IAC1C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,IAAI,CAAY;IAExB,YACC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,EAC7C,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,EAC1C,IAAI,EAAE,SAAS,EAWf;IAED,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAI1C;IAED,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIvC;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzB;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3B;IAED,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9B;IAED,SAAS,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAInC;IAED,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIhC;IAED,YAAY,IAAI,SAAS,CAExB;IAED,sBAAsB,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAgBpG;CACD;AAwBD,wBAAgB,kBAAkB,IAAI,MAAM,EAAE,CAY7C;AAuED,eAAO,IAAI,KAAK,EAAE,KAAK,CAAC;AAKxB,wBAAgB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAYlD;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgB3E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAExD;AA4DD,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AAMD,wBAAgB,gBAAgB,IAAI,aAAa,CAiBhD;AAED,wBAAgB,kBAAkB,IAAI,eAAe,CAQpD;AAED,wBAAgB,cAAc,IAAI,WAAW,CAK5C","sourcesContent":["import * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { EditorTheme, MarkdownTheme, SelectListTheme } from \"@mariozechner/pi-tui\";\nimport { type Static, Type } from \"@sinclair/typebox\";\nimport { TypeCompiler } from \"@sinclair/typebox/compiler\";\nimport chalk from \"chalk\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// ============================================================================\n// Types & Schema\n// ============================================================================\n\nconst ColorValueSchema = Type.Union([\n\tType.String(), // hex \"#ff0000\", var ref \"primary\", or empty \"\"\n\tType.Integer({ minimum: 0, maximum: 255 }), // 256-color index\n]);\n\ntype ColorValue = Static<typeof ColorValueSchema>;\n\nconst ThemeJsonSchema = Type.Object({\n\t$schema: Type.Optional(Type.String()),\n\tname: Type.String(),\n\tvars: Type.Optional(Type.Record(Type.String(), ColorValueSchema)),\n\tcolors: Type.Object({\n\t\t// Core UI (10 colors)\n\t\taccent: ColorValueSchema,\n\t\tborder: ColorValueSchema,\n\t\tborderAccent: ColorValueSchema,\n\t\tborderMuted: ColorValueSchema,\n\t\tsuccess: ColorValueSchema,\n\t\terror: ColorValueSchema,\n\t\twarning: ColorValueSchema,\n\t\tmuted: ColorValueSchema,\n\t\tdim: ColorValueSchema,\n\t\ttext: ColorValueSchema,\n\t\t// Backgrounds & Content Text (7 colors)\n\t\tuserMessageBg: ColorValueSchema,\n\t\tuserMessageText: ColorValueSchema,\n\t\ttoolPendingBg: ColorValueSchema,\n\t\ttoolSuccessBg: ColorValueSchema,\n\t\ttoolErrorBg: ColorValueSchema,\n\t\ttoolTitle: ColorValueSchema,\n\t\ttoolOutput: ColorValueSchema,\n\t\t// Markdown (10 colors)\n\t\tmdHeading: ColorValueSchema,\n\t\tmdLink: ColorValueSchema,\n\t\tmdLinkUrl: ColorValueSchema,\n\t\tmdCode: ColorValueSchema,\n\t\tmdCodeBlock: ColorValueSchema,\n\t\tmdCodeBlockBorder: ColorValueSchema,\n\t\tmdQuote: ColorValueSchema,\n\t\tmdQuoteBorder: ColorValueSchema,\n\t\tmdHr: ColorValueSchema,\n\t\tmdListBullet: ColorValueSchema,\n\t\t// Tool Diffs (3 colors)\n\t\ttoolDiffAdded: ColorValueSchema,\n\t\ttoolDiffRemoved: ColorValueSchema,\n\t\ttoolDiffContext: ColorValueSchema,\n\t\t// Syntax Highlighting (9 colors)\n\t\tsyntaxComment: ColorValueSchema,\n\t\tsyntaxKeyword: ColorValueSchema,\n\t\tsyntaxFunction: ColorValueSchema,\n\t\tsyntaxVariable: ColorValueSchema,\n\t\tsyntaxString: ColorValueSchema,\n\t\tsyntaxNumber: ColorValueSchema,\n\t\tsyntaxType: ColorValueSchema,\n\t\tsyntaxOperator: ColorValueSchema,\n\t\tsyntaxPunctuation: ColorValueSchema,\n\t\t// Thinking Level Borders (5 colors)\n\t\tthinkingOff: ColorValueSchema,\n\t\tthinkingMinimal: ColorValueSchema,\n\t\tthinkingLow: ColorValueSchema,\n\t\tthinkingMedium: ColorValueSchema,\n\t\tthinkingHigh: ColorValueSchema,\n\t}),\n});\n\ntype ThemeJson = Static<typeof ThemeJsonSchema>;\n\nconst validateThemeJson = TypeCompiler.Compile(ThemeJsonSchema);\n\nexport type ThemeColor =\n\t| \"accent\"\n\t| \"border\"\n\t| \"borderAccent\"\n\t| \"borderMuted\"\n\t| \"success\"\n\t| \"error\"\n\t| \"warning\"\n\t| \"muted\"\n\t| \"dim\"\n\t| \"text\"\n\t| \"userMessageText\"\n\t| \"toolTitle\"\n\t| \"toolOutput\"\n\t| \"mdHeading\"\n\t| \"mdLink\"\n\t| \"mdLinkUrl\"\n\t| \"mdCode\"\n\t| \"mdCodeBlock\"\n\t| \"mdCodeBlockBorder\"\n\t| \"mdQuote\"\n\t| \"mdQuoteBorder\"\n\t| \"mdHr\"\n\t| \"mdListBullet\"\n\t| \"toolDiffAdded\"\n\t| \"toolDiffRemoved\"\n\t| \"toolDiffContext\"\n\t| \"syntaxComment\"\n\t| \"syntaxKeyword\"\n\t| \"syntaxFunction\"\n\t| \"syntaxVariable\"\n\t| \"syntaxString\"\n\t| \"syntaxNumber\"\n\t| \"syntaxType\"\n\t| \"syntaxOperator\"\n\t| \"syntaxPunctuation\"\n\t| \"thinkingOff\"\n\t| \"thinkingMinimal\"\n\t| \"thinkingLow\"\n\t| \"thinkingMedium\"\n\t| \"thinkingHigh\";\n\nexport type ThemeBg = \"userMessageBg\" | \"toolPendingBg\" | \"toolSuccessBg\" | \"toolErrorBg\";\n\ntype ColorMode = \"truecolor\" | \"256color\";\n\n// ============================================================================\n// Color Utilities\n// ============================================================================\n\nfunction detectColorMode(): ColorMode {\n\tconst colorterm = process.env.COLORTERM;\n\tif (colorterm === \"truecolor\" || colorterm === \"24bit\") {\n\t\treturn \"truecolor\";\n\t}\n\tconst term = process.env.TERM || \"\";\n\tif (term.includes(\"256color\")) {\n\t\treturn \"256color\";\n\t}\n\treturn \"256color\";\n}\n\nfunction hexToRgb(hex: string): { r: number; g: number; b: number } {\n\tconst cleaned = hex.replace(\"#\", \"\");\n\tif (cleaned.length !== 6) {\n\t\tthrow new Error(`Invalid hex color: ${hex}`);\n\t}\n\tconst r = parseInt(cleaned.substring(0, 2), 16);\n\tconst g = parseInt(cleaned.substring(2, 4), 16);\n\tconst b = parseInt(cleaned.substring(4, 6), 16);\n\tif (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) {\n\t\tthrow new Error(`Invalid hex color: ${hex}`);\n\t}\n\treturn { r, g, b };\n}\n\nfunction rgbTo256(r: number, g: number, b: number): number {\n\tconst rIndex = Math.round((r / 255) * 5);\n\tconst gIndex = Math.round((g / 255) * 5);\n\tconst bIndex = Math.round((b / 255) * 5);\n\treturn 16 + 36 * rIndex + 6 * gIndex + bIndex;\n}\n\nfunction hexTo256(hex: string): number {\n\tconst { r, g, b } = hexToRgb(hex);\n\treturn rgbTo256(r, g, b);\n}\n\nfunction fgAnsi(color: string | number, mode: ColorMode): string {\n\tif (color === \"\") return \"\\x1b[39m\";\n\tif (typeof color === \"number\") return `\\x1b[38;5;${color}m`;\n\tif (color.startsWith(\"#\")) {\n\t\tif (mode === \"truecolor\") {\n\t\t\tconst { r, g, b } = hexToRgb(color);\n\t\t\treturn `\\x1b[38;2;${r};${g};${b}m`;\n\t\t} else {\n\t\t\tconst index = hexTo256(color);\n\t\t\treturn `\\x1b[38;5;${index}m`;\n\t\t}\n\t}\n\tthrow new Error(`Invalid color value: ${color}`);\n}\n\nfunction bgAnsi(color: string | number, mode: ColorMode): string {\n\tif (color === \"\") return \"\\x1b[49m\";\n\tif (typeof color === \"number\") return `\\x1b[48;5;${color}m`;\n\tif (color.startsWith(\"#\")) {\n\t\tif (mode === \"truecolor\") {\n\t\t\tconst { r, g, b } = hexToRgb(color);\n\t\t\treturn `\\x1b[48;2;${r};${g};${b}m`;\n\t\t} else {\n\t\t\tconst index = hexTo256(color);\n\t\t\treturn `\\x1b[48;5;${index}m`;\n\t\t}\n\t}\n\tthrow new Error(`Invalid color value: ${color}`);\n}\n\nfunction resolveVarRefs(\n\tvalue: ColorValue,\n\tvars: Record<string, ColorValue>,\n\tvisited = new Set<string>(),\n): string | number {\n\tif (typeof value === \"number\" || value === \"\" || value.startsWith(\"#\")) {\n\t\treturn value;\n\t}\n\tif (visited.has(value)) {\n\t\tthrow new Error(`Circular variable reference detected: ${value}`);\n\t}\n\tif (!(value in vars)) {\n\t\tthrow new Error(`Variable reference not found: ${value}`);\n\t}\n\tvisited.add(value);\n\treturn resolveVarRefs(vars[value], vars, visited);\n}\n\nfunction resolveThemeColors<T extends Record<string, ColorValue>>(\n\tcolors: T,\n\tvars: Record<string, ColorValue> = {},\n): Record<keyof T, string | number> {\n\tconst resolved: Record<string, string | number> = {};\n\tfor (const [key, value] of Object.entries(colors)) {\n\t\tresolved[key] = resolveVarRefs(value, vars);\n\t}\n\treturn resolved as Record<keyof T, string | number>;\n}\n\n// ============================================================================\n// Theme Class\n// ============================================================================\n\nexport class Theme {\n\tprivate fgColors: Map<ThemeColor, string>;\n\tprivate bgColors: Map<ThemeBg, string>;\n\tprivate mode: ColorMode;\n\n\tconstructor(\n\t\tfgColors: Record<ThemeColor, string | number>,\n\t\tbgColors: Record<ThemeBg, string | number>,\n\t\tmode: ColorMode,\n\t) {\n\t\tthis.mode = mode;\n\t\tthis.fgColors = new Map();\n\t\tfor (const [key, value] of Object.entries(fgColors) as [ThemeColor, string | number][]) {\n\t\t\tthis.fgColors.set(key, fgAnsi(value, mode));\n\t\t}\n\t\tthis.bgColors = new Map();\n\t\tfor (const [key, value] of Object.entries(bgColors) as [ThemeBg, string | number][]) {\n\t\t\tthis.bgColors.set(key, bgAnsi(value, mode));\n\t\t}\n\t}\n\n\tfg(color: ThemeColor, text: string): string {\n\t\tconst ansi = this.fgColors.get(color);\n\t\tif (!ansi) throw new Error(`Unknown theme color: ${color}`);\n\t\treturn `${ansi}${text}\\x1b[39m`; // Reset only foreground color\n\t}\n\n\tbg(color: ThemeBg, text: string): string {\n\t\tconst ansi = this.bgColors.get(color);\n\t\tif (!ansi) throw new Error(`Unknown theme background color: ${color}`);\n\t\treturn `${ansi}${text}\\x1b[49m`; // Reset only background color\n\t}\n\n\tbold(text: string): string {\n\t\treturn chalk.bold(text);\n\t}\n\n\titalic(text: string): string {\n\t\treturn chalk.italic(text);\n\t}\n\n\tunderline(text: string): string {\n\t\treturn chalk.underline(text);\n\t}\n\n\tgetFgAnsi(color: ThemeColor): string {\n\t\tconst ansi = this.fgColors.get(color);\n\t\tif (!ansi) throw new Error(`Unknown theme color: ${color}`);\n\t\treturn ansi;\n\t}\n\n\tgetBgAnsi(color: ThemeBg): string {\n\t\tconst ansi = this.bgColors.get(color);\n\t\tif (!ansi) throw new Error(`Unknown theme background color: ${color}`);\n\t\treturn ansi;\n\t}\n\n\tgetColorMode(): ColorMode {\n\t\treturn this.mode;\n\t}\n\n\tgetThinkingBorderColor(level: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\"): (str: string) => string {\n\t\t// Map thinking levels to dedicated theme colors\n\t\tswitch (level) {\n\t\t\tcase \"off\":\n\t\t\t\treturn (str: string) => this.fg(\"thinkingOff\", str);\n\t\t\tcase \"minimal\":\n\t\t\t\treturn (str: string) => this.fg(\"thinkingMinimal\", str);\n\t\t\tcase \"low\":\n\t\t\t\treturn (str: string) => this.fg(\"thinkingLow\", str);\n\t\t\tcase \"medium\":\n\t\t\t\treturn (str: string) => this.fg(\"thinkingMedium\", str);\n\t\t\tcase \"high\":\n\t\t\t\treturn (str: string) => this.fg(\"thinkingHigh\", str);\n\t\t\tdefault:\n\t\t\t\treturn (str: string) => this.fg(\"thinkingOff\", str);\n\t\t}\n\t}\n}\n\n// ============================================================================\n// Theme Loading\n// ============================================================================\n\nlet BUILTIN_THEMES: Record<string, ThemeJson> | undefined;\n\nfunction getBuiltinThemes(): Record<string, ThemeJson> {\n\tif (!BUILTIN_THEMES) {\n\t\tconst darkPath = path.join(__dirname, \"dark.json\");\n\t\tconst lightPath = path.join(__dirname, \"light.json\");\n\t\tBUILTIN_THEMES = {\n\t\t\tdark: JSON.parse(fs.readFileSync(darkPath, \"utf-8\")) as ThemeJson,\n\t\t\tlight: JSON.parse(fs.readFileSync(lightPath, \"utf-8\")) as ThemeJson,\n\t\t};\n\t}\n\treturn BUILTIN_THEMES;\n}\n\nfunction getThemesDir(): string {\n\treturn path.join(os.homedir(), \".pi\", \"agent\", \"themes\");\n}\n\nexport function getAvailableThemes(): string[] {\n\tconst themes = new Set<string>(Object.keys(getBuiltinThemes()));\n\tconst themesDir = getThemesDir();\n\tif (fs.existsSync(themesDir)) {\n\t\tconst files = fs.readdirSync(themesDir);\n\t\tfor (const file of files) {\n\t\t\tif (file.endsWith(\".json\")) {\n\t\t\t\tthemes.add(file.slice(0, -5));\n\t\t\t}\n\t\t}\n\t}\n\treturn Array.from(themes).sort();\n}\n\nfunction loadThemeJson(name: string): ThemeJson {\n\tconst builtinThemes = getBuiltinThemes();\n\tif (name in builtinThemes) {\n\t\treturn builtinThemes[name];\n\t}\n\tconst themesDir = getThemesDir();\n\tconst themePath = path.join(themesDir, `${name}.json`);\n\tif (!fs.existsSync(themePath)) {\n\t\tthrow new Error(`Theme not found: ${name}`);\n\t}\n\tconst content = fs.readFileSync(themePath, \"utf-8\");\n\tlet json: unknown;\n\ttry {\n\t\tjson = JSON.parse(content);\n\t} catch (error) {\n\t\tthrow new Error(`Failed to parse theme ${name}: ${error}`);\n\t}\n\tif (!validateThemeJson.Check(json)) {\n\t\tconst errors = Array.from(validateThemeJson.Errors(json));\n\t\tconst errorMessages = errors.map((e) => ` - ${e.path}: ${e.message}`).join(\"\\n\");\n\t\tthrow new Error(`Invalid theme ${name}:\\n${errorMessages}`);\n\t}\n\treturn json as ThemeJson;\n}\n\nfunction createTheme(themeJson: ThemeJson, mode?: ColorMode): Theme {\n\tconst colorMode = mode ?? detectColorMode();\n\tconst resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);\n\tconst fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, string | number>;\n\tconst bgColors: Record<ThemeBg, string | number> = {} as Record<ThemeBg, string | number>;\n\tconst bgColorKeys: Set<string> = new Set([\"userMessageBg\", \"toolPendingBg\", \"toolSuccessBg\", \"toolErrorBg\"]);\n\tfor (const [key, value] of Object.entries(resolvedColors)) {\n\t\tif (bgColorKeys.has(key)) {\n\t\t\tbgColors[key as ThemeBg] = value;\n\t\t} else {\n\t\t\tfgColors[key as ThemeColor] = value;\n\t\t}\n\t}\n\treturn new Theme(fgColors, bgColors, colorMode);\n}\n\nfunction loadTheme(name: string, mode?: ColorMode): Theme {\n\tconst themeJson = loadThemeJson(name);\n\treturn createTheme(themeJson, mode);\n}\n\nfunction detectTerminalBackground(): \"dark\" | \"light\" {\n\tconst colorfgbg = process.env.COLORFGBG || \"\";\n\tif (colorfgbg) {\n\t\tconst parts = colorfgbg.split(\";\");\n\t\tif (parts.length >= 2) {\n\t\t\tconst bg = parseInt(parts[1], 10);\n\t\t\tif (!Number.isNaN(bg)) {\n\t\t\t\tconst result = bg < 8 ? \"dark\" : \"light\";\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\t}\n\treturn \"dark\";\n}\n\nfunction getDefaultTheme(): string {\n\treturn detectTerminalBackground();\n}\n\n// ============================================================================\n// Global Theme Instance\n// ============================================================================\n\nexport let theme: Theme;\nlet currentThemeName: string | undefined;\nlet themeWatcher: fs.FSWatcher | undefined;\nlet onThemeChangeCallback: (() => void) | undefined;\n\nexport function initTheme(themeName?: string): void {\n\tconst name = themeName ?? getDefaultTheme();\n\tcurrentThemeName = name;\n\ttry {\n\t\ttheme = loadTheme(name);\n\t\tstartThemeWatcher();\n\t} catch (error) {\n\t\t// Theme is invalid - fall back to dark theme silently\n\t\tcurrentThemeName = \"dark\";\n\t\ttheme = loadTheme(\"dark\");\n\t\t// Don't start watcher for fallback theme\n\t}\n}\n\nexport function setTheme(name: string): { success: boolean; error?: string } {\n\tcurrentThemeName = name;\n\ttry {\n\t\ttheme = loadTheme(name);\n\t\tstartThemeWatcher();\n\t\treturn { success: true };\n\t} catch (error) {\n\t\t// Theme is invalid - fall back to dark theme\n\t\tcurrentThemeName = \"dark\";\n\t\ttheme = loadTheme(\"dark\");\n\t\t// Don't start watcher for fallback theme\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t};\n\t}\n}\n\nexport function onThemeChange(callback: () => void): void {\n\tonThemeChangeCallback = callback;\n}\n\nfunction startThemeWatcher(): void {\n\t// Stop existing watcher if any\n\tif (themeWatcher) {\n\t\tthemeWatcher.close();\n\t\tthemeWatcher = undefined;\n\t}\n\n\t// Only watch if it's a custom theme (not built-in)\n\tif (!currentThemeName || currentThemeName === \"dark\" || currentThemeName === \"light\") {\n\t\treturn;\n\t}\n\n\tconst themesDir = getThemesDir();\n\tconst themeFile = path.join(themesDir, `${currentThemeName}.json`);\n\n\t// Only watch if the file exists\n\tif (!fs.existsSync(themeFile)) {\n\t\treturn;\n\t}\n\n\ttry {\n\t\tthemeWatcher = fs.watch(themeFile, (eventType) => {\n\t\t\tif (eventType === \"change\") {\n\t\t\t\t// Debounce rapid changes\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Reload the theme\n\t\t\t\t\t\ttheme = loadTheme(currentThemeName!);\n\t\t\t\t\t\t// Notify callback (to invalidate UI)\n\t\t\t\t\t\tif (onThemeChangeCallback) {\n\t\t\t\t\t\t\tonThemeChangeCallback();\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t// Ignore errors (file might be in invalid state while being edited)\n\t\t\t\t\t}\n\t\t\t\t}, 100);\n\t\t\t} else if (eventType === \"rename\") {\n\t\t\t\t// File was deleted or renamed - fall back to default theme\n\t\t\t\tsetTimeout(() => {\n\t\t\t\t\tif (!fs.existsSync(themeFile)) {\n\t\t\t\t\t\tcurrentThemeName = \"dark\";\n\t\t\t\t\t\ttheme = loadTheme(\"dark\");\n\t\t\t\t\t\tif (themeWatcher) {\n\t\t\t\t\t\t\tthemeWatcher.close();\n\t\t\t\t\t\t\tthemeWatcher = undefined;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (onThemeChangeCallback) {\n\t\t\t\t\t\t\tonThemeChangeCallback();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}, 100);\n\t\t\t}\n\t\t});\n\t} catch (error) {\n\t\t// Ignore errors starting watcher\n\t}\n}\n\nexport function stopThemeWatcher(): void {\n\tif (themeWatcher) {\n\t\tthemeWatcher.close();\n\t\tthemeWatcher = undefined;\n\t}\n}\n\n// ============================================================================\n// TUI Helpers\n// ============================================================================\n\nexport function getMarkdownTheme(): MarkdownTheme {\n\treturn {\n\t\theading: (text: string) => theme.fg(\"mdHeading\", text),\n\t\tlink: (text: string) => theme.fg(\"mdLink\", text),\n\t\tlinkUrl: (text: string) => theme.fg(\"mdLinkUrl\", text),\n\t\tcode: (text: string) => theme.fg(\"mdCode\", text),\n\t\tcodeBlock: (text: string) => theme.fg(\"mdCodeBlock\", text),\n\t\tcodeBlockBorder: (text: string) => theme.fg(\"mdCodeBlockBorder\", text),\n\t\tquote: (text: string) => theme.fg(\"mdQuote\", text),\n\t\tquoteBorder: (text: string) => theme.fg(\"mdQuoteBorder\", text),\n\t\thr: (text: string) => theme.fg(\"mdHr\", text),\n\t\tlistBullet: (text: string) => theme.fg(\"mdListBullet\", text),\n\t\tbold: (text: string) => theme.bold(text),\n\t\titalic: (text: string) => theme.italic(text),\n\t\tunderline: (text: string) => theme.underline(text),\n\t\tstrikethrough: (text: string) => chalk.strikethrough(text),\n\t};\n}\n\nexport function getSelectListTheme(): SelectListTheme {\n\treturn {\n\t\tselectedPrefix: (text: string) => theme.fg(\"accent\", text),\n\t\tselectedText: (text: string) => theme.fg(\"accent\", text),\n\t\tdescription: (text: string) => theme.fg(\"muted\", text),\n\t\tscrollInfo: (text: string) => theme.fg(\"muted\", text),\n\t\tnoMatch: (text: string) => theme.fg(\"muted\", text),\n\t};\n}\n\nexport function getEditorTheme(): EditorTheme {\n\treturn {\n\t\tborderColor: (text: string) => theme.fg(\"borderMuted\", text),\n\t\tselectList: getSelectListTheme(),\n\t};\n}\n"]}