git-stack-cli 1.14.0 → 1.15.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 (49) hide show
  1. package/README.md +2 -4
  2. package/dist/cjs/index.cjs +310 -180
  3. package/package.json +2 -1
  4. package/scripts/link.ts +14 -0
  5. package/src/app/App.tsx +41 -30
  6. package/src/app/AutoUpdate.tsx +9 -24
  7. package/src/app/CherryPickCheck.tsx +1 -2
  8. package/src/app/Debug.tsx +5 -6
  9. package/src/app/DependencyCheck.tsx +6 -6
  10. package/src/app/DetectInitialPR.tsx +2 -8
  11. package/src/app/DirtyCheck.tsx +51 -26
  12. package/src/app/Exit.tsx +41 -8
  13. package/src/app/FormatText.tsx +1 -5
  14. package/src/app/GatherMetadata.tsx +6 -13
  15. package/src/app/GithubApiError.tsx +1 -1
  16. package/src/app/HandleCtrlCSigint.tsx +36 -0
  17. package/src/app/LocalCommitStatus.tsx +1 -3
  18. package/src/app/LogTimestamp.tsx +1 -5
  19. package/src/app/ManualRebase.tsx +15 -37
  20. package/src/app/MultiSelect.tsx +2 -2
  21. package/src/app/PostRebaseStatus.tsx +2 -0
  22. package/src/app/PreManualRebase.tsx +3 -5
  23. package/src/app/RebaseCheck.tsx +1 -2
  24. package/src/app/SelectCommitRanges.tsx +6 -10
  25. package/src/app/Status.tsx +1 -1
  26. package/src/app/StatusTable.tsx +1 -4
  27. package/src/app/Store.tsx +29 -3
  28. package/src/app/SyncGithub.tsx +15 -45
  29. package/src/app/Table.tsx +4 -14
  30. package/src/app/TextInput.tsx +2 -7
  31. package/src/app/VerboseDebugInfo.tsx +1 -5
  32. package/src/app/YesNoPrompt.tsx +42 -31
  33. package/src/command.ts +8 -17
  34. package/src/commands/Fixup.tsx +17 -24
  35. package/src/commands/Log.tsx +3 -7
  36. package/src/commands/Rebase.tsx +18 -38
  37. package/src/components/ErrorBoundary.tsx +79 -0
  38. package/src/components/ExitingGate.tsx +27 -0
  39. package/src/core/CommitMetadata.ts +1 -1
  40. package/src/core/GitReviseTodo.test.ts +3 -3
  41. package/src/core/GitReviseTodo.ts +6 -8
  42. package/src/core/Metadata.test.ts +4 -4
  43. package/src/core/StackSummaryTable.ts +3 -3
  44. package/src/core/chalk.ts +1 -5
  45. package/src/core/cli.ts +2 -2
  46. package/src/core/github.tsx +15 -14
  47. package/src/core/pretty_json.ts +7 -0
  48. package/src/github/gh.auth_status.test.ts +2 -6
  49. package/src/index.tsx +42 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-stack-cli",
3
- "version": "1.14.0",
3
+ "version": "1.15.1",
4
4
  "description": "",
5
5
  "author": "magus",
6
6
  "license": "MIT",
@@ -24,6 +24,7 @@
24
24
  "dev": "npm run build -- --watch",
25
25
  "build": "rollup -c rollup.config.js",
26
26
  "build:standalone": "GIT_STACK_STANDALONE=true bun run scripts/build-standalone.ts",
27
+ "link": "bun run scripts/link.ts",
27
28
  "release:npm": "bun run scripts/release-npm.ts",
28
29
  "release:github": "bun run scripts/release-github.ts",
29
30
  "release:brew": "bun run scripts/release-brew.ts",
