git-stack-cli 0.3.1 → 0.5.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.
@@ -0,0 +1,127 @@
1
+ import * as React from "react";
2
+ import * as Ink from "ink";
3
+ import * as CommitMetadata from "../core/CommitMetadata.js";
4
+ import * as Metadata from "../core/Metadata.js";
5
+ import { cli } from "../core/cli.js";
6
+ import * as github from "../core/github.js";
7
+ import { invariant } from "../core/invariant.js";
8
+ import { short_id } from "../core/short_id.js";
9
+ import { Await } from "./Await.js";
10
+ import { Brackets } from "./Brackets.js";
11
+ import { Store } from "./Store.js";
12
+ export function ManualRebase(props) {
13
+ return (React.createElement(Await, { fallback: React.createElement(Ink.Text, { color: "yellow" }, "Rebasing commits..."), function: () => run(props) }));
14
+ }
15
+ async function run(props) {
16
+ const state = Store.getState();
17
+ const actions = state.actions;
18
+ const branch_name = state.branch_name;
19
+ const merge_base = state.merge_base;
20
+ const commit_map = state.commit_map;
21
+ invariant(branch_name, "branch_name must exist");
22
+ invariant(merge_base, "merge_base must exist");
23
+ invariant(commit_map, "commit_map must exist");
24
+ // always listen for SIGINT event and restore git state
25
+ process.once("SIGINT", handle_exit);
26
+ const temp_branch_name = `${branch_name}_${short_id()}`;
27
+ const commit_range = await CommitMetadata.range(commit_map);
28
+ // reverse commit list so that we can cherry-pick in order
29
+ commit_range.group_list.reverse();
30
+ let rebase_merge_base = merge_base;
31
+ let rebase_group_index = 0;
32
+ for (let i = 0; i < commit_range.group_list.length; i++) {
33
+ const group = commit_range.group_list[i];
34
+ if (!group.dirty) {
35
+ continue;
36
+ }
37
+ if (i > 0) {
38
+ const last_group = commit_range.group_list[i - 1];
39
+ const last_commit = last_group.commits[last_group.commits.length - 1];
40
+ rebase_merge_base = last_commit.sha;
41
+ rebase_group_index = i;
42
+ }
43
+ break;
44
+ }
45
+ try {
46
+ // create temporary branch based on merge base
47
+ await cli(`git checkout -b ${temp_branch_name} ${rebase_merge_base}`);
48
+ for (let i = rebase_group_index; i < commit_range.group_list.length; i++) {
49
+ const group = commit_range.group_list[i];
50
+ invariant(group.base, "group.base must exist");
51
+ // cherry-pick and amend commits one by one
52
+ for (const commit of group.commits) {
53
+ await cli(`git cherry-pick ${commit.sha}`);
54
+ if (commit.branch_id !== group.id) {
55
+ const new_message = await Metadata.write(commit.message, group.id);
56
+ await cli(`git commit --amend -m "${new_message}"`);
57
+ }
58
+ }
59
+ actions.output(React.createElement(Ink.Text, { color: "yellow", wrap: "truncate-end" },
60
+ "Syncing ",
61
+ React.createElement(Brackets, null, group.pr?.title || group.id),
62
+ "..."));
63
+ if (!props.skipSync) {
64
+ // push to origin since github requires commit shas to line up perfectly
65
+ await cli(`git push -f origin HEAD:${group.id}`);
66
+ if (group.pr) {
67
+ // ensure base matches pr in github
68
+ await github.pr_base(group.id, group.base);
69
+ }
70
+ else {
71
+ // delete local group branch if leftover
72
+ await cli(`git branch -D ${group.id}`, { ignoreExitCode: true });
73
+ // move to temporary branch for creating pr
74
+ await cli(`git checkout -b ${group.id}`);
75
+ // create pr in github
76
+ await github.pr_create(group.id, group.base);
77
+ // move back to temp branch
78
+ await cli(`git checkout ${temp_branch_name}`);
79
+ }
80
+ }
81
+ }
82
+ // after all commits have been cherry-picked and amended
83
+ // move the branch pointer to the newly created temporary branch
84
+ // now we are in locally in sync with github and on the original branch
85
+ await cli(`git branch -f ${branch_name} ${temp_branch_name}`);
86
+ restore_git();
87
+ actions.set((state) => {
88
+ state.step = "post-rebase-status";
89
+ });
90
+ }
91
+ catch (err) {
92
+ actions.output(React.createElement(Ink.Text, { color: "#ef4444" }, "Error during rebase."));
93
+ if (err instanceof Error) {
94
+ actions.debug(React.createElement(Ink.Text, { color: "#ef4444" }, err.message));
95
+ }
96
+ handle_exit();
97
+ }
98
+ // cleanup git operations if cancelled during manual rebase
99
+ function restore_git() {
100
+ // signint handler MUST run synchronously
101
+ // trying to use `await cli(...)` here will silently fail since
102
+ // all children processes receive the SIGINT signal
103
+ const spawn_options = { ignoreExitCode: true };
104
+ // always put self back in original branch
105
+ cli.sync(`git checkout ${branch_name}`, spawn_options);
106
+ // ...and cleanup temporary branch
107
+ cli.sync(`git branch -D ${temp_branch_name}`, spawn_options);
108
+ if (commit_range) {
109
+ // ...and cleanup pr group branches
110
+ for (const group of commit_range.group_list) {
111
+ cli.sync(`git branch -D ${group.id}`, spawn_options);
112
+ }
113
+ }
114
+ }
115
+ function handle_exit() {
116
+ actions.output(React.createElement(Ink.Text, { color: "yellow" },
117
+ "Restoring ",
118
+ React.createElement(Brackets, null, branch_name),
119
+ "..."));
120
+ restore_git();
121
+ actions.output(React.createElement(Ink.Text, { color: "yellow" },
122
+ "Restored ",
123
+ React.createElement(Brackets, null, branch_name),
124
+ "."));
125
+ actions.exit(5);
126
+ }
127
+ }
@@ -1,13 +1,14 @@
1
1
  import * as React from "react";
