@kitlangton/ghui 0.1.12 → 0.1.14
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/package.json +1 -1
- package/src/App.tsx +27 -24
- package/src/ui/DetailsPane.tsx +7 -3
- package/src/ui/FooterHints.tsx +1 -1
- package/src/ui/colors.ts +216 -1
- package/src/ui/modals.tsx +2 -3
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -198,7 +198,7 @@ const copyPullRequestMetadata = async (pullRequest: PullRequestItem) => {
|
|
|
198
198
|
|
|
199
199
|
const isShiftG = (key: { readonly name: string; readonly shift?: boolean }) => key.name === "G" || key.name === "g" && key.shift
|
|
200
200
|
|
|
201
|
-
const
|
|
201
|
+
const isThemeKey = (key: { readonly name: string; readonly ctrl?: boolean; readonly meta?: boolean }) => !key.ctrl && !key.meta && key.name.toLowerCase() === "t"
|
|
202
202
|
|
|
203
203
|
const getDetailPlaceholderContent = ({
|
|
204
204
|
status,
|
|
@@ -278,8 +278,10 @@ export const App = () => {
|
|
|
278
278
|
const getPullRequestDiff = useAtomSet(getPullRequestDiffAtom, { mode: "promise" })
|
|
279
279
|
const getPullRequestMergeInfo = useAtomSet(getPullRequestMergeInfoAtom, { mode: "promise" })
|
|
280
280
|
const mergePullRequest = useAtomSet(mergePullRequestAtom, { mode: "promise" })
|
|
281
|
-
const
|
|
282
|
-
const
|
|
281
|
+
const terminalWidth = width ?? 100
|
|
282
|
+
const terminalHeight = height ?? 24
|
|
283
|
+
const contentWidth = Math.max(1, terminalWidth)
|
|
284
|
+
const isWideLayout = terminalWidth >= 100
|
|
283
285
|
const splitGap = 1
|
|
284
286
|
const sectionPadding = 1
|
|
285
287
|
const leftPaneWidth = isWideLayout ? Math.max(44, Math.floor((contentWidth - splitGap) * 0.56)) : contentWidth
|
|
@@ -287,8 +289,8 @@ export const App = () => {
|
|
|
287
289
|
const dividerJunctionAt = Math.max(1, leftPaneWidth)
|
|
288
290
|
const leftContentWidth = isWideLayout ? Math.max(24, leftPaneWidth - 3) : Math.max(24, contentWidth - sectionPadding * 2)
|
|
289
291
|
const rightContentWidth = isWideLayout ? Math.max(24, rightPaneWidth - sectionPadding * 2) : Math.max(24, contentWidth - sectionPadding * 2)
|
|
290
|
-
const wideDetailLines = Math.max(8,
|
|
291
|
-
const wideBodyHeight = Math.max(8,
|
|
292
|
+
const wideDetailLines = Math.max(8, terminalHeight - 8) // fill available vertical space
|
|
293
|
+
const wideBodyHeight = Math.max(8, terminalHeight - 4)
|
|
292
294
|
const noticeTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
293
295
|
const pendingGTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
294
296
|
const diffPrefetchTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
@@ -557,13 +559,12 @@ export const App = () => {
|
|
|
557
559
|
setMergeModal(initialMergeModalState)
|
|
558
560
|
setThemeModal({
|
|
559
561
|
open: true,
|
|
560
|
-
selectedIndex: Math.max(0, themeDefinitions.findIndex((theme) => theme.id === themeId)),
|
|
561
562
|
initialThemeId: themeId,
|
|
562
563
|
})
|
|
563
564
|
}
|
|
564
565
|
|
|
565
566
|
const closeThemeModal = (confirm: boolean) => {
|
|
566
|
-
const selectedTheme = themeDefinitions
|
|
567
|
+
const selectedTheme = themeDefinitions.find((theme) => theme.id === themeId)
|
|
567
568
|
if (!confirm) {
|
|
568
569
|
setThemeId(themeModal.initialThemeId)
|
|
569
570
|
} else if (selectedTheme) {
|
|
@@ -573,11 +574,11 @@ export const App = () => {
|
|
|
573
574
|
}
|
|
574
575
|
|
|
575
576
|
const moveThemeSelection = (delta: number) => {
|
|
576
|
-
const
|
|
577
|
-
|
|
577
|
+
const currentIndex = Math.max(0, themeDefinitions.findIndex((theme) => theme.id === themeId))
|
|
578
|
+
const selectedIndex = Math.max(0, Math.min(themeDefinitions.length - 1, currentIndex + delta))
|
|
579
|
+
if (selectedIndex === currentIndex) return
|
|
578
580
|
const theme = themeDefinitions[selectedIndex]
|
|
579
581
|
if (theme && theme.id !== themeId) setThemeId(theme.id)
|
|
580
|
-
setThemeModal((current) => ({ ...current, selectedIndex }))
|
|
581
582
|
}
|
|
582
583
|
|
|
583
584
|
const openLabelModal = () => {
|
|
@@ -1032,7 +1033,7 @@ export const App = () => {
|
|
|
1032
1033
|
}
|
|
1033
1034
|
}
|
|
1034
1035
|
|
|
1035
|
-
if (
|
|
1036
|
+
if (isThemeKey(key)) {
|
|
1036
1037
|
openThemeModal()
|
|
1037
1038
|
return
|
|
1038
1039
|
}
|
|
@@ -1183,7 +1184,7 @@ export const App = () => {
|
|
|
1183
1184
|
})
|
|
1184
1185
|
|
|
1185
1186
|
const fullscreenContentWidth = Math.max(24, contentWidth - 2)
|
|
1186
|
-
const fullscreenBodyLines = Math.max(8,
|
|
1187
|
+
const fullscreenBodyLines = Math.max(8, terminalHeight - 8)
|
|
1187
1188
|
const wideFullscreenDetailScrollable = getDetailsPaneHeight({
|
|
1188
1189
|
pullRequest: selectedPullRequest,
|
|
1189
1190
|
contentWidth: fullscreenContentWidth,
|
|
@@ -1214,20 +1215,20 @@ export const App = () => {
|
|
|
1214
1215
|
|
|
1215
1216
|
const longestLabelName = labelModal.availableLabels.reduce((max, label) => Math.max(max, label.name.length), 0)
|
|
1216
1217
|
const labelModalWidth = Math.min(Math.max(42, longestLabelName + 16), 56, contentWidth - 4)
|
|
1217
|
-
const labelModalHeight = Math.min(20,
|
|
1218
|
+
const labelModalHeight = Math.min(20, terminalHeight - 4)
|
|
1218
1219
|
const labelModalLeft = Math.floor((contentWidth - labelModalWidth) / 2)
|
|
1219
|
-
const labelModalTop = Math.floor((
|
|
1220
|
+
const labelModalTop = Math.floor((terminalHeight - labelModalHeight) / 2)
|
|
1220
1221
|
const mergeModalWidth = Math.min(68, Math.max(46, contentWidth - 12))
|
|
1221
|
-
const mergeModalHeight = Math.min(16,
|
|
1222
|
+
const mergeModalHeight = Math.min(16, terminalHeight - 4)
|
|
1222
1223
|
const mergeModalLeft = Math.floor((contentWidth - mergeModalWidth) / 2)
|
|
1223
|
-
const mergeModalTop = Math.floor((
|
|
1224
|
+
const mergeModalTop = Math.floor((terminalHeight - mergeModalHeight) / 2)
|
|
1224
1225
|
const themeModalWidth = Math.min(58, Math.max(38, contentWidth - 12))
|
|
1225
|
-
const themeModalHeight = Math.min(
|
|
1226
|
+
const themeModalHeight = Math.min(16, terminalHeight - 4)
|
|
1226
1227
|
const themeModalLeft = Math.floor((contentWidth - themeModalWidth) / 2)
|
|
1227
|
-
const themeModalTop = Math.floor((
|
|
1228
|
+
const themeModalTop = Math.floor((terminalHeight - themeModalHeight) / 2)
|
|
1228
1229
|
|
|
1229
1230
|
return (
|
|
1230
|
-
<box
|
|
1231
|
+
<box width={terminalWidth} height={terminalHeight} flexDirection="column" backgroundColor={colors.background}>
|
|
1231
1232
|
<box paddingLeft={1} paddingRight={1} flexDirection="column" backgroundColor={colors.panel}>
|
|
1232
1233
|
<PlainLine text={headerLine} fg={colors.muted} bold />
|
|
1233
1234
|
</box>
|
|
@@ -1262,6 +1263,7 @@ export const App = () => {
|
|
|
1262
1263
|
showChecks
|
|
1263
1264
|
placeholderContent={detailPlaceholderContent}
|
|
1264
1265
|
loadingIndicator={loadingIndicator}
|
|
1266
|
+
themeId={themeId}
|
|
1265
1267
|
/>
|
|
1266
1268
|
</scrollbox>
|
|
1267
1269
|
</box>
|
|
@@ -1278,7 +1280,7 @@ export const App = () => {
|
|
|
1278
1280
|
<>
|
|
1279
1281
|
<DetailHeader pullRequest={selectedPullRequest} contentWidth={rightContentWidth} paneWidth={rightPaneWidth} showChecks />
|
|
1280
1282
|
<scrollbox flexGrow={1} verticalScrollbarOptions={{ visible: wideDetailBodyScrollable }}>
|
|
1281
|
-
<DetailBody pullRequest={selectedPullRequest} contentWidth={rightContentWidth} bodyLines={wideDetailLines} loadingIndicator={loadingIndicator} />
|
|
1283
|
+
<DetailBody pullRequest={selectedPullRequest} contentWidth={rightContentWidth} bodyLines={wideDetailLines} loadingIndicator={loadingIndicator} themeId={themeId} />
|
|
1282
1284
|
</scrollbox>
|
|
1283
1285
|
</>
|
|
1284
1286
|
) : (
|
|
@@ -1296,12 +1298,13 @@ export const App = () => {
|
|
|
1296
1298
|
paneWidth={contentWidth}
|
|
1297
1299
|
placeholderContent={detailPlaceholderContent}
|
|
1298
1300
|
loadingIndicator={loadingIndicator}
|
|
1301
|
+
themeId={themeId}
|
|
1299
1302
|
/>
|
|
1300
1303
|
</scrollbox>
|
|
1301
1304
|
</box>
|
|
1302
1305
|
) : (
|
|
1303
|
-
|
|
1304
|
-
<DetailsPane pullRequest={selectedPullRequest} contentWidth={rightContentWidth} paneWidth={contentWidth} placeholderContent={detailPlaceholderContent} loadingIndicator={loadingIndicator} />
|
|
1306
|
+
<box height={wideBodyHeight} flexDirection="column">
|
|
1307
|
+
<DetailsPane pullRequest={selectedPullRequest} contentWidth={rightContentWidth} paneWidth={contentWidth} placeholderContent={detailPlaceholderContent} loadingIndicator={loadingIndicator} themeId={themeId} />
|
|
1305
1308
|
<Divider width={contentWidth} />
|
|
1306
1309
|
<box flexGrow={1} flexDirection="column">
|
|
1307
1310
|
<scrollbox flexGrow={1}>
|
|
@@ -1310,7 +1313,7 @@ export const App = () => {
|
|
|
1310
1313
|
</box>
|
|
1311
1314
|
</scrollbox>
|
|
1312
1315
|
</box>
|
|
1313
|
-
|
|
1316
|
+
</box>
|
|
1314
1317
|
)}
|
|
1315
1318
|
|
|
1316
1319
|
{isWideLayout && !detailFullView && !diffFullView && !isInitialLoading ? (
|
|
@@ -1318,7 +1321,7 @@ export const App = () => {
|
|
|
1318
1321
|
) : (
|
|
1319
1322
|
<Divider width={contentWidth} />
|
|
1320
1323
|
)}
|
|
1321
|
-
<box paddingLeft={1} paddingRight={1} backgroundColor={colors.
|
|
1324
|
+
<box paddingLeft={1} paddingRight={1} backgroundColor={colors.footer}>
|
|
1322
1325
|
{footerNotice ? (
|
|
1323
1326
|
<PlainLine text={footerNotice} fg={colors.count} />
|
|
1324
1327
|
) : (
|
package/src/ui/DetailsPane.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { TextAttributes } from "@opentui/core"
|
|
|
2
2
|
import { Fragment, useMemo } from "react"
|
|
3
3
|
import { formatRelativeDate } from "../date.js"
|
|
4
4
|
import type { CheckItem, PullRequestItem } from "../domain.js"
|
|
5
|
-
import { colors } from "./colors.js"
|
|
5
|
+
import { colors, type ThemeId } from "./colors.js"
|
|
6
6
|
import { diffStatText } from "./diff.js"
|
|
7
7
|
import { centerCell, Divider, fitCell, PlainLine, TextLine } from "./primitives.js"
|
|
8
8
|
import { labelColor, labelTextColor, reviewLabel, shortRepoName, statusColor } from "./pullRequests.js"
|
|
@@ -387,15 +387,17 @@ export const DetailBody = ({
|
|
|
387
387
|
contentWidth,
|
|
388
388
|
bodyLines = DETAIL_BODY_LINES,
|
|
389
389
|
loadingIndicator,
|
|
390
|
+
themeId,
|
|
390
391
|
}: {
|
|
391
392
|
pullRequest: PullRequestItem
|
|
392
393
|
contentWidth: number
|
|
393
394
|
bodyLines?: number
|
|
394
395
|
loadingIndicator: string
|
|
396
|
+
themeId: ThemeId
|
|
395
397
|
}) => {
|
|
396
398
|
const previewLines = useMemo(
|
|
397
399
|
() => bodyPreview(pullRequest.body, contentWidth, bodyLines),
|
|
398
|
-
[pullRequest.body, contentWidth, bodyLines],
|
|
400
|
+
[pullRequest.body, contentWidth, bodyLines, themeId],
|
|
399
401
|
)
|
|
400
402
|
|
|
401
403
|
if (!pullRequest.detailLoaded) {
|
|
@@ -486,6 +488,7 @@ export const DetailsPane = ({
|
|
|
486
488
|
showChecks = false,
|
|
487
489
|
placeholderContent,
|
|
488
490
|
loadingIndicator,
|
|
491
|
+
themeId,
|
|
489
492
|
}: {
|
|
490
493
|
pullRequest: PullRequestItem | null
|
|
491
494
|
contentWidth: number
|
|
@@ -494,6 +497,7 @@ export const DetailsPane = ({
|
|
|
494
497
|
showChecks?: boolean
|
|
495
498
|
placeholderContent: DetailPlaceholderContent
|
|
496
499
|
loadingIndicator: string
|
|
500
|
+
themeId: ThemeId
|
|
497
501
|
}) => {
|
|
498
502
|
const contentHeight = getDetailsPaneHeight({ pullRequest, contentWidth, bodyLines, paneWidth, showChecks })
|
|
499
503
|
|
|
@@ -502,7 +506,7 @@ export const DetailsPane = ({
|
|
|
502
506
|
{pullRequest ? (
|
|
503
507
|
<>
|
|
504
508
|
<DetailHeader pullRequest={pullRequest} contentWidth={contentWidth} paneWidth={paneWidth} showChecks={showChecks} />
|
|
505
|
-
<DetailBody pullRequest={pullRequest} contentWidth={contentWidth} bodyLines={bodyLines} loadingIndicator={loadingIndicator} />
|
|
509
|
+
<DetailBody pullRequest={pullRequest} contentWidth={contentWidth} bodyLines={bodyLines} loadingIndicator={loadingIndicator} themeId={themeId} />
|
|
506
510
|
</>
|
|
507
511
|
) : (
|
|
508
512
|
<>
|
package/src/ui/FooterHints.tsx
CHANGED
|
@@ -86,7 +86,7 @@ export const FooterHints = ({
|
|
|
86
86
|
<TextLine>
|
|
87
87
|
<span fg={colors.count}>/</span>
|
|
88
88
|
<span fg={colors.muted}> filter </span>
|
|
89
|
-
<span fg={colors.count}>
|
|
89
|
+
<span fg={colors.count}>t</span>
|
|
90
90
|
<span fg={colors.muted}> theme </span>
|
|
91
91
|
{showFilterClear ? (
|
|
92
92
|
<>
|
package/src/ui/colors.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export type ThemeId = "ghui" | "tokyo-night" | "opencode"
|
|
1
|
+
export type ThemeId = "ghui" | "tokyo-night" | "catppuccin" | "rose-pine" | "gruvbox" | "nord" | "dracula" | "opencode"
|
|
2
2
|
|
|
3
3
|
export interface ColorPalette {
|
|
4
4
|
readonly background: string
|
|
5
5
|
readonly panel: string
|
|
6
|
+
readonly footer: string
|
|
6
7
|
readonly modalBackground: string
|
|
7
8
|
readonly text: string
|
|
8
9
|
readonly muted: string
|
|
@@ -50,6 +51,7 @@ export interface ThemeDefinition {
|
|
|
50
51
|
const ghuiColors: ColorPalette = {
|
|
51
52
|
background: "#111018",
|
|
52
53
|
panel: "#161923",
|
|
54
|
+
footer: "#1d2430",
|
|
53
55
|
modalBackground: "#1a1a2e",
|
|
54
56
|
text: "#ede7da",
|
|
55
57
|
muted: "#9f9788",
|
|
@@ -90,6 +92,7 @@ const ghuiColors: ColorPalette = {
|
|
|
90
92
|
const tokyoNightColors: ColorPalette = {
|
|
91
93
|
background: "#1a1b26",
|
|
92
94
|
panel: "#16161e",
|
|
95
|
+
footer: "#24283b",
|
|
93
96
|
modalBackground: "#24283b",
|
|
94
97
|
text: "#c0caf5",
|
|
95
98
|
muted: "#787c99",
|
|
@@ -130,6 +133,7 @@ const tokyoNightColors: ColorPalette = {
|
|
|
130
133
|
const opencodeColors: ColorPalette = {
|
|
131
134
|
background: "#0a0a0a",
|
|
132
135
|
panel: "#141414",
|
|
136
|
+
footer: "#1e1e1e",
|
|
133
137
|
modalBackground: "#1e1e1e",
|
|
134
138
|
text: "#eeeeee",
|
|
135
139
|
muted: "#808080",
|
|
@@ -167,9 +171,219 @@ const opencodeColors: ColorPalette = {
|
|
|
167
171
|
},
|
|
168
172
|
}
|
|
169
173
|
|
|
174
|
+
const catppuccinColors: ColorPalette = {
|
|
175
|
+
background: "#1e1e2e",
|
|
176
|
+
panel: "#181825",
|
|
177
|
+
footer: "#313244",
|
|
178
|
+
modalBackground: "#313244",
|
|
179
|
+
text: "#cdd6f4",
|
|
180
|
+
muted: "#7f849c",
|
|
181
|
+
separator: "#45475a",
|
|
182
|
+
accent: "#cba6f7",
|
|
183
|
+
inlineCode: "#f5c2e7",
|
|
184
|
+
error: "#f38ba8",
|
|
185
|
+
selectedBg: "#45475a",
|
|
186
|
+
selectedText: "#f5e0dc",
|
|
187
|
+
count: "#fab387",
|
|
188
|
+
status: {
|
|
189
|
+
draft: "#f9e2af",
|
|
190
|
+
approved: "#a6e3a1",
|
|
191
|
+
changes: "#f38ba8",
|
|
192
|
+
review: "#89b4fa",
|
|
193
|
+
none: "#7f849c",
|
|
194
|
+
passing: "#a6e3a1",
|
|
195
|
+
pending: "#f9e2af",
|
|
196
|
+
failing: "#f38ba8",
|
|
197
|
+
},
|
|
198
|
+
repos: {
|
|
199
|
+
opencode: "#89b4fa",
|
|
200
|
+
"effect-smol": "#a6e3a1",
|
|
201
|
+
"opencode-console": "#f5c2e7",
|
|
202
|
+
opencontrol: "#fab387",
|
|
203
|
+
default: "#74c7ec",
|
|
204
|
+
},
|
|
205
|
+
diff: {
|
|
206
|
+
addedBg: "#243927",
|
|
207
|
+
removedBg: "#3b2532",
|
|
208
|
+
contextBg: "transparent",
|
|
209
|
+
lineNumberBg: "#181825",
|
|
210
|
+
addedLineNumberBg: "#203524",
|
|
211
|
+
removedLineNumberBg: "#36232f",
|
|
212
|
+
},
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const rosePineColors: ColorPalette = {
|
|
216
|
+
background: "#191724",
|
|
217
|
+
panel: "#1f1d2e",
|
|
218
|
+
footer: "#26233a",
|
|
219
|
+
modalBackground: "#26233a",
|
|
220
|
+
text: "#e0def4",
|
|
221
|
+
muted: "#908caa",
|
|
222
|
+
separator: "#524f67",
|
|
223
|
+
accent: "#c4a7e7",
|
|
224
|
+
inlineCode: "#f6c177",
|
|
225
|
+
error: "#eb6f92",
|
|
226
|
+
selectedBg: "#403d52",
|
|
227
|
+
selectedText: "#f6f1ff",
|
|
228
|
+
count: "#ebbcba",
|
|
229
|
+
status: {
|
|
230
|
+
draft: "#f6c177",
|
|
231
|
+
approved: "#9ccfd8",
|
|
232
|
+
changes: "#eb6f92",
|
|
233
|
+
review: "#31748f",
|
|
234
|
+
none: "#908caa",
|
|
235
|
+
passing: "#9ccfd8",
|
|
236
|
+
pending: "#f6c177",
|
|
237
|
+
failing: "#eb6f92",
|
|
238
|
+
},
|
|
239
|
+
repos: {
|
|
240
|
+
opencode: "#31748f",
|
|
241
|
+
"effect-smol": "#9ccfd8",
|
|
242
|
+
"opencode-console": "#c4a7e7",
|
|
243
|
+
opencontrol: "#f6c177",
|
|
244
|
+
default: "#ebbcba",
|
|
245
|
+
},
|
|
246
|
+
diff: {
|
|
247
|
+
addedBg: "#23343a",
|
|
248
|
+
removedBg: "#3a2534",
|
|
249
|
+
contextBg: "transparent",
|
|
250
|
+
lineNumberBg: "#1f1d2e",
|
|
251
|
+
addedLineNumberBg: "#203137",
|
|
252
|
+
removedLineNumberBg: "#352330",
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const gruvboxColors: ColorPalette = {
|
|
257
|
+
background: "#282828",
|
|
258
|
+
panel: "#1d2021",
|
|
259
|
+
footer: "#3c3836",
|
|
260
|
+
modalBackground: "#3c3836",
|
|
261
|
+
text: "#ebdbb2",
|
|
262
|
+
muted: "#928374",
|
|
263
|
+
separator: "#665c54",
|
|
264
|
+
accent: "#fabd2f",
|
|
265
|
+
inlineCode: "#d3869b",
|
|
266
|
+
error: "#fb4934",
|
|
267
|
+
selectedBg: "#504945",
|
|
268
|
+
selectedText: "#fbf1c7",
|
|
269
|
+
count: "#fe8019",
|
|
270
|
+
status: {
|
|
271
|
+
draft: "#fabd2f",
|
|
272
|
+
approved: "#b8bb26",
|
|
273
|
+
changes: "#fb4934",
|
|
274
|
+
review: "#83a598",
|
|
275
|
+
none: "#928374",
|
|
276
|
+
passing: "#b8bb26",
|
|
277
|
+
pending: "#fabd2f",
|
|
278
|
+
failing: "#fb4934",
|
|
279
|
+
},
|
|
280
|
+
repos: {
|
|
281
|
+
opencode: "#83a598",
|
|
282
|
+
"effect-smol": "#b8bb26",
|
|
283
|
+
"opencode-console": "#d3869b",
|
|
284
|
+
opencontrol: "#fe8019",
|
|
285
|
+
default: "#8ec07c",
|
|
286
|
+
},
|
|
287
|
+
diff: {
|
|
288
|
+
addedBg: "#32361f",
|
|
289
|
+
removedBg: "#3c2927",
|
|
290
|
+
contextBg: "transparent",
|
|
291
|
+
lineNumberBg: "#1d2021",
|
|
292
|
+
addedLineNumberBg: "#2f331e",
|
|
293
|
+
removedLineNumberBg: "#382726",
|
|
294
|
+
},
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const nordColors: ColorPalette = {
|
|
298
|
+
background: "#2e3440",
|
|
299
|
+
panel: "#242933",
|
|
300
|
+
footer: "#3b4252",
|
|
301
|
+
modalBackground: "#3b4252",
|
|
302
|
+
text: "#eceff4",
|
|
303
|
+
muted: "#8892a7",
|
|
304
|
+
separator: "#4c566a",
|
|
305
|
+
accent: "#88c0d0",
|
|
306
|
+
inlineCode: "#b48ead",
|
|
307
|
+
error: "#bf616a",
|
|
308
|
+
selectedBg: "#434c5e",
|
|
309
|
+
selectedText: "#eceff4",
|
|
310
|
+
count: "#ebcb8b",
|
|
311
|
+
status: {
|
|
312
|
+
draft: "#ebcb8b",
|
|
313
|
+
approved: "#a3be8c",
|
|
314
|
+
changes: "#bf616a",
|
|
315
|
+
review: "#81a1c1",
|
|
316
|
+
none: "#8892a7",
|
|
317
|
+
passing: "#a3be8c",
|
|
318
|
+
pending: "#ebcb8b",
|
|
319
|
+
failing: "#bf616a",
|
|
320
|
+
},
|
|
321
|
+
repos: {
|
|
322
|
+
opencode: "#81a1c1",
|
|
323
|
+
"effect-smol": "#a3be8c",
|
|
324
|
+
"opencode-console": "#b48ead",
|
|
325
|
+
opencontrol: "#d08770",
|
|
326
|
+
default: "#88c0d0",
|
|
327
|
+
},
|
|
328
|
+
diff: {
|
|
329
|
+
addedBg: "#334033",
|
|
330
|
+
removedBg: "#433238",
|
|
331
|
+
contextBg: "transparent",
|
|
332
|
+
lineNumberBg: "#242933",
|
|
333
|
+
addedLineNumberBg: "#303d31",
|
|
334
|
+
removedLineNumberBg: "#3f3036",
|
|
335
|
+
},
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const draculaColors: ColorPalette = {
|
|
339
|
+
background: "#282a36",
|
|
340
|
+
panel: "#21222c",
|
|
341
|
+
footer: "#343746",
|
|
342
|
+
modalBackground: "#343746",
|
|
343
|
+
text: "#f8f8f2",
|
|
344
|
+
muted: "#8f94b8",
|
|
345
|
+
separator: "#4f5268",
|
|
346
|
+
accent: "#bd93f9",
|
|
347
|
+
inlineCode: "#ff79c6",
|
|
348
|
+
error: "#ff5555",
|
|
349
|
+
selectedBg: "#44475a",
|
|
350
|
+
selectedText: "#f8f8f2",
|
|
351
|
+
count: "#ffb86c",
|
|
352
|
+
status: {
|
|
353
|
+
draft: "#f1fa8c",
|
|
354
|
+
approved: "#50fa7b",
|
|
355
|
+
changes: "#ff5555",
|
|
356
|
+
review: "#8be9fd",
|
|
357
|
+
none: "#8f94b8",
|
|
358
|
+
passing: "#50fa7b",
|
|
359
|
+
pending: "#f1fa8c",
|
|
360
|
+
failing: "#ff5555",
|
|
361
|
+
},
|
|
362
|
+
repos: {
|
|
363
|
+
opencode: "#8be9fd",
|
|
364
|
+
"effect-smol": "#50fa7b",
|
|
365
|
+
"opencode-console": "#ff79c6",
|
|
366
|
+
opencontrol: "#ffb86c",
|
|
367
|
+
default: "#bd93f9",
|
|
368
|
+
},
|
|
369
|
+
diff: {
|
|
370
|
+
addedBg: "#203a29",
|
|
371
|
+
removedBg: "#43272f",
|
|
372
|
+
contextBg: "transparent",
|
|
373
|
+
lineNumberBg: "#21222c",
|
|
374
|
+
addedLineNumberBg: "#1d3627",
|
|
375
|
+
removedLineNumberBg: "#3d252c",
|
|
376
|
+
},
|
|
377
|
+
}
|
|
378
|
+
|
|
170
379
|
export const themeDefinitions: readonly ThemeDefinition[] = [
|
|
171
380
|
{ id: "ghui", name: "GHUI", description: "Warm parchment accents on a deep slate background", colors: ghuiColors },
|
|
172
381
|
{ id: "tokyo-night", name: "Tokyo Night", description: "Cool indigo surfaces with neon editor accents", colors: tokyoNightColors },
|
|
382
|
+
{ id: "catppuccin", name: "Catppuccin", description: "Mocha lavender, peach, and soft pastel contrast", colors: catppuccinColors },
|
|
383
|
+
{ id: "rose-pine", name: "Rose Pine", description: "Muted rose, pine, and gold on dusky violet", colors: rosePineColors },
|
|
384
|
+
{ id: "gruvbox", name: "Gruvbox", description: "Retro warm earth tones with punchy semantic accents", colors: gruvboxColors },
|
|
385
|
+
{ id: "nord", name: "Nord", description: "Arctic blue-gray surfaces with frosty accents", colors: nordColors },
|
|
386
|
+
{ id: "dracula", name: "Dracula", description: "High-contrast purple, pink, cyan, and green", colors: draculaColors },
|
|
173
387
|
{ id: "opencode", name: "OpenCode", description: "Charcoal panels with peach, violet, and blue highlights", colors: opencodeColors },
|
|
174
388
|
] as const
|
|
175
389
|
|
|
@@ -180,6 +394,7 @@ export const colors: ColorPalette = { ...ghuiColors }
|
|
|
180
394
|
export const getThemeDefinition = (id: ThemeId) => themeDefinitions.find((theme) => theme.id === id) ?? themeDefinitions[0]!
|
|
181
395
|
|
|
182
396
|
export const setActiveTheme = (id: ThemeId) => {
|
|
397
|
+
if (activeTheme.id === id) return
|
|
183
398
|
activeTheme = getThemeDefinition(id)
|
|
184
399
|
Object.assign(colors, activeTheme.colors)
|
|
185
400
|
}
|
package/src/ui/modals.tsx
CHANGED
|
@@ -27,7 +27,6 @@ export interface MergeModalState {
|
|
|
27
27
|
|
|
28
28
|
export interface ThemeModalState {
|
|
29
29
|
readonly open: boolean
|
|
30
|
-
readonly selectedIndex: number
|
|
31
30
|
readonly initialThemeId: ThemeId
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -53,7 +52,6 @@ export const initialMergeModalState: MergeModalState = {
|
|
|
53
52
|
|
|
54
53
|
export const initialThemeModalState: ThemeModalState = {
|
|
55
54
|
open: false,
|
|
56
|
-
selectedIndex: 0,
|
|
57
55
|
initialThemeId: "ghui",
|
|
58
56
|
}
|
|
59
57
|
|
|
@@ -279,7 +277,8 @@ export const ThemeModal = ({
|
|
|
279
277
|
const contentWidth = Math.max(14, innerWidth - 2)
|
|
280
278
|
const rowWidth = innerWidth
|
|
281
279
|
const maxVisible = Math.max(1, modalHeight - 7)
|
|
282
|
-
const
|
|
280
|
+
const activeIndex = themeDefinitions.findIndex((theme) => theme.id === activeThemeId)
|
|
281
|
+
const selectedIndex = Math.max(0, activeIndex)
|
|
283
282
|
const selectedTheme = themeDefinitions[selectedIndex]!
|
|
284
283
|
const scrollStart = Math.min(
|
|
285
284
|
Math.max(0, themeDefinitions.length - maxVisible),
|