@akiojin/gwt 4.11.6 → 4.12.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 (199) hide show
  1. package/bin/gwt.js +36 -10
  2. package/dist/claude.d.ts +1 -0
  3. package/dist/claude.d.ts.map +1 -1
  4. package/dist/claude.js +81 -24
  5. package/dist/claude.js.map +1 -1
  6. package/dist/cli/ui/App.solid.d.ts.map +1 -1
  7. package/dist/cli/ui/App.solid.js +280 -50
  8. package/dist/cli/ui/App.solid.js.map +1 -1
  9. package/dist/cli/ui/components/solid/QuickStartStep.d.ts.map +1 -1
  10. package/dist/cli/ui/components/solid/QuickStartStep.js +35 -22
  11. package/dist/cli/ui/components/solid/QuickStartStep.js.map +1 -1
  12. package/dist/cli/ui/components/solid/SelectInput.d.ts.map +1 -1
  13. package/dist/cli/ui/components/solid/SelectInput.js +2 -1
  14. package/dist/cli/ui/components/solid/SelectInput.js.map +1 -1
  15. package/dist/cli/ui/components/solid/WizardController.d.ts.map +1 -1
  16. package/dist/cli/ui/components/solid/WizardController.js +67 -13
  17. package/dist/cli/ui/components/solid/WizardController.js.map +1 -1
  18. package/dist/cli/ui/components/solid/WizardSteps.d.ts +5 -0
  19. package/dist/cli/ui/components/solid/WizardSteps.d.ts.map +1 -1
  20. package/dist/cli/ui/components/solid/WizardSteps.js +50 -70
  21. package/dist/cli/ui/components/solid/WizardSteps.js.map +1 -1
  22. package/dist/cli/ui/core/theme.d.ts +9 -0
  23. package/dist/cli/ui/core/theme.d.ts.map +1 -1
  24. package/dist/cli/ui/core/theme.js +21 -0
  25. package/dist/cli/ui/core/theme.js.map +1 -1
  26. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts +9 -2
  27. package/dist/cli/ui/screens/solid/BranchListScreen.d.ts.map +1 -1
  28. package/dist/cli/ui/screens/solid/BranchListScreen.js +101 -28
  29. package/dist/cli/ui/screens/solid/BranchListScreen.js.map +1 -1
  30. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts +2 -1
  31. package/dist/cli/ui/screens/solid/ConfirmScreen.d.ts.map +1 -1
  32. package/dist/cli/ui/screens/solid/ConfirmScreen.js +11 -3
  33. package/dist/cli/ui/screens/solid/ConfirmScreen.js.map +1 -1
  34. package/dist/cli/ui/screens/solid/EnvironmentScreen.d.ts.map +1 -1
  35. package/dist/cli/ui/screens/solid/EnvironmentScreen.js +9 -10
  36. package/dist/cli/ui/screens/solid/EnvironmentScreen.js.map +1 -1
  37. package/dist/cli/ui/screens/solid/LogScreen.d.ts +7 -1
  38. package/dist/cli/ui/screens/solid/LogScreen.d.ts.map +1 -1
  39. package/dist/cli/ui/screens/solid/LogScreen.js +254 -16
  40. package/dist/cli/ui/screens/solid/LogScreen.js.map +1 -1
  41. package/dist/cli/ui/screens/solid/ProfileEnvScreen.d.ts.map +1 -1
  42. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js +8 -5
  43. package/dist/cli/ui/screens/solid/ProfileEnvScreen.js.map +1 -1
  44. package/dist/cli/ui/screens/solid/SelectorScreen.d.ts.map +1 -1
  45. package/dist/cli/ui/screens/solid/SelectorScreen.js +12 -4
  46. package/dist/cli/ui/screens/solid/SelectorScreen.js.map +1 -1
  47. package/dist/cli/ui/types.d.ts +1 -0
  48. package/dist/cli/ui/types.d.ts.map +1 -1
  49. package/dist/cli/ui/utils/branchFormatter.d.ts +1 -0
  50. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  51. package/dist/cli/ui/utils/branchFormatter.js +29 -7
  52. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  53. package/dist/cli/ui/utils/continueSession.d.ts +14 -0
  54. package/dist/cli/ui/utils/continueSession.d.ts.map +1 -1
  55. package/dist/cli/ui/utils/continueSession.js +61 -3
  56. package/dist/cli/ui/utils/continueSession.js.map +1 -1
  57. package/dist/cli/ui/utils/installedVersionCache.d.ts +33 -0
  58. package/dist/cli/ui/utils/installedVersionCache.d.ts.map +1 -0
  59. package/dist/cli/ui/utils/installedVersionCache.js +59 -0
  60. package/dist/cli/ui/utils/installedVersionCache.js.map +1 -0
  61. package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
  62. package/dist/cli/ui/utils/modelOptions.js +16 -0
  63. package/dist/cli/ui/utils/modelOptions.js.map +1 -1
  64. package/dist/cli/ui/utils/versionCache.d.ts +37 -0
  65. package/dist/cli/ui/utils/versionCache.d.ts.map +1 -0
  66. package/dist/cli/ui/utils/versionCache.js +70 -0
  67. package/dist/cli/ui/utils/versionCache.js.map +1 -0
  68. package/dist/cli/ui/utils/versionFetcher.d.ts +41 -0
  69. package/dist/cli/ui/utils/versionFetcher.d.ts.map +1 -0
  70. package/dist/cli/ui/utils/versionFetcher.js +89 -0
  71. package/dist/cli/ui/utils/versionFetcher.js.map +1 -0
  72. package/dist/codex.d.ts +1 -0
  73. package/dist/codex.d.ts.map +1 -1
  74. package/dist/codex.js +95 -25
  75. package/dist/codex.js.map +1 -1
  76. package/dist/config/index.d.ts.map +1 -1
  77. package/dist/config/index.js +10 -1
  78. package/dist/config/index.js.map +1 -1
  79. package/dist/gemini.d.ts +1 -0
  80. package/dist/gemini.d.ts.map +1 -1
  81. package/dist/gemini.js +36 -3
  82. package/dist/gemini.js.map +1 -1
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +35 -2
  85. package/dist/index.js.map +1 -1
  86. package/dist/launcher.d.ts.map +1 -1
  87. package/dist/launcher.js +43 -8
  88. package/dist/launcher.js.map +1 -1
  89. package/dist/logging/agentOutput.d.ts +21 -0
  90. package/dist/logging/agentOutput.d.ts.map +1 -0
  91. package/dist/logging/agentOutput.js +164 -0
  92. package/dist/logging/agentOutput.js.map +1 -0
  93. package/dist/logging/formatter.d.ts.map +1 -1
  94. package/dist/logging/formatter.js +18 -4
  95. package/dist/logging/formatter.js.map +1 -1
  96. package/dist/logging/logger.d.ts.map +1 -1
  97. package/dist/logging/logger.js +2 -0
  98. package/dist/logging/logger.js.map +1 -1
  99. package/dist/logging/reader.d.ts +22 -0
  100. package/dist/logging/reader.d.ts.map +1 -1
  101. package/dist/logging/reader.js +116 -1
  102. package/dist/logging/reader.js.map +1 -1
  103. package/dist/opentui/index.solid.js +2575 -888
  104. package/dist/services/codingAgentResolver.d.ts.map +1 -1
  105. package/dist/services/codingAgentResolver.js +8 -6
  106. package/dist/services/codingAgentResolver.js.map +1 -1
  107. package/dist/services/dependency-installer.js +2 -2
  108. package/dist/services/dependency-installer.js.map +1 -1
  109. package/dist/shared/codingAgentConstants.d.ts +3 -0
  110. package/dist/shared/codingAgentConstants.d.ts.map +1 -1
  111. package/dist/shared/codingAgentConstants.js +66 -0
  112. package/dist/shared/codingAgentConstants.js.map +1 -1
  113. package/dist/utils/bun-runtime.d.ts +12 -0
  114. package/dist/utils/bun-runtime.d.ts.map +1 -0
  115. package/dist/utils/bun-runtime.js +13 -0
  116. package/dist/utils/bun-runtime.js.map +1 -0
  117. package/dist/utils/session/common.d.ts +8 -0
  118. package/dist/utils/session/common.d.ts.map +1 -1
  119. package/dist/utils/session/common.js +22 -0
  120. package/dist/utils/session/common.js.map +1 -1
  121. package/dist/utils/session/parsers/claude.d.ts +10 -4
  122. package/dist/utils/session/parsers/claude.d.ts.map +1 -1
  123. package/dist/utils/session/parsers/claude.js +64 -18
  124. package/dist/utils/session/parsers/claude.js.map +1 -1
  125. package/dist/utils/session/parsers/codex.d.ts.map +1 -1
  126. package/dist/utils/session/parsers/codex.js +47 -28
  127. package/dist/utils/session/parsers/codex.js.map +1 -1
  128. package/dist/utils/session/parsers/gemini.d.ts.map +1 -1
  129. package/dist/utils/session/parsers/gemini.js +43 -6
  130. package/dist/utils/session/parsers/gemini.js.map +1 -1
  131. package/dist/utils/session/parsers/opencode.d.ts.map +1 -1
  132. package/dist/utils/session/parsers/opencode.js +43 -6
  133. package/dist/utils/session/parsers/opencode.js.map +1 -1
  134. package/dist/utils/session/types.d.ts +7 -0
  135. package/dist/utils/session/types.d.ts.map +1 -1
  136. package/dist/web/client/src/components/ui/alert.d.ts +1 -1
  137. package/dist/worktree.d.ts +4 -1
  138. package/dist/worktree.d.ts.map +1 -1
  139. package/dist/worktree.js +21 -15
  140. package/dist/worktree.js.map +1 -1
  141. package/package.json +2 -1
  142. package/src/claude.ts +99 -28
  143. package/src/cli/ui/App.solid.tsx +373 -51
  144. package/src/cli/ui/__tests__/solid/AppSolid.cleanup.test.tsx +921 -1
  145. package/src/cli/ui/__tests__/solid/BranchListScreen.test.tsx +105 -5
  146. package/src/cli/ui/__tests__/solid/ConfirmScreen.test.tsx +77 -0
  147. package/src/cli/ui/__tests__/solid/LogScreen.test.tsx +351 -0
  148. package/src/cli/ui/__tests__/solid/components/QuickStartStep.test.tsx +73 -2
  149. package/src/cli/ui/__tests__/solid/components/WizardController.test.tsx +71 -0
  150. package/src/cli/ui/__tests__/solid/components/WizardSteps.test.tsx +95 -2
  151. package/src/cli/ui/__tests__/utils/branchFormatter.test.ts +72 -45
  152. package/src/cli/ui/components/solid/QuickStartStep.tsx +35 -23
  153. package/src/cli/ui/components/solid/SearchInput.tsx +1 -1
  154. package/src/cli/ui/components/solid/SelectInput.tsx +4 -0
  155. package/src/cli/ui/components/solid/WizardController.tsx +85 -12
  156. package/src/cli/ui/components/solid/WizardSteps.tsx +78 -90
  157. package/src/cli/ui/core/theme.ts +32 -0
  158. package/src/cli/ui/hooks/solid/useAsyncOperation.ts +8 -6
  159. package/src/cli/ui/hooks/solid/useGitOperations.ts +6 -5
  160. package/src/cli/ui/screens/solid/BranchListScreen.tsx +135 -32
  161. package/src/cli/ui/screens/solid/ConfirmScreen.tsx +20 -8
  162. package/src/cli/ui/screens/solid/EnvironmentScreen.tsx +22 -20
  163. package/src/cli/ui/screens/solid/LogScreen.tsx +364 -35
  164. package/src/cli/ui/screens/solid/ProfileEnvScreen.tsx +19 -15
  165. package/src/cli/ui/screens/solid/SelectorScreen.tsx +25 -14
  166. package/src/cli/ui/screens/solid/SettingsScreen.tsx +5 -3
  167. package/src/cli/ui/types.ts +1 -0
  168. package/src/cli/ui/utils/__tests__/branchFormatter.test.ts +53 -6
  169. package/src/cli/ui/utils/__tests__/installedVersionCache.test.ts +46 -0
  170. package/src/cli/ui/utils/branchFormatter.ts +35 -7
  171. package/src/cli/ui/utils/continueSession.ts +90 -3
  172. package/src/cli/ui/utils/installedVersionCache.ts +84 -0
  173. package/src/cli/ui/utils/modelOptions.test.ts +6 -0
  174. package/src/cli/ui/utils/modelOptions.ts +16 -0
  175. package/src/cli/ui/utils/versionCache.ts +93 -0
  176. package/src/cli/ui/utils/versionFetcher.ts +120 -0
  177. package/src/codex.ts +124 -26
  178. package/src/config/__tests__/saveSession.test.ts +2 -2
  179. package/src/config/index.ts +11 -1
  180. package/src/gemini.ts +50 -4
  181. package/src/index.test.ts +16 -10
  182. package/src/index.ts +41 -1
  183. package/src/launcher.ts +49 -8
  184. package/src/logging/agentOutput.ts +216 -0
  185. package/src/logging/formatter.ts +23 -4
  186. package/src/logging/logger.ts +2 -0
  187. package/src/logging/reader.ts +165 -1
  188. package/src/services/__tests__/BatchMergeService.test.ts +34 -14
  189. package/src/services/codingAgentResolver.ts +12 -5
  190. package/src/services/dependency-installer.ts +2 -2
  191. package/src/shared/codingAgentConstants.ts +73 -0
  192. package/src/utils/bun-runtime.ts +29 -0
  193. package/src/utils/session/common.ts +28 -0
  194. package/src/utils/session/parsers/claude.ts +79 -29
  195. package/src/utils/session/parsers/codex.ts +49 -26
  196. package/src/utils/session/parsers/gemini.ts +46 -5
  197. package/src/utils/session/parsers/opencode.ts +46 -5
  198. package/src/utils/session/types.ts +4 -0
  199. package/src/worktree.ts +28 -15