2
2
  import * as Ink from "ink";
3
- import { v4 as uuid_v4 } from "uuid";
4
3
  import * as CommitMetadata from "../core/CommitMetadata.js";
5
4
  import * as Metadata from "../core/Metadata.js";
6
5
  import { cli } from "../core/cli.js";
7
6
  import * as github from "../core/github.js";
8
7
  import { invariant } from "../core/invariant.js";
8
+ import { short_id } from "../core/short_id.js";
9
9
  import { Await } from "./Await.js";
10
10
  import { Brackets } from "./Brackets.js";
11
+ import { FormatText } from "./FormatText.js";
11
12
  import { Store } from "./Store.js";
12
13
  export function ManualRebase(props) {
13
14
  return (React.createElement(Await, { fallback: React.createElement(Ink.Text, { color: "yellow" }, "Rebasing commits..."), function: () => run(props) }));
@@ -15,17 +16,18 @@ export function ManualRebase(props) {
15
16
  async function run(props) {
16
17
  const state = Store.getState();
17
18
  const actions = state.actions;
19
+ const argv = state.argv;
18
20
  const branch_name = state.branch_name;
19
21
  const merge_base = state.merge_base;
20
22
  const commit_map = state.commit_map;
23
+ invariant(argv, "argv must exist");
21
24
  invariant(branch_name, "branch_name must exist");
22
25
  invariant(merge_base, "merge_base must exist");
23
26
  invariant(commit_map, "commit_map must exist");
24
27
  // always listen for SIGINT event and restore git state
25
28
  process.once("SIGINT", handle_exit);
26
- const temp_branch_name = `${branch_name}_${uuid_v4()}`;
27
- let commit_range;
28
- commit_range = await CommitMetadata.range(commit_map);
29
+ const temp_branch_name = `${branch_name}_${short_id()}`;
30
+ const commit_range = await CommitMetadata.range(commit_map);
29
31
  // reverse commit list so that we can cherry-pick in order
30
32
  commit_range.group_list.reverse();
31
33
  let rebase_merge_base = merge_base;
@@ -57,13 +59,16 @@ async function run(props) {
57
59
  await cli(`git commit --amend -m "${new_message}"`);
58
60
  }
59
61
  }
60
- actions.output(React.createElement(Ink.Text, { color: "yellow", wrap: "truncate-end" },
61
- "Syncing ",
62
- React.createElement(Brackets, null, group.pr?.title || group.id),
63
- "..."));
62
+ actions.output(React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, { color: "yellow", wrap: "truncate-end" }), message: "Syncing {group}\u2026", values: {
63
+ group: (React.createElement(Brackets, null, group.pr?.title || group.title || group.id)),
64
+ } }));
64
65
  if (!props.skipSync) {
65
66
  // push to origin since github requires commit shas to line up perfectly
66
- await cli(`git push -f origin HEAD:${group.id}`);
67
+ const git_push_command = [`git push -f origin HEAD:${group.id}`];
68
+ if (argv["no-verify"]) {
69
+ git_push_command.push("--no-verify");
70
+ }
71
+ await cli(git_push_command.join(" "));
67
72
  if (group.pr) {
68
73
  // ensure base matches pr in github
69
74
  await github.pr_base(group.id, group.base);
@@ -74,7 +79,11 @@ async function run(props) {
74
79
  // move to temporary branch for creating pr
75
80
  await cli(`git checkout -b ${group.id}`);
76
81
  // create pr in github
77
- await github.pr_create(group.id, group.base);
82
+ await github.pr_create({
83
+ branch: group.id,
84
+ base: group.base,
85
+ title: group.title,
86
+ });
78
87
  // move back to temp branch
79
88
  await cli(`git checkout ${temp_branch_name}`);
80
89
  }
@@ -90,9 +99,11 @@ async function run(props) {
90
99
  });
91
100
  }