@@ -0,0 +1,14 @@
1
+ import path from "node:path";
2
+
3
+ import { spawn } from "~/core/spawn";
4
+
5
+ const SCRIPT_DIR = import.meta.dir;
6
+ const PROJECT_DIR = path.join(SCRIPT_DIR, "..");
7
+
8
+ process.chdir(PROJECT_DIR);
9
+
10
+ await spawn.sync("npm unlink git-stack-cli");
11
+ await spawn.sync("npm link");
12
+
13
+ console.debug();
14
+ console.debug("✅", "linked");
package/src/app/App.tsx CHANGED
@@ -8,6 +8,7 @@ import { DetectInitialPR } from "~/app/DetectInitialPR";
8
8
  import { DirtyCheck } from "~/app/DirtyCheck";
9
9
  import { GatherMetadata } from "~/app/GatherMetadata";
10
10
  import { GithubApiError } from "~/app/GithubApiError";
11
+ import { HandleCtrlCSigint } from "~/app/HandleCtrlCSigint";
11
12
  import { LocalCommitStatus } from "~/app/LocalCommitStatus";
12
13
  import { Main } from "~/app/Main";
13
14
  import { Output } from "~/app/Output";
@@ -18,6 +19,8 @@ import { VerboseDebugInfo } from "~/app/VerboseDebugInfo";
18
19
  import { Fixup } from "~/commands/Fixup";
19
20
  import { Log } from "~/commands/Log";
20
21
  import { Rebase } from "~/commands/Rebase";
22
+ import { ErrorBoundary } from "~/components/ErrorBoundary";
23
+ import { ExitingGate } from "~/components/ExitingGate";
21
24
 
22
25
  export function App() {
23
26
  const actions = Store.useActions();
@@ -41,30 +44,36 @@ export function App() {
41
44
 
42
45
  return (
43
46
  <Providers>
44
- <Debug />
45
- <Output />
47
+ <ErrorBoundary>
48
+ <Debug />
49
+ <Output />
46
50
 
47
- <AutoUpdate
48
- name="git-stack-cli"
49
- verbose={argv.verbose || argv.update}
50
- timeoutMs={argv.update ? 30 * 1000 : 2 * 1000}
51
- onOutput={actions.output}
52
- onDone={() => {
53
- if (argv.update) {
54
- actions.exit(0);
55
- }
56
- }}
57
- >
58
- <VerboseDebugInfo>
59
- <DependencyCheck>
60
- <RebaseCheck>
61
- <CherryPickCheck>
62
- <MaybeMain />
63
- </CherryPickCheck>
64
- </RebaseCheck>
65
- </DependencyCheck>
66
- </VerboseDebugInfo>
67
- </AutoUpdate>
51
+ <ExitingGate>
52
+ <AutoUpdate
53
+ name="git-stack-cli"
54
+ verbose={argv.verbose || argv.update}
55
+ timeoutMs={argv.update ? 30 * 1000 : 2 * 1000}
56
+ onOutput={actions.output}
57
+ onDone={() => {
58
+ if (argv.update) {
59
+ actions.exit(0);
60
+ }
61
+ }}
62
+ >
63
+ <VerboseDebugInfo>
64
+ <DependencyCheck>
65
+ <RebaseCheck>
66
+ <CherryPickCheck>
67
+ <MaybeMain />
68
+ </CherryPickCheck>
69
+ </RebaseCheck>
70
+ </DependencyCheck>
71
+ </VerboseDebugInfo>
72
+ </AutoUpdate>
73
+
74
+ <HandleCtrlCSigint />
75
+ </ExitingGate>
76
+ </ErrorBoundary>
68
77
  </Providers>
69
78
  );
70
79
  }
@@ -88,16 +97,18 @@ function MaybeMain() {
88
97
  }
89
98
 
90
99
  return (
91
- <DirtyCheck>
100
+ <React.Fragment>
92
101
  {!argv.verbose ? null : <GithubApiError />}
93
102
 
94
103
  <GatherMetadata>
95
- <LocalCommitStatus>
96
- <DetectInitialPR>
97
- <Main />
98
- </DetectInitialPR>
99
- </LocalCommitStatus>
104
+ <DirtyCheck>
105
+ <LocalCommitStatus>
106
+ <DetectInitialPR>
107
+ <Main />
108
+ </DetectInitialPR>
109
+ </LocalCommitStatus>
110
+ </DirtyCheck>
100
111
  </GatherMetadata>
101
- </DirtyCheck>
112
+ </React.Fragment>
102
113
  );
103
114
  }