@@ -1,13 +1,17 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
- import { createEffect, createMemo, createSignal } from "solid-js";
2
+ import { createEffect, createMemo, createSignal, on } from "solid-js";
3
3
  import { useKeyboard, useTerminalDimensions } from "@opentui/solid";
4
4
  import { TextAttributes } from "@opentui/core";
5
5
  import type { BranchItem, BranchViewMode, Statistics } from "../../types.js";
6
- import { getLatestActivityTimestamp } from "../../utils/branchFormatter.js";
6
+ import {
7
+ formatLocalDateTime,
8
+ getLatestActivityTimestamp,
9
+ } from "../../utils/branchFormatter.js";
7
10
  import stringWidth from "string-width";
8
11
  import { getAgentTerminalColor } from "../../../../utils/coding-agent-colors.js";
9
12
  import { Header } from "../../components/solid/Header.js";
10
- type IndicatorColor = "cyan" | "green" | "yellow" | "red";
13
+ import { selectionStyle } from "../../core/theme.js";
14
+ type IndicatorColor = "cyan" | "green" | "yellow" | "red" | "brightGreen";
11
15
 
12
16
  interface CleanupIndicator {
13
17
  icon: string;
@@ -26,6 +30,7 @@ interface CleanupUIState {
26
30
  footerMessage: CleanupFooterMessage | null;
27
31
  inputLocked: boolean;
28
32
  safetyLoading?: boolean;
33
+ safetyPendingBranches?: Set<string>;
29
34
  }
30
35
 
31
36
  export interface BranchListScreenProps {
@@ -38,7 +43,7 @@ export interface BranchListScreenProps {
38
43
  onCreateBranch?: (branch: BranchItem | null) => void;
39
44
  onRefresh?: () => void;
40
45
  onOpenProfiles?: () => void;
41
- onOpenLogs?: () => void;
46
+ onOpenLogs?: (branch: BranchItem | null) => void;
42
47
  loading?: boolean;
43
48
  error?: Error | null;
44
49
  lastUpdated?: Date | null;
@@ -52,6 +57,12 @@ export interface BranchListScreenProps {
52
57
  helpVisible?: boolean;
53
58
  /** ウィザードポップアップ表示中は入力を無効化 */
54
59
  wizardVisible?: boolean;
60
+ /** 確認ダイアログ表示中は入力を無効化 */
61
+ confirmVisible?: boolean;
62
+ /** カーソル位置(外部から制御する場合) */
63
+ cursorPosition?: number;
64
+ /** カーソル位置変更時のコールバック */
65
+ onCursorPositionChange?: (index: number) => void;
55
66
  }
56
67
 
57
68
  const VIEW_MODES: BranchViewMode[] = ["all", "local", "remote"];
@@ -190,8 +201,8 @@ const fitSegmentsToWidth = (
190
201
  const applySelectionStyle = (segments: TextSegment[]): TextSegment[] =>
191
202
  segments.map((segment) => ({
192
203
  text: segment.text,
193
- fg: "black",
194
- bg: "cyan",
204
+ fg: selectionStyle.fg,
205
+ bg: selectionStyle.bg,
195
206
  }));
196
207
 
197
208
  const CLEANUP_SPINNER_FRAMES = ["-", "\\", "|", "/"];
@@ -298,7 +309,21 @@ export function BranchListScreen(props: BranchListScreenProps) {
298
309
  const [filterQuery, setFilterQuery] = createSignal("");
299
310
  const [filterMode, setFilterMode] = createSignal(false);
300
311
  const [viewMode, setViewMode] = createSignal<BranchViewMode>("all");
301
- const [selectedIndex, setSelectedIndex] = createSignal(0);
312
+
313
+ // カーソル位置: 外部制御(props.cursorPosition)が優先、なければ内部状態を使用
314
+ const [internalSelectedIndex, setInternalSelectedIndex] = createSignal(
315
+ props.cursorPosition ?? 0,
316
+ );
317
+ const selectedIndex = () => props.cursorPosition ?? internalSelectedIndex();
318
+ const setSelectedIndex = (
319
+ value: number | ((prev: number) => number),
320
+ ): void => {
321
+ const newValue =
322
+ typeof value === "function" ? value(selectedIndex()) : value;
323
+ setInternalSelectedIndex(newValue);
324
+ props.onCursorPositionChange?.(newValue);
325
+ };
326
+
302
327
  const [scrollOffset, setScrollOffset] = createSignal(0);
303
328
  const [cleanupSpinnerIndex, setCleanupSpinnerIndex] = createSignal(0);
304
329
  const [cursorIndex, setCursorIndex] = createSignal(0);
@@ -345,11 +370,17 @@ export function BranchListScreen(props: BranchListScreenProps) {
345
370
  () => props.cleanupUI?.footerMessage?.isSpinning ?? false,
346
371
  );
347
372
 
373
+ const safetyPendingActive = createMemo(() => {
374
+ const pending = props.cleanupUI?.safetyPendingBranches;
375
+ if (pending) {
376
+ return pending.size > 0;
377
+ }
378
+ return props.cleanupUI?.safetyLoading === true;
379
+ });
380
+
348
381
  const cleanupSpinnerActive = createMemo(
349
382
  () =>
350
- hasSpinningIndicator() ||
351
- hasSpinningFooter() ||
352
- props.cleanupUI?.safetyLoading === true,
383
+ hasSpinningIndicator() || hasSpinningFooter() || safetyPendingActive(),
353
384
  );
354
385
 
355
386
  createEffect(() => {
@@ -423,12 +454,18 @@ export function BranchListScreen(props: BranchListScreenProps) {
423
454
  return maxWidth;
424
455
  });
425
456
 
426
- createEffect(() => {
427
- filterQuery();
428
- viewMode();
429
- setSelectedIndex(0);
430
- setScrollOffset(0);
431
- });
457
+ // FR-037 / FR-037a: filterQueryまたはviewModeが実際に変更されたときのみカーソルをリセット
458
+ // defer: true により初回実行をスキップし、安全状態更新時のリセットを防止
459
+ createEffect(
460
+ on(
461
+ () => [filterQuery(), viewMode()],
462
+ () => {
463
+ setSelectedIndex(0);
464
+ setScrollOffset(0);
465
+ },
466
+ { defer: true },
467
+ ),
468
+ );
432
469
 
433
470
  createEffect(() => {
434
471
  const total = filteredBranches().length;
@@ -489,14 +526,8 @@ export function BranchListScreen(props: BranchListScreenProps) {
489
526
  return "---";
490
527
  }
491
528
 
492
- const date = new Date(timestamp * 1000);
493
- const year = date.getFullYear();
494
- const month = String(date.getMonth() + 1).padStart(2, "0");
495
- const day = String(date.getDate()).padStart(2, "0");
496
- const hours = String(date.getHours()).padStart(2, "0");
497
- const minutes = String(date.getMinutes()).padStart(2, "0");
498
-
499
- return `${year}-${month}-${day} ${hours}:${minutes}`;
529
+ const formatted = formatLocalDateTime(timestamp * 1000);
530
+ return formatted || "---";
500
531
  };
501
532
 
502
533
  useKeyboard((key) => {
@@ -504,6 +535,9 @@ export function BranchListScreen(props: BranchListScreenProps) {
504
535
  if (props.wizardVisible) {
505
536
  return;
506
537
  }
538
+ if (props.confirmVisible) {
539
+ return;
540
+ }
507
541
  if (props.helpVisible) {
508
542
  return;
509
543
  }
@@ -632,7 +666,8 @@ export function BranchListScreen(props: BranchListScreenProps) {
632
666
  } else if (key.name === "p" || key.sequence === "p") {
633
667
  props.onOpenProfiles?.();
634
668
  } else if (key.name === "l" || key.sequence === "l") {
635
- props.onOpenLogs?.();
669
+ const selected = filteredBranches()[selectedIndex()] ?? null;
670
+ props.onOpenLogs?.(selected);
636
671
  }
637
672
  });
638
673
 
@@ -667,13 +702,17 @@ export function BranchListScreen(props: BranchListScreenProps) {
667
702
  let worktreeColor: IndicatorColor | "gray" = "gray";
668
703
  if (branch.worktreeStatus === "active") {
669
704
  worktreeIcon = "w";
670
- worktreeColor = "green";
705
+ worktreeColor = "brightGreen";
671
706
  } else if (branch.worktreeStatus === "inaccessible") {
672
707
  worktreeIcon = "x";
673
708
  worktreeColor = "red";
674
709
  }
675
710
  const isRemoteBranch = branch.type === "remote";
676
711
  const safetyLoading = props.cleanupUI?.safetyLoading === true;
712
+ const safetyPendingBranches = props.cleanupUI?.safetyPendingBranches;
713
+ const isSafetyPending = safetyPendingBranches
714
+ ? safetyPendingBranches.has(branch.name)
715
+ : safetyLoading;
677
716
  const spinnerFrame = cleanupSpinnerFrame();
678
717
 
679
718
  let safeIcon = " ";
@@ -681,6 +720,9 @@ export function BranchListScreen(props: BranchListScreenProps) {
681
720
  if (isRemoteBranch) {
682
721
  safeIcon = " ";
683
722
  safeColor = undefined;
723
+ } else if (isSafetyPending) {
724
+ safeIcon = spinnerFrame ?? "-";
725
+ safeColor = undefined;
684
726
  } else if (hasUncommitted) {
685
727
  safeIcon = "!";
686
728
  safeColor = "red";
@@ -690,12 +732,9 @@ export function BranchListScreen(props: BranchListScreenProps) {
690
732
  } else if (branch.isUnmerged) {
691
733
  safeIcon = "*";
692
734
  safeColor = "yellow";
693
- } else if (safetyLoading) {
694
- safeIcon = spinnerFrame ?? "-";
695
- safeColor = undefined;
696
735
  } else if (branch.safeToCleanup === true) {
697
- safeIcon = " ";
698
- safeColor = undefined;
736
+ safeIcon = "o";
737
+ safeColor = "brightGreen";
699
738
  } else {
700
739
  safeIcon = "!";
701
740
  safeColor = "red";
@@ -886,6 +925,70 @@ export function BranchListScreen(props: BranchListScreenProps) {
886
925
  return fitSegmentsToWidth(segments, layoutWidth());
887
926
  });
888
927
 
928
+ const statusLegendSegments = createMemo(() => {
929
+ const segments: TextSegment[] = [];
930
+ const separator = " ";
931
+
932
+ appendSegment(segments, {
933
+ text: "Legend: ",
934
+ attributes: TextAttributes.DIM,
935
+ });
936
+
937
+ appendSegment(segments, {
938
+ text: "o",
939
+ fg: "brightGreen",
940
+ attributes: TextAttributes.BOLD,
941
+ });
942
+ appendSegment(segments, {
943
+ text: " Safe",
944
+ fg: "brightGreen",
945
+ });
946
+ appendSegment(segments, {
947
+ text: separator,
948
+ attributes: TextAttributes.DIM,
949
+ });
950
+
951
+ appendSegment(segments, {
952
+ text: "!",
953
+ fg: "red",
954
+ attributes: TextAttributes.BOLD,
955
+ });
956
+ appendSegment(segments, {
957
+ text: " Uncommitted",
958
+ fg: "red",
959
+ });
960
+ appendSegment(segments, {
961
+ text: separator,
962
+ attributes: TextAttributes.DIM,
963
+ });
964
+
965
+ appendSegment(segments, {
966
+ text: "!",
967
+ fg: "yellow",
968
+ attributes: TextAttributes.BOLD,
969
+ });
970
+ appendSegment(segments, {
971
+ text: " Unpushed",
972
+ fg: "yellow",
973
+ });
974
+ appendSegment(segments, {
975
+ text: separator,
976
+ attributes: TextAttributes.DIM,
977
+ });
978
+
979
+ appendSegment(segments, {
980
+ text: "*",
981
+ fg: "yellow",
982
+ attributes: TextAttributes.BOLD,
983
+ });
984
+ appendSegment(segments, {
985
+ text: " Unmerged",
986
+ fg: "yellow",
987
+ });
988
+
989
+ return fitSegmentsToWidth(segments, layoutWidth());
990
+ });
991
+
889
992
  const footerSegments = createMemo(() => {
890
993
  const segments: TextSegment[] = [];
891
994
  const separator = " ";
@@ -971,7 +1074,7 @@ export function BranchListScreen(props: BranchListScreenProps) {
971
1074
  : {})}
972
1075
  />
973
1076
  ) : (
974
- <text>{padLine("", layoutWidth())}</text>
1077
+ renderSegmentLine(statusLegendSegments())
975
1078
  )}
976
1079
 
977
1080
  {props.error && (
@@ -1,7 +1,9 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
- import { TextAttributes } from "@opentui/core";
3
2
  import { useKeyboard } from "@opentui/solid";
4
3
  import { createSignal } from "solid-js";
4
+ import stringWidth from "string-width";
5
+ import { useTerminalSize } from "../../hooks/solid/useTerminalSize.js";
6
+ import { selectionStyle } from "../../core/theme.js";
5
7
 
6
8
  export interface ConfirmScreenProps {
7
9
  message: string;
@@ -10,6 +12,7 @@ export interface ConfirmScreenProps {
10
12
  noLabel?: string;
11
13
  defaultNo?: boolean;
12
14
  helpVisible?: boolean;
15
+ width?: number;
13
16
  }
14
17
 
15
18
  export function ConfirmScreen({
@@ -19,8 +22,16 @@ export function ConfirmScreen({
19
22
  noLabel = "No",
20
23
  defaultNo = false,
21
24
  helpVisible = false,
25
+ width,
22
26
  }: ConfirmScreenProps) {
23
27
  const [selectedIndex, setSelectedIndex] = createSignal(defaultNo ? 1 : 0);
28
+ const terminal = useTerminalSize();
29
+ const contentWidth = () => Math.max(0, width ?? terminal().columns);
30
+
31
+ const padLine = (value: string, width: number) => {
32
+ const padding = Math.max(0, width - stringWidth(value));
33
+ return padding > 0 ? `${value}${" ".repeat(padding)}` : value;
34
+ };
24
35
 
25
36
  const confirm = (confirmed: boolean) => {
26
37
  onConfirm(confirmed);
@@ -54,13 +65,14 @@ export function ConfirmScreen({
54
65
  }
55
66
  });
56
67
 
57
- const renderOption = (label: string, isSelected: boolean) => (
58
- <text
59
- {...(isSelected ? { fg: "cyan", attributes: TextAttributes.BOLD } : {})}
60
- >
61
- {label}
62
- </text>
63
- );
68
+ const renderOption = (label: string, isSelected: boolean) =>
69
+ isSelected ? (
70
+ <text fg={selectionStyle.fg} bg={selectionStyle.bg}>
71
+ {padLine(label, contentWidth())}
72
+ </text>
73
+ ) : (
74
+ <text>{label}</text>
75
+ );
64
76
 
65
77
  return (
66
78
  <box flexDirection="column">
@@ -1,11 +1,12 @@
1
1
  /** @jsxImportSource @opentui/solid */
2
- import { TextAttributes } from "@opentui/core";
3
2
  import { useKeyboard } from "@opentui/solid";
4
3
  import { createMemo } from "solid-js";
5
4
  import { Header } from "../../components/solid/Header.js";
6
5
  import { Footer } from "../../components/solid/Footer.js";
7
6
  import { useTerminalSize } from "../../hooks/solid/useTerminalSize.js";
8
7
  import { useScrollableList } from "../../hooks/solid/useScrollableList.js";
8
+ import stringWidth from "string-width";
9
+ import { selectionStyle } from "../../core/theme.js";
9
10
 
10
11
  export interface EnvironmentVariable {
11
12
  key: string;
@@ -33,6 +34,10 @@ export function EnvironmentScreen({
33
34
  helpVisible = false,
34
35
  }: EnvironmentScreenProps) {
35
36
  const terminal = useTerminalSize();
37
+ const padLine = (value: string, width: number) => {
38
+ const padding = Math.max(0, width - stringWidth(value));
39
+ return padding > 0 ? `${value}${" ".repeat(padding)}` : value;
40
+ };
36
41
  const listHeight = createMemo(() => {
37
42
  const headerRows = 2;
38
43
  const footerRows = 1;
@@ -125,27 +130,24 @@ export function EnvironmentScreen({
125
130
  const absoluteIndex = list.scrollOffset() + index;
126
131
  const isSelected = absoluteIndex === list.selectedIndex();
127
132
  const isHighlighted = highlightSet().has(variable.key);
128
- const attributes = isSelected ? TextAttributes.BOLD : undefined;
129
- const keyColor = isSelected
130
- ? "cyan"
131
- : isHighlighted
132
- ? "yellow"
133
- : undefined;
134
- const valueColor = isSelected ? "cyan" : undefined;
133
+ const keyColor = isHighlighted ? "yellow" : undefined;
135
134
  return (
136
135
  <box flexDirection="row">
137
- <text
138
- {...(keyColor ? { fg: keyColor } : {})}
139
- {...(attributes !== undefined ? { attributes } : {})}
140
- >
141
- {variable.key}
142
- </text>
143
- <text
144
- {...(valueColor ? { fg: valueColor } : {})}
145
- {...(attributes !== undefined ? { attributes } : {})}
146
- >
147
- ={variable.value}
148
- </text>
136
+ {isSelected ? (
137
+ <text fg={selectionStyle.fg} bg={selectionStyle.bg}>
138
+ {padLine(
139
+ `${variable.key}=${variable.value}`,
140
+ terminal().columns,
141
+ )}
142
+ </text>
143
+ ) : (
144
+ <>
145
+ <text {...(keyColor ? { fg: keyColor } : {})}>
146
+ {variable.key}
147
+ </text>
148
+ <text>={variable.value}</text>
149
+ </>
150
+ )}
149
151
  </box>
150
152
  );
151
153
  })}