92
101
  catch (err) {
93
- actions.output(React.createElement(Ink.Text, { color: "#ef4444" }, "Error during rebase."));
102
+ actions.error("Unable to rebase.");
94
103
  if (err instanceof Error) {
95
- actions.debug(React.createElement(Ink.Text, { color: "#ef4444" }, err.message));
104
+ if (actions.isDebug()) {
105
+ actions.error(err.message);
106
+ }
96
107
  }
97
108
  handle_exit();
98
109
  }
@@ -87,7 +87,7 @@ export function MultiSelect(props) {
87
87
  const active = i === index;
88
88
  const selected = selected_set.has(i);
89
89
  const disabled = item.disabled || false;
90
- return (React.createElement(ItemRow, { key: item.label, label: item.label, active: active, selected: selected, disabled: disabled }));
90
+ return (React.createElement(ItemRow, { key: item.label, label: item.label, active: active, selected: selected, disabled: disabled, maxWidth: props.maxWidth }));
91
91
  })));
92
92
  }
93
93
  function ItemRow(props) {
@@ -111,7 +111,7 @@ function ItemRow(props) {
111
111
  }
112
112
  return (React.createElement(Ink.Box, { flexDirection: "row", gap: 1 },
113
113
  React.createElement(Radio, { selected: props.selected, disabled: props.disabled }),
114
- React.createElement(Ink.Box, null,
114
+ React.createElement(Ink.Box, { width: props.maxWidth },
115
115
  React.createElement(Ink.Text, { bold: bold, underline: underline, color: color, dimColor: dimColor, wrap: "truncate-end" }, props.label))));
116
116
  }
117
117
  function Radio(props) {
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ import fs from "node:fs";
3
+ import * as Ink from "ink";
4
+ export function AutoUpdate(props) {
5
+ const props_ref = React.useRef(props);
6
+ props_ref.current = props;
7
+ React.useEffect(() => {
8
+ async function auto_update() {
9
+ try {
10
+ const npm_res = await fetch(`https://registry.npmjs.org/${props.name}`);
11
+ const npm_json = await npm_res.json();
12
+ const latest = npm_json["dist-tags"].latest;
13
+ console.debug({ latest });
14
+ const script_path = process.argv[1];
15
+ const path_list = [process.argv[1]];
16
+ const real_path_list = path_list.map((p) => fs.realpathSync(p));
17
+ console.debug({ real_path_list, path_list });
18
+ // const winner = await Promise.race([
19
+ // sleep(Math.random() * 5000).then(() => "a"),
20
+ // sleep(Math.random() * 5000).then(() => "b"),
21
+ // ]);
22
+ // console.debug({ winner });
23
+ }
24
+ catch (err) {
25
+ console.debug({ err });
26
+ // handled by catch below
27
+ throw err;
28
+ }
29
+ }
30
+ const onError = props_ref.current.onError || (() => { });
31
+ auto_update().catch(onError);
32
+ }, []);
33
+ return React.createElement(Ink.Text, { color: "yellow" }, "Checking for latest version...");
34
+ }
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import * as Ink from "ink";
3
3
  export function Parens(props) {
4
- const color = "#06b6d4";
4
+ const color = "#38bdf8";
5
5
  return (React.createElement(Ink.Text, null,
6
6
  React.createElement(Ink.Text, { color: color }, "("),
7
7
  props.children,
@@ -0,0 +1,21 @@
1
+ import * as React from "react";
2
+ import { invariant } from "../core/invariant.js";
3
+ import { Store } from "./Store.js";
4
+ import { YesNoPrompt } from "./YesNoPrompt.js";
5
+ export function PreLocalMergeRebase() {
6
+ const actions = Store.useActions();
7
+ const argv = Store.useState((state) => state.argv);
8
+ invariant(argv, "argv must exist");
9
+ React.useEffect(() => {
10
+ if (argv.force) {
11
+ Store.setState((state) => {
12
+ state.step = "local-merge-rebase";
13
+ });
14
+ }
15
+ }, [argv]);
16
+ return (React.createElement(YesNoPrompt, { message: "Local branch needs to be rebased, would you like to rebase to update your local branch?", onYes: () => {
17
+ actions.set((state) => {
18
+ state.step = "local-merge-rebase";
19
+ });
20
+ }, onNo: () => actions.exit(0) }));
21
+ }
@@ -1,29 +1,21 @@
1
1
  import * as React from "react";
2
- import * as Ink from "ink";
3
- import { Exit } from "./Exit.js";
2
+ import { invariant } from "../core/invariant.js";
4
3
  import { Store } from "./Store.js";
4
+ import { YesNoPrompt } from "./YesNoPrompt.js";
5
5
  export function PreSelectCommitRanges() {
6
6
  const actions = Store.useActions();
7
- Ink.useInput((input) => {
8
- const inputLower = input.toLowerCase();
9
- switch (inputLower) {
10
- case "n":
11
- return actions.output(React.createElement(Exit, { clear: true, code: 0 }));
12
- case "y":
13
- return actions.set((state) => {
14
- state.step = "select-commit-ranges";
15
- });
7
+ const argv = Store.useState((state) => state.argv);
8
+ invariant(argv, "argv must exist");
9
+ React.useEffect(() => {
10
+ if (argv.force) {
11
+ Store.setState((state) => {
12
+ state.step = "select-commit-ranges";
13
+ });
16
14
  }
17
- });
18
- return (React.createElement(Ink.Box, { flexDirection: "column" },
19
- React.createElement(Ink.Box, { marginTop: 1 },
20
- React.createElement(Ink.Text, { color: "yellow" }, "Some commits are new or outdated, would you like to select new commit ranges?"),
21
- React.createElement(Ink.Text, null, " "),
22
- React.createElement(Ink.Text, { color: "blue" },
23
- "(",
24
- React.createElement(Ink.Text, { color: "gray" },
25
- React.createElement(Ink.Text, { color: "green", dimColor: true }, "Y"),
26
- "/",
27
- React.createElement(Ink.Text, { color: "red", dimColor: true }, "n")),
28
- ")"))));
15
+ }, [argv]);
16
+ return (React.createElement(YesNoPrompt, { message: "Some commits are new or outdated, would you like to select new commit ranges?", onYes: () => {
17
+ actions.set((state) => {
18
+ state.step = "select-commit-ranges";
19
+ });
20
+ }, onNo: () => actions.exit(0) }));
29
21
  }
@@ -1,8 +1,18 @@
1
1
  import * as React from "react";
2
+ import { invariant } from "../core/invariant.js";
2
3
  import { Store } from "./Store.js";
3
4
  import { YesNoPrompt } from "./YesNoPrompt.js";
4
5
  export function PreSelectCommitRanges() {
5
6
  const actions = Store.useActions();
7
+ const argv = Store.useState((state) => state.argv);
8
+ invariant(argv, "argv must exist");
9
+ React.useEffect(() => {
10
+ if (argv.force) {
11
+ Store.setState((state) => {
12
+ state.step = "select-commit-ranges";
13
+ });
14
+ }
15
+ }, [argv]);
6
16
  return (React.createElement(YesNoPrompt, { message: "Some commits are new or outdated, would you like to select new commit ranges?", onYes: () => {
7
17
  actions.set((state) => {
8
18
  state.step = "select-commit-ranges";
@@ -1,12 +1,14 @@
1
1
  import * as React from "react";
2
2
  import * as Ink from "ink";
3
- import { v4 as uuid_v4 } from "uuid";
4
3
  import { invariant } from "../core/invariant.js";
4
+ import { short_id } from "../core/short_id.js";
5
5
  import { wrap_index } from "../core/wrap_index.js";
6
+ import { Brackets } from "./Brackets.js";
6
7
  import { FormatText } from "./FormatText.js";
7
8
  import { MultiSelect } from "./MultiSelect.js";
8
9
  import { Parens } from "./Parens.js";
9
10
  import { Store } from "./Store.js";
11
+ import { TextInput } from "./TextInput.js";
10
12
  export function SelectCommitRanges() {
11
13
  const commit_range = Store.useState((state) => state.commit_range);
12
14
  invariant(commit_range, "commit_range must exist");
@@ -14,11 +16,10 @@ export function SelectCommitRanges() {
14
16
  }
15
17
  function SelectCommitRangesInternal(props) {
16
18
  const actions = Store.useActions();
17
- const [new_group_list, create_group] = React.useReducer((group_list, id) => {
18
- return group_list.concat({
19
- id,
20
- title: id,
21
- });
19
+ const [selected_group_id, set_selected_group_id] = React.useState(props.commit_range.UNASSIGNED);
20
+ const [group_input, set_group_input] = React.useState(false);
21
+ const [new_group_list, create_group] = React.useReducer((group_list, group) => {
22
+ return group_list.concat(group);
22
23
  }, []);
23
24
  const [commit_map, update_commit_map] = React.useReducer((map, args) => {
24
25
  map.set(args.key, args.value);
@@ -32,7 +33,6 @@ function SelectCommitRangesInternal(props) {
32
33
  });
33
34
  const group_list = [];
34
35
  // detect if there are unassigned commits
35
- // unshift an unassigned group if so to collect them
36
36
  let unassigned_count = 0;
37
37
  for (const [, group_id] of commit_map.entries()) {
38
38
  if (group_id === null) {
@@ -40,53 +40,56 @@ function SelectCommitRangesInternal(props) {
40
40
  unassigned_count++;
41
41
  }
42
42
  }
43
- if (unassigned_count) {
44
- group_list.push({
45
- id: "unassigned",
46
- title: "Unassigned",
47
- });
48
- }
49
43
  group_list.push(...new_group_list);
44
+ const total_group_count = new_group_list.length + props.commit_range.group_list.length;
50
45
  for (const group of props.commit_range.group_list) {
51
- if (group.id !== props.commit_range.UNASSIGNED) {
52
- group_list.push({
53
- id: group.id,
54
- title: group.pr?.title || group.id,
55
- });
46
+ if (group.pr?.state === "MERGED")
47
+ continue;
48
+ if (group.id === props.commit_range.UNASSIGNED) {
49
+ // only include unassigned group when there are no other groups
50
+ if (total_group_count === 1) {
51
+ group_list.push({
52
+ id: group.id,
53
+ title: "Unassigned",
54
+ });
55
+ }
56
+ continue;
56
57
  }
58
+ group_list.push({
59
+ id: group.id,
60
+ title: group.pr?.title || group.id,
61
+ });
62
+ }
63
+ let current_index = group_list.findIndex((g) => g.id === selected_group_id);
64
+ if (current_index === -1) {
65
+ current_index = 0;
57
66
  }
58
- const [selected_group_id, set_selected_group_id] = React.useState(group_list[0].id);
59
- const isUnassigned = selected_group_id === "unassigned";
60
- const current_index = group_list.findIndex((g) => g.id === selected_group_id);
61
67
  Ink.useInput((input, key) => {
62
68
  const inputLower = input.toLowerCase();
63
69
  const hasUnassignedCommits = unassigned_count > 0;
64
- if (!hasUnassignedCommits && (inputLower === "r" || inputLower === "s")) {
70
+ if (!hasUnassignedCommits && inputLower === "s") {
65
71
  actions.set((state) => {
66
72
  state.commit_map = {};
67
73
  for (const [sha, id] of commit_map.entries()) {
68
74
  if (id) {
69
- state.commit_map[sha] = id;
75
+ const group = new_group_list.find((g) => g.id === id);
76
+ // console.debug({ sha, id, group });
77
+ if (group) {
78
+ state.commit_map[sha] = group;
79
+ }
70
80
  }
71
81
  }
72
82
  switch (inputLower) {
73
83
  case "s":
74
84
  state.step = "manual-rebase";
75
85
  break;
76
- case "r":
77
- state.step = "manual-rebase-no-sync";
78
86
  }
79
87
  });
80
88
  return;
81
89
  }
82
90
  // only allow create when on unassigned group
83
91
  if (hasUnassignedCommits && inputLower === "c") {
84
- const id = uuid_v4();
85
- actions.output(React.createElement(Ink.Box, null,
86
- React.createElement(Ink.Text, { dimColor: true }, "Created new group "),
87
- React.createElement(Ink.Text, { color: "blueBright" }, id)));
88
- create_group(id);
89
- set_selected_group_id(id);
92
+ set_group_input(true);
90
93
  return;
91
94
  }
92
95
  if (key.leftArrow) {
@@ -101,21 +104,11 @@ function SelectCommitRangesInternal(props) {
101
104
  }
102
105
  });
103
106
  const group = group_list[current_index];
104
- // <- (2/4) #742 Title A ->
105
- const max_group_label_width = 64;
106
- let group_title_width = max_group_label_width;
107
- const left_arrow = `${SYMBOL.left} `;
108
- const right_arrow = ` ${SYMBOL.right}`;
109
- const group_position = `(${current_index + 1}/${group_list.length}) `;
110
- const title = group.title || "Unassigned";
111
- group_title_width -= group_position.length;
112
- group_title_width -= left_arrow.length + right_arrow.length;
113
- group_title_width = Math.min(title.length, group_title_width);
114
107
  const items = props.commit_range.commit_list.map((commit) => {
115
108
  const commit_metadata_id = commit_map.get(commit.sha);
116
109
  const selected = commit_metadata_id !== null;
117
110
  let disabled;
118
- if (isUnassigned) {
111
+ if (group.id === props.commit_range.UNASSIGNED) {
119
112
  disabled = true;
120
113
  }
121
114
  else {
@@ -129,11 +122,25 @@ function SelectCommitRangesInternal(props) {
129
122
  };
130
123
  });
131
124
  items.reverse();
132
- // console.debug({ current_index, group, isUnassigned });
125
+ // <- (2/4) #742 Title A ->
126
+ const left_arrow = `${SYMBOL.left} `;
127
+ const right_arrow = ` ${SYMBOL.right}`;
128
+ const group_position = `(${current_index + 1}/${group_list.length}) `;
129
+ const max_group_label_width = 80;
130
+ let group_title_width = max_group_label_width;
131
+ group_title_width -= group_position.length;
132
+ group_title_width -= left_arrow.length + right_arrow.length;
133
+ group_title_width = Math.min(group.title.length, group_title_width);
134
+ let max_item_width = max_group_label_width;
135
+ max_item_width -= left_arrow.length + right_arrow.length;
133
136
  return (React.createElement(Ink.Box, { flexDirection: "column" },
134
137
  React.createElement(Ink.Box, { height: 1 }),
135
- React.createElement(MultiSelect, { key: group.id, items: items, onSelect: (args) => {
138
+ React.createElement(MultiSelect, { key: group.id, items: items, maxWidth: max_item_width, onSelect: (args) => {
136
139
  // console.debug("onSelect", args);
140
+ if (group_input) {
141
+ // console.debug("group_input is true, ignoring onSelect");
142
+ return;
143
+ }
137
144
  const key = args.item.sha;
138
145
  let value;
139
146
  if (args.selected) {
@@ -149,7 +156,7 @@ function SelectCommitRangesInternal(props) {
149
156
  React.createElement(Ink.Text, null, left_arrow),
150
157
  React.createElement(Ink.Text, null, group_position),
151
158
  React.createElement(Ink.Box, { width: group_title_width, justifyContent: "center" },
152
- React.createElement(Ink.Text, { wrap: "truncate-end" }, title)),
159
+ React.createElement(Ink.Text, { wrap: "truncate-end" }, group.title)),
153
160
  React.createElement(Ink.Text, null, right_arrow)),
154
161
  React.createElement(Ink.Box, { height: 1 }),
155
162
  unassigned_count > 0 ? (React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, { color: "gray" }), message: "{count} unassigned commits, press {c} to {create} a new group", values: {
@@ -159,34 +166,42 @@ function SelectCommitRangesInternal(props) {
159
166
  React.createElement(Parens, null, "c"),
160
167
  "reate")),
161
168
  } })) : (React.createElement(React.Fragment, null,
162
- React.createElement(Ink.Text, null,
163
- "🎉 Done! Press ",
164
- React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, "s"),
165
- " to ",
166
- React.createElement(Ink.Text, { bold: true, color: "#22c55e" },
167
- React.createElement(Parens, null, "s"),
168
- "ync"),
169
- " the commits to Github"),
170
- React.createElement(Ink.Text, { color: "gray" },
171
- React.createElement(Ink.Text, null, "Press "),
172
- React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, "r"),
173
- " to locally ",
174
- React.createElement(Ink.Text, { bold: true, color: "#22c55e" },
175
- React.createElement(Parens, null, "r"),
176
- "ebase"),
177
- " only"))),
169
+ React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, null), message: "\uD83C\uDF89 Done! Press {s} to {sync} the commits to Github", values: {
170
+ s: (React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, "s")),
171
+ sync: (React.createElement(Ink.Text, { bold: true, color: "#22c55e" },
172
+ React.createElement(Parens, null, "s"),
173
+ "ync")),
174
+ } }))),
175
+ !group_input ? null : (React.createElement(React.Fragment, null,
176
+ React.createElement(Ink.Box, { height: 1 }),
177
+ React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, { color: "gray" }), message: "Enter a title for the PR {note}", values: {
178
+ note: (React.createElement(Parens, null,
179
+ React.createElement(FormatText, { message: "press {enter} to submit", values: {
180
+ enter: (React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, SYMBOL.enter)),
181
+ } }))),
182
+ } }),
183
+ React.createElement(TextInput, { onSubmit: submit_group_input }),
184
+ React.createElement(Ink.Box, { height: 1 }))),
178
185
  React.createElement(Ink.Box, null,
179
- React.createElement(Ink.Text, { color: "gray" },
180
- React.createElement(Ink.Text, null, "Press "),
181
- React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, SYMBOL.left),
182
- " and ",
183
- React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, SYMBOL.right),
184
- " to view PR groups")),
186
+ React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, { color: "gray" }), message: "Press {left} and {right} to view PR groups", values: {
187
+ left: (React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, SYMBOL.left)),
188
+ right: (React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, SYMBOL.right)),
189
+ } })),
185
190
  React.createElement(Ink.Box, null,
186
- React.createElement(Ink.Text, { color: "gray" },
187
- React.createElement(Ink.Text, null, "Press "),
188
- React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, SYMBOL.enter),
189
- " to toggle commit selection"))));
191
+ React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, { color: "gray" }), message: "Press {enter} to toggle commit selection", values: {
192
+ enter: (React.createElement(Ink.Text, { bold: true, color: "#22c55e" }, SYMBOL.enter)),
193
+ } }))));
194
+ function submit_group_input(title) {
195
+ const id = short_id();
196
+ actions.output(React.createElement(FormatText, { wrapper: React.createElement(Ink.Text, { dimColor: true }), message: "Created new group {group} {note}", values: {
197
+ group: React.createElement(Brackets, null, title),
198
+ note: React.createElement(Parens, null, id),
199
+ } }));
200
+ // console.debug("submit_group_input", { title, id });
201
+ create_group({ id, title });
202
+ set_selected_group_id(id);
203
+ set_group_input(false);
204
+ }
190
205
  }
191
206
  const SYMBOL = {
192
207
  left: "←",