@@ -67,14 +67,10 @@ export function AutoUpdate(props: Props) {
67
67
 
68
68
  async function auto_update() {
69
69
  if (props_ref.current.verbose) {
70
- handle_output(
71
- <Ink.Text key="init">Checking for latest version...</Ink.Text>
72
- );
70
+ handle_output(<Ink.Text key="init">Checking for latest version...</Ink.Text>);
73
71
  }
74
72
 
75
- const timeout_ms = is_finite_value(props.timeoutMs)
76
- ? props.timeoutMs
77
- : 2 * 1000;
73
+ const timeout_ms = is_finite_value(props.timeoutMs) ? props.timeoutMs : 2 * 1000;
78
74
 
79
75
  const npm_json = await Promise.race([
80
76
  fetch_json(`https://registry.npmjs.org/${props.name}`),
@@ -94,12 +90,7 @@ export function AutoUpdate(props: Props) {
94
90
  const script_dir = path.dirname(script_path);
95
91
 
96
92
  // dist/ts/index.js
97
- const package_json_path = path.join(
98
- script_dir,
99
- "..",
100
- "..",
101
- "package.json"
102
- );
93
+ const package_json_path = path.join(script_dir, "..", "..", "package.json");
103
94
 
104
95
  type PackageJson = { version: string };
105
96
  const package_json = await read_json<PackageJson>(package_json_path);
@@ -121,7 +112,7 @@ export function AutoUpdate(props: Props) {
121
112
  latest_version: <Brackets>{latest_version}</Brackets>,
122
113
  local_version: <Brackets>{local_version}</Brackets>,
123
114
  }}
124
- />
115
+ />,
125
116
  );
126
117
  }
127
118
 
@@ -134,7 +125,7 @@ export function AutoUpdate(props: Props) {
134
125
  if (semver_result === -1) {
135
126
  // latest version is less than or equal to local version, skip auto update
136
127
  throw new Error(
137
- `latest version < local_version, skipping auto update [${latest_version} < ${local_version}]`
128
+ `latest version < local_version, skipping auto update [${latest_version} < ${local_version}]`,
138
129
  );
139
130
  }
140
131
 
@@ -156,7 +147,7 @@ export function AutoUpdate(props: Props) {
156
147
  handle_output(
157
148
  <Ink.Text key="error" color={colors.red}>
158
149
  {error?.message}
159
- </Ink.Text>
150
+ </Ink.Text>,
160
151
  );
161
152
  }
162
153
  })
@@ -185,16 +176,10 @@ export function AutoUpdate(props: Props) {
185
176
  wrapper={<Ink.Text />}
186
177
  message="Installing {name}@{version}..."
187
178
  values={{
188
- name: (
189
- <Ink.Text color={colors.yellow}>{props.name}</Ink.Text>
190
- ),
191
- version: (
192
- <Ink.Text color={colors.blue}>
193
- {state.latest_version}
194
- </Ink.Text>
195
- ),
179
+ name: <Ink.Text color={colors.yellow}>{props.name}</Ink.Text>,
180
+ version: <Ink.Text color={colors.blue}>{state.latest_version}</Ink.Text>,
196
181
  }}
197
- />
182
+ />,
198
183
  );
199
184
 
200
185
  patch({ status: "install" });
@@ -40,8 +40,7 @@ export function CherryPickCheck(props: Props) {
40
40
  <YesNoPrompt
41
41
  message={
42
42
  <Ink.Text color={colors.yellow}>
43
- <Command>git cherry-pick</Command> detected, would you like to
44
- abort it?
43
+ <Command>git cherry-pick</Command> detected, would you like to abort it?
45
44
  </Ink.Text>
46
45
  }
47
46
  onYes={async () => {
package/src/app/Debug.tsx CHANGED
@@ -8,6 +8,7 @@ import * as Ink from "ink-cjs";
8
8
  import { Store } from "~/app/Store";
9
9
  import { colors } from "~/core/colors";
10
10
  import * as json from "~/core/json";
11
+ import { pretty_json } from "~/core/pretty_json";
11
12
  import { safe_rm } from "~/core/safe_rm";
12
13
 
13
14
  export function Debug() {
@@ -19,12 +20,10 @@ export function Debug() {
19
20
  React.useEffect(
20
21
  function debugMessageOnce() {
21
22
  if (debug) {
22
- actions.output(
23
- <Ink.Text color={colors.yellow}>Debug mode enabled</Ink.Text>
24
- );
23
+ actions.output(<Ink.Text color={colors.yellow}>Debug mode enabled</Ink.Text>);
25
24
  }
26
25
  },
27
- [argv]
26
+ [argv],
28
27
  );
29
28
 
30
29
  React.useEffect(
@@ -41,11 +40,11 @@ export function Debug() {
41
40
  await safe_rm(output_file);
42
41
 
43
42
  const serialized = json.serialize(state);
44
- const content = JSON.stringify(serialized, null, 2);
43
+ const content = pretty_json(serialized);
45
44
  await fs.writeFile(output_file, content);
46
45
  }
47
46
  },
48
- [argv, state]
47
+ [argv, state],
49
48
  );
50
49
 
51
50
  return null;
@@ -55,7 +55,7 @@ function CheckGit(props: Props) {
55
55
  actions.output(
56
56
  <Ink.Text color={colors.yellow}>
57
57
  <Command>git</Command> must be installed.
58
- </Ink.Text>
58
+ </Ink.Text>,
59
59
  );
60
60
 
61
61
  actions.exit(2);
@@ -86,7 +86,7 @@ function CheckGithubCli(props: Props) {
86
86
  actions.output(
87
87
  <Ink.Text color={colors.yellow}>
88
88
  <Command>gh</Command> must be installed.
89
- </Ink.Text>
89
+ </Ink.Text>,
90
90
  );
91
91
 
92
92
  actions.output(
@@ -98,7 +98,7 @@ function CheckGithubCli(props: Props) {
98
98
 
99
99
  command: <Command>gh</Command>,
100
100
  }}
101
- />
101
+ />,
102
102
  );
103
103
 
104
104
  actions.exit(3);
@@ -150,7 +150,7 @@ function CheckGithubCliAuth(props: Props) {
150
150
 
151
151
  command: <Command>gh auth login</Command>,
152
152
  }}
153
- />
153
+ />,
154
154
  );
155
155
 
156
156
  actions.exit(4);
@@ -181,7 +181,7 @@ function CheckGitRevise(props: Props) {
181
181
  actions.output(
182
182
  <Ink.Text color={colors.yellow}>
183
183
  <Command>git revise</Command> must be installed.
184
- </Ink.Text>
184
+ </Ink.Text>,
185
185
  );
186
186
 
187
187
  actions.output(
@@ -199,7 +199,7 @@ function CheckGitRevise(props: Props) {
199
199
  </Parens>
200
200
  ),
201
201
  }}
202
- />
202
+ />,
203
203
  );
204
204
 
205
205
  actions.exit(10);
@@ -97,11 +97,7 @@ export function DetectInitialPR(props: Props) {
97
97
  return (
98
98
  <Await
99
99
  function={run}
100
- fallback={
101
- <Ink.Text color={colors.yellow}>
102
- Checking for existing PR on Github…
103
- </Ink.Text>
104
- }
100
+ fallback={<Ink.Text color={colors.yellow}>Checking for existing PR on Github…</Ink.Text>}
105
101
  />
106
102
  );
107
103
  }
@@ -167,9 +163,7 @@ export function DetectInitialPR(props: Props) {
167
163
  // get latest merge_base relative to local master
168
164
  const rebase_group_index = 0;
169
165
 
170
- const rebase_merge_base = (
171
- await cli(`git merge-base HEAD ${master_branch}`)
172
- ).stdout;
166
+ const rebase_merge_base = (await cli(`git merge-base HEAD ${master_branch}`)).stdout;
173
167
 
174
168
  await GitReviseTodo.execute({
175
169
  rebase_group_index,
@@ -15,7 +15,7 @@ type Props = {
15
15
  };
16
16
 
17
17
  type State = {
18
- status: "init" | "prompt" | "done";
18
+ status: "init" | "prompt" | "stash" | "done";
19
19
  };
20
20
 
21
21
  function reducer(state: State, patch: Partial<State>) {
@@ -33,36 +33,58 @@ export function DirtyCheck(props: Props) {
33
33
  case "done":
34
34
  return props.children;
35
35
 
36
+ case "stash":
37
+ return (
38
+ <FormatText
39
+ wrapper={<Ink.Text color={colors.yellow} />}
40
+ message="📦 Stashing uncommitted changes…"
41
+ />
42
+ );
43
+
36
44
  case "prompt":
37
45
  return (
38
- <YesNoPrompt
39
- message={
40
- <Ink.Box flexDirection="column">
41
- <FormatText
42
- wrapper={<Ink.Text color={colors.yellow} />}
43
- message="{git} repo has uncommitted changes."
44
- values={{
45
- git: <Command>git</Command>,
46
- git_stack: <Command>git stack</Command>,
47
- }}
48
- />
46
+ <Ink.Box flexDirection="column">
47
+ <FormatText
48
+ wrapper={<Ink.Text color={colors.yellow} />}
49
+ message="⚠️ Uncommitted changes detected. {git_stack} needs a clean working tree."
50
+ values={{
51
+ git: <Command>git</Command>,
52
+ git_stack: <Command>git stack</Command>,
53
+ }}
54
+ />
55
+
56
+ <YesNoPrompt
57
+ message={
49
58
  <FormatText
50
59
  wrapper={<Ink.Text color={colors.yellow} />}
51
- message="Changes may be lost during {git_stack}, are you sure you want to proceed?"
60
+ message="{git_stash} changes to proceed?"
52
61
  values={{
53
- git: <Command>git</Command>,
54
- git_stack: <Command>git stack</Command>,
62
+ git_stash: <Command>git stash</Command>,
55
63
  }}
56
64
  />
57
- </Ink.Box>
58
- }
59
- onYes={async () => {
60
- patch({ status: "done" });
61
- }}
62
- onNo={async () => {
63
- actions.exit(0);
64
- }}
65
- />
65
+ }
66
+ onYes={async () => {
67
+ patch({ status: "stash" });
68
+
69
+ await cli("git stash --include-untracked");
70
+
71
+ actions.output(
72
+ <Ink.Text color={colors.yellow}>
73
+ <FormatText message="📦 Changes saved to stash" />
74
+ </Ink.Text>,
75
+ );
76
+
77
+ actions.set((state) => {
78
+ state.is_dirty_check_stash = true;
79
+ });
80
+
81
+ patch({ status: "done" });
82
+ }}
83
+ onNo={async () => {
84
+ actions.exit(0);
85
+ }}
86
+ />
87
+ </Ink.Box>
66
88
  );
67
89
 
68
90
  default:
@@ -84,8 +106,11 @@ export function DirtyCheck(props: Props) {
84
106
  try {
85
107
  const git_dirty = (await cli(`git status --porcelain`)).stdout;
86
108
 
87
- const status = git_dirty ? "prompt" : "done";
88
- patch({ status });
109
+ if (!git_dirty) {
110
+ patch({ status: "done" });
111
+ } else {
112
+ patch({ status: "prompt" });
113
+ }
89
114
  } catch (err) {
90
115
  actions.error("Must be run from within a git repository.");
91
116
 
package/src/app/Exit.tsx CHANGED
@@ -1,6 +1,11 @@
1
1
  import * as React from "react";
2
2
 
3
+ import * as Ink from "ink-cjs";
4
+
3
5
  import { Store } from "~/app/Store";
6
+ import { cli } from "~/core/cli";
7
+ import { colors } from "~/core/colors";
8
+ import { sleep } from "~/core/sleep";
4
9
 
5
10
  type Props = {
6
11
  clear: boolean;
@@ -8,17 +13,45 @@ type Props = {
8
13
  };
9
14
 
10
15
  export function Exit(props: Props) {
11
- const actions = Store.useActions();
12
-
13
16
  React.useEffect(() => {
14
- if (props.clear) {
15
- actions.clear();
16
- }
17
+ // immediately handle exit on mount
18
+ handle_exit().catch((err) => {
19
+ // eslint-disable-next-line no-console
20
+ console.error(err);
21
+ });
22
+
23
+ async function handle_exit() {
24
+ const state = Store.getState();
25
+ const actions = state.actions;
26
+
27
+ actions.debug(`[Exit] handle_exit ${JSON.stringify(props)}`);
28
+
29
+ let exit_code = props.code;
17
30
 
18
- actions.unmount();
31
+ // run abort_handler if it exists
32
+ if (state.abort_handler) {
33
+ exit_code = await state.abort_handler();
34
+ }
19
35
 
20
- process.exitCode = props.code;
21
- process.exit();
36
+ // restore git stash if necessary
37
+ if (state.is_dirty_check_stash) {
38
+ await cli("git stash pop");
39
+ actions.output(<Ink.Text color={colors.green}>✅ Changes restored from stash</Ink.Text>);
40
+ }
41
+
42
+ // ensure output has a chance to render
43
+ await sleep(1);
44
+
45
+ // finally handle the actual app and process exit
46
+ if (props.clear) {
47
+ actions.clear();
48
+ }
49
+
50
+ actions.unmount();
51
+
52
+ process.exitCode = exit_code;
53
+ process.exit();
54
+ }
22
55
  }, [props.clear, props.code]);
23
56
 
24
57
  return null;
@@ -13,11 +13,7 @@ export function FormatText(props: Props) {
13
13
  const wrapper = (props.wrapper as React.ReactElement) || <Ink.Text />;
14
14
 
15
15
  return (
16
- <FormattedMessage
17
- id="FormatText"
18
- defaultMessage={props.message}
19
- values={props.values}
20
- >
16
+ <FormattedMessage id="FormatText" defaultMessage={props.message} values={props.values}>
21
17
  {(chunks) => {
22
18
  return React.cloneElement(wrapper, {}, chunks);
23
19
  }}
@@ -15,9 +15,7 @@ type Props = {
15
15
  };
16
16
 
17
17
  export function GatherMetadata(props: Props) {
18
- const fallback = (
19
- <Ink.Text color={colors.yellow}>Gathering local git information…</Ink.Text>
20
- );
18
+ const fallback = <Ink.Text color={colors.yellow}>Gathering local git information…</Ink.Text>;
21
19
 
22
20
  return (
23
21
  <Await fallback={fallback} function={run}>
@@ -41,14 +39,12 @@ async function run() {
41
39
  values={{
42
40
  branch: <Brackets>{argv.branch}</Brackets>,
43
41
  }}
44
- />
42
+ />,
45
43
  );
46
44
 
47
45
  master_branch = argv.branch;
48
46
  } else {
49
- const detect_master = await cli(
50
- `git branch --list "${BRANCH.master}" --color=never`
51
- );
47
+ const detect_master = await cli(`git branch --list "${BRANCH.master}" --color=never`);
52
48
 
53
49
  if (detect_master.stdout !== "") {
54
50
  master_branch = BRANCH.master;
@@ -60,7 +56,7 @@ async function run() {
60
56
  master: <Brackets>{BRANCH.master}</Brackets>,
61
57
  main: <Brackets>{BRANCH.main}</Brackets>,
62
58
  }}
63
- />
59
+ />,
64
60
  );
65
61
 
66
62
  master_branch = BRANCH.main;
@@ -84,15 +80,12 @@ async function run() {
84
80
  }
85
81
 
86
82
  const head = (await cli("git rev-parse HEAD")).stdout;
87
- const merge_base = (await cli(`git merge-base HEAD ${master_branch}`))
88
- .stdout;
83
+ const merge_base = (await cli(`git merge-base HEAD ${master_branch}`)).stdout;
89
84
 
90
85
  // handle when there are no detected changes
91
86
  if (head === merge_base) {
92
87
  actions.newline();
93
- actions.output(
94
- <Ink.Text color={colors.gray}>No changes detected.</Ink.Text>
95
- );
88
+ actions.output(<Ink.Text color={colors.gray}>No changes detected.</Ink.Text>);
96
89
  actions.exit(0);
97
90
  return;
98
91
  }
@@ -69,6 +69,6 @@ async function run() {
69
69
  {time_until}
70
70
  </Ink.Text>
71
71
  </Parens>
72
- </Ink.Text>
72
+ </Ink.Text>,
73
73
  );
74
74
  }
@@ -0,0 +1,36 @@
1
+ import * as React from "react";
2
+
3
+ import * as Ink from "ink-cjs";
4
+
5
+ import { FormatText } from "~/app/FormatText";
6
+ import { Store } from "~/app/Store";
7
+ import { colors } from "~/core/colors";
8
+ import { sleep } from "~/core/sleep";
9
+
10
+ export function HandleCtrlCSigint() {
11
+ const actions = Store.useActions();
12
+
13
+ Ink.useInput((input, key) => {
14
+ handle_input().catch((err) => {
15
+ // eslint-disable-next-line no-console
16
+ console.error(err);
17
+ });
18
+
19
+ async function handle_input() {
20
+ if (input === "c" && key.ctrl) {
21
+ actions.clear();
22
+
23
+ actions.output(
24
+ <Ink.Text color={colors.red}>
25
+ <FormatText message="🚨 Ctrl+C detected" />
26
+ </Ink.Text>,
27
+ );
28
+
29
+ await sleep(1);
30
+ actions.exit(235);
31
+ }
32
+ }
33
+ });
34
+
35
+ return null;
36
+ }
@@ -15,9 +15,7 @@ type Props = {
15
15
  export function LocalCommitStatus(props: Props) {
16
16
  const argv = Store.useState((state) => state.argv);
17
17
 
18
- const fallback = (
19
- <Ink.Text color={colors.yellow}>Fetching PR status from Github…</Ink.Text>
20
- );
18
+ const fallback = <Ink.Text color={colors.yellow}>Fetching PR status from Github…</Ink.Text>;
21
19
 
22
20
  if (argv["mock-metadata"]) {
23
21
  return (
@@ -4,9 +4,5 @@ import * as Ink from "ink-cjs";
4
4
  import { DateTime } from "luxon";
5
5
 
6
6
  export function LogTimestamp() {
7
- return (
8
- <Ink.Text dimColor>
9
- {DateTime.now().toFormat("[yyyy-MM-dd HH:mm:ss.SSS] ")}
10
- </Ink.Text>
11
- );
7
+ return <Ink.Text dimColor>{DateTime.now().toFormat("[yyyy-MM-dd HH:mm:ss.SSS] ")}</Ink.Text>;
12
8
  }