@ogpoyraz/wtx 0.8.8 → 0.8.9
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/dist/cli.mjs +1273 -416
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -14672,7 +14672,7 @@ function getVersion() {
|
|
|
14672
14672
|
return "0.0.0";
|
|
14673
14673
|
}
|
|
14674
14674
|
}
|
|
14675
|
-
var DepsManagerEnum, DepsStrategyEnum, DepsConfigSchema, AgentConfigSchema, PortsConfigSchema, LEGACY_REPO_KEYS, RepoConfigSchema, ConfigSchema, VERSION;
|
|
14675
|
+
var DepsManagerEnum, DepsStrategyEnum, DepsConfigSchema, AgentConfigSchema, PortsConfigSchema, TuiConfigSchema, LEGACY_REPO_KEYS, RepoConfigSchema, ConfigSchema, VERSION;
|
|
14676
14676
|
var init_types = __esm(() => {
|
|
14677
14677
|
init_v4();
|
|
14678
14678
|
DepsManagerEnum = exports_external.enum(["auto", "npm", "bun", "pnpm", "yarn", "go", "python", "cargo"]);
|
|
@@ -14688,6 +14688,10 @@ var init_types = __esm(() => {
|
|
|
14688
14688
|
min: exports_external.number().int().min(1024).max(65535).default(4100),
|
|
14689
14689
|
max: exports_external.number().int().min(1024).max(65535).default(4999)
|
|
14690
14690
|
}).refine((v) => v.min <= v.max, { message: "ports.min must be less than or equal to ports.max" });
|
|
14691
|
+
TuiConfigSchema = exports_external.object({
|
|
14692
|
+
leftPaneWidthWeight: exports_external.number().int().min(1).max(10).default(3),
|
|
14693
|
+
rightPaneWidthWeight: exports_external.number().int().min(1).max(10).default(7)
|
|
14694
|
+
});
|
|
14691
14695
|
LEGACY_REPO_KEYS = {
|
|
14692
14696
|
pr: "check_prs",
|
|
14693
14697
|
forge: "forge_provider",
|
|
@@ -14730,7 +14734,8 @@ var init_types = __esm(() => {
|
|
|
14730
14734
|
user: exports_external.string().trim().min(1, { message: "must not be empty if provided" }).nullable().default(null),
|
|
14731
14735
|
repos: exports_external.record(exports_external.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/, "Invalid repo key format"), RepoConfigSchema),
|
|
14732
14736
|
agents: exports_external.record(exports_external.string().regex(/^[a-z][a-z0-9_-]*$/, "Agent names must be lowercase alphanumeric with dashes/underscores"), AgentConfigSchema).optional(),
|
|
14733
|
-
ports: PortsConfigSchema.default({ min: 4100, max: 4999 })
|
|
14737
|
+
ports: PortsConfigSchema.default({ min: 4100, max: 4999 }),
|
|
14738
|
+
tui: TuiConfigSchema.default({ leftPaneWidthWeight: 3, rightPaneWidthWeight: 7 })
|
|
14734
14739
|
});
|
|
14735
14740
|
VERSION = getVersion();
|
|
14736
14741
|
});
|
|
@@ -25010,80 +25015,6 @@ var init_owner = __esm(() => {
|
|
|
25010
25015
|
init_git();
|
|
25011
25016
|
});
|
|
25012
25017
|
|
|
25013
|
-
// src/lib/agents.ts
|
|
25014
|
-
function resolveAgentCommand(name, configured) {
|
|
25015
|
-
return configured?.[name]?.command ?? DEFAULT_AGENTS[name] ?? null;
|
|
25016
|
-
}
|
|
25017
|
-
function listAvailableAgents(configured) {
|
|
25018
|
-
return [...new Set([...Object.keys(DEFAULT_AGENTS), ...Object.keys(configured ?? {})])];
|
|
25019
|
-
}
|
|
25020
|
-
async function isTmuxAvailable() {
|
|
25021
|
-
if (tmuxAvailable !== undefined)
|
|
25022
|
-
return tmuxAvailable;
|
|
25023
|
-
try {
|
|
25024
|
-
await execa("tmux", ["-V"]);
|
|
25025
|
-
tmuxAvailable = true;
|
|
25026
|
-
} catch {
|
|
25027
|
-
tmuxAvailable = false;
|
|
25028
|
-
}
|
|
25029
|
-
return tmuxAvailable;
|
|
25030
|
-
}
|
|
25031
|
-
function tmuxSessionName(repoName, branch) {
|
|
25032
|
-
const sanitized = `${repoName}-${branch}`.replace(/[\/.:]/g, "-");
|
|
25033
|
-
return `wtx-${sanitized}`.substring(0, 60);
|
|
25034
|
-
}
|
|
25035
|
-
function buildTmuxArgs(session, wtPath, cmd) {
|
|
25036
|
-
return ["new-session", "-d", "-s", session, "-c", wtPath, cmd];
|
|
25037
|
-
}
|
|
25038
|
-
async function spawnAgentInWorktree(commandTemplate, wtPath, opts = {}) {
|
|
25039
|
-
const cmd = buildAgentCommand(commandTemplate, wtPath, opts.prompt, {
|
|
25040
|
-
branch: opts.branch,
|
|
25041
|
-
repo: opts.repoName
|
|
25042
|
-
});
|
|
25043
|
-
const session = opts.repoName && opts.branch ? tmuxSessionName(opts.repoName, opts.branch) : undefined;
|
|
25044
|
-
if (opts.dryRun) {
|
|
25045
|
-
if (session && await isTmuxAvailable()) {
|
|
25046
|
-
return { mode: "tmux", session };
|
|
25047
|
-
}
|
|
25048
|
-
return { mode: "direct" };
|
|
25049
|
-
}
|
|
25050
|
-
if (session && await isTmuxAvailable()) {
|
|
25051
|
-
try {
|
|
25052
|
-
const args = buildTmuxArgs(session, wtPath, cmd);
|
|
25053
|
-
await execa("tmux", args, { stdio: "inherit" });
|
|
25054
|
-
return { mode: "tmux", session };
|
|
25055
|
-
} catch {}
|
|
25056
|
-
}
|
|
25057
|
-
await execa(cmd, { shell: true, cwd: wtPath, stdio: "inherit" });
|
|
25058
|
-
return { mode: "direct" };
|
|
25059
|
-
}
|
|
25060
|
-
function shellQuote(value) {
|
|
25061
|
-
return `'${value.replaceAll("'", `'''`)}'`;
|
|
25062
|
-
}
|
|
25063
|
-
function buildAgentCommand(commandTemplate, wtPath, prompt, vars) {
|
|
25064
|
-
let cmd = commandTemplate.replaceAll("{wt}", shellQuote(wtPath));
|
|
25065
|
-
if (vars?.branch) {
|
|
25066
|
-
cmd = cmd.replaceAll("{branch}", shellQuote(vars.branch));
|
|
25067
|
-
}
|
|
25068
|
-
if (vars?.repo) {
|
|
25069
|
-
cmd = cmd.replaceAll("{repo}", shellQuote(vars.repo));
|
|
25070
|
-
}
|
|
25071
|
-
if (prompt) {
|
|
25072
|
-
cmd += ` ${shellQuote(prompt)}`;
|
|
25073
|
-
}
|
|
25074
|
-
return cmd;
|
|
25075
|
-
}
|
|
25076
|
-
var DEFAULT_AGENTS, tmuxAvailable = undefined;
|
|
25077
|
-
var init_agents = __esm(() => {
|
|
25078
|
-
init_execa();
|
|
25079
|
-
DEFAULT_AGENTS = {
|
|
25080
|
-
claude: "claude",
|
|
25081
|
-
codex: "codex",
|
|
25082
|
-
opencode: "opencode",
|
|
25083
|
-
cursor: "cursor"
|
|
25084
|
-
};
|
|
25085
|
-
});
|
|
25086
|
-
|
|
25087
25018
|
// src/lib/forge/types.ts
|
|
25088
25019
|
function displayStateRank(state) {
|
|
25089
25020
|
return DISPLAY_PRIORITY.indexOf(state);
|
|
@@ -25935,28 +25866,133 @@ var init_WorktreeTable = __esm(() => {
|
|
|
25935
25866
|
init_use_tap();
|
|
25936
25867
|
});
|
|
25937
25868
|
|
|
25938
|
-
// src/tui/
|
|
25939
|
-
import { jsx as jsx2, jsxs as jsxs2
|
|
25940
|
-
function
|
|
25869
|
+
// src/tui/tabs/TabBar.tsx
|
|
25870
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "@opentui/react/jsx-runtime";
|
|
25871
|
+
function TabItem({
|
|
25872
|
+
id,
|
|
25873
|
+
label,
|
|
25874
|
+
closable,
|
|
25875
|
+
active,
|
|
25876
|
+
onSelect,
|
|
25877
|
+
onClose
|
|
25878
|
+
}) {
|
|
25879
|
+
const tap = useTapHandler(() => onSelect(id));
|
|
25880
|
+
const closeTap = useTapHandler((e) => {
|
|
25881
|
+
e.stopPropagation();
|
|
25882
|
+
onClose?.(id);
|
|
25883
|
+
});
|
|
25884
|
+
return /* @__PURE__ */ jsxs2("box", {
|
|
25885
|
+
flexDirection: "row",
|
|
25886
|
+
paddingRight: 1,
|
|
25887
|
+
...tap,
|
|
25888
|
+
children: [
|
|
25889
|
+
/* @__PURE__ */ jsx2("text", {
|
|
25890
|
+
fg: active ? tokens.accent : tokens.dim,
|
|
25891
|
+
attributes: active ? 1 : 0,
|
|
25892
|
+
children: label
|
|
25893
|
+
}),
|
|
25894
|
+
closable ? /* @__PURE__ */ jsx2("box", {
|
|
25895
|
+
...closeTap,
|
|
25896
|
+
style: { marginLeft: 1 },
|
|
25897
|
+
children: /* @__PURE__ */ jsx2("text", {
|
|
25898
|
+
fg: active ? tokens.accent : tokens.dim,
|
|
25899
|
+
children: "✕"
|
|
25900
|
+
})
|
|
25901
|
+
}) : null,
|
|
25902
|
+
/* @__PURE__ */ jsx2("text", {
|
|
25903
|
+
children: " "
|
|
25904
|
+
})
|
|
25905
|
+
]
|
|
25906
|
+
});
|
|
25907
|
+
}
|
|
25908
|
+
function TabBar({ tabs, activeId, onSelect, onClose }) {
|
|
25909
|
+
return /* @__PURE__ */ jsx2("box", {
|
|
25910
|
+
flexDirection: "row",
|
|
25911
|
+
width: "100%",
|
|
25912
|
+
alignItems: "center",
|
|
25913
|
+
paddingLeft: 1,
|
|
25914
|
+
children: tabs.map((tab) => /* @__PURE__ */ jsx2(TabItem, {
|
|
25915
|
+
id: tab.id,
|
|
25916
|
+
label: tab.label,
|
|
25917
|
+
closable: tab.closable,
|
|
25918
|
+
active: tab.id === activeId,
|
|
25919
|
+
onSelect,
|
|
25920
|
+
onClose
|
|
25921
|
+
}, tab.id))
|
|
25922
|
+
});
|
|
25923
|
+
}
|
|
25924
|
+
var init_TabBar = __esm(() => {
|
|
25925
|
+
init_theme();
|
|
25926
|
+
init_use_tap();
|
|
25927
|
+
});
|
|
25928
|
+
|
|
25929
|
+
// src/tui/tabs/TabPane.tsx
|
|
25930
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
25931
|
+
function TabPane({
|
|
25932
|
+
selectedRow,
|
|
25933
|
+
tabs,
|
|
25934
|
+
activeId,
|
|
25935
|
+
focused,
|
|
25936
|
+
canAdd,
|
|
25937
|
+
onSelect,
|
|
25938
|
+
onAdd,
|
|
25939
|
+
onClose,
|
|
25940
|
+
onFocusTerminal: _onFocusTerminal,
|
|
25941
|
+
onBlurTerminal: _onBlurTerminal
|
|
25942
|
+
}) {
|
|
25943
|
+
const activeTab = tabs.find((t) => t.id === activeId) ?? tabs[0] ?? null;
|
|
25944
|
+
const borderColor = focused ? tokens.accent : tokens.border;
|
|
25945
|
+
return /* @__PURE__ */ jsxs3("box", {
|
|
25946
|
+
flexDirection: "column",
|
|
25947
|
+
flexGrow: 1,
|
|
25948
|
+
width: "100%",
|
|
25949
|
+
height: "100%",
|
|
25950
|
+
border: true,
|
|
25951
|
+
borderColor,
|
|
25952
|
+
focusedBorderColor: borderColor,
|
|
25953
|
+
focusable: false,
|
|
25954
|
+
id: "detail-pane",
|
|
25955
|
+
children: [
|
|
25956
|
+
/* @__PURE__ */ jsx3(TabBar, {
|
|
25957
|
+
tabs,
|
|
25958
|
+
activeId,
|
|
25959
|
+
canAdd,
|
|
25960
|
+
onSelect,
|
|
25961
|
+
onAdd,
|
|
25962
|
+
onClose
|
|
25963
|
+
}),
|
|
25964
|
+
/* @__PURE__ */ jsx3("box", {
|
|
25965
|
+
flexGrow: 1,
|
|
25966
|
+
width: "100%",
|
|
25967
|
+
flexDirection: "column",
|
|
25968
|
+
children: activeTab ? activeTab.render({ worktree: selectedRow, isActive: true }) : /* @__PURE__ */ jsx3("text", {
|
|
25969
|
+
fg: tokens.dim,
|
|
25970
|
+
children: "No tab"
|
|
25971
|
+
})
|
|
25972
|
+
})
|
|
25973
|
+
]
|
|
25974
|
+
});
|
|
25975
|
+
}
|
|
25976
|
+
var init_TabPane = __esm(() => {
|
|
25977
|
+
init_theme();
|
|
25978
|
+
init_TabBar();
|
|
25979
|
+
});
|
|
25980
|
+
|
|
25981
|
+
// src/tui/components/DetailsTab.tsx
|
|
25982
|
+
import { jsx as jsx4, jsxs as jsxs4, Fragment } from "@opentui/react/jsx-runtime";
|
|
25983
|
+
function DetailsContent({ selectedRow }) {
|
|
25941
25984
|
const prTap = useTapHandler(() => {
|
|
25942
25985
|
if (selectedRow?.prUrl)
|
|
25943
25986
|
openInBrowser(selectedRow.prUrl);
|
|
25944
25987
|
});
|
|
25945
25988
|
if (!selectedRow) {
|
|
25946
|
-
return /* @__PURE__ */
|
|
25947
|
-
id: "detail-pane",
|
|
25989
|
+
return /* @__PURE__ */ jsx4("box", {
|
|
25948
25990
|
flexGrow: 1,
|
|
25949
25991
|
width: "100%",
|
|
25950
25992
|
height: "100%",
|
|
25951
|
-
border: true,
|
|
25952
|
-
borderColor: tokens.border,
|
|
25953
|
-
focusedBorderColor: tokens.border,
|
|
25954
|
-
focusable: false,
|
|
25955
|
-
title: "Details",
|
|
25956
|
-
padding: 1,
|
|
25957
25993
|
justifyContent: "center",
|
|
25958
25994
|
alignItems: "center",
|
|
25959
|
-
children: /* @__PURE__ */
|
|
25995
|
+
children: /* @__PURE__ */ jsx4("text", {
|
|
25960
25996
|
fg: tokens.dim,
|
|
25961
25997
|
children: "No worktree selected"
|
|
25962
25998
|
})
|
|
@@ -25984,26 +26020,20 @@ function DetailPane({ selectedRow }) {
|
|
|
25984
26020
|
baseChanged
|
|
25985
26021
|
} = selectedRow;
|
|
25986
26022
|
const aheadBehindStr = ahead !== null && behind !== null ? `${ahead} ahead, ${behind} behind` : "unknown";
|
|
25987
|
-
return /* @__PURE__ */
|
|
25988
|
-
id: "detail-pane",
|
|
26023
|
+
return /* @__PURE__ */ jsxs4("scrollbox", {
|
|
25989
26024
|
flexGrow: 1,
|
|
25990
26025
|
width: "100%",
|
|
25991
26026
|
height: "100%",
|
|
25992
|
-
border: true,
|
|
25993
|
-
borderColor: tokens.border,
|
|
25994
|
-
focusedBorderColor: tokens.border,
|
|
25995
|
-
focusable: false,
|
|
25996
|
-
title: "Details",
|
|
25997
|
-
padding: 1,
|
|
25998
26027
|
focused: false,
|
|
26028
|
+
padding: 1,
|
|
25999
26029
|
children: [
|
|
26000
|
-
/* @__PURE__ */
|
|
26030
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26001
26031
|
children: [
|
|
26002
|
-
/* @__PURE__ */
|
|
26032
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26003
26033
|
fg: tokens.dim,
|
|
26004
26034
|
children: "Repo:"
|
|
26005
26035
|
}),
|
|
26006
|
-
/* @__PURE__ */
|
|
26036
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26007
26037
|
fg: tokens.fg,
|
|
26008
26038
|
children: [
|
|
26009
26039
|
" ",
|
|
@@ -26012,13 +26042,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26012
26042
|
})
|
|
26013
26043
|
]
|
|
26014
26044
|
}),
|
|
26015
|
-
/* @__PURE__ */
|
|
26045
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26016
26046
|
children: [
|
|
26017
|
-
/* @__PURE__ */
|
|
26047
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26018
26048
|
fg: tokens.dim,
|
|
26019
26049
|
children: "Branch:"
|
|
26020
26050
|
}),
|
|
26021
|
-
/* @__PURE__ */
|
|
26051
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26022
26052
|
fg: tokens.fg,
|
|
26023
26053
|
children: [
|
|
26024
26054
|
" ",
|
|
@@ -26029,13 +26059,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26029
26059
|
})
|
|
26030
26060
|
]
|
|
26031
26061
|
}),
|
|
26032
|
-
/* @__PURE__ */
|
|
26062
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26033
26063
|
children: [
|
|
26034
|
-
/* @__PURE__ */
|
|
26064
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26035
26065
|
fg: tokens.dim,
|
|
26036
26066
|
children: "Path:"
|
|
26037
26067
|
}),
|
|
26038
|
-
/* @__PURE__ */
|
|
26068
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26039
26069
|
fg: tokens.fg,
|
|
26040
26070
|
children: [
|
|
26041
26071
|
" ",
|
|
@@ -26044,13 +26074,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26044
26074
|
})
|
|
26045
26075
|
]
|
|
26046
26076
|
}),
|
|
26047
|
-
/* @__PURE__ */
|
|
26077
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26048
26078
|
children: [
|
|
26049
|
-
/* @__PURE__ */
|
|
26079
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26050
26080
|
fg: tokens.dim,
|
|
26051
26081
|
children: "Commit:"
|
|
26052
26082
|
}),
|
|
26053
|
-
/* @__PURE__ */
|
|
26083
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26054
26084
|
fg: tokens.fg,
|
|
26055
26085
|
children: [
|
|
26056
26086
|
" ",
|
|
@@ -26059,13 +26089,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26059
26089
|
})
|
|
26060
26090
|
]
|
|
26061
26091
|
}),
|
|
26062
|
-
owner && /* @__PURE__ */
|
|
26092
|
+
owner && /* @__PURE__ */ jsxs4("text", {
|
|
26063
26093
|
children: [
|
|
26064
|
-
/* @__PURE__ */
|
|
26094
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26065
26095
|
fg: tokens.dim,
|
|
26066
26096
|
children: "Owner:"
|
|
26067
26097
|
}),
|
|
26068
|
-
/* @__PURE__ */
|
|
26098
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26069
26099
|
fg: tokens.fg,
|
|
26070
26100
|
children: [
|
|
26071
26101
|
" ",
|
|
@@ -26074,13 +26104,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26074
26104
|
})
|
|
26075
26105
|
]
|
|
26076
26106
|
}),
|
|
26077
|
-
/* @__PURE__ */
|
|
26107
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26078
26108
|
children: [
|
|
26079
|
-
/* @__PURE__ */
|
|
26109
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26080
26110
|
fg: tokens.dim,
|
|
26081
26111
|
children: "Status:"
|
|
26082
26112
|
}),
|
|
26083
|
-
/* @__PURE__ */
|
|
26113
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26084
26114
|
fg: tokens.fg,
|
|
26085
26115
|
children: [
|
|
26086
26116
|
" ",
|
|
@@ -26089,13 +26119,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26089
26119
|
})
|
|
26090
26120
|
]
|
|
26091
26121
|
}),
|
|
26092
|
-
base2 && /* @__PURE__ */
|
|
26122
|
+
base2 && /* @__PURE__ */ jsxs4("text", {
|
|
26093
26123
|
children: [
|
|
26094
|
-
/* @__PURE__ */
|
|
26124
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26095
26125
|
fg: tokens.dim,
|
|
26096
26126
|
children: "Base:"
|
|
26097
26127
|
}),
|
|
26098
|
-
/* @__PURE__ */
|
|
26128
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26099
26129
|
fg: tokens.accent,
|
|
26100
26130
|
children: [
|
|
26101
26131
|
" ",
|
|
@@ -26104,26 +26134,26 @@ function DetailPane({ selectedRow }) {
|
|
|
26104
26134
|
})
|
|
26105
26135
|
]
|
|
26106
26136
|
}),
|
|
26107
|
-
baseChanged && /* @__PURE__ */
|
|
26137
|
+
baseChanged && /* @__PURE__ */ jsxs4("text", {
|
|
26108
26138
|
children: [
|
|
26109
|
-
/* @__PURE__ */
|
|
26139
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26110
26140
|
fg: tokens.dim,
|
|
26111
26141
|
children: "Base state:"
|
|
26112
26142
|
}),
|
|
26113
|
-
/* @__PURE__ */
|
|
26143
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26114
26144
|
fg: tokens.warning,
|
|
26115
26145
|
children: " moved since recorded"
|
|
26116
26146
|
})
|
|
26117
26147
|
]
|
|
26118
26148
|
}),
|
|
26119
|
-
/* @__PURE__ */
|
|
26149
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26120
26150
|
style: { marginTop: 1 },
|
|
26121
26151
|
children: [
|
|
26122
|
-
/* @__PURE__ */
|
|
26152
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26123
26153
|
fg: tokens.dim,
|
|
26124
26154
|
children: base2 ? "vs base:" : "vs main:"
|
|
26125
26155
|
}),
|
|
26126
|
-
/* @__PURE__ */
|
|
26156
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26127
26157
|
fg: tokens.fg,
|
|
26128
26158
|
children: [
|
|
26129
26159
|
" ",
|
|
@@ -26132,13 +26162,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26132
26162
|
})
|
|
26133
26163
|
]
|
|
26134
26164
|
}),
|
|
26135
|
-
/* @__PURE__ */
|
|
26165
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26136
26166
|
children: [
|
|
26137
|
-
/* @__PURE__ */
|
|
26167
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26138
26168
|
fg: tokens.dim,
|
|
26139
26169
|
children: "Deps:"
|
|
26140
26170
|
}),
|
|
26141
|
-
/* @__PURE__ */
|
|
26171
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26142
26172
|
fg: tokens.fg,
|
|
26143
26173
|
children: [
|
|
26144
26174
|
" ",
|
|
@@ -26147,13 +26177,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26147
26177
|
})
|
|
26148
26178
|
]
|
|
26149
26179
|
}),
|
|
26150
|
-
rebaseStatus && /* @__PURE__ */
|
|
26180
|
+
rebaseStatus && /* @__PURE__ */ jsxs4("text", {
|
|
26151
26181
|
children: [
|
|
26152
|
-
/* @__PURE__ */
|
|
26182
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26153
26183
|
fg: tokens.dim,
|
|
26154
26184
|
children: "Rebase:"
|
|
26155
26185
|
}),
|
|
26156
|
-
/* @__PURE__ */
|
|
26186
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26157
26187
|
fg: tokens.error,
|
|
26158
26188
|
children: [
|
|
26159
26189
|
" ",
|
|
@@ -26162,12 +26192,12 @@ function DetailPane({ selectedRow }) {
|
|
|
26162
26192
|
})
|
|
26163
26193
|
]
|
|
26164
26194
|
}),
|
|
26165
|
-
prNumber !== null && /* @__PURE__ */
|
|
26195
|
+
prNumber !== null && /* @__PURE__ */ jsxs4(Fragment, {
|
|
26166
26196
|
children: [
|
|
26167
|
-
/* @__PURE__ */
|
|
26197
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26168
26198
|
style: { marginTop: 1 },
|
|
26169
26199
|
children: [
|
|
26170
|
-
/* @__PURE__ */
|
|
26200
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26171
26201
|
fg: tokens.accent,
|
|
26172
26202
|
children: [
|
|
26173
26203
|
"PR #",
|
|
@@ -26175,7 +26205,7 @@ function DetailPane({ selectedRow }) {
|
|
|
26175
26205
|
":"
|
|
26176
26206
|
]
|
|
26177
26207
|
}),
|
|
26178
|
-
/* @__PURE__ */
|
|
26208
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26179
26209
|
fg: tokens.dim,
|
|
26180
26210
|
children: [
|
|
26181
26211
|
" ",
|
|
@@ -26184,13 +26214,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26184
26214
|
})
|
|
26185
26215
|
]
|
|
26186
26216
|
}),
|
|
26187
|
-
prChecks && /* @__PURE__ */
|
|
26217
|
+
prChecks && /* @__PURE__ */ jsxs4("text", {
|
|
26188
26218
|
children: [
|
|
26189
|
-
/* @__PURE__ */
|
|
26219
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26190
26220
|
fg: tokens.dim,
|
|
26191
26221
|
children: "Checks:"
|
|
26192
26222
|
}),
|
|
26193
|
-
/* @__PURE__ */
|
|
26223
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26194
26224
|
fg: tokens.fg,
|
|
26195
26225
|
children: [
|
|
26196
26226
|
" ",
|
|
@@ -26199,20 +26229,20 @@ function DetailPane({ selectedRow }) {
|
|
|
26199
26229
|
})
|
|
26200
26230
|
]
|
|
26201
26231
|
}),
|
|
26202
|
-
prUrl && /* @__PURE__ */
|
|
26232
|
+
prUrl && /* @__PURE__ */ jsx4("box", {
|
|
26203
26233
|
...prTap,
|
|
26204
|
-
children: /* @__PURE__ */
|
|
26234
|
+
children: /* @__PURE__ */ jsxs4("text", {
|
|
26205
26235
|
selectable: false,
|
|
26206
26236
|
children: [
|
|
26207
|
-
/* @__PURE__ */
|
|
26237
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26208
26238
|
fg: tokens.dim,
|
|
26209
26239
|
children: "URL:"
|
|
26210
26240
|
}),
|
|
26211
|
-
/* @__PURE__ */
|
|
26241
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26212
26242
|
fg: tokens.accent,
|
|
26213
26243
|
children: ` ${prUrl}`
|
|
26214
26244
|
}),
|
|
26215
|
-
/* @__PURE__ */
|
|
26245
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26216
26246
|
fg: tokens.dim,
|
|
26217
26247
|
children: " ↗ click"
|
|
26218
26248
|
})
|
|
@@ -26221,11 +26251,11 @@ function DetailPane({ selectedRow }) {
|
|
|
26221
26251
|
})
|
|
26222
26252
|
]
|
|
26223
26253
|
}),
|
|
26224
|
-
dirtyFiles.length > 0 && /* @__PURE__ */
|
|
26254
|
+
dirtyFiles.length > 0 && /* @__PURE__ */ jsxs4("box", {
|
|
26225
26255
|
flexDirection: "column",
|
|
26226
26256
|
style: { marginTop: 1 },
|
|
26227
26257
|
children: [
|
|
26228
|
-
/* @__PURE__ */
|
|
26258
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26229
26259
|
fg: tokens.warning,
|
|
26230
26260
|
children: [
|
|
26231
26261
|
"Dirty Files (",
|
|
@@ -26233,14 +26263,14 @@ function DetailPane({ selectedRow }) {
|
|
|
26233
26263
|
"):"
|
|
26234
26264
|
]
|
|
26235
26265
|
}),
|
|
26236
|
-
dirtyFiles.slice(0, 20).map((file2, idx) => /* @__PURE__ */
|
|
26266
|
+
dirtyFiles.slice(0, 20).map((file2, idx) => /* @__PURE__ */ jsxs4("text", {
|
|
26237
26267
|
fg: tokens.warning,
|
|
26238
26268
|
children: [
|
|
26239
26269
|
" ",
|
|
26240
26270
|
file2
|
|
26241
26271
|
]
|
|
26242
26272
|
}, idx)),
|
|
26243
|
-
dirtyFiles.length > 20 && /* @__PURE__ */
|
|
26273
|
+
dirtyFiles.length > 20 && /* @__PURE__ */ jsxs4("text", {
|
|
26244
26274
|
fg: tokens.warning,
|
|
26245
26275
|
children: [
|
|
26246
26276
|
" ...and ",
|
|
@@ -26250,17 +26280,17 @@ function DetailPane({ selectedRow }) {
|
|
|
26250
26280
|
})
|
|
26251
26281
|
]
|
|
26252
26282
|
}),
|
|
26253
|
-
/* @__PURE__ */
|
|
26283
|
+
/* @__PURE__ */ jsxs4("box", {
|
|
26254
26284
|
flexDirection: "column",
|
|
26255
26285
|
style: { marginTop: 1 },
|
|
26256
26286
|
children: [
|
|
26257
|
-
/* @__PURE__ */
|
|
26287
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26258
26288
|
fg: tokens.dim,
|
|
26259
26289
|
children: "Actions:"
|
|
26260
26290
|
}),
|
|
26261
|
-
!isMainCheckout && /* @__PURE__ */
|
|
26291
|
+
!isMainCheckout && /* @__PURE__ */ jsxs4(Fragment, {
|
|
26262
26292
|
children: [
|
|
26263
|
-
/* @__PURE__ */
|
|
26293
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26264
26294
|
fg: tokens.dim,
|
|
26265
26295
|
children: [
|
|
26266
26296
|
" i install deps (",
|
|
@@ -26268,44 +26298,218 @@ function DetailPane({ selectedRow }) {
|
|
|
26268
26298
|
")"
|
|
26269
26299
|
]
|
|
26270
26300
|
}),
|
|
26271
|
-
/* @__PURE__ */
|
|
26301
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26272
26302
|
fg: tokens.dim,
|
|
26273
26303
|
children: " m rename worktree"
|
|
26274
26304
|
})
|
|
26275
26305
|
]
|
|
26276
26306
|
}),
|
|
26277
|
-
/* @__PURE__ */
|
|
26307
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26278
26308
|
fg: tokens.dim,
|
|
26279
26309
|
children: " p pull branch"
|
|
26280
26310
|
}),
|
|
26281
|
-
/* @__PURE__ */
|
|
26311
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26282
26312
|
fg: tokens.dim,
|
|
26283
26313
|
children: " b rebase · s sync · o open in IDE · d remove"
|
|
26314
|
+
}),
|
|
26315
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26316
|
+
fg: tokens.dim,
|
|
26317
|
+
children: " t new terminal session (max 5)"
|
|
26284
26318
|
})
|
|
26285
26319
|
]
|
|
26286
26320
|
})
|
|
26287
26321
|
]
|
|
26288
26322
|
});
|
|
26289
26323
|
}
|
|
26290
|
-
var
|
|
26324
|
+
var init_DetailsTab = __esm(() => {
|
|
26325
|
+
init_theme();
|
|
26326
|
+
init_use_tap();
|
|
26327
|
+
});
|
|
26328
|
+
|
|
26329
|
+
// src/tui/components/TerminalView.tsx
|
|
26330
|
+
import { useState as useState2, useEffect as useEffect3, useRef as useRef4, useCallback as useCallback3 } from "react";
|
|
26331
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "@opentui/react/jsx-runtime";
|
|
26332
|
+
function TerminalView({ session, focused, onFocus, onSend, onResize, registerListener, unregisterListener }) {
|
|
26333
|
+
const [draft, setDraft] = useState2("");
|
|
26334
|
+
const focusTap = useTapHandler(() => onFocus());
|
|
26335
|
+
const termRef = useRef4(null);
|
|
26336
|
+
const handleData = useCallback3((data, source) => {
|
|
26337
|
+
if (source !== "input")
|
|
26338
|
+
return;
|
|
26339
|
+
if (session.usePty && focused)
|
|
26340
|
+
return;
|
|
26341
|
+
onSend(data);
|
|
26342
|
+
}, [onSend, focused, session.id, session.usePty]);
|
|
26343
|
+
const handleResize = useCallback3((cols, rows) => {
|
|
26344
|
+
onResize?.(cols, rows);
|
|
26345
|
+
}, [onResize]);
|
|
26346
|
+
useEffect3(() => {
|
|
26347
|
+
if (!session.usePty || !session.terminal)
|
|
26348
|
+
return;
|
|
26349
|
+
if (!termRef.current || !registerListener || !unregisterListener)
|
|
26350
|
+
return;
|
|
26351
|
+
try {
|
|
26352
|
+
termRef.current.write(new TextEncoder().encode("\x1B[2J\x1B[H"));
|
|
26353
|
+
} catch {}
|
|
26354
|
+
const write = (data) => {
|
|
26355
|
+
try {
|
|
26356
|
+
termRef.current?.write(data);
|
|
26357
|
+
} catch {}
|
|
26358
|
+
};
|
|
26359
|
+
registerListener(session.id, write);
|
|
26360
|
+
return () => unregisterListener(session.id);
|
|
26361
|
+
}, [session.id, session.usePty, session.terminal, registerListener, unregisterListener]);
|
|
26362
|
+
useEffect3(() => {
|
|
26363
|
+
if (!session.usePty)
|
|
26364
|
+
return;
|
|
26365
|
+
if (!termRef.current)
|
|
26366
|
+
return;
|
|
26367
|
+
try {
|
|
26368
|
+
if (focused)
|
|
26369
|
+
termRef.current.focus?.();
|
|
26370
|
+
else
|
|
26371
|
+
termRef.current.blur?.();
|
|
26372
|
+
} catch {}
|
|
26373
|
+
}, [focused, session.usePty]);
|
|
26374
|
+
if (session.usePty && session.terminal) {
|
|
26375
|
+
return /* @__PURE__ */ jsxs5("box", {
|
|
26376
|
+
flexDirection: "column",
|
|
26377
|
+
flexGrow: 1,
|
|
26378
|
+
width: "100%",
|
|
26379
|
+
height: "100%",
|
|
26380
|
+
...focusTap,
|
|
26381
|
+
children: [
|
|
26382
|
+
/* @__PURE__ */ jsx5("box", {
|
|
26383
|
+
flexGrow: 1,
|
|
26384
|
+
width: "100%",
|
|
26385
|
+
height: "100%",
|
|
26386
|
+
flexDirection: "column",
|
|
26387
|
+
padding: 1,
|
|
26388
|
+
children: /* @__PURE__ */ jsx5("embedded-terminal", {
|
|
26389
|
+
ref: termRef,
|
|
26390
|
+
width: "100%",
|
|
26391
|
+
height: "100%",
|
|
26392
|
+
cols: session.cols,
|
|
26393
|
+
rows: session.rows,
|
|
26394
|
+
maxScrollback: 1000,
|
|
26395
|
+
onData: handleData,
|
|
26396
|
+
onTerminalResize: handleResize,
|
|
26397
|
+
selectable: true,
|
|
26398
|
+
focused
|
|
26399
|
+
})
|
|
26400
|
+
}),
|
|
26401
|
+
session.exited !== null && /* @__PURE__ */ jsxs5("text", {
|
|
26402
|
+
fg: tokens.warning,
|
|
26403
|
+
children: [
|
|
26404
|
+
"— exited with code ",
|
|
26405
|
+
session.exited,
|
|
26406
|
+
" — press t for new session"
|
|
26407
|
+
]
|
|
26408
|
+
}),
|
|
26409
|
+
!focused && /* @__PURE__ */ jsxs5("text", {
|
|
26410
|
+
fg: tokens.dim,
|
|
26411
|
+
children: [
|
|
26412
|
+
"Click to focus · Ctrl+G or click table to unfocus · ",
|
|
26413
|
+
session.label,
|
|
26414
|
+
" PTY"
|
|
26415
|
+
]
|
|
26416
|
+
}),
|
|
26417
|
+
focused && /* @__PURE__ */ jsx5("text", {
|
|
26418
|
+
fg: tokens.accent,
|
|
26419
|
+
children: "Focused PTY — all keys go to shell · Ctrl+G to unfocus"
|
|
26420
|
+
})
|
|
26421
|
+
]
|
|
26422
|
+
});
|
|
26423
|
+
}
|
|
26424
|
+
const lines = session.lines;
|
|
26425
|
+
return /* @__PURE__ */ jsxs5("box", {
|
|
26426
|
+
flexDirection: "column",
|
|
26427
|
+
flexGrow: 1,
|
|
26428
|
+
width: "100%",
|
|
26429
|
+
height: "100%",
|
|
26430
|
+
...focusTap,
|
|
26431
|
+
children: [
|
|
26432
|
+
/* @__PURE__ */ jsxs5("scrollbox", {
|
|
26433
|
+
flexGrow: 1,
|
|
26434
|
+
width: "100%",
|
|
26435
|
+
border: false,
|
|
26436
|
+
focused: false,
|
|
26437
|
+
style: { marginBottom: 1 },
|
|
26438
|
+
children: [
|
|
26439
|
+
lines.length === 0 ? /* @__PURE__ */ jsx5("text", {
|
|
26440
|
+
fg: tokens.dim,
|
|
26441
|
+
children: "No output yet — type a command below."
|
|
26442
|
+
}) : lines.slice(-200).map((line, idx) => /* @__PURE__ */ jsx5("text", {
|
|
26443
|
+
fg: session.exited !== null ? tokens.dim : tokens.fg,
|
|
26444
|
+
children: line || " "
|
|
26445
|
+
}, idx)),
|
|
26446
|
+
session.exited !== null && /* @__PURE__ */ jsxs5("text", {
|
|
26447
|
+
fg: tokens.warning,
|
|
26448
|
+
children: [
|
|
26449
|
+
"— exited with code ",
|
|
26450
|
+
session.exited,
|
|
26451
|
+
" —"
|
|
26452
|
+
]
|
|
26453
|
+
})
|
|
26454
|
+
]
|
|
26455
|
+
}),
|
|
26456
|
+
/* @__PURE__ */ jsxs5("box", {
|
|
26457
|
+
flexDirection: "row",
|
|
26458
|
+
gap: 1,
|
|
26459
|
+
width: "100%",
|
|
26460
|
+
children: [
|
|
26461
|
+
/* @__PURE__ */ jsx5("text", {
|
|
26462
|
+
fg: tokens.dim,
|
|
26463
|
+
children: "$"
|
|
26464
|
+
}),
|
|
26465
|
+
/* @__PURE__ */ jsx5("box", {
|
|
26466
|
+
flexGrow: 1,
|
|
26467
|
+
children: /* @__PURE__ */ jsx5("input", {
|
|
26468
|
+
focused,
|
|
26469
|
+
value: draft,
|
|
26470
|
+
placeholder: focused ? "Type command, Enter to send, Esc to unfocus" : "Click to focus terminal",
|
|
26471
|
+
onInput: (v) => setDraft(v),
|
|
26472
|
+
onSubmit: () => {
|
|
26473
|
+
if (!draft)
|
|
26474
|
+
return;
|
|
26475
|
+
onSend(draft + `
|
|
26476
|
+
`);
|
|
26477
|
+
setDraft("");
|
|
26478
|
+
}
|
|
26479
|
+
})
|
|
26480
|
+
})
|
|
26481
|
+
]
|
|
26482
|
+
}),
|
|
26483
|
+
!focused && /* @__PURE__ */ jsx5("text", {
|
|
26484
|
+
fg: tokens.dim,
|
|
26485
|
+
children: "Click terminal or press 't' to focus · Ctrl+G or click table to unfocus"
|
|
26486
|
+
}),
|
|
26487
|
+
focused && /* @__PURE__ */ jsx5("text", {
|
|
26488
|
+
fg: tokens.accent,
|
|
26489
|
+
children: "Focused — typing goes to shell · Ctrl+G to unfocus · max 5 per worktree persist across switches"
|
|
26490
|
+
})
|
|
26491
|
+
]
|
|
26492
|
+
});
|
|
26493
|
+
}
|
|
26494
|
+
var init_TerminalView = __esm(() => {
|
|
26291
26495
|
init_theme();
|
|
26292
26496
|
init_use_tap();
|
|
26293
26497
|
});
|
|
26294
26498
|
|
|
26295
26499
|
// src/tui/components/Footer.tsx
|
|
26296
|
-
import { jsx as
|
|
26500
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
|
|
26297
26501
|
function HintItem({ hintKey, label, isFirst, onClick }) {
|
|
26298
26502
|
const tap = useTapHandler(() => onClick?.(hintKey));
|
|
26299
|
-
return /* @__PURE__ */
|
|
26503
|
+
return /* @__PURE__ */ jsx6("box", {
|
|
26300
26504
|
flexDirection: "row",
|
|
26301
26505
|
...onClick ? tap : {},
|
|
26302
|
-
children: /* @__PURE__ */
|
|
26506
|
+
children: /* @__PURE__ */ jsxs6("text", {
|
|
26303
26507
|
children: [
|
|
26304
|
-
/* @__PURE__ */
|
|
26508
|
+
/* @__PURE__ */ jsx6("span", {
|
|
26305
26509
|
fg: tokens.dim,
|
|
26306
26510
|
children: isFirst ? "" : " · "
|
|
26307
26511
|
}),
|
|
26308
|
-
/* @__PURE__ */
|
|
26512
|
+
/* @__PURE__ */ jsx6("span", {
|
|
26309
26513
|
fg: tokens.dim,
|
|
26310
26514
|
children: `${hintKey} ${label}`
|
|
26311
26515
|
})
|
|
@@ -26315,15 +26519,15 @@ function HintItem({ hintKey, label, isFirst, onClick }) {
|
|
|
26315
26519
|
}
|
|
26316
26520
|
function ErrorBadge({ count: count2, onClick }) {
|
|
26317
26521
|
const tap = useTapHandler(() => onClick?.());
|
|
26318
|
-
return /* @__PURE__ */
|
|
26522
|
+
return /* @__PURE__ */ jsx6("box", {
|
|
26319
26523
|
...onClick ? tap : {},
|
|
26320
|
-
children: /* @__PURE__ */
|
|
26524
|
+
children: /* @__PURE__ */ jsxs6("text", {
|
|
26321
26525
|
children: [
|
|
26322
|
-
/* @__PURE__ */
|
|
26526
|
+
/* @__PURE__ */ jsx6("span", {
|
|
26323
26527
|
fg: tokens.error,
|
|
26324
26528
|
children: `${count2} error${count2 !== 1 ? "s" : ""}`
|
|
26325
26529
|
}),
|
|
26326
|
-
/* @__PURE__ */
|
|
26530
|
+
/* @__PURE__ */ jsx6("span", {
|
|
26327
26531
|
fg: tokens.dim,
|
|
26328
26532
|
children: " · e view"
|
|
26329
26533
|
})
|
|
@@ -26333,7 +26537,7 @@ function ErrorBadge({ count: count2, onClick }) {
|
|
|
26333
26537
|
}
|
|
26334
26538
|
function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinnerFrame, filter, onHintClick, onErrorClick }) {
|
|
26335
26539
|
const frame = spinnerFrame ?? "◌";
|
|
26336
|
-
return /* @__PURE__ */
|
|
26540
|
+
return /* @__PURE__ */ jsxs6("box", {
|
|
26337
26541
|
id: "footer",
|
|
26338
26542
|
width: "100%",
|
|
26339
26543
|
height: 3,
|
|
@@ -26344,29 +26548,29 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
|
|
|
26344
26548
|
justifyContent: "space-between",
|
|
26345
26549
|
alignItems: "center",
|
|
26346
26550
|
children: [
|
|
26347
|
-
busyText ? /* @__PURE__ */
|
|
26551
|
+
busyText ? /* @__PURE__ */ jsx6("text", {
|
|
26348
26552
|
fg: tokens.accent,
|
|
26349
26553
|
children: `${frame} ${busyText}`
|
|
26350
|
-
}) : /* @__PURE__ */
|
|
26554
|
+
}) : /* @__PURE__ */ jsx6("box", {
|
|
26351
26555
|
flexDirection: "row",
|
|
26352
26556
|
gap: 1,
|
|
26353
|
-
children: HINTS.map(([key, label], idx) => /* @__PURE__ */
|
|
26557
|
+
children: HINTS.map(([key, label], idx) => /* @__PURE__ */ jsx6(HintItem, {
|
|
26354
26558
|
hintKey: key,
|
|
26355
26559
|
label,
|
|
26356
26560
|
isFirst: idx === 0,
|
|
26357
26561
|
onClick: onHintClick
|
|
26358
26562
|
}, key))
|
|
26359
26563
|
}),
|
|
26360
|
-
/* @__PURE__ */
|
|
26564
|
+
/* @__PURE__ */ jsxs6("box", {
|
|
26361
26565
|
flexDirection: "row",
|
|
26362
26566
|
gap: 2,
|
|
26363
26567
|
alignItems: "center",
|
|
26364
26568
|
children: [
|
|
26365
|
-
message ? /* @__PURE__ */
|
|
26569
|
+
message ? /* @__PURE__ */ jsx6("text", {
|
|
26366
26570
|
fg: tokens.warning,
|
|
26367
26571
|
children: message
|
|
26368
26572
|
}) : null,
|
|
26369
|
-
filter ? /* @__PURE__ */
|
|
26573
|
+
filter ? /* @__PURE__ */ jsxs6("text", {
|
|
26370
26574
|
fg: "magenta",
|
|
26371
26575
|
children: [
|
|
26372
26576
|
"filter: ",
|
|
@@ -26378,17 +26582,17 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
|
|
|
26378
26582
|
")"
|
|
26379
26583
|
]
|
|
26380
26584
|
}) : null,
|
|
26381
|
-
errorCount > 0 ? /* @__PURE__ */
|
|
26585
|
+
errorCount > 0 ? /* @__PURE__ */ jsx6(ErrorBadge, {
|
|
26382
26586
|
count: errorCount,
|
|
26383
26587
|
onClick: onErrorClick
|
|
26384
26588
|
}) : null,
|
|
26385
|
-
/* @__PURE__ */
|
|
26589
|
+
/* @__PURE__ */ jsx6(HintItem, {
|
|
26386
26590
|
hintKey: "?",
|
|
26387
26591
|
label: "help",
|
|
26388
26592
|
isFirst: errorCount === 0,
|
|
26389
26593
|
onClick: onHintClick
|
|
26390
26594
|
}),
|
|
26391
|
-
/* @__PURE__ */
|
|
26595
|
+
/* @__PURE__ */ jsx6("text", {
|
|
26392
26596
|
fg: tokens.dim,
|
|
26393
26597
|
children: loading ? `${frame} Refreshing…` : `Updated ${lastRefreshed}`
|
|
26394
26598
|
})
|
|
@@ -26408,16 +26612,16 @@ var init_Footer = __esm(() => {
|
|
|
26408
26612
|
});
|
|
26409
26613
|
|
|
26410
26614
|
// src/tui/components/Divider.tsx
|
|
26411
|
-
import { useState as
|
|
26615
|
+
import { useState as useState3, useRef as useRef5, useCallback as useCallback4 } from "react";
|
|
26412
26616
|
import { useRenderer } from "@opentui/react";
|
|
26413
|
-
import { jsx as
|
|
26617
|
+
import { jsx as jsx7 } from "@opentui/react/jsx-runtime";
|
|
26414
26618
|
function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
26415
26619
|
const renderer = useRenderer();
|
|
26416
|
-
const [hovered, setHovered] =
|
|
26417
|
-
const [dragging, setDragging] =
|
|
26418
|
-
const startXRef =
|
|
26419
|
-
const startRatioRef =
|
|
26420
|
-
const handleMouseDown =
|
|
26620
|
+
const [hovered, setHovered] = useState3(false);
|
|
26621
|
+
const [dragging, setDragging] = useState3(false);
|
|
26622
|
+
const startXRef = useRef5(null);
|
|
26623
|
+
const startRatioRef = useRef5(splitRatio);
|
|
26624
|
+
const handleMouseDown = useCallback4((e) => {
|
|
26421
26625
|
if (e.button !== 0)
|
|
26422
26626
|
return;
|
|
26423
26627
|
startXRef.current = e.x;
|
|
@@ -26427,7 +26631,7 @@ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
|
26427
26631
|
renderer.clearSelection();
|
|
26428
26632
|
e.stopPropagation();
|
|
26429
26633
|
}, [splitRatio, onDraggingChange, renderer]);
|
|
26430
|
-
const handleMouseUp =
|
|
26634
|
+
const handleMouseUp = useCallback4((e) => {
|
|
26431
26635
|
if (e.button !== 0)
|
|
26432
26636
|
return;
|
|
26433
26637
|
startXRef.current = null;
|
|
@@ -26435,7 +26639,7 @@ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
|
26435
26639
|
onDraggingChange?.(false);
|
|
26436
26640
|
e.stopPropagation();
|
|
26437
26641
|
}, [onDraggingChange]);
|
|
26438
|
-
const handleMouseDrag =
|
|
26642
|
+
const handleMouseDrag = useCallback4((e) => {
|
|
26439
26643
|
if (startXRef.current === null)
|
|
26440
26644
|
return;
|
|
26441
26645
|
renderer.clearSelection();
|
|
@@ -26450,7 +26654,7 @@ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
|
26450
26654
|
onMouseDrag: handleMouseDrag,
|
|
26451
26655
|
onMouseMove: handleMouseDrag
|
|
26452
26656
|
};
|
|
26453
|
-
return /* @__PURE__ */
|
|
26657
|
+
return /* @__PURE__ */ jsx7("box", {
|
|
26454
26658
|
width: 3,
|
|
26455
26659
|
height: "100%",
|
|
26456
26660
|
backgroundColor: dragging ? tokens.selectionBg : hovered ? tokens.panelBg : undefined,
|
|
@@ -26459,7 +26663,7 @@ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
|
26459
26663
|
onMouseOut: () => setHovered(false),
|
|
26460
26664
|
alignItems: "center",
|
|
26461
26665
|
justifyContent: "center",
|
|
26462
|
-
children: /* @__PURE__ */
|
|
26666
|
+
children: /* @__PURE__ */ jsx7("text", {
|
|
26463
26667
|
fg: dragging ? tokens.accent : hovered ? tokens.borderActive : tokens.border,
|
|
26464
26668
|
...dragHandlers,
|
|
26465
26669
|
onMouseOver: () => setHovered(true),
|
|
@@ -26474,9 +26678,9 @@ var init_Divider = __esm(() => {
|
|
|
26474
26678
|
});
|
|
26475
26679
|
|
|
26476
26680
|
// src/tui/components/Overlay.tsx
|
|
26477
|
-
import { jsx as
|
|
26681
|
+
import { jsx as jsx8 } from "@opentui/react/jsx-runtime";
|
|
26478
26682
|
function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
|
|
26479
|
-
return /* @__PURE__ */
|
|
26683
|
+
return /* @__PURE__ */ jsx8("box", {
|
|
26480
26684
|
id: "overlay-scrim",
|
|
26481
26685
|
position: "absolute",
|
|
26482
26686
|
width: "100%",
|
|
@@ -26488,7 +26692,7 @@ function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
|
|
|
26488
26692
|
flexDirection: "row",
|
|
26489
26693
|
justifyContent: "center",
|
|
26490
26694
|
alignItems: "center",
|
|
26491
|
-
children: /* @__PURE__ */
|
|
26695
|
+
children: /* @__PURE__ */ jsx8("box", {
|
|
26492
26696
|
id: "overlay-panel",
|
|
26493
26697
|
width,
|
|
26494
26698
|
border: true,
|
|
@@ -26508,37 +26712,38 @@ var init_Overlay = __esm(() => {
|
|
|
26508
26712
|
});
|
|
26509
26713
|
|
|
26510
26714
|
// src/tui/components/HelpOverlay.tsx
|
|
26511
|
-
import { jsx as
|
|
26715
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
|
|
26512
26716
|
function HelpOverlay() {
|
|
26513
|
-
return /* @__PURE__ */
|
|
26717
|
+
return /* @__PURE__ */ jsxs7(Overlay, {
|
|
26514
26718
|
title: "Help",
|
|
26515
26719
|
borderColor: tokens.border,
|
|
26516
26720
|
width: 84,
|
|
26517
26721
|
children: [
|
|
26518
|
-
/* @__PURE__ */
|
|
26722
|
+
/* @__PURE__ */ jsx9("box", {
|
|
26519
26723
|
flexDirection: "column",
|
|
26520
26724
|
style: { maxHeight: 36 },
|
|
26521
|
-
children: /* @__PURE__ */
|
|
26725
|
+
children: /* @__PURE__ */ jsx9("scrollbox", {
|
|
26522
26726
|
flexGrow: 1,
|
|
26523
|
-
children: HELP_ENTRIES.map(([key, desc]) => /* @__PURE__ */
|
|
26727
|
+
children: HELP_ENTRIES.map(([key, desc]) => /* @__PURE__ */ jsxs7("box", {
|
|
26524
26728
|
flexDirection: "row",
|
|
26525
26729
|
gap: 1,
|
|
26526
26730
|
children: [
|
|
26527
|
-
/* @__PURE__ */
|
|
26731
|
+
/* @__PURE__ */ jsx9("box", {
|
|
26528
26732
|
width: 18,
|
|
26529
26733
|
flexShrink: 0,
|
|
26530
|
-
|
|
26734
|
+
justifyContent: "flex-end",
|
|
26735
|
+
children: /* @__PURE__ */ jsx9("text", {
|
|
26531
26736
|
fg: tokens.accent,
|
|
26532
26737
|
children: key
|
|
26533
26738
|
})
|
|
26534
26739
|
}),
|
|
26535
|
-
/* @__PURE__ */
|
|
26740
|
+
/* @__PURE__ */ jsx9("text", {
|
|
26536
26741
|
fg: tokens.dim,
|
|
26537
26742
|
children: "-"
|
|
26538
26743
|
}),
|
|
26539
|
-
/* @__PURE__ */
|
|
26744
|
+
/* @__PURE__ */ jsx9("box", {
|
|
26540
26745
|
flexGrow: 1,
|
|
26541
|
-
children: /* @__PURE__ */
|
|
26746
|
+
children: /* @__PURE__ */ jsx9("text", {
|
|
26542
26747
|
fg: tokens.fg,
|
|
26543
26748
|
children: desc
|
|
26544
26749
|
})
|
|
@@ -26547,7 +26752,7 @@ function HelpOverlay() {
|
|
|
26547
26752
|
}, key))
|
|
26548
26753
|
})
|
|
26549
26754
|
}),
|
|
26550
|
-
/* @__PURE__ */
|
|
26755
|
+
/* @__PURE__ */ jsx9("text", {
|
|
26551
26756
|
style: { marginTop: 1, fg: tokens.dim },
|
|
26552
26757
|
children: "Press any key to close"
|
|
26553
26758
|
})
|
|
@@ -26572,7 +26777,9 @@ var init_HelpOverlay = __esm(() => {
|
|
|
26572
26777
|
["i", "Install dependencies in selection (worktrees and main)"],
|
|
26573
26778
|
["m", "Rename selected worktree (branch + directory)"],
|
|
26574
26779
|
["o", "Open selected in IDE"],
|
|
26575
|
-
["
|
|
26780
|
+
["t", "New terminal session (max 5 per worktree, Details ↔ Session tabs)"],
|
|
26781
|
+
["click tab", "Switch Details ↔ Session (professional tabs with ▎ active)"],
|
|
26782
|
+
["Ctrl+G / click table", "Leave terminal focus — focused terminal gets all keys"],
|
|
26576
26783
|
["d", "Remove selected worktree(s)"],
|
|
26577
26784
|
["e", "View data warnings (when count > 0)"],
|
|
26578
26785
|
["r", "Refresh data"],
|
|
@@ -26586,50 +26793,50 @@ var init_HelpOverlay = __esm(() => {
|
|
|
26586
26793
|
});
|
|
26587
26794
|
|
|
26588
26795
|
// src/tui/components/ConfirmModal.tsx
|
|
26589
|
-
import { jsx as
|
|
26796
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
|
|
26590
26797
|
function ConfirmModal({ title, message, onConfirm, onCancel }) {
|
|
26591
26798
|
const yesTap = useTapHandler(() => onConfirm?.());
|
|
26592
26799
|
const noTap = useTapHandler(() => onCancel?.());
|
|
26593
|
-
return /* @__PURE__ */
|
|
26800
|
+
return /* @__PURE__ */ jsx10(Overlay, {
|
|
26594
26801
|
title,
|
|
26595
26802
|
borderColor: tokens.warning,
|
|
26596
|
-
children: /* @__PURE__ */
|
|
26803
|
+
children: /* @__PURE__ */ jsxs8("box", {
|
|
26597
26804
|
flexDirection: "column",
|
|
26598
26805
|
justifyContent: "center",
|
|
26599
26806
|
alignItems: "center",
|
|
26600
26807
|
children: [
|
|
26601
|
-
/* @__PURE__ */
|
|
26808
|
+
/* @__PURE__ */ jsx10("text", {
|
|
26602
26809
|
children: message
|
|
26603
26810
|
}),
|
|
26604
|
-
/* @__PURE__ */
|
|
26811
|
+
/* @__PURE__ */ jsxs8("box", {
|
|
26605
26812
|
flexDirection: "row",
|
|
26606
26813
|
gap: 2,
|
|
26607
26814
|
style: { marginTop: 2 },
|
|
26608
26815
|
children: [
|
|
26609
|
-
/* @__PURE__ */
|
|
26816
|
+
/* @__PURE__ */ jsx10("box", {
|
|
26610
26817
|
...onConfirm ? yesTap : {},
|
|
26611
|
-
children: /* @__PURE__ */
|
|
26818
|
+
children: /* @__PURE__ */ jsxs8("text", {
|
|
26612
26819
|
children: [
|
|
26613
|
-
/* @__PURE__ */
|
|
26820
|
+
/* @__PURE__ */ jsx10("span", {
|
|
26614
26821
|
fg: tokens.dim,
|
|
26615
26822
|
children: "[y] "
|
|
26616
26823
|
}),
|
|
26617
|
-
/* @__PURE__ */
|
|
26824
|
+
/* @__PURE__ */ jsx10("span", {
|
|
26618
26825
|
fg: tokens.success,
|
|
26619
26826
|
children: "Yes"
|
|
26620
26827
|
})
|
|
26621
26828
|
]
|
|
26622
26829
|
})
|
|
26623
26830
|
}),
|
|
26624
|
-
/* @__PURE__ */
|
|
26831
|
+
/* @__PURE__ */ jsx10("box", {
|
|
26625
26832
|
...onCancel ? noTap : {},
|
|
26626
|
-
children: /* @__PURE__ */
|
|
26833
|
+
children: /* @__PURE__ */ jsxs8("text", {
|
|
26627
26834
|
children: [
|
|
26628
|
-
/* @__PURE__ */
|
|
26835
|
+
/* @__PURE__ */ jsx10("span", {
|
|
26629
26836
|
fg: tokens.dim,
|
|
26630
26837
|
children: "[n/esc] "
|
|
26631
26838
|
}),
|
|
26632
|
-
/* @__PURE__ */
|
|
26839
|
+
/* @__PURE__ */ jsx10("span", {
|
|
26633
26840
|
children: "Cancel"
|
|
26634
26841
|
})
|
|
26635
26842
|
]
|
|
@@ -26705,52 +26912,52 @@ async function runWtxAction(args, onLine) {
|
|
|
26705
26912
|
}
|
|
26706
26913
|
|
|
26707
26914
|
// src/tui/components/ActionLogModal.tsx
|
|
26708
|
-
import { jsx as
|
|
26915
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
|
|
26709
26916
|
function ActionLogModal({ title, lines, done, exitCode, remaining }) {
|
|
26710
|
-
return /* @__PURE__ */
|
|
26917
|
+
return /* @__PURE__ */ jsxs9(Overlay, {
|
|
26711
26918
|
title,
|
|
26712
26919
|
borderColor: tokens.border,
|
|
26713
26920
|
children: [
|
|
26714
|
-
/* @__PURE__ */
|
|
26921
|
+
/* @__PURE__ */ jsx11("box", {
|
|
26715
26922
|
flexGrow: 1,
|
|
26716
26923
|
flexDirection: "column",
|
|
26717
26924
|
style: { minHeight: 10, maxHeight: 20 },
|
|
26718
|
-
children: /* @__PURE__ */
|
|
26925
|
+
children: /* @__PURE__ */ jsx11("scrollbox", {
|
|
26719
26926
|
flexGrow: 1,
|
|
26720
26927
|
stickyScroll: true,
|
|
26721
|
-
children: lines.map((line, i2) => /* @__PURE__ */
|
|
26928
|
+
children: lines.map((line, i2) => /* @__PURE__ */ jsx11("text", {
|
|
26722
26929
|
fg: line.type === "err" ? tokens.error : tokens.fg,
|
|
26723
26930
|
children: line.text
|
|
26724
26931
|
}, i2))
|
|
26725
26932
|
})
|
|
26726
26933
|
}),
|
|
26727
|
-
/* @__PURE__ */
|
|
26934
|
+
/* @__PURE__ */ jsxs9("box", {
|
|
26728
26935
|
marginTop: 1,
|
|
26729
26936
|
flexDirection: "row",
|
|
26730
26937
|
justifyContent: "center",
|
|
26731
26938
|
children: [
|
|
26732
|
-
!done && /* @__PURE__ */
|
|
26939
|
+
!done && /* @__PURE__ */ jsx11("text", {
|
|
26733
26940
|
fg: tokens.warning,
|
|
26734
26941
|
children: "Running..."
|
|
26735
26942
|
}),
|
|
26736
|
-
done && exitCode === 0 && /* @__PURE__ */
|
|
26943
|
+
done && exitCode === 0 && /* @__PURE__ */ jsx11("text", {
|
|
26737
26944
|
fg: tokens.success,
|
|
26738
26945
|
children: "✓ Done (exit 0)"
|
|
26739
26946
|
}),
|
|
26740
|
-
done && exitCode !== 0 && /* @__PURE__ */
|
|
26947
|
+
done && exitCode !== 0 && /* @__PURE__ */ jsxs9("text", {
|
|
26741
26948
|
children: [
|
|
26742
|
-
/* @__PURE__ */
|
|
26949
|
+
/* @__PURE__ */ jsxs9("span", {
|
|
26743
26950
|
fg: tokens.error,
|
|
26744
26951
|
children: [
|
|
26745
26952
|
"✗ Exit ",
|
|
26746
26953
|
exitCode
|
|
26747
26954
|
]
|
|
26748
26955
|
}),
|
|
26749
|
-
remaining !== undefined && remaining > 0 && /* @__PURE__ */
|
|
26956
|
+
remaining !== undefined && remaining > 0 && /* @__PURE__ */ jsx11("span", {
|
|
26750
26957
|
fg: tokens.warning,
|
|
26751
26958
|
children: ` · ${remaining} more failure${remaining > 1 ? "s" : ""}`
|
|
26752
26959
|
}),
|
|
26753
|
-
/* @__PURE__ */
|
|
26960
|
+
/* @__PURE__ */ jsx11("span", {
|
|
26754
26961
|
fg: tokens.dim,
|
|
26755
26962
|
children: " - press any key to close"
|
|
26756
26963
|
})
|
|
@@ -26837,8 +27044,8 @@ var init_history = __esm(() => {
|
|
|
26837
27044
|
});
|
|
26838
27045
|
|
|
26839
27046
|
// src/tui/components/HistoryOverlay.tsx
|
|
26840
|
-
import { useEffect as
|
|
26841
|
-
import { jsx as
|
|
27047
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
27048
|
+
import { jsx as jsx12, jsxs as jsxs10 } from "@opentui/react/jsx-runtime";
|
|
26842
27049
|
function formatShortTime(iso) {
|
|
26843
27050
|
const d = new Date(iso);
|
|
26844
27051
|
if (Number.isNaN(d.getTime()))
|
|
@@ -26851,16 +27058,16 @@ function HistoryEntryLine({ entry }) {
|
|
|
26851
27058
|
const duration3 = entry.durationMs !== null && entry.durationMs !== undefined ? ` ${entry.durationMs}ms` : "";
|
|
26852
27059
|
const prefix = `${formatShortTime(entry.ts)} ${entry.source.padEnd(8)} ${args}`;
|
|
26853
27060
|
if (entry.exit === null || entry.exit === undefined) {
|
|
26854
|
-
return /* @__PURE__ */
|
|
27061
|
+
return /* @__PURE__ */ jsxs10("text", {
|
|
26855
27062
|
children: [
|
|
26856
|
-
/* @__PURE__ */
|
|
27063
|
+
/* @__PURE__ */ jsxs10("span", {
|
|
26857
27064
|
fg: tokens.dim,
|
|
26858
27065
|
children: [
|
|
26859
27066
|
"◌ ",
|
|
26860
27067
|
prefix
|
|
26861
27068
|
]
|
|
26862
27069
|
}),
|
|
26863
|
-
duration3 && /* @__PURE__ */
|
|
27070
|
+
duration3 && /* @__PURE__ */ jsx12("span", {
|
|
26864
27071
|
fg: tokens.dim,
|
|
26865
27072
|
children: duration3
|
|
26866
27073
|
})
|
|
@@ -26868,32 +27075,32 @@ function HistoryEntryLine({ entry }) {
|
|
|
26868
27075
|
});
|
|
26869
27076
|
}
|
|
26870
27077
|
if (entry.exit === 0) {
|
|
26871
|
-
return /* @__PURE__ */
|
|
27078
|
+
return /* @__PURE__ */ jsxs10("text", {
|
|
26872
27079
|
children: [
|
|
26873
|
-
/* @__PURE__ */
|
|
27080
|
+
/* @__PURE__ */ jsxs10("span", {
|
|
26874
27081
|
fg: tokens.success,
|
|
26875
27082
|
children: [
|
|
26876
27083
|
"✓ ",
|
|
26877
27084
|
prefix
|
|
26878
27085
|
]
|
|
26879
27086
|
}),
|
|
26880
|
-
duration3 && /* @__PURE__ */
|
|
27087
|
+
duration3 && /* @__PURE__ */ jsx12("span", {
|
|
26881
27088
|
fg: tokens.dim,
|
|
26882
27089
|
children: duration3
|
|
26883
27090
|
})
|
|
26884
27091
|
]
|
|
26885
27092
|
});
|
|
26886
27093
|
}
|
|
26887
|
-
return /* @__PURE__ */
|
|
27094
|
+
return /* @__PURE__ */ jsxs10("text", {
|
|
26888
27095
|
children: [
|
|
26889
|
-
/* @__PURE__ */
|
|
27096
|
+
/* @__PURE__ */ jsx12("span", {
|
|
26890
27097
|
fg: tokens.error,
|
|
26891
27098
|
children: "✗ "
|
|
26892
27099
|
}),
|
|
26893
|
-
/* @__PURE__ */
|
|
27100
|
+
/* @__PURE__ */ jsx12("span", {
|
|
26894
27101
|
children: prefix
|
|
26895
27102
|
}),
|
|
26896
|
-
/* @__PURE__ */
|
|
27103
|
+
/* @__PURE__ */ jsxs10("span", {
|
|
26897
27104
|
fg: tokens.dim,
|
|
26898
27105
|
children: [
|
|
26899
27106
|
" (exit ",
|
|
@@ -26906,32 +27113,32 @@ function HistoryEntryLine({ entry }) {
|
|
|
26906
27113
|
});
|
|
26907
27114
|
}
|
|
26908
27115
|
function HistoryOverlay() {
|
|
26909
|
-
const [entries, setEntries] =
|
|
26910
|
-
|
|
27116
|
+
const [entries, setEntries] = useState4([]);
|
|
27117
|
+
useEffect4(() => {
|
|
26911
27118
|
setEntries(readRecentHistory(500));
|
|
26912
27119
|
}, []);
|
|
26913
|
-
return /* @__PURE__ */
|
|
27120
|
+
return /* @__PURE__ */ jsxs10(Overlay, {
|
|
26914
27121
|
title: "Action History",
|
|
26915
27122
|
borderColor: tokens.border,
|
|
26916
27123
|
width: 110,
|
|
26917
27124
|
children: [
|
|
26918
|
-
/* @__PURE__ */
|
|
27125
|
+
/* @__PURE__ */ jsx12("box", {
|
|
26919
27126
|
flexGrow: 1,
|
|
26920
27127
|
flexDirection: "column",
|
|
26921
27128
|
style: { minHeight: 30, maxHeight: 46 },
|
|
26922
|
-
children: entries.length === 0 ? /* @__PURE__ */
|
|
27129
|
+
children: entries.length === 0 ? /* @__PURE__ */ jsx12("text", {
|
|
26923
27130
|
fg: tokens.dim,
|
|
26924
27131
|
children: "No actions recorded yet."
|
|
26925
|
-
}) : /* @__PURE__ */
|
|
27132
|
+
}) : /* @__PURE__ */ jsx12("scrollbox", {
|
|
26926
27133
|
flexGrow: 1,
|
|
26927
|
-
children: entries.map((entry, i2) => /* @__PURE__ */
|
|
27134
|
+
children: entries.map((entry, i2) => /* @__PURE__ */ jsx12(HistoryEntryLine, {
|
|
26928
27135
|
entry
|
|
26929
27136
|
}, i2))
|
|
26930
27137
|
})
|
|
26931
27138
|
}),
|
|
26932
|
-
/* @__PURE__ */
|
|
27139
|
+
/* @__PURE__ */ jsx12("box", {
|
|
26933
27140
|
marginTop: 1,
|
|
26934
|
-
children: /* @__PURE__ */
|
|
27141
|
+
children: /* @__PURE__ */ jsx12("text", {
|
|
26935
27142
|
fg: tokens.dim,
|
|
26936
27143
|
children: "Press any key to close · newest first"
|
|
26937
27144
|
})
|
|
@@ -26946,28 +27153,28 @@ var init_HistoryOverlay = __esm(() => {
|
|
|
26946
27153
|
});
|
|
26947
27154
|
|
|
26948
27155
|
// src/tui/components/InputModal.tsx
|
|
26949
|
-
import { useState as
|
|
26950
|
-
import { jsx as
|
|
27156
|
+
import { useState as useState5 } from "react";
|
|
27157
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
|
|
26951
27158
|
function InputModal({ title, placeholder, initialValue, errorMessage: errorMessage2, onSubmit }) {
|
|
26952
|
-
const [value, setValue] =
|
|
26953
|
-
return /* @__PURE__ */
|
|
27159
|
+
const [value, setValue] = useState5(initialValue ?? "");
|
|
27160
|
+
return /* @__PURE__ */ jsx13(Overlay, {
|
|
26954
27161
|
title,
|
|
26955
27162
|
borderColor: tokens.accent,
|
|
26956
|
-
children: /* @__PURE__ */
|
|
27163
|
+
children: /* @__PURE__ */ jsxs11("box", {
|
|
26957
27164
|
flexDirection: "column",
|
|
26958
27165
|
children: [
|
|
26959
|
-
/* @__PURE__ */
|
|
27166
|
+
/* @__PURE__ */ jsx13("input", {
|
|
26960
27167
|
focused: true,
|
|
26961
27168
|
value: initialValue ?? "",
|
|
26962
27169
|
placeholder,
|
|
26963
27170
|
onInput: (val) => setValue(val),
|
|
26964
27171
|
onSubmit: () => onSubmit(value)
|
|
26965
27172
|
}),
|
|
26966
|
-
errorMessage2 ? /* @__PURE__ */
|
|
27173
|
+
errorMessage2 ? /* @__PURE__ */ jsx13("text", {
|
|
26967
27174
|
fg: tokens.error,
|
|
26968
27175
|
style: { marginTop: 1 },
|
|
26969
27176
|
children: errorMessage2
|
|
26970
|
-
}) : /* @__PURE__ */
|
|
27177
|
+
}) : /* @__PURE__ */ jsx13("text", {
|
|
26971
27178
|
fg: tokens.dim,
|
|
26972
27179
|
style: { marginTop: 1 },
|
|
26973
27180
|
children: "Press Enter to submit, empty to cancel"
|
|
@@ -26982,23 +27189,23 @@ var init_InputModal = __esm(() => {
|
|
|
26982
27189
|
});
|
|
26983
27190
|
|
|
26984
27191
|
// src/tui/components/ChoiceModal.tsx
|
|
26985
|
-
import { useState as
|
|
27192
|
+
import { useState as useState6 } from "react";
|
|
26986
27193
|
import { useKeyboard } from "@opentui/react";
|
|
26987
|
-
import { jsx as
|
|
27194
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "@opentui/react/jsx-runtime";
|
|
26988
27195
|
function ChoiceOptionRow({ option, index, isSelected, onSelect }) {
|
|
26989
27196
|
const tap = useTapHandler(() => onSelect(option.value));
|
|
26990
|
-
return /* @__PURE__ */
|
|
27197
|
+
return /* @__PURE__ */ jsxs12("box", {
|
|
26991
27198
|
flexDirection: "column",
|
|
26992
27199
|
backgroundColor: isSelected ? tokens.selectionBg : undefined,
|
|
26993
27200
|
...tap,
|
|
26994
27201
|
children: [
|
|
26995
|
-
/* @__PURE__ */
|
|
26996
|
-
children: /* @__PURE__ */
|
|
27202
|
+
/* @__PURE__ */ jsx14("text", {
|
|
27203
|
+
children: /* @__PURE__ */ jsx14("span", {
|
|
26997
27204
|
fg: isSelected ? tokens.bright : tokens.fg,
|
|
26998
27205
|
children: `${index + 1}. ${option.label}`
|
|
26999
27206
|
})
|
|
27000
27207
|
}),
|
|
27001
|
-
option.desc && /* @__PURE__ */
|
|
27208
|
+
option.desc && /* @__PURE__ */ jsx14("text", {
|
|
27002
27209
|
fg: tokens.dim,
|
|
27003
27210
|
children: ` ${option.desc}`
|
|
27004
27211
|
})
|
|
@@ -27006,7 +27213,7 @@ function ChoiceOptionRow({ option, index, isSelected, onSelect }) {
|
|
|
27006
27213
|
});
|
|
27007
27214
|
}
|
|
27008
27215
|
function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
|
|
27009
|
-
const [index, setIndex] =
|
|
27216
|
+
const [index, setIndex] = useState6(Math.min(Math.max(initialIndex, 0), options.length - 1));
|
|
27010
27217
|
useKeyboard((key) => {
|
|
27011
27218
|
if (key.name === "escape") {
|
|
27012
27219
|
onCancel();
|
|
@@ -27033,19 +27240,19 @@ function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
|
|
|
27033
27240
|
onSubmit(option.value);
|
|
27034
27241
|
}
|
|
27035
27242
|
});
|
|
27036
|
-
return /* @__PURE__ */
|
|
27243
|
+
return /* @__PURE__ */ jsx14(Overlay, {
|
|
27037
27244
|
title,
|
|
27038
27245
|
borderColor: tokens.accent,
|
|
27039
|
-
children: /* @__PURE__ */
|
|
27246
|
+
children: /* @__PURE__ */ jsxs12("box", {
|
|
27040
27247
|
flexDirection: "column",
|
|
27041
27248
|
children: [
|
|
27042
|
-
options.map((option, i2) => /* @__PURE__ */
|
|
27249
|
+
options.map((option, i2) => /* @__PURE__ */ jsx14(ChoiceOptionRow, {
|
|
27043
27250
|
option,
|
|
27044
27251
|
index: i2,
|
|
27045
27252
|
isSelected: i2 === index,
|
|
27046
27253
|
onSelect: onSubmit
|
|
27047
27254
|
}, option.value)),
|
|
27048
|
-
/* @__PURE__ */
|
|
27255
|
+
/* @__PURE__ */ jsxs12("text", {
|
|
27049
27256
|
fg: tokens.dim,
|
|
27050
27257
|
style: { marginTop: 1 },
|
|
27051
27258
|
children: [
|
|
@@ -27065,16 +27272,16 @@ var init_ChoiceModal = __esm(() => {
|
|
|
27065
27272
|
});
|
|
27066
27273
|
|
|
27067
27274
|
// src/tui/components/ConfigOverlay.tsx
|
|
27068
|
-
import { useState as
|
|
27275
|
+
import { useState as useState7, useEffect as useEffect5 } from "react";
|
|
27069
27276
|
import { useKeyboard as useKeyboard2 } from "@opentui/react";
|
|
27070
|
-
import { jsx as
|
|
27277
|
+
import { jsx as jsx15, jsxs as jsxs13, Fragment as Fragment2 } from "@opentui/react/jsx-runtime";
|
|
27071
27278
|
function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
27072
|
-
const [config2, setConfig] =
|
|
27073
|
-
const [viewState, setViewState] =
|
|
27074
|
-
const [selectedIndex, setSelectedIndex] =
|
|
27075
|
-
const [windowStart, setWindowStart] =
|
|
27076
|
-
const [editState, setEditState] =
|
|
27077
|
-
|
|
27279
|
+
const [config2, setConfig] = useState7(null);
|
|
27280
|
+
const [viewState, setViewState] = useState7({ type: "main" });
|
|
27281
|
+
const [selectedIndex, setSelectedIndex] = useState7(1);
|
|
27282
|
+
const [windowStart, setWindowStart] = useState7(0);
|
|
27283
|
+
const [editState, setEditState] = useState7({ type: "none" });
|
|
27284
|
+
useEffect5(() => {
|
|
27078
27285
|
try {
|
|
27079
27286
|
setConfig(loadConfig());
|
|
27080
27287
|
} catch (err) {
|
|
@@ -27110,7 +27317,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27110
27317
|
detailRows.push({ type: "remove_repo" });
|
|
27111
27318
|
}
|
|
27112
27319
|
const activeRows = viewState.type === "main" ? mainRows : detailRows;
|
|
27113
|
-
|
|
27320
|
+
useEffect5(() => {
|
|
27114
27321
|
if (!activeRows || selectedIndex >= activeRows.length) {
|
|
27115
27322
|
let newIdx = activeRows.length - 1;
|
|
27116
27323
|
while (newIdx > 0 && activeRows[newIdx]?.type === "header") {
|
|
@@ -27330,17 +27537,17 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27330
27537
|
hint = "Remove this repository from config";
|
|
27331
27538
|
}
|
|
27332
27539
|
}
|
|
27333
|
-
return /* @__PURE__ */
|
|
27540
|
+
return /* @__PURE__ */ jsxs13(Fragment2, {
|
|
27334
27541
|
children: [
|
|
27335
|
-
/* @__PURE__ */
|
|
27542
|
+
/* @__PURE__ */ jsx15(Overlay, {
|
|
27336
27543
|
title: "Configuration",
|
|
27337
27544
|
borderColor: tokens.border,
|
|
27338
|
-
children: /* @__PURE__ */
|
|
27545
|
+
children: /* @__PURE__ */ jsxs13("box", {
|
|
27339
27546
|
flexDirection: "column",
|
|
27340
27547
|
width: "100%",
|
|
27341
27548
|
height: 24,
|
|
27342
27549
|
children: [
|
|
27343
|
-
/* @__PURE__ */
|
|
27550
|
+
/* @__PURE__ */ jsx15("box", {
|
|
27344
27551
|
flexDirection: "column",
|
|
27345
27552
|
flexGrow: 1,
|
|
27346
27553
|
overflow: "hidden",
|
|
@@ -27348,7 +27555,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27348
27555
|
const i2 = windowStart + idx;
|
|
27349
27556
|
const isSelected = i2 === selectedIndex;
|
|
27350
27557
|
if (row.type === "header") {
|
|
27351
|
-
return /* @__PURE__ */
|
|
27558
|
+
return /* @__PURE__ */ jsx15("text", {
|
|
27352
27559
|
fg: tokens.dim,
|
|
27353
27560
|
style: { marginTop: i2 === 0 ? 0 : 1 },
|
|
27354
27561
|
children: row.label
|
|
@@ -27377,11 +27584,11 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27377
27584
|
labelText = "Remove this repo";
|
|
27378
27585
|
valText = "";
|
|
27379
27586
|
}
|
|
27380
|
-
return /* @__PURE__ */
|
|
27587
|
+
return /* @__PURE__ */ jsxs13("box", {
|
|
27381
27588
|
flexDirection: "row",
|
|
27382
27589
|
width: "100%",
|
|
27383
27590
|
children: [
|
|
27384
|
-
/* @__PURE__ */
|
|
27591
|
+
/* @__PURE__ */ jsxs13("text", {
|
|
27385
27592
|
fg: isSelected ? tokens.bright : tokens.fg,
|
|
27386
27593
|
bg: isSelected ? tokens.selectionBg : undefined,
|
|
27387
27594
|
width: 24,
|
|
@@ -27390,7 +27597,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27390
27597
|
labelText
|
|
27391
27598
|
]
|
|
27392
27599
|
}),
|
|
27393
|
-
valText && /* @__PURE__ */
|
|
27600
|
+
valText && /* @__PURE__ */ jsx15("text", {
|
|
27394
27601
|
fg: isSelected ? tokens.accent : tokens.dim,
|
|
27395
27602
|
bg: isSelected ? tokens.selectionBg : undefined,
|
|
27396
27603
|
flexShrink: 1,
|
|
@@ -27400,14 +27607,14 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27400
27607
|
}, i2);
|
|
27401
27608
|
})
|
|
27402
27609
|
}),
|
|
27403
|
-
/* @__PURE__ */
|
|
27610
|
+
/* @__PURE__ */ jsx15("box", {
|
|
27404
27611
|
flexDirection: "row",
|
|
27405
27612
|
border: true,
|
|
27406
27613
|
borderColor: tokens.border,
|
|
27407
27614
|
paddingTop: 1,
|
|
27408
27615
|
marginTop: 1,
|
|
27409
27616
|
minHeight: 3,
|
|
27410
|
-
children: /* @__PURE__ */
|
|
27617
|
+
children: /* @__PURE__ */ jsx15("text", {
|
|
27411
27618
|
fg: tokens.dim,
|
|
27412
27619
|
children: hint
|
|
27413
27620
|
})
|
|
@@ -27415,14 +27622,14 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27415
27622
|
]
|
|
27416
27623
|
})
|
|
27417
27624
|
}),
|
|
27418
|
-
editState.type === "input" && /* @__PURE__ */
|
|
27625
|
+
editState.type === "input" && /* @__PURE__ */ jsx15(InputModal, {
|
|
27419
27626
|
title: editState.title,
|
|
27420
27627
|
initialValue: editState.currentValue,
|
|
27421
27628
|
placeholder: "Enter value...",
|
|
27422
27629
|
errorMessage: editState.error,
|
|
27423
27630
|
onSubmit: handleInputSubmit
|
|
27424
27631
|
}),
|
|
27425
|
-
editState.type === "confirm_remove" && /* @__PURE__ */
|
|
27632
|
+
editState.type === "confirm_remove" && /* @__PURE__ */ jsx15(ConfirmModal, {
|
|
27426
27633
|
title: "Remove Repo",
|
|
27427
27634
|
message: `Remove repo ${editState.repo} from config?`
|
|
27428
27635
|
})
|
|
@@ -27439,29 +27646,29 @@ var init_ConfigOverlay = __esm(() => {
|
|
|
27439
27646
|
});
|
|
27440
27647
|
|
|
27441
27648
|
// src/tui/components/WarningsOverlay.tsx
|
|
27442
|
-
import { jsx as
|
|
27649
|
+
import { jsx as jsx16, jsxs as jsxs14 } from "@opentui/react/jsx-runtime";
|
|
27443
27650
|
function WarningsOverlay({ warnings, onAcknowledge, onClose }) {
|
|
27444
27651
|
const ackTap = useTapHandler(() => onAcknowledge?.());
|
|
27445
27652
|
const closeTap = useTapHandler(() => onClose?.());
|
|
27446
|
-
return /* @__PURE__ */
|
|
27653
|
+
return /* @__PURE__ */ jsxs14(Overlay, {
|
|
27447
27654
|
title: `Warnings (${warnings.length})`,
|
|
27448
27655
|
borderColor: tokens.warning,
|
|
27449
27656
|
width: 80,
|
|
27450
27657
|
children: [
|
|
27451
|
-
/* @__PURE__ */
|
|
27658
|
+
/* @__PURE__ */ jsx16("box", {
|
|
27452
27659
|
flexDirection: "column",
|
|
27453
27660
|
style: { maxHeight: 28 },
|
|
27454
|
-
children: /* @__PURE__ */
|
|
27661
|
+
children: /* @__PURE__ */ jsx16("scrollbox", {
|
|
27455
27662
|
flexGrow: 1,
|
|
27456
|
-
children: warnings.map((warning, i2) => /* @__PURE__ */
|
|
27663
|
+
children: warnings.map((warning, i2) => /* @__PURE__ */ jsxs14("box", {
|
|
27457
27664
|
flexDirection: "column",
|
|
27458
27665
|
style: { marginBottom: 1 },
|
|
27459
27666
|
children: [
|
|
27460
|
-
/* @__PURE__ */
|
|
27667
|
+
/* @__PURE__ */ jsx16("text", {
|
|
27461
27668
|
fg: tokens.warning,
|
|
27462
27669
|
children: `⚠ ${warning.repoName}`
|
|
27463
27670
|
}),
|
|
27464
|
-
wrapText(warning.message, 72).map((line, j) => /* @__PURE__ */
|
|
27671
|
+
wrapText(warning.message, 72).map((line, j) => /* @__PURE__ */ jsx16("text", {
|
|
27465
27672
|
fg: tokens.fg,
|
|
27466
27673
|
children: ` ${line}`
|
|
27467
27674
|
}, j))
|
|
@@ -27469,36 +27676,36 @@ function WarningsOverlay({ warnings, onAcknowledge, onClose }) {
|
|
|
27469
27676
|
}, i2))
|
|
27470
27677
|
})
|
|
27471
27678
|
}),
|
|
27472
|
-
/* @__PURE__ */
|
|
27679
|
+
/* @__PURE__ */ jsxs14("box", {
|
|
27473
27680
|
flexDirection: "row",
|
|
27474
27681
|
gap: 3,
|
|
27475
27682
|
style: { marginTop: 2 },
|
|
27476
27683
|
justifyContent: "center",
|
|
27477
27684
|
children: [
|
|
27478
|
-
/* @__PURE__ */
|
|
27685
|
+
/* @__PURE__ */ jsx16("box", {
|
|
27479
27686
|
...ackTap,
|
|
27480
|
-
children: /* @__PURE__ */
|
|
27687
|
+
children: /* @__PURE__ */ jsxs14("text", {
|
|
27481
27688
|
children: [
|
|
27482
|
-
/* @__PURE__ */
|
|
27689
|
+
/* @__PURE__ */ jsx16("span", {
|
|
27483
27690
|
fg: tokens.dim,
|
|
27484
27691
|
children: "[a] "
|
|
27485
27692
|
}),
|
|
27486
|
-
/* @__PURE__ */
|
|
27693
|
+
/* @__PURE__ */ jsx16("span", {
|
|
27487
27694
|
fg: tokens.success,
|
|
27488
27695
|
children: "Acknowledge"
|
|
27489
27696
|
})
|
|
27490
27697
|
]
|
|
27491
27698
|
})
|
|
27492
27699
|
}),
|
|
27493
|
-
/* @__PURE__ */
|
|
27700
|
+
/* @__PURE__ */ jsx16("box", {
|
|
27494
27701
|
...closeTap,
|
|
27495
|
-
children: /* @__PURE__ */
|
|
27702
|
+
children: /* @__PURE__ */ jsxs14("text", {
|
|
27496
27703
|
children: [
|
|
27497
|
-
/* @__PURE__ */
|
|
27704
|
+
/* @__PURE__ */ jsx16("span", {
|
|
27498
27705
|
fg: tokens.dim,
|
|
27499
27706
|
children: "[esc] "
|
|
27500
27707
|
}),
|
|
27501
|
-
/* @__PURE__ */
|
|
27708
|
+
/* @__PURE__ */ jsx16("span", {
|
|
27502
27709
|
children: "Close"
|
|
27503
27710
|
})
|
|
27504
27711
|
]
|
|
@@ -27506,7 +27713,7 @@ function WarningsOverlay({ warnings, onAcknowledge, onClose }) {
|
|
|
27506
27713
|
})
|
|
27507
27714
|
]
|
|
27508
27715
|
}),
|
|
27509
|
-
/* @__PURE__ */
|
|
27716
|
+
/* @__PURE__ */ jsx16("text", {
|
|
27510
27717
|
style: { marginTop: 1, fg: tokens.dim },
|
|
27511
27718
|
children: "Press a to acknowledge and clear errors"
|
|
27512
27719
|
})
|
|
@@ -27521,10 +27728,10 @@ var init_WarningsOverlay = __esm(() => {
|
|
|
27521
27728
|
});
|
|
27522
27729
|
|
|
27523
27730
|
// src/tui/hooks/useSpinnerFrame.ts
|
|
27524
|
-
import { useEffect as
|
|
27731
|
+
import { useEffect as useEffect6, useState as useState8 } from "react";
|
|
27525
27732
|
function useSpinnerFrame(active) {
|
|
27526
|
-
const [index, setIndex] =
|
|
27527
|
-
|
|
27733
|
+
const [index, setIndex] = useState8(0);
|
|
27734
|
+
useEffect6(() => {
|
|
27528
27735
|
if (!active)
|
|
27529
27736
|
return;
|
|
27530
27737
|
const id = setInterval(() => setIndex((i2) => (i2 + 1) % FRAMES.length), INTERVAL_MS);
|
|
@@ -27537,11 +27744,393 @@ var init_useSpinnerFrame = __esm(() => {
|
|
|
27537
27744
|
FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
27538
27745
|
});
|
|
27539
27746
|
|
|
27747
|
+
// src/tui/hooks/useTerminalSessions.ts
|
|
27748
|
+
import { useState as useState9, useCallback as useCallback5, useRef as useRef6, useEffect as useEffect7 } from "react";
|
|
27749
|
+
function worktreeKeyFor(repoName, branch, path37) {
|
|
27750
|
+
return path37 || `${repoName}:${branch}`;
|
|
27751
|
+
}
|
|
27752
|
+
function nextTerminalSessionLabel(existingCount) {
|
|
27753
|
+
return `Session ${existingCount + 1}`;
|
|
27754
|
+
}
|
|
27755
|
+
function isSameSize(session, cols, rows) {
|
|
27756
|
+
return session.cols === cols && session.rows === rows;
|
|
27757
|
+
}
|
|
27758
|
+
function updateSessionSize(session, cols, rows) {
|
|
27759
|
+
return isSameSize(session, cols, rows) ? session : { ...session, cols, rows };
|
|
27760
|
+
}
|
|
27761
|
+
function relabelTerminalSessions(sessions) {
|
|
27762
|
+
return sessions.map((session, index) => ({ ...session, label: nextTerminalSessionLabel(index) }));
|
|
27763
|
+
}
|
|
27764
|
+
function useTerminalSessions() {
|
|
27765
|
+
const [sessionsByKey, setSessionsByKey] = useState9(() => new Map);
|
|
27766
|
+
const procsRef = useRef6(new Map);
|
|
27767
|
+
const listenersRef = useRef6(new Map);
|
|
27768
|
+
const rawBuffersRef = useRef6(new Map);
|
|
27769
|
+
const getSessions = useCallback5((repoName, branch, path37) => {
|
|
27770
|
+
const key = worktreeKeyFor(repoName, branch, path37);
|
|
27771
|
+
return sessionsByKey.get(key) ?? [];
|
|
27772
|
+
}, [sessionsByKey]);
|
|
27773
|
+
const getKey = useCallback5((repoName, branch, path37) => worktreeKeyFor(repoName, branch, path37), []);
|
|
27774
|
+
const createSession = useCallback5((repoName, branch, path37, opts) => {
|
|
27775
|
+
const key = worktreeKeyFor(repoName, branch, path37);
|
|
27776
|
+
const existing = sessionsByKey.get(key) ?? [];
|
|
27777
|
+
if (existing.length >= MAX_TERMINAL_SESSIONS) {
|
|
27778
|
+
return { session: null, error: `Max ${MAX_TERMINAL_SESSIONS} sessions per worktree` };
|
|
27779
|
+
}
|
|
27780
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
27781
|
+
const label = nextTerminalSessionLabel(existing.length);
|
|
27782
|
+
const cols = opts?.cols ?? 80;
|
|
27783
|
+
const rows = opts?.rows ?? 24;
|
|
27784
|
+
const session = {
|
|
27785
|
+
id,
|
|
27786
|
+
label,
|
|
27787
|
+
worktreeKey: key,
|
|
27788
|
+
worktreePath: path37,
|
|
27789
|
+
repoName,
|
|
27790
|
+
branch,
|
|
27791
|
+
lines: [],
|
|
27792
|
+
inputBuffer: "",
|
|
27793
|
+
exited: null,
|
|
27794
|
+
proc: null,
|
|
27795
|
+
terminal: null,
|
|
27796
|
+
cols,
|
|
27797
|
+
rows,
|
|
27798
|
+
usePty: false
|
|
27799
|
+
};
|
|
27800
|
+
const shell = process.env.SHELL || "/bin/bash";
|
|
27801
|
+
const appendPipe = (data) => {
|
|
27802
|
+
const text = new TextDecoder().decode(data);
|
|
27803
|
+
const parts = text.split(`
|
|
27804
|
+
`);
|
|
27805
|
+
setSessionsByKey((prev) => {
|
|
27806
|
+
const next = new Map(prev);
|
|
27807
|
+
const list = next.get(key);
|
|
27808
|
+
if (!list)
|
|
27809
|
+
return prev;
|
|
27810
|
+
const idx = list.findIndex((s) => s.id === id);
|
|
27811
|
+
if (idx === -1)
|
|
27812
|
+
return prev;
|
|
27813
|
+
const updated = [...list];
|
|
27814
|
+
const sess = updated[idx];
|
|
27815
|
+
const newLines = [...sess.lines];
|
|
27816
|
+
for (let i2 = 0;i2 < parts.length; i2++) {
|
|
27817
|
+
const part = parts[i2];
|
|
27818
|
+
if (i2 === 0 && newLines.length > 0) {
|
|
27819
|
+
if (part)
|
|
27820
|
+
newLines[newLines.length - 1] = (newLines[newLines.length - 1] ?? "") + part;
|
|
27821
|
+
} else {
|
|
27822
|
+
if (part || i2 < parts.length - 1)
|
|
27823
|
+
newLines.push(part);
|
|
27824
|
+
}
|
|
27825
|
+
}
|
|
27826
|
+
if (newLines.length > 1000)
|
|
27827
|
+
newLines.splice(0, newLines.length - 1000);
|
|
27828
|
+
updated[idx] = { ...sess, lines: newLines };
|
|
27829
|
+
next.set(key, updated);
|
|
27830
|
+
return next;
|
|
27831
|
+
});
|
|
27832
|
+
};
|
|
27833
|
+
const onPtyData = (data) => {
|
|
27834
|
+
const buf = rawBuffersRef.current.get(id) ?? [];
|
|
27835
|
+
buf.push(data);
|
|
27836
|
+
if (buf.length > 500)
|
|
27837
|
+
buf.shift();
|
|
27838
|
+
rawBuffersRef.current.set(id, buf);
|
|
27839
|
+
const listener = listenersRef.current.get(id);
|
|
27840
|
+
if (listener) {
|
|
27841
|
+
listener(data);
|
|
27842
|
+
return;
|
|
27843
|
+
}
|
|
27844
|
+
};
|
|
27845
|
+
const onExit2 = (code) => {
|
|
27846
|
+
setSessionsByKey((prev) => {
|
|
27847
|
+
const next = new Map(prev);
|
|
27848
|
+
const list = next.get(key);
|
|
27849
|
+
if (!list)
|
|
27850
|
+
return prev;
|
|
27851
|
+
const idx = list.findIndex((s) => s.id === id);
|
|
27852
|
+
if (idx === -1)
|
|
27853
|
+
return prev;
|
|
27854
|
+
const updated = [...list];
|
|
27855
|
+
updated[idx] = { ...updated[idx], exited: code ?? 0, proc: null, terminal: null };
|
|
27856
|
+
next.set(key, updated);
|
|
27857
|
+
return next;
|
|
27858
|
+
});
|
|
27859
|
+
procsRef.current.delete(id);
|
|
27860
|
+
listenersRef.current.delete(id);
|
|
27861
|
+
};
|
|
27862
|
+
let usePty = false;
|
|
27863
|
+
try {
|
|
27864
|
+
if (typeof Bun !== "undefined" && typeof Bun.spawn === "function") {
|
|
27865
|
+
try {
|
|
27866
|
+
const proc = Bun.spawn([shell], {
|
|
27867
|
+
cwd: path37 || process.cwd(),
|
|
27868
|
+
env: { ...process.env, TERM: "xterm-256color", COLORTERM: "truecolor", WTX_SESSION: label },
|
|
27869
|
+
terminal: {
|
|
27870
|
+
cols,
|
|
27871
|
+
rows,
|
|
27872
|
+
name: "xterm-256color",
|
|
27873
|
+
data(_terminal, data) {
|
|
27874
|
+
onPtyData(data);
|
|
27875
|
+
},
|
|
27876
|
+
exit(_terminal, exitCode) {
|
|
27877
|
+
onExit2(exitCode);
|
|
27878
|
+
}
|
|
27879
|
+
}
|
|
27880
|
+
});
|
|
27881
|
+
if (proc && proc.terminal) {
|
|
27882
|
+
session.proc = proc;
|
|
27883
|
+
session.terminal = proc.terminal;
|
|
27884
|
+
session.usePty = true;
|
|
27885
|
+
usePty = true;
|
|
27886
|
+
procsRef.current.set(id, proc);
|
|
27887
|
+
(async () => {
|
|
27888
|
+
try {
|
|
27889
|
+
const code = await proc.exited;
|
|
27890
|
+
onExit2(code);
|
|
27891
|
+
} catch {}
|
|
27892
|
+
})();
|
|
27893
|
+
}
|
|
27894
|
+
} catch {}
|
|
27895
|
+
}
|
|
27896
|
+
if (!usePty)
|
|
27897
|
+
throw new Error("pty not available");
|
|
27898
|
+
} catch {
|
|
27899
|
+
try {
|
|
27900
|
+
const proc = Bun.spawn([shell], {
|
|
27901
|
+
cwd: path37 || process.cwd(),
|
|
27902
|
+
stdin: "pipe",
|
|
27903
|
+
stdout: "pipe",
|
|
27904
|
+
stderr: "pipe",
|
|
27905
|
+
env: { ...process.env, TERM: "xterm-256color", WTX_SESSION: label }
|
|
27906
|
+
});
|
|
27907
|
+
session.proc = proc;
|
|
27908
|
+
session.usePty = false;
|
|
27909
|
+
procsRef.current.set(id, proc);
|
|
27910
|
+
(async () => {
|
|
27911
|
+
const stdout = proc.stdout;
|
|
27912
|
+
if (stdout) {
|
|
27913
|
+
const reader = stdout.getReader();
|
|
27914
|
+
try {
|
|
27915
|
+
while (true) {
|
|
27916
|
+
const { value, done } = await reader.read();
|
|
27917
|
+
if (done)
|
|
27918
|
+
break;
|
|
27919
|
+
if (value)
|
|
27920
|
+
appendPipe(value);
|
|
27921
|
+
}
|
|
27922
|
+
} catch {}
|
|
27923
|
+
}
|
|
27924
|
+
})();
|
|
27925
|
+
(async () => {
|
|
27926
|
+
const stderr = proc.stderr;
|
|
27927
|
+
if (stderr) {
|
|
27928
|
+
const reader = stderr.getReader();
|
|
27929
|
+
try {
|
|
27930
|
+
while (true) {
|
|
27931
|
+
const { value, done } = await reader.read();
|
|
27932
|
+
if (done)
|
|
27933
|
+
break;
|
|
27934
|
+
if (value)
|
|
27935
|
+
appendPipe(value);
|
|
27936
|
+
}
|
|
27937
|
+
} catch {}
|
|
27938
|
+
}
|
|
27939
|
+
})();
|
|
27940
|
+
(async () => {
|
|
27941
|
+
try {
|
|
27942
|
+
const code = await proc.exited;
|
|
27943
|
+
onExit2(code);
|
|
27944
|
+
} catch {}
|
|
27945
|
+
})();
|
|
27946
|
+
} catch (e) {
|
|
27947
|
+
session.lines = [`Failed to spawn shell: ${e.message}`];
|
|
27948
|
+
}
|
|
27949
|
+
}
|
|
27950
|
+
if (session.usePty) {
|
|
27951
|
+
session.lines = [`${label} — ${path37} (${shell}) [pty]`, ""];
|
|
27952
|
+
} else if (session.lines.length === 0) {
|
|
27953
|
+
session.lines = [`${label} — ${path37} (${shell})`, ""];
|
|
27954
|
+
}
|
|
27955
|
+
setSessionsByKey((prev) => {
|
|
27956
|
+
const next = new Map(prev);
|
|
27957
|
+
const list = next.get(key) ?? [];
|
|
27958
|
+
next.set(key, [...list, session]);
|
|
27959
|
+
return next;
|
|
27960
|
+
});
|
|
27961
|
+
return { session };
|
|
27962
|
+
}, [sessionsByKey]);
|
|
27963
|
+
const removeSession = useCallback5((repoName, branch, path37, id) => {
|
|
27964
|
+
const key = worktreeKeyFor(repoName, branch, path37);
|
|
27965
|
+
setSessionsByKey((prev) => {
|
|
27966
|
+
const next = new Map(prev);
|
|
27967
|
+
const list = next.get(key);
|
|
27968
|
+
if (!list)
|
|
27969
|
+
return prev;
|
|
27970
|
+
const sess = list.find((s) => s.id === id);
|
|
27971
|
+
if (sess?.proc) {
|
|
27972
|
+
try {
|
|
27973
|
+
if (sess.terminal) {
|
|
27974
|
+
try {
|
|
27975
|
+
sess.terminal.close();
|
|
27976
|
+
} catch {}
|
|
27977
|
+
}
|
|
27978
|
+
sess.proc.kill();
|
|
27979
|
+
} catch {}
|
|
27980
|
+
procsRef.current.delete(id);
|
|
27981
|
+
listenersRef.current.delete(id);
|
|
27982
|
+
rawBuffersRef.current.delete(id);
|
|
27983
|
+
}
|
|
27984
|
+
const filtered = list.filter((s) => s.id !== id);
|
|
27985
|
+
const relabeled = relabelTerminalSessions(filtered);
|
|
27986
|
+
if (relabeled.length === 0) {
|
|
27987
|
+
next.delete(key);
|
|
27988
|
+
} else {
|
|
27989
|
+
next.set(key, relabeled);
|
|
27990
|
+
}
|
|
27991
|
+
return next;
|
|
27992
|
+
});
|
|
27993
|
+
}, []);
|
|
27994
|
+
const sendInput = useCallback5((repoName, branch, path37, id, data) => {
|
|
27995
|
+
const key = worktreeKeyFor(repoName, branch, path37);
|
|
27996
|
+
const list = sessionsByKey.get(key) ?? [];
|
|
27997
|
+
const sess = list.find((s) => s.id === id);
|
|
27998
|
+
if (!sess?.proc)
|
|
27999
|
+
return;
|
|
28000
|
+
try {
|
|
28001
|
+
if (sess.usePty && sess.terminal) {
|
|
28002
|
+
if (typeof data === "string") {
|
|
28003
|
+
sess.terminal.write(new TextEncoder().encode(data));
|
|
28004
|
+
} else {
|
|
28005
|
+
sess.terminal.write(data);
|
|
28006
|
+
}
|
|
28007
|
+
return;
|
|
28008
|
+
}
|
|
28009
|
+
const proc = sess.proc;
|
|
28010
|
+
const str = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
28011
|
+
if (proc.stdin && typeof proc.stdin.write === "function") {
|
|
28012
|
+
proc.stdin.write(str);
|
|
28013
|
+
} else if (proc.stdin instanceof WritableStream) {
|
|
28014
|
+
const writer = proc.stdin.getWriter();
|
|
28015
|
+
writer.write(new TextEncoder().encode(str));
|
|
28016
|
+
writer.releaseLock();
|
|
28017
|
+
} else {
|
|
28018
|
+
proc.stdin?.write?.(str);
|
|
28019
|
+
}
|
|
28020
|
+
} catch {}
|
|
28021
|
+
if (!sess.usePty) {
|
|
28022
|
+
const str = typeof data === "string" ? data : new TextDecoder().decode(data);
|
|
28023
|
+
if (str.endsWith(`
|
|
28024
|
+
`)) {
|
|
28025
|
+
const cmd = str.slice(0, -1);
|
|
28026
|
+
if (cmd) {
|
|
28027
|
+
setSessionsByKey((prev) => {
|
|
28028
|
+
const next = new Map(prev);
|
|
28029
|
+
const l = next.get(key);
|
|
28030
|
+
if (!l)
|
|
28031
|
+
return prev;
|
|
28032
|
+
const idx = l.findIndex((s2) => s2.id === id);
|
|
28033
|
+
if (idx === -1)
|
|
28034
|
+
return prev;
|
|
28035
|
+
const updated = [...l];
|
|
28036
|
+
const s = updated[idx];
|
|
28037
|
+
updated[idx] = { ...s, lines: [...s.lines, `$ ${cmd}`] };
|
|
28038
|
+
next.set(key, updated);
|
|
28039
|
+
return next;
|
|
28040
|
+
});
|
|
28041
|
+
}
|
|
28042
|
+
}
|
|
28043
|
+
}
|
|
28044
|
+
}, [sessionsByKey]);
|
|
28045
|
+
const resizeSession = useCallback5((repoName, branch, path37, id, cols, rows) => {
|
|
28046
|
+
const key = worktreeKeyFor(repoName, branch, path37);
|
|
28047
|
+
setSessionsByKey((prev) => {
|
|
28048
|
+
const list = prev.get(key);
|
|
28049
|
+
if (!list)
|
|
28050
|
+
return prev;
|
|
28051
|
+
const idx = list.findIndex((s) => s.id === id);
|
|
28052
|
+
if (idx === -1)
|
|
28053
|
+
return prev;
|
|
28054
|
+
const sess = list[idx];
|
|
28055
|
+
if (isSameSize(sess, cols, rows))
|
|
28056
|
+
return prev;
|
|
28057
|
+
try {
|
|
28058
|
+
sess.terminal?.resize?.(cols, rows);
|
|
28059
|
+
} catch {}
|
|
28060
|
+
const next = new Map(prev);
|
|
28061
|
+
const updated = [...list];
|
|
28062
|
+
updated[idx] = updateSessionSize(sess, cols, rows);
|
|
28063
|
+
next.set(key, updated);
|
|
28064
|
+
return next;
|
|
28065
|
+
});
|
|
28066
|
+
}, []);
|
|
28067
|
+
const registerListener = useCallback5((id, fn) => {
|
|
28068
|
+
listenersRef.current.set(id, fn);
|
|
28069
|
+
const buf = rawBuffersRef.current.get(id);
|
|
28070
|
+
if (buf) {
|
|
28071
|
+
for (const chunk of buf) {
|
|
28072
|
+
try {
|
|
28073
|
+
fn(chunk);
|
|
28074
|
+
} catch {}
|
|
28075
|
+
}
|
|
28076
|
+
}
|
|
28077
|
+
}, []);
|
|
28078
|
+
const unregisterListener = useCallback5((id) => {
|
|
28079
|
+
listenersRef.current.delete(id);
|
|
28080
|
+
}, []);
|
|
28081
|
+
const cleanupAll = useCallback5(() => {
|
|
28082
|
+
for (const proc of procsRef.current.values()) {
|
|
28083
|
+
try {
|
|
28084
|
+
if (proc.terminal) {
|
|
28085
|
+
try {
|
|
28086
|
+
proc.terminal.close();
|
|
28087
|
+
} catch {}
|
|
28088
|
+
}
|
|
28089
|
+
proc.kill();
|
|
28090
|
+
} catch {}
|
|
28091
|
+
}
|
|
28092
|
+
procsRef.current.clear();
|
|
28093
|
+
listenersRef.current.clear();
|
|
28094
|
+
rawBuffersRef.current.clear();
|
|
28095
|
+
}, []);
|
|
28096
|
+
useEffect7(() => {
|
|
28097
|
+
return () => cleanupAll();
|
|
28098
|
+
}, [cleanupAll]);
|
|
28099
|
+
const pruneKey = useCallback5((key) => {
|
|
28100
|
+
setSessionsByKey((prev) => {
|
|
28101
|
+
const next = new Map(prev);
|
|
28102
|
+
const list = next.get(key);
|
|
28103
|
+
if (!list)
|
|
28104
|
+
return prev;
|
|
28105
|
+
for (const s of list) {
|
|
28106
|
+
if (s.proc) {
|
|
28107
|
+
try {
|
|
28108
|
+
if (s.terminal) {
|
|
28109
|
+
try {
|
|
28110
|
+
s.terminal.close();
|
|
28111
|
+
} catch {}
|
|
28112
|
+
}
|
|
28113
|
+
s.proc.kill();
|
|
28114
|
+
} catch {}
|
|
28115
|
+
procsRef.current.delete(s.id);
|
|
28116
|
+
listenersRef.current.delete(s.id);
|
|
28117
|
+
rawBuffersRef.current.delete(s.id);
|
|
28118
|
+
}
|
|
28119
|
+
}
|
|
28120
|
+
next.delete(key);
|
|
28121
|
+
return next;
|
|
28122
|
+
});
|
|
28123
|
+
}, []);
|
|
28124
|
+
return { sessionsByKey, getSessions, getKey, createSession, removeSession, sendInput, resizeSession, registerListener, unregisterListener, pruneKey, cleanupAll };
|
|
28125
|
+
}
|
|
28126
|
+
var MAX_TERMINAL_SESSIONS = 5;
|
|
28127
|
+
var init_useTerminalSessions = () => {};
|
|
28128
|
+
|
|
27540
28129
|
// src/tui/components/App.tsx
|
|
27541
|
-
import { useState as
|
|
28130
|
+
import { useState as useState10, useEffect as useEffect8, useMemo, useRef as useRef7, useCallback as useCallback6 } from "react";
|
|
27542
28131
|
import { existsSync as existsSync4 } from "node:fs";
|
|
27543
28132
|
import { useKeyboard as useKeyboard3, useRenderer as useRenderer2, useSelectionHandler, useTerminalDimensions } from "@opentui/react";
|
|
27544
|
-
import { jsx as
|
|
28133
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "@opentui/react/jsx-runtime";
|
|
27545
28134
|
function getWorktreePathFor(repoName, branch) {
|
|
27546
28135
|
try {
|
|
27547
28136
|
const config2 = loadConfig();
|
|
@@ -27554,37 +28143,51 @@ function getWorktreePathFor(repoName, branch) {
|
|
|
27554
28143
|
function App({ opts }) {
|
|
27555
28144
|
const renderer = useRenderer2();
|
|
27556
28145
|
const { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh, clearWarnings } = useWorktrees(opts);
|
|
27557
|
-
const [selectedIndex, setSelectedIndex] =
|
|
27558
|
-
const [modal, setModal] =
|
|
27559
|
-
const [actionMessage, setActionMessage] =
|
|
27560
|
-
const messageTimer =
|
|
27561
|
-
const [ops, setOps] =
|
|
27562
|
-
const [failedLogs, setFailedLogs] =
|
|
27563
|
-
const nextOpId =
|
|
27564
|
-
const [createModal, setCreateModal] =
|
|
27565
|
-
const [createError, setCreateError] =
|
|
27566
|
-
const [createBaseModal, setCreateBaseModal] =
|
|
27567
|
-
const [createBaseError, setCreateBaseError] =
|
|
27568
|
-
const [createDepsChoice, setCreateDepsChoice] =
|
|
27569
|
-
const [pullPrModal, setPullPrModal] =
|
|
27570
|
-
const [pullPrError, setPullPrError] =
|
|
27571
|
-
const [pullForceChoice, setPullForceChoice] =
|
|
27572
|
-
const [renameModal, setRenameModal] =
|
|
27573
|
-
const [renameError, setRenameError] =
|
|
27574
|
-
const [configOpen, setConfigOpen] =
|
|
27575
|
-
const [refreshScopes, setRefreshScopes] =
|
|
27576
|
-
const [filterText, setFilterText] =
|
|
27577
|
-
const [isFiltering, setIsFiltering] =
|
|
27578
|
-
const [selection, setSelection] =
|
|
27579
|
-
const [splitRatio, setSplitRatio] =
|
|
27580
|
-
|
|
27581
|
-
|
|
27582
|
-
|
|
27583
|
-
|
|
27584
|
-
|
|
27585
|
-
|
|
27586
|
-
|
|
27587
|
-
|
|
28146
|
+
const [selectedIndex, setSelectedIndex] = useState10(0);
|
|
28147
|
+
const [modal, setModal] = useState10({ type: "none" });
|
|
28148
|
+
const [actionMessage, setActionMessage] = useState10();
|
|
28149
|
+
const messageTimer = useRef7(null);
|
|
28150
|
+
const [ops, setOps] = useState10([]);
|
|
28151
|
+
const [failedLogs, setFailedLogs] = useState10([]);
|
|
28152
|
+
const nextOpId = useRef7(1);
|
|
28153
|
+
const [createModal, setCreateModal] = useState10(false);
|
|
28154
|
+
const [createError, setCreateError] = useState10();
|
|
28155
|
+
const [createBaseModal, setCreateBaseModal] = useState10(null);
|
|
28156
|
+
const [createBaseError, setCreateBaseError] = useState10();
|
|
28157
|
+
const [createDepsChoice, setCreateDepsChoice] = useState10(null);
|
|
28158
|
+
const [pullPrModal, setPullPrModal] = useState10(false);
|
|
28159
|
+
const [pullPrError, setPullPrError] = useState10();
|
|
28160
|
+
const [pullForceChoice, setPullForceChoice] = useState10(null);
|
|
28161
|
+
const [renameModal, setRenameModal] = useState10(false);
|
|
28162
|
+
const [renameError, setRenameError] = useState10();
|
|
28163
|
+
const [configOpen, setConfigOpen] = useState10(false);
|
|
28164
|
+
const [refreshScopes, setRefreshScopes] = useState10([]);
|
|
28165
|
+
const [filterText, setFilterText] = useState10("");
|
|
28166
|
+
const [isFiltering, setIsFiltering] = useState10(false);
|
|
28167
|
+
const [selection, setSelection] = useState10(new Set);
|
|
28168
|
+
const [splitRatio, setSplitRatio] = useState10(() => {
|
|
28169
|
+
try {
|
|
28170
|
+
const cfg = loadConfig();
|
|
28171
|
+
const left = cfg.tui?.leftPaneWidthWeight ?? 3;
|
|
28172
|
+
const right = cfg.tui?.rightPaneWidthWeight ?? 7;
|
|
28173
|
+
const total = left + right;
|
|
28174
|
+
if (total > 0)
|
|
28175
|
+
return left / total;
|
|
28176
|
+
} catch {}
|
|
28177
|
+
return 3 / 10;
|
|
28178
|
+
});
|
|
28179
|
+
const [isResizing, setIsResizing] = useState10(false);
|
|
28180
|
+
const terminalSessions = useTerminalSessions();
|
|
28181
|
+
const [activeTabByWorktree, setActiveTabByWorktree] = useState10(() => new Map);
|
|
28182
|
+
const [terminalFocused, setTerminalFocused] = useState10(false);
|
|
28183
|
+
const { width: termW, height: termH } = useTerminalDimensions();
|
|
28184
|
+
const totalWidth = termW || renderer.width || 80;
|
|
28185
|
+
const totalHeight = termH || renderer.height || 24;
|
|
28186
|
+
useEffect8(() => {
|
|
28187
|
+
if (termW)
|
|
28188
|
+
setSplitRatio((prev) => clampSplitRatio(termW, prev));
|
|
28189
|
+
}, [termW]);
|
|
28190
|
+
const doRefresh = useCallback6(async (scope) => {
|
|
27588
28191
|
const targets = scope ?? [
|
|
27589
28192
|
...new Set([...blocks.map((b) => b.repoName), ...pendingRepos])
|
|
27590
28193
|
];
|
|
@@ -27595,18 +28198,18 @@ function App({ opts }) {
|
|
|
27595
28198
|
setRefreshScopes([]);
|
|
27596
28199
|
}
|
|
27597
28200
|
}, [blocks, pendingRepos, refresh]);
|
|
27598
|
-
|
|
28201
|
+
useEffect8(() => {
|
|
27599
28202
|
if (error52) {
|
|
27600
28203
|
setModal({ type: "error", message: error52 });
|
|
27601
28204
|
}
|
|
27602
28205
|
}, [error52]);
|
|
27603
|
-
const flash =
|
|
28206
|
+
const flash = useCallback6((message, ms = 3000) => {
|
|
27604
28207
|
if (messageTimer.current)
|
|
27605
28208
|
clearTimeout(messageTimer.current);
|
|
27606
28209
|
setActionMessage(message);
|
|
27607
28210
|
messageTimer.current = setTimeout(() => setActionMessage(undefined), ms);
|
|
27608
28211
|
}, []);
|
|
27609
|
-
const copySelectedText =
|
|
28212
|
+
const copySelectedText = useCallback6((warnOnEmpty) => {
|
|
27610
28213
|
if (isResizing) {
|
|
27611
28214
|
renderer.clearSelection();
|
|
27612
28215
|
return;
|
|
@@ -27636,12 +28239,130 @@ function App({ opts }) {
|
|
|
27636
28239
|
const flatRows = useMemo(() => displayBlocks.flatMap((b) => b.rows.filter((r) => !r.isPendingCreate)), [displayBlocks]);
|
|
27637
28240
|
const totalRows = blocks.flatMap((b) => b.rows).length;
|
|
27638
28241
|
const maxIndex = Math.max(0, flatRows.length - 1);
|
|
27639
|
-
|
|
28242
|
+
useEffect8(() => {
|
|
27640
28243
|
if (selectedIndex > maxIndex && maxIndex >= 0) {
|
|
27641
28244
|
setSelectedIndex(maxIndex);
|
|
27642
28245
|
}
|
|
27643
28246
|
}, [maxIndex, selectedIndex]);
|
|
27644
28247
|
const selectedRow = flatRows[selectedIndex] ?? null;
|
|
28248
|
+
const worktreeKey = selectedRow ? terminalSessions.getKey(selectedRow.repoName, selectedRow.branch, selectedRow.path) : "";
|
|
28249
|
+
const sessionsForSelected = selectedRow ? terminalSessions.getSessions(selectedRow.repoName, selectedRow.branch, selectedRow.path) : [];
|
|
28250
|
+
const activeTabId = worktreeKey ? activeTabByWorktree.get(worktreeKey) ?? "details" : "details";
|
|
28251
|
+
useEffect8(() => {
|
|
28252
|
+
if (worktreeKey && !activeTabByWorktree.has(worktreeKey)) {
|
|
28253
|
+
setActiveTabByWorktree((prev) => {
|
|
28254
|
+
if (prev.has(worktreeKey))
|
|
28255
|
+
return prev;
|
|
28256
|
+
const next = new Map(prev);
|
|
28257
|
+
next.set(worktreeKey, "details");
|
|
28258
|
+
return next;
|
|
28259
|
+
});
|
|
28260
|
+
}
|
|
28261
|
+
}, [worktreeKey, activeTabByWorktree]);
|
|
28262
|
+
useEffect8(() => {
|
|
28263
|
+
if (sessionsForSelected.length === 0 && activeTabId !== "details") {
|
|
28264
|
+
setActiveTabByWorktree((prev) => {
|
|
28265
|
+
const next = new Map(prev);
|
|
28266
|
+
next.set(worktreeKey, "details");
|
|
28267
|
+
return next;
|
|
28268
|
+
});
|
|
28269
|
+
setTerminalFocused(false);
|
|
28270
|
+
} else if (sessionsForSelected.length > 0) {
|
|
28271
|
+
const exists = sessionsForSelected.some((s) => s.id === activeTabId) || activeTabId === "details";
|
|
28272
|
+
if (!exists) {
|
|
28273
|
+
setActiveTabByWorktree((prev) => {
|
|
28274
|
+
const next = new Map(prev);
|
|
28275
|
+
next.set(worktreeKey, sessionsForSelected[0].id);
|
|
28276
|
+
return next;
|
|
28277
|
+
});
|
|
28278
|
+
}
|
|
28279
|
+
}
|
|
28280
|
+
}, [sessionsForSelected, activeTabId, worktreeKey]);
|
|
28281
|
+
const handleSelectTab = useCallback6((id) => {
|
|
28282
|
+
if (!worktreeKey)
|
|
28283
|
+
return;
|
|
28284
|
+
setActiveTabByWorktree((prev) => {
|
|
28285
|
+
const next = new Map(prev);
|
|
28286
|
+
next.set(worktreeKey, id);
|
|
28287
|
+
return next;
|
|
28288
|
+
});
|
|
28289
|
+
const isSession = sessionsForSelected.some((s) => s.id === id);
|
|
28290
|
+
setTerminalFocused(isSession);
|
|
28291
|
+
}, [worktreeKey, sessionsForSelected]);
|
|
28292
|
+
const handleAddSession = useCallback6(() => {
|
|
28293
|
+
if (!selectedRow) {
|
|
28294
|
+
flash("No worktree selected");
|
|
28295
|
+
return;
|
|
28296
|
+
}
|
|
28297
|
+
const rawLeftTmp = Math.floor(totalWidth * splitRatio);
|
|
28298
|
+
const leftColsTmp = Math.max(20, Math.min(totalWidth - 20 - DIVIDER_WIDTH, rawLeftTmp));
|
|
28299
|
+
const rightColsTmp = Math.max(20, totalWidth - leftColsTmp - DIVIDER_WIDTH);
|
|
28300
|
+
const cols = Math.max(20, rightColsTmp - 4);
|
|
28301
|
+
const rows = Math.max(10, totalHeight - 10);
|
|
28302
|
+
const { session, error: error53 } = terminalSessions.createSession(selectedRow.repoName, selectedRow.branch, selectedRow.path, { cols, rows });
|
|
28303
|
+
if (error53 || !session) {
|
|
28304
|
+
flash(error53 ?? "Failed to create session");
|
|
28305
|
+
return;
|
|
28306
|
+
}
|
|
28307
|
+
const key = terminalSessions.getKey(selectedRow.repoName, selectedRow.branch, selectedRow.path);
|
|
28308
|
+
setActiveTabByWorktree((prev) => {
|
|
28309
|
+
const next = new Map(prev);
|
|
28310
|
+
next.set(key, session.id);
|
|
28311
|
+
return next;
|
|
28312
|
+
});
|
|
28313
|
+
setTerminalFocused(true);
|
|
28314
|
+
flash(`Session ${session.label} created`, 2000);
|
|
28315
|
+
}, [selectedRow, terminalSessions, flash, totalWidth, splitRatio, totalHeight]);
|
|
28316
|
+
const handleCloseSession = useCallback6((id) => {
|
|
28317
|
+
if (!selectedRow)
|
|
28318
|
+
return;
|
|
28319
|
+
terminalSessions.removeSession(selectedRow.repoName, selectedRow.branch, selectedRow.path, id);
|
|
28320
|
+
const key = terminalSessions.getKey(selectedRow.repoName, selectedRow.branch, selectedRow.path);
|
|
28321
|
+
setActiveTabByWorktree((prev) => {
|
|
28322
|
+
const next = new Map(prev);
|
|
28323
|
+
const current = next.get(key);
|
|
28324
|
+
if (current === id)
|
|
28325
|
+
next.set(key, "details");
|
|
28326
|
+
return next;
|
|
28327
|
+
});
|
|
28328
|
+
setTerminalFocused(false);
|
|
28329
|
+
flash("Session closed", 2000);
|
|
28330
|
+
}, [selectedRow, terminalSessions, flash]);
|
|
28331
|
+
useEffect8(() => {
|
|
28332
|
+
const existingKeys = new Set(blocks.flatMap((b) => b.rows.map((r) => terminalSessions.getKey(r.repoName, r.branch, r.path))));
|
|
28333
|
+
for (const key of terminalSessions.sessionsByKey.keys()) {
|
|
28334
|
+
if (!existingKeys.has(key))
|
|
28335
|
+
terminalSessions.pruneKey(key);
|
|
28336
|
+
}
|
|
28337
|
+
}, [blocks, terminalSessions]);
|
|
28338
|
+
const tabsForSelected = useMemo(() => {
|
|
28339
|
+
const base2 = [
|
|
28340
|
+
{
|
|
28341
|
+
id: "details",
|
|
28342
|
+
label: "Details",
|
|
28343
|
+
render: ({ worktree }) => /* @__PURE__ */ jsx17(DetailsContent, {
|
|
28344
|
+
selectedRow: worktree
|
|
28345
|
+
})
|
|
28346
|
+
}
|
|
28347
|
+
];
|
|
28348
|
+
for (const s of sessionsForSelected) {
|
|
28349
|
+
base2.push({
|
|
28350
|
+
id: s.id,
|
|
28351
|
+
label: s.label,
|
|
28352
|
+
closable: true,
|
|
28353
|
+
render: () => /* @__PURE__ */ jsx17(TerminalView, {
|
|
28354
|
+
session: s,
|
|
28355
|
+
focused: terminalFocused && activeTabId === s.id,
|
|
28356
|
+
onFocus: () => setTerminalFocused(true),
|
|
28357
|
+
onSend: (data) => terminalSessions.sendInput(s.repoName, s.branch, s.worktreePath, s.id, data),
|
|
28358
|
+
onResize: (cols, rows) => terminalSessions.resizeSession(s.repoName, s.branch, s.worktreePath, s.id, cols, rows),
|
|
28359
|
+
registerListener: terminalSessions.registerListener,
|
|
28360
|
+
unregisterListener: terminalSessions.unregisterListener
|
|
28361
|
+
})
|
|
28362
|
+
});
|
|
28363
|
+
}
|
|
28364
|
+
return base2;
|
|
28365
|
+
}, [sessionsForSelected, terminalFocused, activeTabId, terminalSessions]);
|
|
27645
28366
|
const { repoVerbs, rowVerbs } = useMemo(() => {
|
|
27646
28367
|
const rv = new Map;
|
|
27647
28368
|
const nv = new Map;
|
|
@@ -27780,7 +28501,7 @@ function App({ opts }) {
|
|
|
27780
28501
|
args.push("--force");
|
|
27781
28502
|
executeOp(op, args, [repoName]);
|
|
27782
28503
|
};
|
|
27783
|
-
const handleHintClick =
|
|
28504
|
+
const handleHintClick = useCallback6((key) => {
|
|
27784
28505
|
if (key === "c") {
|
|
27785
28506
|
setConfigOpen(true);
|
|
27786
28507
|
return;
|
|
@@ -27932,8 +28653,72 @@ function App({ opts }) {
|
|
|
27932
28653
|
setModal({ type: "confirm_sync", rows: targets });
|
|
27933
28654
|
return;
|
|
27934
28655
|
}
|
|
27935
|
-
|
|
28656
|
+
if (key === "t") {
|
|
28657
|
+
if (!selectedRow)
|
|
28658
|
+
return;
|
|
28659
|
+
const sessions = terminalSessions.getSessions(selectedRow.repoName, selectedRow.branch, selectedRow.path);
|
|
28660
|
+
if (sessions.length >= MAX_TERMINAL_SESSIONS) {
|
|
28661
|
+
flash(`Max ${MAX_TERMINAL_SESSIONS} sessions per worktree`);
|
|
28662
|
+
return;
|
|
28663
|
+
}
|
|
28664
|
+
handleAddSession();
|
|
28665
|
+
return;
|
|
28666
|
+
}
|
|
28667
|
+
}, [selectedRow, selection, warnings, busyRepos, busyRowPaths, doRefresh, flash, blocks, terminalSessions]);
|
|
27936
28668
|
useKeyboard3((key) => {
|
|
28669
|
+
if (terminalFocused) {
|
|
28670
|
+
if (key.ctrl && key.name === "g") {
|
|
28671
|
+
setTerminalFocused(false);
|
|
28672
|
+
return;
|
|
28673
|
+
}
|
|
28674
|
+
const activeSession = sessionsForSelected.find((s) => s.id === activeTabId);
|
|
28675
|
+
if (activeSession) {
|
|
28676
|
+
let data = null;
|
|
28677
|
+
const seq = key.sequence;
|
|
28678
|
+
const raw = key.raw;
|
|
28679
|
+
if (key.ctrl && key.name && key.name.length === 1) {
|
|
28680
|
+
const code = key.name.charCodeAt(0) - 96;
|
|
28681
|
+
if (code >= 1 && code <= 26)
|
|
28682
|
+
data = String.fromCharCode(code);
|
|
28683
|
+
else if (seq)
|
|
28684
|
+
data = seq;
|
|
28685
|
+
else if (raw)
|
|
28686
|
+
data = raw;
|
|
28687
|
+
} else if (seq) {
|
|
28688
|
+
data = seq;
|
|
28689
|
+
} else if (raw) {
|
|
28690
|
+
data = raw;
|
|
28691
|
+
} else if (key.name === "return" || key.name === "enter") {
|
|
28692
|
+
data = "\r";
|
|
28693
|
+
} else if (key.name === "backspace") {
|
|
28694
|
+
data = "";
|
|
28695
|
+
} else if (key.name === "tab") {
|
|
28696
|
+
data = "\t";
|
|
28697
|
+
} else if (key.name === "escape") {
|
|
28698
|
+
data = "\x1B";
|
|
28699
|
+
} else if (key.name && key.name.length === 1) {
|
|
28700
|
+
data = key.name;
|
|
28701
|
+
}
|
|
28702
|
+
if (data !== null) {
|
|
28703
|
+
terminalSessions.sendInput(activeSession.repoName, activeSession.branch, activeSession.worktreePath, activeSession.id, data);
|
|
28704
|
+
}
|
|
28705
|
+
}
|
|
28706
|
+
return;
|
|
28707
|
+
}
|
|
28708
|
+
if (key.ctrl && key.name === "g") {
|
|
28709
|
+
if (sessionsForSelected.length > 0) {
|
|
28710
|
+
const targetId = activeTabId !== "details" ? activeTabId : sessionsForSelected[0].id;
|
|
28711
|
+
if (worktreeKey) {
|
|
28712
|
+
setActiveTabByWorktree((prev) => {
|
|
28713
|
+
const next = new Map(prev);
|
|
28714
|
+
next.set(worktreeKey, targetId);
|
|
28715
|
+
return next;
|
|
28716
|
+
});
|
|
28717
|
+
}
|
|
28718
|
+
setTerminalFocused(true);
|
|
28719
|
+
return;
|
|
28720
|
+
}
|
|
28721
|
+
}
|
|
27937
28722
|
if (isFiltering) {
|
|
27938
28723
|
if (key.name === "escape") {
|
|
27939
28724
|
setIsFiltering(false);
|
|
@@ -28245,108 +29030,106 @@ function App({ opts }) {
|
|
|
28245
29030
|
setModal({ type: "confirm_sync", rows: targets });
|
|
28246
29031
|
return;
|
|
28247
29032
|
}
|
|
28248
|
-
if (key.name === "
|
|
28249
|
-
|
|
28250
|
-
|
|
28251
|
-
return;
|
|
28252
|
-
const target = targets[0];
|
|
28253
|
-
if (!target)
|
|
29033
|
+
if (key.name === "t") {
|
|
29034
|
+
if (!selectedRow) {
|
|
29035
|
+
flash("No worktree selected");
|
|
28254
29036
|
return;
|
|
28255
|
-
|
|
28256
|
-
|
|
28257
|
-
|
|
29037
|
+
}
|
|
29038
|
+
const sessions = terminalSessions.getSessions(selectedRow.repoName, selectedRow.branch, selectedRow.path);
|
|
29039
|
+
if (sessions.length >= MAX_TERMINAL_SESSIONS) {
|
|
29040
|
+
flash(`Max ${MAX_TERMINAL_SESSIONS} sessions per worktree`);
|
|
28258
29041
|
return;
|
|
28259
29042
|
}
|
|
28260
|
-
(
|
|
28261
|
-
|
|
28262
|
-
try {
|
|
28263
|
-
const config2 = await loadConfig();
|
|
28264
|
-
const cmdTemplate = resolveAgentCommand("claude", config2.agents) ?? "claude";
|
|
28265
|
-
const result = await spawnAgentInWorktree(cmdTemplate, target.path, { repoName: target.repoName, branch: target.branch });
|
|
28266
|
-
appendHistory({
|
|
28267
|
-
ts: new Date().toISOString(),
|
|
28268
|
-
source: "terminal",
|
|
28269
|
-
command: "agent",
|
|
28270
|
-
args: ["agent", target.branch, "--repo", target.repoName],
|
|
28271
|
-
durationMs: Date.now() - startedAt,
|
|
28272
|
-
exit: 0
|
|
28273
|
-
});
|
|
28274
|
-
flash(`Agent spawned (${result.mode}${result.session ? ` session ${result.session}` : ""})`, 5000);
|
|
28275
|
-
} catch (e) {
|
|
28276
|
-
appendHistory({
|
|
28277
|
-
ts: new Date().toISOString(),
|
|
28278
|
-
source: "terminal",
|
|
28279
|
-
command: "agent",
|
|
28280
|
-
args: ["agent", target.branch, "--repo", target.repoName],
|
|
28281
|
-
durationMs: Date.now() - startedAt,
|
|
28282
|
-
exit: 1
|
|
28283
|
-
});
|
|
28284
|
-
flash(`Agent failed: ${e.message}`, 5000);
|
|
28285
|
-
}
|
|
28286
|
-
})();
|
|
29043
|
+
handleAddSession();
|
|
29044
|
+
return;
|
|
28287
29045
|
}
|
|
28288
29046
|
});
|
|
28289
29047
|
const rawLeft = Math.floor(totalWidth * splitRatio);
|
|
28290
29048
|
const leftCols = Math.max(20, Math.min(totalWidth - 20 - DIVIDER_WIDTH, rawLeft));
|
|
28291
29049
|
const rightCols = Math.max(20, totalWidth - leftCols - DIVIDER_WIDTH);
|
|
28292
|
-
|
|
29050
|
+
const paneCols = Math.max(20, rightCols - 4);
|
|
29051
|
+
const paneRows = Math.max(10, totalHeight - 10);
|
|
29052
|
+
useEffect8(() => {
|
|
29053
|
+
for (const sessions of terminalSessions.sessionsByKey.values()) {
|
|
29054
|
+
for (const s of sessions) {
|
|
29055
|
+
if (s.usePty && s.terminal && (s.cols !== paneCols || s.rows !== paneRows)) {
|
|
29056
|
+
terminalSessions.resizeSession(s.repoName, s.branch, s.worktreePath, s.id, paneCols, paneRows);
|
|
29057
|
+
}
|
|
29058
|
+
}
|
|
29059
|
+
}
|
|
29060
|
+
}, [paneCols, paneRows, terminalSessions]);
|
|
29061
|
+
useEffect8(() => {
|
|
29062
|
+
setTerminalFocused(false);
|
|
29063
|
+
}, [selectedIndex]);
|
|
29064
|
+
return /* @__PURE__ */ jsxs15("box", {
|
|
28293
29065
|
flexDirection: "column",
|
|
28294
29066
|
width: "100%",
|
|
28295
29067
|
height: "100%",
|
|
28296
29068
|
children: [
|
|
28297
|
-
/* @__PURE__ */
|
|
29069
|
+
/* @__PURE__ */ jsxs15("box", {
|
|
28298
29070
|
flexDirection: "row",
|
|
28299
29071
|
width: "100%",
|
|
28300
29072
|
flexGrow: 1,
|
|
28301
29073
|
children: [
|
|
28302
|
-
/* @__PURE__ */
|
|
29074
|
+
/* @__PURE__ */ jsx17("box", {
|
|
28303
29075
|
width: leftCols,
|
|
28304
29076
|
height: "100%",
|
|
28305
29077
|
flexDirection: "column",
|
|
28306
|
-
|
|
29078
|
+
onMouseDown: () => setTerminalFocused(false),
|
|
29079
|
+
children: /* @__PURE__ */ jsx17(WorktreeTable, {
|
|
28307
29080
|
blocks: displayBlocks,
|
|
28308
29081
|
selectedIndex,
|
|
28309
29082
|
selection,
|
|
28310
29083
|
frame: spinnerFrame,
|
|
28311
29084
|
repoVerbs,
|
|
28312
29085
|
rowVerbs,
|
|
28313
|
-
onRowClick: (idx) =>
|
|
29086
|
+
onRowClick: (idx) => {
|
|
29087
|
+
setTerminalFocused(false);
|
|
29088
|
+
setSelectedIndex(idx);
|
|
29089
|
+
},
|
|
28314
29090
|
onToggleSelect: (path37) => setSelection((prev) => toggleSelection(prev, path37))
|
|
28315
29091
|
})
|
|
28316
29092
|
}),
|
|
28317
|
-
/* @__PURE__ */
|
|
29093
|
+
/* @__PURE__ */ jsx17(Divider, {
|
|
28318
29094
|
splitRatio,
|
|
28319
29095
|
totalWidth,
|
|
28320
29096
|
onChange: setSplitRatio,
|
|
28321
29097
|
onDraggingChange: setIsResizing
|
|
28322
29098
|
}),
|
|
28323
|
-
/* @__PURE__ */
|
|
29099
|
+
/* @__PURE__ */ jsx17("box", {
|
|
28324
29100
|
width: rightCols,
|
|
28325
29101
|
height: "100%",
|
|
28326
29102
|
flexDirection: "column",
|
|
28327
|
-
children: /* @__PURE__ */
|
|
28328
|
-
selectedRow
|
|
29103
|
+
children: /* @__PURE__ */ jsx17(TabPane, {
|
|
29104
|
+
selectedRow,
|
|
29105
|
+
tabs: tabsForSelected,
|
|
29106
|
+
activeId: activeTabId,
|
|
29107
|
+
focused: terminalFocused && tabsForSelected.some((t) => t.id === activeTabId && t.id !== "details"),
|
|
29108
|
+
canAdd: sessionsForSelected.length < MAX_TERMINAL_SESSIONS,
|
|
29109
|
+
onSelect: handleSelectTab,
|
|
29110
|
+
onAdd: handleAddSession,
|
|
29111
|
+
onClose: handleCloseSession
|
|
28329
29112
|
})
|
|
28330
29113
|
})
|
|
28331
29114
|
]
|
|
28332
29115
|
}),
|
|
28333
|
-
isFiltering && /* @__PURE__ */
|
|
29116
|
+
isFiltering && /* @__PURE__ */ jsxs15("box", {
|
|
28334
29117
|
flexDirection: "row",
|
|
28335
29118
|
paddingX: 1,
|
|
28336
29119
|
border: true,
|
|
28337
29120
|
borderColor: "magenta",
|
|
28338
29121
|
children: [
|
|
28339
|
-
/* @__PURE__ */
|
|
29122
|
+
/* @__PURE__ */ jsx17("text", {
|
|
28340
29123
|
children: "filter: "
|
|
28341
29124
|
}),
|
|
28342
|
-
/* @__PURE__ */
|
|
29125
|
+
/* @__PURE__ */ jsx17("input", {
|
|
28343
29126
|
focused: true,
|
|
28344
29127
|
placeholder: "Type to filter...",
|
|
28345
29128
|
onInput: (v) => setFilterText(v)
|
|
28346
29129
|
})
|
|
28347
29130
|
]
|
|
28348
29131
|
}),
|
|
28349
|
-
/* @__PURE__ */
|
|
29132
|
+
/* @__PURE__ */ jsx17(Footer, {
|
|
28350
29133
|
loading: loading || refreshing,
|
|
28351
29134
|
lastRefreshed,
|
|
28352
29135
|
errorCount: warnings.length,
|
|
@@ -28357,9 +29140,9 @@ function App({ opts }) {
|
|
|
28357
29140
|
onHintClick: handleHintClick,
|
|
28358
29141
|
onErrorClick: () => setModal({ type: "warnings" })
|
|
28359
29142
|
}),
|
|
28360
|
-
modal.type === "help" && /* @__PURE__ */
|
|
28361
|
-
modal.type === "history" && /* @__PURE__ */
|
|
28362
|
-
modal.type === "warnings" && /* @__PURE__ */
|
|
29143
|
+
modal.type === "help" && /* @__PURE__ */ jsx17(HelpOverlay, {}),
|
|
29144
|
+
modal.type === "history" && /* @__PURE__ */ jsx17(HistoryOverlay, {}),
|
|
29145
|
+
modal.type === "warnings" && /* @__PURE__ */ jsx17(WarningsOverlay, {
|
|
28363
29146
|
warnings,
|
|
28364
29147
|
onAcknowledge: () => {
|
|
28365
29148
|
clearWarnings();
|
|
@@ -28367,7 +29150,7 @@ function App({ opts }) {
|
|
|
28367
29150
|
},
|
|
28368
29151
|
onClose: () => setModal({ type: "none" })
|
|
28369
29152
|
}),
|
|
28370
|
-
modal.type === "error" && /* @__PURE__ */
|
|
29153
|
+
modal.type === "error" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28371
29154
|
title: "Error",
|
|
28372
29155
|
message: modal.message,
|
|
28373
29156
|
onConfirm: () => {
|
|
@@ -28385,7 +29168,7 @@ function App({ opts }) {
|
|
|
28385
29168
|
}
|
|
28386
29169
|
}
|
|
28387
29170
|
}),
|
|
28388
|
-
modal.type === "confirm_remove" && /* @__PURE__ */
|
|
29171
|
+
modal.type === "confirm_remove" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28389
29172
|
title: `Remove ${modal.rows.length} Worktree(s)`,
|
|
28390
29173
|
message: (() => {
|
|
28391
29174
|
const count2 = modal.rows.length;
|
|
@@ -28414,7 +29197,7 @@ function App({ opts }) {
|
|
|
28414
29197
|
},
|
|
28415
29198
|
onCancel: () => setModal({ type: "none" })
|
|
28416
29199
|
}),
|
|
28417
|
-
modal.type === "confirm_rebase" && /* @__PURE__ */
|
|
29200
|
+
modal.type === "confirm_rebase" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28418
29201
|
title: `Rebase ${modal.rows.length} Worktree(s)`,
|
|
28419
29202
|
message: `Are you sure you want to fetch and rebase ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`,
|
|
28420
29203
|
onConfirm: () => {
|
|
@@ -28424,7 +29207,7 @@ function App({ opts }) {
|
|
|
28424
29207
|
},
|
|
28425
29208
|
onCancel: () => setModal({ type: "none" })
|
|
28426
29209
|
}),
|
|
28427
|
-
modal.type === "confirm_sync" && /* @__PURE__ */
|
|
29210
|
+
modal.type === "confirm_sync" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28428
29211
|
title: `Sync ${modal.rows.length} Worktree(s)`,
|
|
28429
29212
|
message: `Are you sure you want to sync ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`,
|
|
28430
29213
|
onConfirm: () => {
|
|
@@ -28434,7 +29217,7 @@ function App({ opts }) {
|
|
|
28434
29217
|
},
|
|
28435
29218
|
onCancel: () => setModal({ type: "none" })
|
|
28436
29219
|
}),
|
|
28437
|
-
createModal && /* @__PURE__ */
|
|
29220
|
+
createModal && /* @__PURE__ */ jsx17(InputModal, {
|
|
28438
29221
|
title: `New worktree branch in ${selectedRow?.repoName ?? ""}`,
|
|
28439
29222
|
placeholder: "Branch name (empty to cancel)",
|
|
28440
29223
|
errorMessage: createError,
|
|
@@ -28461,7 +29244,7 @@ function App({ opts }) {
|
|
|
28461
29244
|
setCreateBaseModal({ branch, repoName });
|
|
28462
29245
|
}
|
|
28463
29246
|
}),
|
|
28464
|
-
createBaseModal && /* @__PURE__ */
|
|
29247
|
+
createBaseModal && /* @__PURE__ */ jsx17(InputModal, {
|
|
28465
29248
|
title: `Base ref for ${createBaseModal.branch}`,
|
|
28466
29249
|
placeholder: "origin/main (empty for default main)",
|
|
28467
29250
|
errorMessage: createBaseError,
|
|
@@ -28477,7 +29260,7 @@ function App({ opts }) {
|
|
|
28477
29260
|
setCreateDepsChoice({ branch, repoName, base: base2 || undefined });
|
|
28478
29261
|
}
|
|
28479
29262
|
}),
|
|
28480
|
-
createDepsChoice && /* @__PURE__ */
|
|
29263
|
+
createDepsChoice && /* @__PURE__ */ jsx17(ChoiceModal, {
|
|
28481
29264
|
title: `Dependencies for ${createDepsChoice.branch}`,
|
|
28482
29265
|
options: DEPS_CHOICES,
|
|
28483
29266
|
onSubmit: (choice) => {
|
|
@@ -28487,7 +29270,7 @@ function App({ opts }) {
|
|
|
28487
29270
|
},
|
|
28488
29271
|
onCancel: () => setCreateDepsChoice(null)
|
|
28489
29272
|
}),
|
|
28490
|
-
pullPrModal && /* @__PURE__ */
|
|
29273
|
+
pullPrModal && /* @__PURE__ */ jsx17(InputModal, {
|
|
28491
29274
|
title: `Pull PR into ${selectedRow?.repoName ?? ""}`,
|
|
28492
29275
|
placeholder: "https://github.com/owner/repo/pull/123",
|
|
28493
29276
|
errorMessage: pullPrError,
|
|
@@ -28515,7 +29298,7 @@ function App({ opts }) {
|
|
|
28515
29298
|
setPullForceChoice({ link, repoName });
|
|
28516
29299
|
}
|
|
28517
29300
|
}),
|
|
28518
|
-
pullForceChoice && /* @__PURE__ */
|
|
29301
|
+
pullForceChoice && /* @__PURE__ */ jsx17(ChoiceModal, {
|
|
28519
29302
|
title: `Pull ${pullForceChoice.link.split("/").pop()}?`,
|
|
28520
29303
|
options: [
|
|
28521
29304
|
{ value: "normal", label: "Pull (skip if exists)", desc: "Fails if branch already exists" },
|
|
@@ -28528,7 +29311,7 @@ function App({ opts }) {
|
|
|
28528
29311
|
},
|
|
28529
29312
|
onCancel: () => setPullForceChoice(null)
|
|
28530
29313
|
}),
|
|
28531
|
-
renameModal && selectedRow && /* @__PURE__ */
|
|
29314
|
+
renameModal && selectedRow && /* @__PURE__ */ jsx17(InputModal, {
|
|
28532
29315
|
title: `Rename branch ${selectedRow.branch}`,
|
|
28533
29316
|
initialValue: selectedRow.branch,
|
|
28534
29317
|
placeholder: "New branch name",
|
|
@@ -28553,7 +29336,7 @@ function App({ opts }) {
|
|
|
28553
29336
|
setModal({ type: "confirm_rename", row: target, to });
|
|
28554
29337
|
}
|
|
28555
29338
|
}),
|
|
28556
|
-
modal.type === "confirm_rename" && /* @__PURE__ */
|
|
29339
|
+
modal.type === "confirm_rename" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28557
29340
|
title: "Rename Worktree",
|
|
28558
29341
|
message: [
|
|
28559
29342
|
`${modal.row.branch} → ${modal.to}`,
|
|
@@ -28584,12 +29367,12 @@ function App({ opts }) {
|
|
|
28584
29367
|
},
|
|
28585
29368
|
onCancel: () => setModal({ type: "none" })
|
|
28586
29369
|
}),
|
|
28587
|
-
configOpen && /* @__PURE__ */
|
|
29370
|
+
configOpen && /* @__PURE__ */ jsx17(ConfigOverlay, {
|
|
28588
29371
|
onClose: () => setConfigOpen(false),
|
|
28589
29372
|
onSaved: () => void doRefresh(),
|
|
28590
29373
|
onError: (msg) => setModal({ type: "error", message: msg })
|
|
28591
29374
|
}),
|
|
28592
|
-
failedLogs.length > 0 && /* @__PURE__ */
|
|
29375
|
+
failedLogs.length > 0 && /* @__PURE__ */ jsx17(ActionLogModal, {
|
|
28593
29376
|
title: failedLogs[0].title,
|
|
28594
29377
|
lines: failedLogs[0].lines,
|
|
28595
29378
|
done: true,
|
|
@@ -28603,7 +29386,9 @@ var VERBS, capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1), DEPS_CHOI
|
|
|
28603
29386
|
var init_App = __esm(() => {
|
|
28604
29387
|
init_useWorktrees();
|
|
28605
29388
|
init_WorktreeTable();
|
|
28606
|
-
|
|
29389
|
+
init_TabPane();
|
|
29390
|
+
init_DetailsTab();
|
|
29391
|
+
init_TerminalView();
|
|
28607
29392
|
init_Footer();
|
|
28608
29393
|
init_Divider();
|
|
28609
29394
|
init_HelpOverlay();
|
|
@@ -28616,10 +29401,9 @@ var init_App = __esm(() => {
|
|
|
28616
29401
|
init_ConfigOverlay();
|
|
28617
29402
|
init_WarningsOverlay();
|
|
28618
29403
|
init_utils();
|
|
28619
|
-
init_agents();
|
|
28620
29404
|
init_config();
|
|
28621
|
-
init_history();
|
|
28622
29405
|
init_useSpinnerFrame();
|
|
29406
|
+
init_useTerminalSessions();
|
|
28623
29407
|
VERBS = {
|
|
28624
29408
|
create: "creating worktree",
|
|
28625
29409
|
remove: "deleting",
|
|
@@ -28643,16 +29427,16 @@ var exports_tui = {};
|
|
|
28643
29427
|
__export(exports_tui, {
|
|
28644
29428
|
runTerminal: () => runTerminal
|
|
28645
29429
|
});
|
|
28646
|
-
import { createCliRenderer, TextTableRenderable } from "@opentui/core";
|
|
29430
|
+
import { createCliRenderer, TextTableRenderable, EmbeddedTerminalRenderable } from "@opentui/core";
|
|
28647
29431
|
import { createRoot, extend as extend2 } from "@opentui/react";
|
|
28648
|
-
import { jsx as
|
|
29432
|
+
import { jsx as jsx18 } from "@opentui/react/jsx-runtime";
|
|
28649
29433
|
async function runTerminal(opts) {
|
|
28650
29434
|
const renderer = await createCliRenderer({
|
|
28651
29435
|
exitOnCtrlC: false
|
|
28652
29436
|
});
|
|
28653
29437
|
const root = createRoot(renderer);
|
|
28654
29438
|
try {
|
|
28655
|
-
root.render(/* @__PURE__ */
|
|
29439
|
+
root.render(/* @__PURE__ */ jsx18(App, {
|
|
28656
29440
|
opts
|
|
28657
29441
|
}));
|
|
28658
29442
|
} catch (err) {
|
|
@@ -28663,7 +29447,7 @@ async function runTerminal(opts) {
|
|
|
28663
29447
|
}
|
|
28664
29448
|
var init_tui = __esm(() => {
|
|
28665
29449
|
init_App();
|
|
28666
|
-
extend2({ "text-table": TextTableRenderable });
|
|
29450
|
+
extend2({ "text-table": TextTableRenderable, "embedded-terminal": EmbeddedTerminalRenderable });
|
|
28667
29451
|
});
|
|
28668
29452
|
|
|
28669
29453
|
// node_modules/commander/lib/error.js
|
|
@@ -31002,7 +31786,80 @@ async function runPostCreateSetup(params) {
|
|
|
31002
31786
|
init_deps();
|
|
31003
31787
|
init_forge();
|
|
31004
31788
|
init_owner();
|
|
31005
|
-
|
|
31789
|
+
|
|
31790
|
+
// src/lib/agents.ts
|
|
31791
|
+
init_execa();
|
|
31792
|
+
var DEFAULT_AGENTS = {
|
|
31793
|
+
claude: "claude",
|
|
31794
|
+
codex: "codex",
|
|
31795
|
+
opencode: "opencode",
|
|
31796
|
+
cursor: "cursor"
|
|
31797
|
+
};
|
|
31798
|
+
function resolveAgentCommand(name, configured) {
|
|
31799
|
+
return configured?.[name]?.command ?? DEFAULT_AGENTS[name] ?? null;
|
|
31800
|
+
}
|
|
31801
|
+
function listAvailableAgents(configured) {
|
|
31802
|
+
return [...new Set([...Object.keys(DEFAULT_AGENTS), ...Object.keys(configured ?? {})])];
|
|
31803
|
+
}
|
|
31804
|
+
var tmuxAvailable = undefined;
|
|
31805
|
+
async function isTmuxAvailable() {
|
|
31806
|
+
if (tmuxAvailable !== undefined)
|
|
31807
|
+
return tmuxAvailable;
|
|
31808
|
+
try {
|
|
31809
|
+
await execa("tmux", ["-V"]);
|
|
31810
|
+
tmuxAvailable = true;
|
|
31811
|
+
} catch {
|
|
31812
|
+
tmuxAvailable = false;
|
|
31813
|
+
}
|
|
31814
|
+
return tmuxAvailable;
|
|
31815
|
+
}
|
|
31816
|
+
function tmuxSessionName(repoName, branch) {
|
|
31817
|
+
const sanitized = `${repoName}-${branch}`.replace(/[\/.:]/g, "-");
|
|
31818
|
+
return `wtx-${sanitized}`.substring(0, 60);
|
|
31819
|
+
}
|
|
31820
|
+
function buildTmuxArgs(session, wtPath, cmd) {
|
|
31821
|
+
return ["new-session", "-d", "-s", session, "-c", wtPath, cmd];
|
|
31822
|
+
}
|
|
31823
|
+
async function spawnAgentInWorktree(commandTemplate, wtPath, opts = {}) {
|
|
31824
|
+
const cmd = buildAgentCommand(commandTemplate, wtPath, opts.prompt, {
|
|
31825
|
+
branch: opts.branch,
|
|
31826
|
+
repo: opts.repoName
|
|
31827
|
+
});
|
|
31828
|
+
const session = opts.repoName && opts.branch ? tmuxSessionName(opts.repoName, opts.branch) : undefined;
|
|
31829
|
+
if (opts.dryRun) {
|
|
31830
|
+
if (session && await isTmuxAvailable()) {
|
|
31831
|
+
return { mode: "tmux", session };
|
|
31832
|
+
}
|
|
31833
|
+
return { mode: "direct" };
|
|
31834
|
+
}
|
|
31835
|
+
if (session && await isTmuxAvailable()) {
|
|
31836
|
+
try {
|
|
31837
|
+
const args = buildTmuxArgs(session, wtPath, cmd);
|
|
31838
|
+
await execa("tmux", args, { stdio: "inherit" });
|
|
31839
|
+
return { mode: "tmux", session };
|
|
31840
|
+
} catch {}
|
|
31841
|
+
}
|
|
31842
|
+
await execa(cmd, { shell: true, cwd: wtPath, stdio: "inherit" });
|
|
31843
|
+
return { mode: "direct" };
|
|
31844
|
+
}
|
|
31845
|
+
function shellQuote(value) {
|
|
31846
|
+
return `'${value.replaceAll("'", `'''`)}'`;
|
|
31847
|
+
}
|
|
31848
|
+
function buildAgentCommand(commandTemplate, wtPath, prompt, vars) {
|
|
31849
|
+
let cmd = commandTemplate.replaceAll("{wt}", shellQuote(wtPath));
|
|
31850
|
+
if (vars?.branch) {
|
|
31851
|
+
cmd = cmd.replaceAll("{branch}", shellQuote(vars.branch));
|
|
31852
|
+
}
|
|
31853
|
+
if (vars?.repo) {
|
|
31854
|
+
cmd = cmd.replaceAll("{repo}", shellQuote(vars.repo));
|
|
31855
|
+
}
|
|
31856
|
+
if (prompt) {
|
|
31857
|
+
cmd += ` ${shellQuote(prompt)}`;
|
|
31858
|
+
}
|
|
31859
|
+
return cmd;
|
|
31860
|
+
}
|
|
31861
|
+
|
|
31862
|
+
// src/commands/create.ts
|
|
31006
31863
|
var DEPS_STRATEGIES = ["auto", "link", "symlink", "install", "off"];
|
|
31007
31864
|
async function applyDepsStrategy(strategy, wtPath, mainPath, opts) {
|
|
31008
31865
|
if (strategy === "install") {
|