@vladimirven/openswe 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +203 -0
- package/CLAUDE.md +203 -0
- package/README.md +166 -0
- package/bun.lock +447 -0
- package/bunfig.toml +4 -0
- package/package.json +42 -0
- package/src/app.tsx +84 -0
- package/src/components/App.tsx +526 -0
- package/src/components/ConfirmDialog.tsx +88 -0
- package/src/components/Footer.tsx +50 -0
- package/src/components/HelpModal.tsx +136 -0
- package/src/components/IssueSelectorModal.tsx +701 -0
- package/src/components/ManualSessionModal.tsx +191 -0
- package/src/components/PhaseProgress.tsx +45 -0
- package/src/components/Preview.tsx +249 -0
- package/src/components/ProviderSwitcherModal.tsx +156 -0
- package/src/components/ScrollableText.tsx +120 -0
- package/src/components/SessionCard.tsx +60 -0
- package/src/components/SessionList.tsx +79 -0
- package/src/components/SessionTerminal.tsx +89 -0
- package/src/components/StatusBar.tsx +84 -0
- package/src/components/ThemeSwitcherModal.tsx +237 -0
- package/src/components/index.ts +58 -0
- package/src/components/session-utils.ts +337 -0
- package/src/components/theme.ts +206 -0
- package/src/components/types.ts +215 -0
- package/src/config/defaults.ts +44 -0
- package/src/config/env.ts +67 -0
- package/src/config/global.ts +252 -0
- package/src/config/index.ts +171 -0
- package/src/config/types.ts +131 -0
- package/src/core/.gitkeep +0 -0
- package/src/core/index.ts +5 -0
- package/src/core/parser.ts +62 -0
- package/src/core/process-manager.ts +52 -0
- package/src/core/session.ts +423 -0
- package/src/core/tmux.ts +206 -0
- package/src/git/.gitkeep +0 -0
- package/src/git/index.ts +8 -0
- package/src/git/repo.ts +443 -0
- package/src/git/worktree.ts +317 -0
- package/src/github/.gitkeep +0 -0
- package/src/github/client.ts +208 -0
- package/src/github/index.ts +8 -0
- package/src/github/issues.ts +351 -0
- package/src/index.ts +369 -0
- package/src/prompts/.gitkeep +0 -0
- package/src/prompts/index.ts +1 -0
- package/src/prompts/swe-system.ts +22 -0
- package/src/providers/claude.ts +103 -0
- package/src/providers/index.ts +21 -0
- package/src/providers/opencode.ts +98 -0
- package/src/providers/registry.ts +53 -0
- package/src/providers/types.ts +117 -0
- package/src/store/buffers.ts +234 -0
- package/src/store/db.test.ts +579 -0
- package/src/store/db.ts +249 -0
- package/src/store/index.ts +101 -0
- package/src/store/project.ts +119 -0
- package/src/store/schema.sql +71 -0
- package/src/store/sessions.ts +454 -0
- package/src/store/types.ts +194 -0
- package/src/theme/context.tsx +170 -0
- package/src/theme/custom.ts +134 -0
- package/src/theme/index.ts +58 -0
- package/src/theme/loader.ts +264 -0
- package/src/theme/themes/aura.json +69 -0
- package/src/theme/themes/ayu.json +80 -0
- package/src/theme/themes/carbonfox.json +248 -0
- package/src/theme/themes/catppuccin-frappe.json +233 -0
- package/src/theme/themes/catppuccin-macchiato.json +233 -0
- package/src/theme/themes/catppuccin.json +112 -0
- package/src/theme/themes/cobalt2.json +228 -0
- package/src/theme/themes/cursor.json +249 -0
- package/src/theme/themes/dracula.json +219 -0
- package/src/theme/themes/everforest.json +241 -0
- package/src/theme/themes/flexoki.json +237 -0
- package/src/theme/themes/github.json +233 -0
- package/src/theme/themes/gruvbox.json +242 -0
- package/src/theme/themes/kanagawa.json +77 -0
- package/src/theme/themes/lucent-orng.json +237 -0
- package/src/theme/themes/material.json +235 -0
- package/src/theme/themes/matrix.json +77 -0
- package/src/theme/themes/mercury.json +252 -0
- package/src/theme/themes/monokai.json +221 -0
- package/src/theme/themes/nightowl.json +221 -0
- package/src/theme/themes/nord.json +223 -0
- package/src/theme/themes/one-dark.json +84 -0
- package/src/theme/themes/opencode.json +245 -0
- package/src/theme/themes/orng.json +249 -0
- package/src/theme/themes/osaka-jade.json +93 -0
- package/src/theme/themes/palenight.json +222 -0
- package/src/theme/themes/rosepine.json +234 -0
- package/src/theme/themes/solarized.json +223 -0
- package/src/theme/themes/synthwave84.json +226 -0
- package/src/theme/themes/tokyonight.json +243 -0
- package/src/theme/themes/vercel.json +245 -0
- package/src/theme/themes/vesper.json +218 -0
- package/src/theme/themes/zenburn.json +223 -0
- package/src/theme/types.ts +225 -0
- package/src/types/sql.d.ts +4 -0
- package/src/utils/ansi-parser.ts +225 -0
- package/src/utils/format.ts +46 -0
- package/src/utils/id.ts +15 -0
- package/src/utils/logger.ts +112 -0
- package/src/utils/prerequisites.ts +118 -0
- package/src/utils/shell.ts +9 -0
- package/src/wizard/flows.ts +419 -0
- package/src/wizard/index.ts +37 -0
- package/src/wizard/prompts.ts +190 -0
- package/src/workspace/detect.test.ts +51 -0
- package/src/workspace/detect.ts +223 -0
- package/src/workspace/index.ts +71 -0
- package/src/workspace/init.ts +131 -0
- package/src/workspace/paths.ts +143 -0
- package/src/workspace/project.ts +164 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/theme.json",
|
|
3
|
+
"defs": {
|
|
4
|
+
"bg": "#3f3f3f",
|
|
5
|
+
"bgAlt": "#4f4f4f",
|
|
6
|
+
"bgPanel": "#5f5f5f",
|
|
7
|
+
"fg": "#dcdccc",
|
|
8
|
+
"fgMuted": "#9f9f9f",
|
|
9
|
+
"red": "#cc9393",
|
|
10
|
+
"redBright": "#dca3a3",
|
|
11
|
+
"green": "#7f9f7f",
|
|
12
|
+
"greenBright": "#8fb28f",
|
|
13
|
+
"yellow": "#f0dfaf",
|
|
14
|
+
"yellowDim": "#e0cf9f",
|
|
15
|
+
"blue": "#8cd0d3",
|
|
16
|
+
"blueDim": "#7cb8bb",
|
|
17
|
+
"magenta": "#dc8cc3",
|
|
18
|
+
"cyan": "#93e0e3",
|
|
19
|
+
"orange": "#dfaf8f"
|
|
20
|
+
},
|
|
21
|
+
"theme": {
|
|
22
|
+
"primary": {
|
|
23
|
+
"dark": "blue",
|
|
24
|
+
"light": "#5f7f8f"
|
|
25
|
+
},
|
|
26
|
+
"secondary": {
|
|
27
|
+
"dark": "magenta",
|
|
28
|
+
"light": "#8f5f8f"
|
|
29
|
+
},
|
|
30
|
+
"accent": {
|
|
31
|
+
"dark": "cyan",
|
|
32
|
+
"light": "#5f8f8f"
|
|
33
|
+
},
|
|
34
|
+
"error": {
|
|
35
|
+
"dark": "red",
|
|
36
|
+
"light": "#8f5f5f"
|
|
37
|
+
},
|
|
38
|
+
"warning": {
|
|
39
|
+
"dark": "yellow",
|
|
40
|
+
"light": "#8f8f5f"
|
|
41
|
+
},
|
|
42
|
+
"success": {
|
|
43
|
+
"dark": "green",
|
|
44
|
+
"light": "#5f8f5f"
|
|
45
|
+
},
|
|
46
|
+
"info": {
|
|
47
|
+
"dark": "orange",
|
|
48
|
+
"light": "#8f7f5f"
|
|
49
|
+
},
|
|
50
|
+
"text": {
|
|
51
|
+
"dark": "fg",
|
|
52
|
+
"light": "#3f3f3f"
|
|
53
|
+
},
|
|
54
|
+
"textMuted": {
|
|
55
|
+
"dark": "fgMuted",
|
|
56
|
+
"light": "#6f6f6f"
|
|
57
|
+
},
|
|
58
|
+
"background": {
|
|
59
|
+
"dark": "bg",
|
|
60
|
+
"light": "#ffffef"
|
|
61
|
+
},
|
|
62
|
+
"backgroundPanel": {
|
|
63
|
+
"dark": "bgAlt",
|
|
64
|
+
"light": "#f5f5e5"
|
|
65
|
+
},
|
|
66
|
+
"backgroundElement": {
|
|
67
|
+
"dark": "bgPanel",
|
|
68
|
+
"light": "#ebebdb"
|
|
69
|
+
},
|
|
70
|
+
"border": {
|
|
71
|
+
"dark": "#5f5f5f",
|
|
72
|
+
"light": "#d0d0c0"
|
|
73
|
+
},
|
|
74
|
+
"borderActive": {
|
|
75
|
+
"dark": "blue",
|
|
76
|
+
"light": "#5f7f8f"
|
|
77
|
+
},
|
|
78
|
+
"borderSubtle": {
|
|
79
|
+
"dark": "#4f4f4f",
|
|
80
|
+
"light": "#e0e0d0"
|
|
81
|
+
},
|
|
82
|
+
"diffAdded": {
|
|
83
|
+
"dark": "green",
|
|
84
|
+
"light": "#5f8f5f"
|
|
85
|
+
},
|
|
86
|
+
"diffRemoved": {
|
|
87
|
+
"dark": "red",
|
|
88
|
+
"light": "#8f5f5f"
|
|
89
|
+
},
|
|
90
|
+
"diffContext": {
|
|
91
|
+
"dark": "fgMuted",
|
|
92
|
+
"light": "#6f6f6f"
|
|
93
|
+
},
|
|
94
|
+
"diffHunkHeader": {
|
|
95
|
+
"dark": "cyan",
|
|
96
|
+
"light": "#5f8f8f"
|
|
97
|
+
},
|
|
98
|
+
"diffHighlightAdded": {
|
|
99
|
+
"dark": "greenBright",
|
|
100
|
+
"light": "#5f8f5f"
|
|
101
|
+
},
|
|
102
|
+
"diffHighlightRemoved": {
|
|
103
|
+
"dark": "redBright",
|
|
104
|
+
"light": "#8f5f5f"
|
|
105
|
+
},
|
|
106
|
+
"diffAddedBg": {
|
|
107
|
+
"dark": "#4f5f4f",
|
|
108
|
+
"light": "#efffef"
|
|
109
|
+
},
|
|
110
|
+
"diffRemovedBg": {
|
|
111
|
+
"dark": "#5f4f4f",
|
|
112
|
+
"light": "#ffefef"
|
|
113
|
+
},
|
|
114
|
+
"diffContextBg": {
|
|
115
|
+
"dark": "bgAlt",
|
|
116
|
+
"light": "#f5f5e5"
|
|
117
|
+
},
|
|
118
|
+
"diffLineNumber": {
|
|
119
|
+
"dark": "#6f6f6f",
|
|
120
|
+
"light": "#b0b0a0"
|
|
121
|
+
},
|
|
122
|
+
"diffAddedLineNumberBg": {
|
|
123
|
+
"dark": "#4f5f4f",
|
|
124
|
+
"light": "#efffef"
|
|
125
|
+
},
|
|
126
|
+
"diffRemovedLineNumberBg": {
|
|
127
|
+
"dark": "#5f4f4f",
|
|
128
|
+
"light": "#ffefef"
|
|
129
|
+
},
|
|
130
|
+
"markdownText": {
|
|
131
|
+
"dark": "fg",
|
|
132
|
+
"light": "#3f3f3f"
|
|
133
|
+
},
|
|
134
|
+
"markdownHeading": {
|
|
135
|
+
"dark": "yellow",
|
|
136
|
+
"light": "#8f8f5f"
|
|
137
|
+
},
|
|
138
|
+
"markdownLink": {
|
|
139
|
+
"dark": "blue",
|
|
140
|
+
"light": "#5f7f8f"
|
|
141
|
+
},
|
|
142
|
+
"markdownLinkText": {
|
|
143
|
+
"dark": "cyan",
|
|
144
|
+
"light": "#5f8f8f"
|
|
145
|
+
},
|
|
146
|
+
"markdownCode": {
|
|
147
|
+
"dark": "green",
|
|
148
|
+
"light": "#5f8f5f"
|
|
149
|
+
},
|
|
150
|
+
"markdownBlockQuote": {
|
|
151
|
+
"dark": "fgMuted",
|
|
152
|
+
"light": "#6f6f6f"
|
|
153
|
+
},
|
|
154
|
+
"markdownEmph": {
|
|
155
|
+
"dark": "yellowDim",
|
|
156
|
+
"light": "#8f8f5f"
|
|
157
|
+
},
|
|
158
|
+
"markdownStrong": {
|
|
159
|
+
"dark": "orange",
|
|
160
|
+
"light": "#8f7f5f"
|
|
161
|
+
},
|
|
162
|
+
"markdownHorizontalRule": {
|
|
163
|
+
"dark": "fgMuted",
|
|
164
|
+
"light": "#6f6f6f"
|
|
165
|
+
},
|
|
166
|
+
"markdownListItem": {
|
|
167
|
+
"dark": "blue",
|
|
168
|
+
"light": "#5f7f8f"
|
|
169
|
+
},
|
|
170
|
+
"markdownListEnumeration": {
|
|
171
|
+
"dark": "cyan",
|
|
172
|
+
"light": "#5f8f8f"
|
|
173
|
+
},
|
|
174
|
+
"markdownImage": {
|
|
175
|
+
"dark": "blue",
|
|
176
|
+
"light": "#5f7f8f"
|
|
177
|
+
},
|
|
178
|
+
"markdownImageText": {
|
|
179
|
+
"dark": "cyan",
|
|
180
|
+
"light": "#5f8f8f"
|
|
181
|
+
},
|
|
182
|
+
"markdownCodeBlock": {
|
|
183
|
+
"dark": "fg",
|
|
184
|
+
"light": "#3f3f3f"
|
|
185
|
+
},
|
|
186
|
+
"syntaxComment": {
|
|
187
|
+
"dark": "#7f9f7f",
|
|
188
|
+
"light": "#5f7f5f"
|
|
189
|
+
},
|
|
190
|
+
"syntaxKeyword": {
|
|
191
|
+
"dark": "yellow",
|
|
192
|
+
"light": "#8f8f5f"
|
|
193
|
+
},
|
|
194
|
+
"syntaxFunction": {
|
|
195
|
+
"dark": "blue",
|
|
196
|
+
"light": "#5f7f8f"
|
|
197
|
+
},
|
|
198
|
+
"syntaxVariable": {
|
|
199
|
+
"dark": "fg",
|
|
200
|
+
"light": "#3f3f3f"
|
|
201
|
+
},
|
|
202
|
+
"syntaxString": {
|
|
203
|
+
"dark": "red",
|
|
204
|
+
"light": "#8f5f5f"
|
|
205
|
+
},
|
|
206
|
+
"syntaxNumber": {
|
|
207
|
+
"dark": "greenBright",
|
|
208
|
+
"light": "#5f8f5f"
|
|
209
|
+
},
|
|
210
|
+
"syntaxType": {
|
|
211
|
+
"dark": "cyan",
|
|
212
|
+
"light": "#5f8f8f"
|
|
213
|
+
},
|
|
214
|
+
"syntaxOperator": {
|
|
215
|
+
"dark": "yellow",
|
|
216
|
+
"light": "#8f8f5f"
|
|
217
|
+
},
|
|
218
|
+
"syntaxPunctuation": {
|
|
219
|
+
"dark": "fg",
|
|
220
|
+
"light": "#3f3f3f"
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme type definitions for OpenSWE
|
|
3
|
+
*
|
|
4
|
+
* Matches opencode's JSON theme schema for full compatibility
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Color Types
|
|
9
|
+
// ============================================================================
|
|
10
|
+
|
|
11
|
+
/** Hex color string */
|
|
12
|
+
export type HexColor = `#${string}`
|
|
13
|
+
|
|
14
|
+
/** Reference to a color defined in defs */
|
|
15
|
+
export type RefName = string
|
|
16
|
+
|
|
17
|
+
/** Dark/light mode variants */
|
|
18
|
+
export interface ColorVariant {
|
|
19
|
+
dark: HexColor | RefName
|
|
20
|
+
light: HexColor | RefName
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** A color value can be a hex, a reference, or a variant object */
|
|
24
|
+
export type ColorValue = HexColor | RefName | ColorVariant
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Theme JSON Schema
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/** Core theme colors (required) */
|
|
31
|
+
export interface ThemeColors {
|
|
32
|
+
primary: ColorValue
|
|
33
|
+
secondary: ColorValue
|
|
34
|
+
accent: ColorValue
|
|
35
|
+
error: ColorValue
|
|
36
|
+
warning: ColorValue
|
|
37
|
+
success: ColorValue
|
|
38
|
+
info: ColorValue
|
|
39
|
+
text: ColorValue
|
|
40
|
+
textMuted: ColorValue
|
|
41
|
+
background: ColorValue
|
|
42
|
+
backgroundPanel: ColorValue
|
|
43
|
+
backgroundElement: ColorValue
|
|
44
|
+
border: ColorValue
|
|
45
|
+
borderActive: ColorValue
|
|
46
|
+
borderSubtle: ColorValue
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Extended theme colors for diff views and syntax highlighting */
|
|
50
|
+
export interface ThemeColorsExtended extends ThemeColors {
|
|
51
|
+
// Diff colors
|
|
52
|
+
diffAdded?: ColorValue
|
|
53
|
+
diffRemoved?: ColorValue
|
|
54
|
+
diffContext?: ColorValue
|
|
55
|
+
diffHunkHeader?: ColorValue
|
|
56
|
+
diffHighlightAdded?: ColorValue
|
|
57
|
+
diffHighlightRemoved?: ColorValue
|
|
58
|
+
diffAddedBg?: ColorValue
|
|
59
|
+
diffRemovedBg?: ColorValue
|
|
60
|
+
diffContextBg?: ColorValue
|
|
61
|
+
diffLineNumber?: ColorValue
|
|
62
|
+
diffAddedLineNumberBg?: ColorValue
|
|
63
|
+
diffRemovedLineNumberBg?: ColorValue
|
|
64
|
+
|
|
65
|
+
// Markdown colors
|
|
66
|
+
markdownText?: ColorValue
|
|
67
|
+
markdownHeading?: ColorValue
|
|
68
|
+
markdownLink?: ColorValue
|
|
69
|
+
markdownLinkText?: ColorValue
|
|
70
|
+
markdownCode?: ColorValue
|
|
71
|
+
markdownBlockQuote?: ColorValue
|
|
72
|
+
markdownEmph?: ColorValue
|
|
73
|
+
markdownStrong?: ColorValue
|
|
74
|
+
markdownHorizontalRule?: ColorValue
|
|
75
|
+
markdownListItem?: ColorValue
|
|
76
|
+
markdownListEnumeration?: ColorValue
|
|
77
|
+
markdownImage?: ColorValue
|
|
78
|
+
markdownImageText?: ColorValue
|
|
79
|
+
markdownCodeBlock?: ColorValue
|
|
80
|
+
|
|
81
|
+
// Syntax highlighting colors
|
|
82
|
+
syntaxComment?: ColorValue
|
|
83
|
+
syntaxKeyword?: ColorValue
|
|
84
|
+
syntaxFunction?: ColorValue
|
|
85
|
+
syntaxVariable?: ColorValue
|
|
86
|
+
syntaxString?: ColorValue
|
|
87
|
+
syntaxNumber?: ColorValue
|
|
88
|
+
syntaxType?: ColorValue
|
|
89
|
+
syntaxOperator?: ColorValue
|
|
90
|
+
syntaxPunctuation?: ColorValue
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** JSON theme file structure */
|
|
94
|
+
export interface ThemeJson {
|
|
95
|
+
$schema?: string
|
|
96
|
+
defs?: Record<string, HexColor | RefName>
|
|
97
|
+
theme: ThemeColorsExtended
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ============================================================================
|
|
101
|
+
// Resolved Theme
|
|
102
|
+
// ============================================================================
|
|
103
|
+
|
|
104
|
+
/** Core resolved theme colors (all hex strings) */
|
|
105
|
+
export interface ResolvedTheme {
|
|
106
|
+
// Core colors
|
|
107
|
+
primary: string
|
|
108
|
+
secondary: string
|
|
109
|
+
accent: string
|
|
110
|
+
error: string
|
|
111
|
+
warning: string
|
|
112
|
+
success: string
|
|
113
|
+
info: string
|
|
114
|
+
text: string
|
|
115
|
+
textMuted: string
|
|
116
|
+
background: string
|
|
117
|
+
backgroundPanel: string
|
|
118
|
+
backgroundElement: string
|
|
119
|
+
border: string
|
|
120
|
+
borderActive: string
|
|
121
|
+
borderSubtle: string
|
|
122
|
+
|
|
123
|
+
// Diff colors (with defaults)
|
|
124
|
+
diffAdded: string
|
|
125
|
+
diffRemoved: string
|
|
126
|
+
diffContext: string
|
|
127
|
+
diffHunkHeader: string
|
|
128
|
+
diffHighlightAdded: string
|
|
129
|
+
diffHighlightRemoved: string
|
|
130
|
+
diffAddedBg: string
|
|
131
|
+
diffRemovedBg: string
|
|
132
|
+
diffContextBg: string
|
|
133
|
+
diffLineNumber: string
|
|
134
|
+
diffAddedLineNumberBg: string
|
|
135
|
+
diffRemovedLineNumberBg: string
|
|
136
|
+
|
|
137
|
+
// Markdown colors (with defaults)
|
|
138
|
+
markdownText: string
|
|
139
|
+
markdownHeading: string
|
|
140
|
+
markdownLink: string
|
|
141
|
+
markdownLinkText: string
|
|
142
|
+
markdownCode: string
|
|
143
|
+
markdownBlockQuote: string
|
|
144
|
+
markdownEmph: string
|
|
145
|
+
markdownStrong: string
|
|
146
|
+
markdownHorizontalRule: string
|
|
147
|
+
markdownListItem: string
|
|
148
|
+
markdownListEnumeration: string
|
|
149
|
+
markdownImage: string
|
|
150
|
+
markdownImageText: string
|
|
151
|
+
markdownCodeBlock: string
|
|
152
|
+
|
|
153
|
+
// Syntax highlighting (with defaults)
|
|
154
|
+
syntaxComment: string
|
|
155
|
+
syntaxKeyword: string
|
|
156
|
+
syntaxFunction: string
|
|
157
|
+
syntaxVariable: string
|
|
158
|
+
syntaxString: string
|
|
159
|
+
syntaxNumber: string
|
|
160
|
+
syntaxType: string
|
|
161
|
+
syntaxOperator: string
|
|
162
|
+
syntaxPunctuation: string
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/** Theme mode */
|
|
166
|
+
export type ThemeMode = "dark" | "light"
|
|
167
|
+
|
|
168
|
+
/** Theme name from bundled themes */
|
|
169
|
+
export type BundledThemeName =
|
|
170
|
+
| "aura"
|
|
171
|
+
| "ayu"
|
|
172
|
+
| "carbonfox"
|
|
173
|
+
| "catppuccin"
|
|
174
|
+
| "catppuccin-frappe"
|
|
175
|
+
| "catppuccin-macchiato"
|
|
176
|
+
| "cobalt2"
|
|
177
|
+
| "cursor"
|
|
178
|
+
| "dracula"
|
|
179
|
+
| "everforest"
|
|
180
|
+
| "flexoki"
|
|
181
|
+
| "github"
|
|
182
|
+
| "gruvbox"
|
|
183
|
+
| "kanagawa"
|
|
184
|
+
| "lucent-orng"
|
|
185
|
+
| "material"
|
|
186
|
+
| "matrix"
|
|
187
|
+
| "mercury"
|
|
188
|
+
| "monokai"
|
|
189
|
+
| "nightowl"
|
|
190
|
+
| "nord"
|
|
191
|
+
| "one-dark"
|
|
192
|
+
| "opencode"
|
|
193
|
+
| "orng"
|
|
194
|
+
| "osaka-jade"
|
|
195
|
+
| "palenight"
|
|
196
|
+
| "rosepine"
|
|
197
|
+
| "solarized"
|
|
198
|
+
| "synthwave84"
|
|
199
|
+
| "tokyonight"
|
|
200
|
+
| "vercel"
|
|
201
|
+
| "vesper"
|
|
202
|
+
| "zenburn"
|
|
203
|
+
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// Type Guards
|
|
206
|
+
// ============================================================================
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Check if a value is a hex color string
|
|
210
|
+
*/
|
|
211
|
+
export function isHexColor(val: unknown): val is HexColor {
|
|
212
|
+
return typeof val === "string" && val.startsWith("#")
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Check if a value is a color variant object
|
|
217
|
+
*/
|
|
218
|
+
export function isColorVariant(val: unknown): val is ColorVariant {
|
|
219
|
+
return (
|
|
220
|
+
typeof val === "object" &&
|
|
221
|
+
val !== null &&
|
|
222
|
+
"dark" in val &&
|
|
223
|
+
"light" in val
|
|
224
|
+
)
|
|
225
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ANSI SGR sequence parser
|
|
3
|
+
*
|
|
4
|
+
* Parses ANSI escape sequences and returns styled text segments
|
|
5
|
+
* for rendering in the Preview component.
|
|
6
|
+
*
|
|
7
|
+
* Handles SGR (Select Graphic Rendition) sequences for colors/styles
|
|
8
|
+
* and strips other control sequences (cursor movement, screen clearing, etc.)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Strip non-SGR ANSI sequences from text
|
|
13
|
+
*
|
|
14
|
+
* Removes cursor movement, screen clearing, and other control sequences
|
|
15
|
+
* while preserving SGR color/style sequences (those ending with 'm')
|
|
16
|
+
*/
|
|
17
|
+
function stripNonSGRSequences(text: string): string {
|
|
18
|
+
// Match CSI sequences that are NOT SGR (don't end with 'm')
|
|
19
|
+
// CSI format: ESC [ <params> <final byte>
|
|
20
|
+
// SGR ends with 'm', others end with A-L, P-Z, @, etc.
|
|
21
|
+
// Also strips OSC sequences (ESC ] ... BEL/ST)
|
|
22
|
+
return text
|
|
23
|
+
// Remove CSI sequences that don't end with 'm' (cursor movement, clearing, etc.)
|
|
24
|
+
.replace(/\x1B\[[0-9;]*[A-LN-Za-ln-z@`{}|~]/g, "")
|
|
25
|
+
// Remove OSC sequences (ESC ] ... BEL or ESC ] ... ESC \)
|
|
26
|
+
.replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)?/g, "")
|
|
27
|
+
// Remove other escape sequences (ESC followed by single char)
|
|
28
|
+
.replace(/\x1B[^[\]]/g, "")
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AnsiSegment {
|
|
32
|
+
text: string
|
|
33
|
+
fg?: string // Hex color
|
|
34
|
+
bg?: string // Hex color
|
|
35
|
+
bold?: boolean
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Standard 16-color palette (colors 0-15)
|
|
39
|
+
const STANDARD_COLORS: Record<number, string> = {
|
|
40
|
+
0: "#000000", // Black
|
|
41
|
+
1: "#cd0000", // Red
|
|
42
|
+
2: "#00cd00", // Green
|
|
43
|
+
3: "#cdcd00", // Yellow
|
|
44
|
+
4: "#0000ee", // Blue
|
|
45
|
+
5: "#cd00cd", // Magenta
|
|
46
|
+
6: "#00cdcd", // Cyan
|
|
47
|
+
7: "#e5e5e5", // White
|
|
48
|
+
8: "#7f7f7f", // Bright Black (Gray)
|
|
49
|
+
9: "#ff0000", // Bright Red
|
|
50
|
+
10: "#00ff00", // Bright Green
|
|
51
|
+
11: "#ffff00", // Bright Yellow
|
|
52
|
+
12: "#5c5cff", // Bright Blue
|
|
53
|
+
13: "#ff00ff", // Bright Magenta
|
|
54
|
+
14: "#00ffff", // Bright Cyan
|
|
55
|
+
15: "#ffffff", // Bright White
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Convert 256-color index to hex color
|
|
60
|
+
*/
|
|
61
|
+
function color256ToHex(n: number): string {
|
|
62
|
+
// Standard colors (0-15)
|
|
63
|
+
if (n < 16) {
|
|
64
|
+
return STANDARD_COLORS[n] ?? "#ffffff"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Color cube (16-231): 6x6x6 cube
|
|
68
|
+
if (n < 232) {
|
|
69
|
+
const idx = n - 16
|
|
70
|
+
const r = Math.floor(idx / 36)
|
|
71
|
+
const g = Math.floor((idx % 36) / 6)
|
|
72
|
+
const b = idx % 6
|
|
73
|
+
|
|
74
|
+
const toHex = (v: number) => (v === 0 ? 0 : 55 + v * 40).toString(16).padStart(2, "0")
|
|
75
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Grayscale (232-255): 24 shades
|
|
79
|
+
const gray = (n - 232) * 10 + 8
|
|
80
|
+
const hex = gray.toString(16).padStart(2, "0")
|
|
81
|
+
return `#${hex}${hex}${hex}`
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Convert RGB values to hex color
|
|
86
|
+
*/
|
|
87
|
+
function rgbToHex(r: number, g: number, b: number): string {
|
|
88
|
+
const toHex = (v: number) => Math.max(0, Math.min(255, v)).toString(16).padStart(2, "0")
|
|
89
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
interface AnsiState {
|
|
93
|
+
fg?: string
|
|
94
|
+
bg?: string
|
|
95
|
+
bold: boolean
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Parse SGR (Select Graphic Rendition) parameters and update state
|
|
100
|
+
*/
|
|
101
|
+
function parseSGR(params: number[], state: AnsiState): void {
|
|
102
|
+
let i = 0
|
|
103
|
+
while (i < params.length) {
|
|
104
|
+
const code = params[i] ?? 0
|
|
105
|
+
|
|
106
|
+
if (code === 0) {
|
|
107
|
+
// Reset all
|
|
108
|
+
state.fg = undefined
|
|
109
|
+
state.bg = undefined
|
|
110
|
+
state.bold = false
|
|
111
|
+
} else if (code === 1) {
|
|
112
|
+
// Bold on
|
|
113
|
+
state.bold = true
|
|
114
|
+
} else if (code === 22) {
|
|
115
|
+
// Bold off
|
|
116
|
+
state.bold = false
|
|
117
|
+
} else if (code >= 30 && code <= 37) {
|
|
118
|
+
// Standard foreground colors
|
|
119
|
+
state.fg = STANDARD_COLORS[code - 30]
|
|
120
|
+
} else if (code === 39) {
|
|
121
|
+
// Default foreground
|
|
122
|
+
state.fg = undefined
|
|
123
|
+
} else if (code >= 40 && code <= 47) {
|
|
124
|
+
// Standard background colors
|
|
125
|
+
state.bg = STANDARD_COLORS[code - 40]
|
|
126
|
+
} else if (code === 49) {
|
|
127
|
+
// Default background
|
|
128
|
+
state.bg = undefined
|
|
129
|
+
} else if (code >= 90 && code <= 97) {
|
|
130
|
+
// Bright foreground colors
|
|
131
|
+
state.fg = STANDARD_COLORS[code - 90 + 8]
|
|
132
|
+
} else if (code >= 100 && code <= 107) {
|
|
133
|
+
// Bright background colors
|
|
134
|
+
state.bg = STANDARD_COLORS[code - 100 + 8]
|
|
135
|
+
} else if (code === 38) {
|
|
136
|
+
// Extended foreground color
|
|
137
|
+
const nextParam = params[i + 1]
|
|
138
|
+
const colorIndex = params[i + 2]
|
|
139
|
+
if (nextParam === 5 && colorIndex !== undefined) {
|
|
140
|
+
// 256-color mode: 38;5;N
|
|
141
|
+
state.fg = color256ToHex(colorIndex)
|
|
142
|
+
i += 2
|
|
143
|
+
} else if (nextParam === 2 && params[i + 4] !== undefined) {
|
|
144
|
+
// True color mode: 38;2;R;G;B
|
|
145
|
+
state.fg = rgbToHex(params[i + 2] ?? 0, params[i + 3] ?? 0, params[i + 4] ?? 0)
|
|
146
|
+
i += 4
|
|
147
|
+
}
|
|
148
|
+
} else if (code === 48) {
|
|
149
|
+
// Extended background color
|
|
150
|
+
const nextParam = params[i + 1]
|
|
151
|
+
const colorIndex = params[i + 2]
|
|
152
|
+
if (nextParam === 5 && colorIndex !== undefined) {
|
|
153
|
+
// 256-color mode: 48;5;N
|
|
154
|
+
state.bg = color256ToHex(colorIndex)
|
|
155
|
+
i += 2
|
|
156
|
+
} else if (nextParam === 2 && params[i + 4] !== undefined) {
|
|
157
|
+
// True color mode: 48;2;R;G;B
|
|
158
|
+
state.bg = rgbToHex(params[i + 2] ?? 0, params[i + 3] ?? 0, params[i + 4] ?? 0)
|
|
159
|
+
i += 4
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Ignore unhandled codes
|
|
163
|
+
|
|
164
|
+
i++
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Parse a line containing ANSI escape sequences into styled segments
|
|
170
|
+
*/
|
|
171
|
+
export function parseAnsiLine(line: string): AnsiSegment[] {
|
|
172
|
+
const segments: AnsiSegment[] = []
|
|
173
|
+
const state: AnsiState = { bold: false }
|
|
174
|
+
|
|
175
|
+
// First strip non-SGR sequences (cursor movement, etc.) that would cause garbling
|
|
176
|
+
const cleanedLine = stripNonSGRSequences(line)
|
|
177
|
+
|
|
178
|
+
// Match ANSI SGR sequences: ESC [ params m
|
|
179
|
+
const regex = /\x1B\[([0-9;]*)m/g
|
|
180
|
+
let lastIndex = 0
|
|
181
|
+
let match: RegExpExecArray | null
|
|
182
|
+
|
|
183
|
+
while ((match = regex.exec(cleanedLine)) !== null) {
|
|
184
|
+
// Add text before this escape sequence
|
|
185
|
+
if (match.index > lastIndex) {
|
|
186
|
+
const text = cleanedLine.slice(lastIndex, match.index)
|
|
187
|
+
if (text) {
|
|
188
|
+
segments.push({
|
|
189
|
+
text,
|
|
190
|
+
fg: state.fg,
|
|
191
|
+
bg: state.bg,
|
|
192
|
+
bold: state.bold || undefined,
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Parse SGR parameters
|
|
198
|
+
const paramStr = match[1] ?? ""
|
|
199
|
+
const params = paramStr === "" ? [0] : paramStr.split(";").map((s) => parseInt(s, 10) || 0)
|
|
200
|
+
parseSGR(params, state)
|
|
201
|
+
|
|
202
|
+
lastIndex = regex.lastIndex
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Add remaining text after last escape sequence
|
|
206
|
+
if (lastIndex < cleanedLine.length) {
|
|
207
|
+
const text = cleanedLine.slice(lastIndex)
|
|
208
|
+
if (text) {
|
|
209
|
+
segments.push({
|
|
210
|
+
text,
|
|
211
|
+
fg: state.fg,
|
|
212
|
+
bg: state.bg,
|
|
213
|
+
bold: state.bold || undefined,
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// If no segments, return single segment with cleaned text
|
|
219
|
+
if (segments.length === 0) {
|
|
220
|
+
const stripped = cleanedLine.replace(/\x1B\[[0-9;]*m/g, "")
|
|
221
|
+
return [{ text: stripped || " " }]
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return segments
|
|
225
|
+
}
|