@ogpoyraz/wtx 0.8.7 → 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 +1355 -424
- 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);
|
|
@@ -25561,10 +25492,11 @@ function useWorktrees(opts) {
|
|
|
25561
25492
|
}
|
|
25562
25493
|
}
|
|
25563
25494
|
}, [opts]);
|
|
25495
|
+
const clearWarnings = useCallback(() => setWarnings([]), []);
|
|
25564
25496
|
useEffect(() => {
|
|
25565
25497
|
refresh();
|
|
25566
25498
|
}, [refresh]);
|
|
25567
|
-
return { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh };
|
|
25499
|
+
return { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh, clearWarnings };
|
|
25568
25500
|
}
|
|
25569
25501
|
var init_useWorktrees = __esm(() => {
|
|
25570
25502
|
init_data();
|
|
@@ -25865,6 +25797,8 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
|
|
|
25865
25797
|
height: "100%",
|
|
25866
25798
|
border: true,
|
|
25867
25799
|
borderColor: tokens.border,
|
|
25800
|
+
focusedBorderColor: tokens.border,
|
|
25801
|
+
focusable: false,
|
|
25868
25802
|
title: "Worktrees",
|
|
25869
25803
|
paddingX: 1,
|
|
25870
25804
|
focused: false,
|
|
@@ -25932,26 +25866,133 @@ var init_WorktreeTable = __esm(() => {
|
|
|
25932
25866
|
init_use_tap();
|
|
25933
25867
|
});
|
|
25934
25868
|
|
|
25935
|
-
// src/tui/
|
|
25936
|
-
import { jsx as jsx2, jsxs as jsxs2
|
|
25937
|
-
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 }) {
|
|
25938
25984
|
const prTap = useTapHandler(() => {
|
|
25939
25985
|
if (selectedRow?.prUrl)
|
|
25940
25986
|
openInBrowser(selectedRow.prUrl);
|
|
25941
25987
|
});
|
|
25942
25988
|
if (!selectedRow) {
|
|
25943
|
-
return /* @__PURE__ */
|
|
25944
|
-
id: "detail-pane",
|
|
25989
|
+
return /* @__PURE__ */ jsx4("box", {
|
|
25945
25990
|
flexGrow: 1,
|
|
25946
25991
|
width: "100%",
|
|
25947
25992
|
height: "100%",
|
|
25948
|
-
border: true,
|
|
25949
|
-
borderColor: tokens.border,
|
|
25950
|
-
title: "Details",
|
|
25951
|
-
padding: 1,
|
|
25952
25993
|
justifyContent: "center",
|
|
25953
25994
|
alignItems: "center",
|
|
25954
|
-
children: /* @__PURE__ */
|
|
25995
|
+
children: /* @__PURE__ */ jsx4("text", {
|
|
25955
25996
|
fg: tokens.dim,
|
|
25956
25997
|
children: "No worktree selected"
|
|
25957
25998
|
})
|
|
@@ -25979,24 +26020,20 @@ function DetailPane({ selectedRow }) {
|
|
|
25979
26020
|
baseChanged
|
|
25980
26021
|
} = selectedRow;
|
|
25981
26022
|
const aheadBehindStr = ahead !== null && behind !== null ? `${ahead} ahead, ${behind} behind` : "unknown";
|
|
25982
|
-
return /* @__PURE__ */
|
|
25983
|
-
id: "detail-pane",
|
|
26023
|
+
return /* @__PURE__ */ jsxs4("scrollbox", {
|
|
25984
26024
|
flexGrow: 1,
|
|
25985
26025
|
width: "100%",
|
|
25986
26026
|
height: "100%",
|
|
25987
|
-
border: true,
|
|
25988
|
-
borderColor: tokens.border,
|
|
25989
|
-
title: "Details",
|
|
25990
|
-
padding: 1,
|
|
25991
26027
|
focused: false,
|
|
26028
|
+
padding: 1,
|
|
25992
26029
|
children: [
|
|
25993
|
-
/* @__PURE__ */
|
|
26030
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
25994
26031
|
children: [
|
|
25995
|
-
/* @__PURE__ */
|
|
26032
|
+
/* @__PURE__ */ jsx4("span", {
|
|
25996
26033
|
fg: tokens.dim,
|
|
25997
26034
|
children: "Repo:"
|
|
25998
26035
|
}),
|
|
25999
|
-
/* @__PURE__ */
|
|
26036
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26000
26037
|
fg: tokens.fg,
|
|
26001
26038
|
children: [
|
|
26002
26039
|
" ",
|
|
@@ -26005,13 +26042,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26005
26042
|
})
|
|
26006
26043
|
]
|
|
26007
26044
|
}),
|
|
26008
|
-
/* @__PURE__ */
|
|
26045
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26009
26046
|
children: [
|
|
26010
|
-
/* @__PURE__ */
|
|
26047
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26011
26048
|
fg: tokens.dim,
|
|
26012
26049
|
children: "Branch:"
|
|
26013
26050
|
}),
|
|
26014
|
-
/* @__PURE__ */
|
|
26051
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26015
26052
|
fg: tokens.fg,
|
|
26016
26053
|
children: [
|
|
26017
26054
|
" ",
|
|
@@ -26022,13 +26059,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26022
26059
|
})
|
|
26023
26060
|
]
|
|
26024
26061
|
}),
|
|
26025
|
-
/* @__PURE__ */
|
|
26062
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26026
26063
|
children: [
|
|
26027
|
-
/* @__PURE__ */
|
|
26064
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26028
26065
|
fg: tokens.dim,
|
|
26029
26066
|
children: "Path:"
|
|
26030
26067
|
}),
|
|
26031
|
-
/* @__PURE__ */
|
|
26068
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26032
26069
|
fg: tokens.fg,
|
|
26033
26070
|
children: [
|
|
26034
26071
|
" ",
|
|
@@ -26037,13 +26074,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26037
26074
|
})
|
|
26038
26075
|
]
|
|
26039
26076
|
}),
|
|
26040
|
-
/* @__PURE__ */
|
|
26077
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26041
26078
|
children: [
|
|
26042
|
-
/* @__PURE__ */
|
|
26079
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26043
26080
|
fg: tokens.dim,
|
|
26044
26081
|
children: "Commit:"
|
|
26045
26082
|
}),
|
|
26046
|
-
/* @__PURE__ */
|
|
26083
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26047
26084
|
fg: tokens.fg,
|
|
26048
26085
|
children: [
|
|
26049
26086
|
" ",
|
|
@@ -26052,13 +26089,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26052
26089
|
})
|
|
26053
26090
|
]
|
|
26054
26091
|
}),
|
|
26055
|
-
owner && /* @__PURE__ */
|
|
26092
|
+
owner && /* @__PURE__ */ jsxs4("text", {
|
|
26056
26093
|
children: [
|
|
26057
|
-
/* @__PURE__ */
|
|
26094
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26058
26095
|
fg: tokens.dim,
|
|
26059
26096
|
children: "Owner:"
|
|
26060
26097
|
}),
|
|
26061
|
-
/* @__PURE__ */
|
|
26098
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26062
26099
|
fg: tokens.fg,
|
|
26063
26100
|
children: [
|
|
26064
26101
|
" ",
|
|
@@ -26067,13 +26104,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26067
26104
|
})
|
|
26068
26105
|
]
|
|
26069
26106
|
}),
|
|
26070
|
-
/* @__PURE__ */
|
|
26107
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26071
26108
|
children: [
|
|
26072
|
-
/* @__PURE__ */
|
|
26109
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26073
26110
|
fg: tokens.dim,
|
|
26074
26111
|
children: "Status:"
|
|
26075
26112
|
}),
|
|
26076
|
-
/* @__PURE__ */
|
|
26113
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26077
26114
|
fg: tokens.fg,
|
|
26078
26115
|
children: [
|
|
26079
26116
|
" ",
|
|
@@ -26082,13 +26119,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26082
26119
|
})
|
|
26083
26120
|
]
|
|
26084
26121
|
}),
|
|
26085
|
-
base2 && /* @__PURE__ */
|
|
26122
|
+
base2 && /* @__PURE__ */ jsxs4("text", {
|
|
26086
26123
|
children: [
|
|
26087
|
-
/* @__PURE__ */
|
|
26124
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26088
26125
|
fg: tokens.dim,
|
|
26089
26126
|
children: "Base:"
|
|
26090
26127
|
}),
|
|
26091
|
-
/* @__PURE__ */
|
|
26128
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26092
26129
|
fg: tokens.accent,
|
|
26093
26130
|
children: [
|
|
26094
26131
|
" ",
|
|
@@ -26097,26 +26134,26 @@ function DetailPane({ selectedRow }) {
|
|
|
26097
26134
|
})
|
|
26098
26135
|
]
|
|
26099
26136
|
}),
|
|
26100
|
-
baseChanged && /* @__PURE__ */
|
|
26137
|
+
baseChanged && /* @__PURE__ */ jsxs4("text", {
|
|
26101
26138
|
children: [
|
|
26102
|
-
/* @__PURE__ */
|
|
26139
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26103
26140
|
fg: tokens.dim,
|
|
26104
26141
|
children: "Base state:"
|
|
26105
26142
|
}),
|
|
26106
|
-
/* @__PURE__ */
|
|
26143
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26107
26144
|
fg: tokens.warning,
|
|
26108
26145
|
children: " moved since recorded"
|
|
26109
26146
|
})
|
|
26110
26147
|
]
|
|
26111
26148
|
}),
|
|
26112
|
-
/* @__PURE__ */
|
|
26149
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26113
26150
|
style: { marginTop: 1 },
|
|
26114
26151
|
children: [
|
|
26115
|
-
/* @__PURE__ */
|
|
26152
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26116
26153
|
fg: tokens.dim,
|
|
26117
26154
|
children: base2 ? "vs base:" : "vs main:"
|
|
26118
26155
|
}),
|
|
26119
|
-
/* @__PURE__ */
|
|
26156
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26120
26157
|
fg: tokens.fg,
|
|
26121
26158
|
children: [
|
|
26122
26159
|
" ",
|
|
@@ -26125,13 +26162,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26125
26162
|
})
|
|
26126
26163
|
]
|
|
26127
26164
|
}),
|
|
26128
|
-
/* @__PURE__ */
|
|
26165
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26129
26166
|
children: [
|
|
26130
|
-
/* @__PURE__ */
|
|
26167
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26131
26168
|
fg: tokens.dim,
|
|
26132
26169
|
children: "Deps:"
|
|
26133
26170
|
}),
|
|
26134
|
-
/* @__PURE__ */
|
|
26171
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26135
26172
|
fg: tokens.fg,
|
|
26136
26173
|
children: [
|
|
26137
26174
|
" ",
|
|
@@ -26140,13 +26177,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26140
26177
|
})
|
|
26141
26178
|
]
|
|
26142
26179
|
}),
|
|
26143
|
-
rebaseStatus && /* @__PURE__ */
|
|
26180
|
+
rebaseStatus && /* @__PURE__ */ jsxs4("text", {
|
|
26144
26181
|
children: [
|
|
26145
|
-
/* @__PURE__ */
|
|
26182
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26146
26183
|
fg: tokens.dim,
|
|
26147
26184
|
children: "Rebase:"
|
|
26148
26185
|
}),
|
|
26149
|
-
/* @__PURE__ */
|
|
26186
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26150
26187
|
fg: tokens.error,
|
|
26151
26188
|
children: [
|
|
26152
26189
|
" ",
|
|
@@ -26155,12 +26192,12 @@ function DetailPane({ selectedRow }) {
|
|
|
26155
26192
|
})
|
|
26156
26193
|
]
|
|
26157
26194
|
}),
|
|
26158
|
-
prNumber !== null && /* @__PURE__ */
|
|
26195
|
+
prNumber !== null && /* @__PURE__ */ jsxs4(Fragment, {
|
|
26159
26196
|
children: [
|
|
26160
|
-
/* @__PURE__ */
|
|
26197
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26161
26198
|
style: { marginTop: 1 },
|
|
26162
26199
|
children: [
|
|
26163
|
-
/* @__PURE__ */
|
|
26200
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26164
26201
|
fg: tokens.accent,
|
|
26165
26202
|
children: [
|
|
26166
26203
|
"PR #",
|
|
@@ -26168,7 +26205,7 @@ function DetailPane({ selectedRow }) {
|
|
|
26168
26205
|
":"
|
|
26169
26206
|
]
|
|
26170
26207
|
}),
|
|
26171
|
-
/* @__PURE__ */
|
|
26208
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26172
26209
|
fg: tokens.dim,
|
|
26173
26210
|
children: [
|
|
26174
26211
|
" ",
|
|
@@ -26177,13 +26214,13 @@ function DetailPane({ selectedRow }) {
|
|
|
26177
26214
|
})
|
|
26178
26215
|
]
|
|
26179
26216
|
}),
|
|
26180
|
-
prChecks && /* @__PURE__ */
|
|
26217
|
+
prChecks && /* @__PURE__ */ jsxs4("text", {
|
|
26181
26218
|
children: [
|
|
26182
|
-
/* @__PURE__ */
|
|
26219
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26183
26220
|
fg: tokens.dim,
|
|
26184
26221
|
children: "Checks:"
|
|
26185
26222
|
}),
|
|
26186
|
-
/* @__PURE__ */
|
|
26223
|
+
/* @__PURE__ */ jsxs4("span", {
|
|
26187
26224
|
fg: tokens.fg,
|
|
26188
26225
|
children: [
|
|
26189
26226
|
" ",
|
|
@@ -26192,20 +26229,20 @@ function DetailPane({ selectedRow }) {
|
|
|
26192
26229
|
})
|
|
26193
26230
|
]
|
|
26194
26231
|
}),
|
|
26195
|
-
prUrl && /* @__PURE__ */
|
|
26232
|
+
prUrl && /* @__PURE__ */ jsx4("box", {
|
|
26196
26233
|
...prTap,
|
|
26197
|
-
children: /* @__PURE__ */
|
|
26234
|
+
children: /* @__PURE__ */ jsxs4("text", {
|
|
26198
26235
|
selectable: false,
|
|
26199
26236
|
children: [
|
|
26200
|
-
/* @__PURE__ */
|
|
26237
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26201
26238
|
fg: tokens.dim,
|
|
26202
26239
|
children: "URL:"
|
|
26203
26240
|
}),
|
|
26204
|
-
/* @__PURE__ */
|
|
26241
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26205
26242
|
fg: tokens.accent,
|
|
26206
26243
|
children: ` ${prUrl}`
|
|
26207
26244
|
}),
|
|
26208
|
-
/* @__PURE__ */
|
|
26245
|
+
/* @__PURE__ */ jsx4("span", {
|
|
26209
26246
|
fg: tokens.dim,
|
|
26210
26247
|
children: " ↗ click"
|
|
26211
26248
|
})
|
|
@@ -26214,11 +26251,11 @@ function DetailPane({ selectedRow }) {
|
|
|
26214
26251
|
})
|
|
26215
26252
|
]
|
|
26216
26253
|
}),
|
|
26217
|
-
dirtyFiles.length > 0 && /* @__PURE__ */
|
|
26254
|
+
dirtyFiles.length > 0 && /* @__PURE__ */ jsxs4("box", {
|
|
26218
26255
|
flexDirection: "column",
|
|
26219
26256
|
style: { marginTop: 1 },
|
|
26220
26257
|
children: [
|
|
26221
|
-
/* @__PURE__ */
|
|
26258
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26222
26259
|
fg: tokens.warning,
|
|
26223
26260
|
children: [
|
|
26224
26261
|
"Dirty Files (",
|
|
@@ -26226,14 +26263,14 @@ function DetailPane({ selectedRow }) {
|
|
|
26226
26263
|
"):"
|
|
26227
26264
|
]
|
|
26228
26265
|
}),
|
|
26229
|
-
dirtyFiles.slice(0, 20).map((file2, idx) => /* @__PURE__ */
|
|
26266
|
+
dirtyFiles.slice(0, 20).map((file2, idx) => /* @__PURE__ */ jsxs4("text", {
|
|
26230
26267
|
fg: tokens.warning,
|
|
26231
26268
|
children: [
|
|
26232
26269
|
" ",
|
|
26233
26270
|
file2
|
|
26234
26271
|
]
|
|
26235
26272
|
}, idx)),
|
|
26236
|
-
dirtyFiles.length > 20 && /* @__PURE__ */
|
|
26273
|
+
dirtyFiles.length > 20 && /* @__PURE__ */ jsxs4("text", {
|
|
26237
26274
|
fg: tokens.warning,
|
|
26238
26275
|
children: [
|
|
26239
26276
|
" ...and ",
|
|
@@ -26243,17 +26280,17 @@ function DetailPane({ selectedRow }) {
|
|
|
26243
26280
|
})
|
|
26244
26281
|
]
|
|
26245
26282
|
}),
|
|
26246
|
-
/* @__PURE__ */
|
|
26283
|
+
/* @__PURE__ */ jsxs4("box", {
|
|
26247
26284
|
flexDirection: "column",
|
|
26248
26285
|
style: { marginTop: 1 },
|
|
26249
26286
|
children: [
|
|
26250
|
-
/* @__PURE__ */
|
|
26287
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26251
26288
|
fg: tokens.dim,
|
|
26252
26289
|
children: "Actions:"
|
|
26253
26290
|
}),
|
|
26254
|
-
!isMainCheckout && /* @__PURE__ */
|
|
26291
|
+
!isMainCheckout && /* @__PURE__ */ jsxs4(Fragment, {
|
|
26255
26292
|
children: [
|
|
26256
|
-
/* @__PURE__ */
|
|
26293
|
+
/* @__PURE__ */ jsxs4("text", {
|
|
26257
26294
|
fg: tokens.dim,
|
|
26258
26295
|
children: [
|
|
26259
26296
|
" i install deps (",
|
|
@@ -26261,44 +26298,218 @@ function DetailPane({ selectedRow }) {
|
|
|
26261
26298
|
")"
|
|
26262
26299
|
]
|
|
26263
26300
|
}),
|
|
26264
|
-
/* @__PURE__ */
|
|
26301
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26265
26302
|
fg: tokens.dim,
|
|
26266
26303
|
children: " m rename worktree"
|
|
26267
26304
|
})
|
|
26268
26305
|
]
|
|
26269
26306
|
}),
|
|
26270
|
-
/* @__PURE__ */
|
|
26307
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26271
26308
|
fg: tokens.dim,
|
|
26272
26309
|
children: " p pull branch"
|
|
26273
26310
|
}),
|
|
26274
|
-
/* @__PURE__ */
|
|
26311
|
+
/* @__PURE__ */ jsx4("text", {
|
|
26275
26312
|
fg: tokens.dim,
|
|
26276
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)"
|
|
26277
26318
|
})
|
|
26278
26319
|
]
|
|
26279
26320
|
})
|
|
26280
26321
|
]
|
|
26281
26322
|
});
|
|
26282
26323
|
}
|
|
26283
|
-
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(() => {
|
|
26284
26495
|
init_theme();
|
|
26285
26496
|
init_use_tap();
|
|
26286
26497
|
});
|
|
26287
26498
|
|
|
26288
26499
|
// src/tui/components/Footer.tsx
|
|
26289
|
-
import { jsx as
|
|
26500
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "@opentui/react/jsx-runtime";
|
|
26290
26501
|
function HintItem({ hintKey, label, isFirst, onClick }) {
|
|
26291
26502
|
const tap = useTapHandler(() => onClick?.(hintKey));
|
|
26292
|
-
return /* @__PURE__ */
|
|
26503
|
+
return /* @__PURE__ */ jsx6("box", {
|
|
26293
26504
|
flexDirection: "row",
|
|
26294
26505
|
...onClick ? tap : {},
|
|
26295
|
-
children: /* @__PURE__ */
|
|
26506
|
+
children: /* @__PURE__ */ jsxs6("text", {
|
|
26296
26507
|
children: [
|
|
26297
|
-
/* @__PURE__ */
|
|
26508
|
+
/* @__PURE__ */ jsx6("span", {
|
|
26298
26509
|
fg: tokens.dim,
|
|
26299
26510
|
children: isFirst ? "" : " · "
|
|
26300
26511
|
}),
|
|
26301
|
-
/* @__PURE__ */
|
|
26512
|
+
/* @__PURE__ */ jsx6("span", {
|
|
26302
26513
|
fg: tokens.dim,
|
|
26303
26514
|
children: `${hintKey} ${label}`
|
|
26304
26515
|
})
|
|
@@ -26308,15 +26519,15 @@ function HintItem({ hintKey, label, isFirst, onClick }) {
|
|
|
26308
26519
|
}
|
|
26309
26520
|
function ErrorBadge({ count: count2, onClick }) {
|
|
26310
26521
|
const tap = useTapHandler(() => onClick?.());
|
|
26311
|
-
return /* @__PURE__ */
|
|
26522
|
+
return /* @__PURE__ */ jsx6("box", {
|
|
26312
26523
|
...onClick ? tap : {},
|
|
26313
|
-
children: /* @__PURE__ */
|
|
26524
|
+
children: /* @__PURE__ */ jsxs6("text", {
|
|
26314
26525
|
children: [
|
|
26315
|
-
/* @__PURE__ */
|
|
26526
|
+
/* @__PURE__ */ jsx6("span", {
|
|
26316
26527
|
fg: tokens.error,
|
|
26317
26528
|
children: `${count2} error${count2 !== 1 ? "s" : ""}`
|
|
26318
26529
|
}),
|
|
26319
|
-
/* @__PURE__ */
|
|
26530
|
+
/* @__PURE__ */ jsx6("span", {
|
|
26320
26531
|
fg: tokens.dim,
|
|
26321
26532
|
children: " · e view"
|
|
26322
26533
|
})
|
|
@@ -26326,7 +26537,7 @@ function ErrorBadge({ count: count2, onClick }) {
|
|
|
26326
26537
|
}
|
|
26327
26538
|
function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinnerFrame, filter, onHintClick, onErrorClick }) {
|
|
26328
26539
|
const frame = spinnerFrame ?? "◌";
|
|
26329
|
-
return /* @__PURE__ */
|
|
26540
|
+
return /* @__PURE__ */ jsxs6("box", {
|
|
26330
26541
|
id: "footer",
|
|
26331
26542
|
width: "100%",
|
|
26332
26543
|
height: 3,
|
|
@@ -26337,28 +26548,29 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
|
|
|
26337
26548
|
justifyContent: "space-between",
|
|
26338
26549
|
alignItems: "center",
|
|
26339
26550
|
children: [
|
|
26340
|
-
busyText ? /* @__PURE__ */
|
|
26551
|
+
busyText ? /* @__PURE__ */ jsx6("text", {
|
|
26341
26552
|
fg: tokens.accent,
|
|
26342
26553
|
children: `${frame} ${busyText}`
|
|
26343
|
-
}) : /* @__PURE__ */
|
|
26554
|
+
}) : /* @__PURE__ */ jsx6("box", {
|
|
26344
26555
|
flexDirection: "row",
|
|
26345
26556
|
gap: 1,
|
|
26346
|
-
children: HINTS.map(([key, label], idx) => /* @__PURE__ */
|
|
26557
|
+
children: HINTS.map(([key, label], idx) => /* @__PURE__ */ jsx6(HintItem, {
|
|
26347
26558
|
hintKey: key,
|
|
26348
26559
|
label,
|
|
26349
26560
|
isFirst: idx === 0,
|
|
26350
26561
|
onClick: onHintClick
|
|
26351
26562
|
}, key))
|
|
26352
26563
|
}),
|
|
26353
|
-
/* @__PURE__ */
|
|
26564
|
+
/* @__PURE__ */ jsxs6("box", {
|
|
26354
26565
|
flexDirection: "row",
|
|
26355
26566
|
gap: 2,
|
|
26567
|
+
alignItems: "center",
|
|
26356
26568
|
children: [
|
|
26357
|
-
message ? /* @__PURE__ */
|
|
26569
|
+
message ? /* @__PURE__ */ jsx6("text", {
|
|
26358
26570
|
fg: tokens.warning,
|
|
26359
26571
|
children: message
|
|
26360
26572
|
}) : null,
|
|
26361
|
-
filter ? /* @__PURE__ */
|
|
26573
|
+
filter ? /* @__PURE__ */ jsxs6("text", {
|
|
26362
26574
|
fg: "magenta",
|
|
26363
26575
|
children: [
|
|
26364
26576
|
"filter: ",
|
|
@@ -26370,11 +26582,17 @@ function Footer({ loading, lastRefreshed, errorCount, message, busyText, spinner
|
|
|
26370
26582
|
")"
|
|
26371
26583
|
]
|
|
26372
26584
|
}) : null,
|
|
26373
|
-
errorCount > 0 ? /* @__PURE__ */
|
|
26585
|
+
errorCount > 0 ? /* @__PURE__ */ jsx6(ErrorBadge, {
|
|
26374
26586
|
count: errorCount,
|
|
26375
26587
|
onClick: onErrorClick
|
|
26376
26588
|
}) : null,
|
|
26377
|
-
/* @__PURE__ */
|
|
26589
|
+
/* @__PURE__ */ jsx6(HintItem, {
|
|
26590
|
+
hintKey: "?",
|
|
26591
|
+
label: "help",
|
|
26592
|
+
isFirst: errorCount === 0,
|
|
26593
|
+
onClick: onHintClick
|
|
26594
|
+
}),
|
|
26595
|
+
/* @__PURE__ */ jsx6("text", {
|
|
26378
26596
|
fg: tokens.dim,
|
|
26379
26597
|
children: loading ? `${frame} Refreshing…` : `Updated ${lastRefreshed}`
|
|
26380
26598
|
})
|
|
@@ -26388,33 +26606,22 @@ var init_Footer = __esm(() => {
|
|
|
26388
26606
|
init_theme();
|
|
26389
26607
|
init_use_tap();
|
|
26390
26608
|
HINTS = [
|
|
26391
|
-
["c", "config"],
|
|
26392
26609
|
["n", "create"],
|
|
26393
|
-
["
|
|
26394
|
-
["?", "help"],
|
|
26395
|
-
["H", "history"],
|
|
26396
|
-
["o", "ide"],
|
|
26397
|
-
["i", "install"],
|
|
26398
|
-
["p", "pull"],
|
|
26399
|
-
["b", "rebase"],
|
|
26400
|
-
["r", "refresh"],
|
|
26401
|
-
["d", "remove"],
|
|
26402
|
-
["m", "rename"],
|
|
26403
|
-
["s", "sync"]
|
|
26610
|
+
["d", "remove"]
|
|
26404
26611
|
];
|
|
26405
26612
|
});
|
|
26406
26613
|
|
|
26407
26614
|
// src/tui/components/Divider.tsx
|
|
26408
|
-
import { useState as
|
|
26615
|
+
import { useState as useState3, useRef as useRef5, useCallback as useCallback4 } from "react";
|
|
26409
26616
|
import { useRenderer } from "@opentui/react";
|
|
26410
|
-
import { jsx as
|
|
26617
|
+
import { jsx as jsx7 } from "@opentui/react/jsx-runtime";
|
|
26411
26618
|
function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
26412
26619
|
const renderer = useRenderer();
|
|
26413
|
-
const [hovered, setHovered] =
|
|
26414
|
-
const [dragging, setDragging] =
|
|
26415
|
-
const startXRef =
|
|
26416
|
-
const startRatioRef =
|
|
26417
|
-
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) => {
|
|
26418
26625
|
if (e.button !== 0)
|
|
26419
26626
|
return;
|
|
26420
26627
|
startXRef.current = e.x;
|
|
@@ -26424,7 +26631,7 @@ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
|
26424
26631
|
renderer.clearSelection();
|
|
26425
26632
|
e.stopPropagation();
|
|
26426
26633
|
}, [splitRatio, onDraggingChange, renderer]);
|
|
26427
|
-
const handleMouseUp =
|
|
26634
|
+
const handleMouseUp = useCallback4((e) => {
|
|
26428
26635
|
if (e.button !== 0)
|
|
26429
26636
|
return;
|
|
26430
26637
|
startXRef.current = null;
|
|
@@ -26432,7 +26639,7 @@ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
|
26432
26639
|
onDraggingChange?.(false);
|
|
26433
26640
|
e.stopPropagation();
|
|
26434
26641
|
}, [onDraggingChange]);
|
|
26435
|
-
const handleMouseDrag =
|
|
26642
|
+
const handleMouseDrag = useCallback4((e) => {
|
|
26436
26643
|
if (startXRef.current === null)
|
|
26437
26644
|
return;
|
|
26438
26645
|
renderer.clearSelection();
|
|
@@ -26447,7 +26654,7 @@ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
|
26447
26654
|
onMouseDrag: handleMouseDrag,
|
|
26448
26655
|
onMouseMove: handleMouseDrag
|
|
26449
26656
|
};
|
|
26450
|
-
return /* @__PURE__ */
|
|
26657
|
+
return /* @__PURE__ */ jsx7("box", {
|
|
26451
26658
|
width: 3,
|
|
26452
26659
|
height: "100%",
|
|
26453
26660
|
backgroundColor: dragging ? tokens.selectionBg : hovered ? tokens.panelBg : undefined,
|
|
@@ -26456,7 +26663,7 @@ function Divider({ splitRatio, totalWidth, onChange, onDraggingChange }) {
|
|
|
26456
26663
|
onMouseOut: () => setHovered(false),
|
|
26457
26664
|
alignItems: "center",
|
|
26458
26665
|
justifyContent: "center",
|
|
26459
|
-
children: /* @__PURE__ */
|
|
26666
|
+
children: /* @__PURE__ */ jsx7("text", {
|
|
26460
26667
|
fg: dragging ? tokens.accent : hovered ? tokens.borderActive : tokens.border,
|
|
26461
26668
|
...dragHandlers,
|
|
26462
26669
|
onMouseOver: () => setHovered(true),
|
|
@@ -26471,9 +26678,9 @@ var init_Divider = __esm(() => {
|
|
|
26471
26678
|
});
|
|
26472
26679
|
|
|
26473
26680
|
// src/tui/components/Overlay.tsx
|
|
26474
|
-
import { jsx as
|
|
26681
|
+
import { jsx as jsx8 } from "@opentui/react/jsx-runtime";
|
|
26475
26682
|
function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
|
|
26476
|
-
return /* @__PURE__ */
|
|
26683
|
+
return /* @__PURE__ */ jsx8("box", {
|
|
26477
26684
|
id: "overlay-scrim",
|
|
26478
26685
|
position: "absolute",
|
|
26479
26686
|
width: "100%",
|
|
@@ -26485,7 +26692,7 @@ function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
|
|
|
26485
26692
|
flexDirection: "row",
|
|
26486
26693
|
justifyContent: "center",
|
|
26487
26694
|
alignItems: "center",
|
|
26488
|
-
children: /* @__PURE__ */
|
|
26695
|
+
children: /* @__PURE__ */ jsx8("box", {
|
|
26489
26696
|
id: "overlay-panel",
|
|
26490
26697
|
width,
|
|
26491
26698
|
border: true,
|
|
@@ -26505,34 +26712,47 @@ var init_Overlay = __esm(() => {
|
|
|
26505
26712
|
});
|
|
26506
26713
|
|
|
26507
26714
|
// src/tui/components/HelpOverlay.tsx
|
|
26508
|
-
import { jsx as
|
|
26715
|
+
import { jsx as jsx9, jsxs as jsxs7 } from "@opentui/react/jsx-runtime";
|
|
26509
26716
|
function HelpOverlay() {
|
|
26510
|
-
return /* @__PURE__ */
|
|
26717
|
+
return /* @__PURE__ */ jsxs7(Overlay, {
|
|
26511
26718
|
title: "Help",
|
|
26512
26719
|
borderColor: tokens.border,
|
|
26720
|
+
width: 84,
|
|
26513
26721
|
children: [
|
|
26514
|
-
/* @__PURE__ */
|
|
26722
|
+
/* @__PURE__ */ jsx9("box", {
|
|
26515
26723
|
flexDirection: "column",
|
|
26516
|
-
style: { maxHeight:
|
|
26517
|
-
children: /* @__PURE__ */
|
|
26724
|
+
style: { maxHeight: 36 },
|
|
26725
|
+
children: /* @__PURE__ */ jsx9("scrollbox", {
|
|
26518
26726
|
flexGrow: 1,
|
|
26519
|
-
children: HELP_ENTRIES.map(([key, desc]) => /* @__PURE__ */
|
|
26727
|
+
children: HELP_ENTRIES.map(([key, desc]) => /* @__PURE__ */ jsxs7("box", {
|
|
26728
|
+
flexDirection: "row",
|
|
26729
|
+
gap: 1,
|
|
26520
26730
|
children: [
|
|
26521
|
-
/* @__PURE__ */
|
|
26522
|
-
|
|
26523
|
-
|
|
26731
|
+
/* @__PURE__ */ jsx9("box", {
|
|
26732
|
+
width: 18,
|
|
26733
|
+
flexShrink: 0,
|
|
26734
|
+
justifyContent: "flex-end",
|
|
26735
|
+
children: /* @__PURE__ */ jsx9("text", {
|
|
26736
|
+
fg: tokens.accent,
|
|
26737
|
+
children: key
|
|
26738
|
+
})
|
|
26524
26739
|
}),
|
|
26525
|
-
/* @__PURE__ */
|
|
26526
|
-
|
|
26740
|
+
/* @__PURE__ */ jsx9("text", {
|
|
26741
|
+
fg: tokens.dim,
|
|
26742
|
+
children: "-"
|
|
26527
26743
|
}),
|
|
26528
|
-
/* @__PURE__ */
|
|
26529
|
-
|
|
26744
|
+
/* @__PURE__ */ jsx9("box", {
|
|
26745
|
+
flexGrow: 1,
|
|
26746
|
+
children: /* @__PURE__ */ jsx9("text", {
|
|
26747
|
+
fg: tokens.fg,
|
|
26748
|
+
children: desc
|
|
26749
|
+
})
|
|
26530
26750
|
})
|
|
26531
26751
|
]
|
|
26532
26752
|
}, key))
|
|
26533
26753
|
})
|
|
26534
26754
|
}),
|
|
26535
|
-
/* @__PURE__ */
|
|
26755
|
+
/* @__PURE__ */ jsx9("text", {
|
|
26536
26756
|
style: { marginTop: 1, fg: tokens.dim },
|
|
26537
26757
|
children: "Press any key to close"
|
|
26538
26758
|
})
|
|
@@ -26557,7 +26777,9 @@ var init_HelpOverlay = __esm(() => {
|
|
|
26557
26777
|
["i", "Install dependencies in selection (worktrees and main)"],
|
|
26558
26778
|
["m", "Rename selected worktree (branch + directory)"],
|
|
26559
26779
|
["o", "Open selected in IDE"],
|
|
26560
|
-
["
|
|
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"],
|
|
26561
26783
|
["d", "Remove selected worktree(s)"],
|
|
26562
26784
|
["e", "View data warnings (when count > 0)"],
|
|
26563
26785
|
["r", "Refresh data"],
|
|
@@ -26571,50 +26793,50 @@ var init_HelpOverlay = __esm(() => {
|
|
|
26571
26793
|
});
|
|
26572
26794
|
|
|
26573
26795
|
// src/tui/components/ConfirmModal.tsx
|
|
26574
|
-
import { jsx as
|
|
26796
|
+
import { jsx as jsx10, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
|
|
26575
26797
|
function ConfirmModal({ title, message, onConfirm, onCancel }) {
|
|
26576
26798
|
const yesTap = useTapHandler(() => onConfirm?.());
|
|
26577
26799
|
const noTap = useTapHandler(() => onCancel?.());
|
|
26578
|
-
return /* @__PURE__ */
|
|
26800
|
+
return /* @__PURE__ */ jsx10(Overlay, {
|
|
26579
26801
|
title,
|
|
26580
26802
|
borderColor: tokens.warning,
|
|
26581
|
-
children: /* @__PURE__ */
|
|
26803
|
+
children: /* @__PURE__ */ jsxs8("box", {
|
|
26582
26804
|
flexDirection: "column",
|
|
26583
26805
|
justifyContent: "center",
|
|
26584
26806
|
alignItems: "center",
|
|
26585
26807
|
children: [
|
|
26586
|
-
/* @__PURE__ */
|
|
26808
|
+
/* @__PURE__ */ jsx10("text", {
|
|
26587
26809
|
children: message
|
|
26588
26810
|
}),
|
|
26589
|
-
/* @__PURE__ */
|
|
26811
|
+
/* @__PURE__ */ jsxs8("box", {
|
|
26590
26812
|
flexDirection: "row",
|
|
26591
26813
|
gap: 2,
|
|
26592
26814
|
style: { marginTop: 2 },
|
|
26593
26815
|
children: [
|
|
26594
|
-
/* @__PURE__ */
|
|
26816
|
+
/* @__PURE__ */ jsx10("box", {
|
|
26595
26817
|
...onConfirm ? yesTap : {},
|
|
26596
|
-
children: /* @__PURE__ */
|
|
26818
|
+
children: /* @__PURE__ */ jsxs8("text", {
|
|
26597
26819
|
children: [
|
|
26598
|
-
/* @__PURE__ */
|
|
26820
|
+
/* @__PURE__ */ jsx10("span", {
|
|
26599
26821
|
fg: tokens.dim,
|
|
26600
26822
|
children: "[y] "
|
|
26601
26823
|
}),
|
|
26602
|
-
/* @__PURE__ */
|
|
26824
|
+
/* @__PURE__ */ jsx10("span", {
|
|
26603
26825
|
fg: tokens.success,
|
|
26604
26826
|
children: "Yes"
|
|
26605
26827
|
})
|
|
26606
26828
|
]
|
|
26607
26829
|
})
|
|
26608
26830
|
}),
|
|
26609
|
-
/* @__PURE__ */
|
|
26831
|
+
/* @__PURE__ */ jsx10("box", {
|
|
26610
26832
|
...onCancel ? noTap : {},
|
|
26611
|
-
children: /* @__PURE__ */
|
|
26833
|
+
children: /* @__PURE__ */ jsxs8("text", {
|
|
26612
26834
|
children: [
|
|
26613
|
-
/* @__PURE__ */
|
|
26835
|
+
/* @__PURE__ */ jsx10("span", {
|
|
26614
26836
|
fg: tokens.dim,
|
|
26615
26837
|
children: "[n/esc] "
|
|
26616
26838
|
}),
|
|
26617
|
-
/* @__PURE__ */
|
|
26839
|
+
/* @__PURE__ */ jsx10("span", {
|
|
26618
26840
|
children: "Cancel"
|
|
26619
26841
|
})
|
|
26620
26842
|
]
|
|
@@ -26690,52 +26912,52 @@ async function runWtxAction(args, onLine) {
|
|
|
26690
26912
|
}
|
|
26691
26913
|
|
|
26692
26914
|
// src/tui/components/ActionLogModal.tsx
|
|
26693
|
-
import { jsx as
|
|
26915
|
+
import { jsx as jsx11, jsxs as jsxs9 } from "@opentui/react/jsx-runtime";
|
|
26694
26916
|
function ActionLogModal({ title, lines, done, exitCode, remaining }) {
|
|
26695
|
-
return /* @__PURE__ */
|
|
26917
|
+
return /* @__PURE__ */ jsxs9(Overlay, {
|
|
26696
26918
|
title,
|
|
26697
26919
|
borderColor: tokens.border,
|
|
26698
26920
|
children: [
|
|
26699
|
-
/* @__PURE__ */
|
|
26921
|
+
/* @__PURE__ */ jsx11("box", {
|
|
26700
26922
|
flexGrow: 1,
|
|
26701
26923
|
flexDirection: "column",
|
|
26702
26924
|
style: { minHeight: 10, maxHeight: 20 },
|
|
26703
|
-
children: /* @__PURE__ */
|
|
26925
|
+
children: /* @__PURE__ */ jsx11("scrollbox", {
|
|
26704
26926
|
flexGrow: 1,
|
|
26705
26927
|
stickyScroll: true,
|
|
26706
|
-
children: lines.map((line, i2) => /* @__PURE__ */
|
|
26928
|
+
children: lines.map((line, i2) => /* @__PURE__ */ jsx11("text", {
|
|
26707
26929
|
fg: line.type === "err" ? tokens.error : tokens.fg,
|
|
26708
26930
|
children: line.text
|
|
26709
26931
|
}, i2))
|
|
26710
26932
|
})
|
|
26711
26933
|
}),
|
|
26712
|
-
/* @__PURE__ */
|
|
26934
|
+
/* @__PURE__ */ jsxs9("box", {
|
|
26713
26935
|
marginTop: 1,
|
|
26714
26936
|
flexDirection: "row",
|
|
26715
26937
|
justifyContent: "center",
|
|
26716
26938
|
children: [
|
|
26717
|
-
!done && /* @__PURE__ */
|
|
26939
|
+
!done && /* @__PURE__ */ jsx11("text", {
|
|
26718
26940
|
fg: tokens.warning,
|
|
26719
26941
|
children: "Running..."
|
|
26720
26942
|
}),
|
|
26721
|
-
done && exitCode === 0 && /* @__PURE__ */
|
|
26943
|
+
done && exitCode === 0 && /* @__PURE__ */ jsx11("text", {
|
|
26722
26944
|
fg: tokens.success,
|
|
26723
26945
|
children: "✓ Done (exit 0)"
|
|
26724
26946
|
}),
|
|
26725
|
-
done && exitCode !== 0 && /* @__PURE__ */
|
|
26947
|
+
done && exitCode !== 0 && /* @__PURE__ */ jsxs9("text", {
|
|
26726
26948
|
children: [
|
|
26727
|
-
/* @__PURE__ */
|
|
26949
|
+
/* @__PURE__ */ jsxs9("span", {
|
|
26728
26950
|
fg: tokens.error,
|
|
26729
26951
|
children: [
|
|
26730
26952
|
"✗ Exit ",
|
|
26731
26953
|
exitCode
|
|
26732
26954
|
]
|
|
26733
26955
|
}),
|
|
26734
|
-
remaining !== undefined && remaining > 0 && /* @__PURE__ */
|
|
26956
|
+
remaining !== undefined && remaining > 0 && /* @__PURE__ */ jsx11("span", {
|
|
26735
26957
|
fg: tokens.warning,
|
|
26736
26958
|
children: ` · ${remaining} more failure${remaining > 1 ? "s" : ""}`
|
|
26737
26959
|
}),
|
|
26738
|
-
/* @__PURE__ */
|
|
26960
|
+
/* @__PURE__ */ jsx11("span", {
|
|
26739
26961
|
fg: tokens.dim,
|
|
26740
26962
|
children: " - press any key to close"
|
|
26741
26963
|
})
|
|
@@ -26822,8 +27044,8 @@ var init_history = __esm(() => {
|
|
|
26822
27044
|
});
|
|
26823
27045
|
|
|
26824
27046
|
// src/tui/components/HistoryOverlay.tsx
|
|
26825
|
-
import { useEffect as
|
|
26826
|
-
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";
|
|
26827
27049
|
function formatShortTime(iso) {
|
|
26828
27050
|
const d = new Date(iso);
|
|
26829
27051
|
if (Number.isNaN(d.getTime()))
|
|
@@ -26836,16 +27058,16 @@ function HistoryEntryLine({ entry }) {
|
|
|
26836
27058
|
const duration3 = entry.durationMs !== null && entry.durationMs !== undefined ? ` ${entry.durationMs}ms` : "";
|
|
26837
27059
|
const prefix = `${formatShortTime(entry.ts)} ${entry.source.padEnd(8)} ${args}`;
|
|
26838
27060
|
if (entry.exit === null || entry.exit === undefined) {
|
|
26839
|
-
return /* @__PURE__ */
|
|
27061
|
+
return /* @__PURE__ */ jsxs10("text", {
|
|
26840
27062
|
children: [
|
|
26841
|
-
/* @__PURE__ */
|
|
27063
|
+
/* @__PURE__ */ jsxs10("span", {
|
|
26842
27064
|
fg: tokens.dim,
|
|
26843
27065
|
children: [
|
|
26844
27066
|
"◌ ",
|
|
26845
27067
|
prefix
|
|
26846
27068
|
]
|
|
26847
27069
|
}),
|
|
26848
|
-
duration3 && /* @__PURE__ */
|
|
27070
|
+
duration3 && /* @__PURE__ */ jsx12("span", {
|
|
26849
27071
|
fg: tokens.dim,
|
|
26850
27072
|
children: duration3
|
|
26851
27073
|
})
|
|
@@ -26853,32 +27075,32 @@ function HistoryEntryLine({ entry }) {
|
|
|
26853
27075
|
});
|
|
26854
27076
|
}
|
|
26855
27077
|
if (entry.exit === 0) {
|
|
26856
|
-
return /* @__PURE__ */
|
|
27078
|
+
return /* @__PURE__ */ jsxs10("text", {
|
|
26857
27079
|
children: [
|
|
26858
|
-
/* @__PURE__ */
|
|
27080
|
+
/* @__PURE__ */ jsxs10("span", {
|
|
26859
27081
|
fg: tokens.success,
|
|
26860
27082
|
children: [
|
|
26861
27083
|
"✓ ",
|
|
26862
27084
|
prefix
|
|
26863
27085
|
]
|
|
26864
27086
|
}),
|
|
26865
|
-
duration3 && /* @__PURE__ */
|
|
27087
|
+
duration3 && /* @__PURE__ */ jsx12("span", {
|
|
26866
27088
|
fg: tokens.dim,
|
|
26867
27089
|
children: duration3
|
|
26868
27090
|
})
|
|
26869
27091
|
]
|
|
26870
27092
|
});
|
|
26871
27093
|
}
|
|
26872
|
-
return /* @__PURE__ */
|
|
27094
|
+
return /* @__PURE__ */ jsxs10("text", {
|
|
26873
27095
|
children: [
|
|
26874
|
-
/* @__PURE__ */
|
|
27096
|
+
/* @__PURE__ */ jsx12("span", {
|
|
26875
27097
|
fg: tokens.error,
|
|
26876
27098
|
children: "✗ "
|
|
26877
27099
|
}),
|
|
26878
|
-
/* @__PURE__ */
|
|
27100
|
+
/* @__PURE__ */ jsx12("span", {
|
|
26879
27101
|
children: prefix
|
|
26880
27102
|
}),
|
|
26881
|
-
/* @__PURE__ */
|
|
27103
|
+
/* @__PURE__ */ jsxs10("span", {
|
|
26882
27104
|
fg: tokens.dim,
|
|
26883
27105
|
children: [
|
|
26884
27106
|
" (exit ",
|
|
@@ -26891,32 +27113,32 @@ function HistoryEntryLine({ entry }) {
|
|
|
26891
27113
|
});
|
|
26892
27114
|
}
|
|
26893
27115
|
function HistoryOverlay() {
|
|
26894
|
-
const [entries, setEntries] =
|
|
26895
|
-
|
|
27116
|
+
const [entries, setEntries] = useState4([]);
|
|
27117
|
+
useEffect4(() => {
|
|
26896
27118
|
setEntries(readRecentHistory(500));
|
|
26897
27119
|
}, []);
|
|
26898
|
-
return /* @__PURE__ */
|
|
27120
|
+
return /* @__PURE__ */ jsxs10(Overlay, {
|
|
26899
27121
|
title: "Action History",
|
|
26900
27122
|
borderColor: tokens.border,
|
|
26901
27123
|
width: 110,
|
|
26902
27124
|
children: [
|
|
26903
|
-
/* @__PURE__ */
|
|
27125
|
+
/* @__PURE__ */ jsx12("box", {
|
|
26904
27126
|
flexGrow: 1,
|
|
26905
27127
|
flexDirection: "column",
|
|
26906
27128
|
style: { minHeight: 30, maxHeight: 46 },
|
|
26907
|
-
children: entries.length === 0 ? /* @__PURE__ */
|
|
27129
|
+
children: entries.length === 0 ? /* @__PURE__ */ jsx12("text", {
|
|
26908
27130
|
fg: tokens.dim,
|
|
26909
27131
|
children: "No actions recorded yet."
|
|
26910
|
-
}) : /* @__PURE__ */
|
|
27132
|
+
}) : /* @__PURE__ */ jsx12("scrollbox", {
|
|
26911
27133
|
flexGrow: 1,
|
|
26912
|
-
children: entries.map((entry, i2) => /* @__PURE__ */
|
|
27134
|
+
children: entries.map((entry, i2) => /* @__PURE__ */ jsx12(HistoryEntryLine, {
|
|
26913
27135
|
entry
|
|
26914
27136
|
}, i2))
|
|
26915
27137
|
})
|
|
26916
27138
|
}),
|
|
26917
|
-
/* @__PURE__ */
|
|
27139
|
+
/* @__PURE__ */ jsx12("box", {
|
|
26918
27140
|
marginTop: 1,
|
|
26919
|
-
children: /* @__PURE__ */
|
|
27141
|
+
children: /* @__PURE__ */ jsx12("text", {
|
|
26920
27142
|
fg: tokens.dim,
|
|
26921
27143
|
children: "Press any key to close · newest first"
|
|
26922
27144
|
})
|
|
@@ -26931,28 +27153,28 @@ var init_HistoryOverlay = __esm(() => {
|
|
|
26931
27153
|
});
|
|
26932
27154
|
|
|
26933
27155
|
// src/tui/components/InputModal.tsx
|
|
26934
|
-
import { useState as
|
|
26935
|
-
import { jsx as
|
|
27156
|
+
import { useState as useState5 } from "react";
|
|
27157
|
+
import { jsx as jsx13, jsxs as jsxs11 } from "@opentui/react/jsx-runtime";
|
|
26936
27158
|
function InputModal({ title, placeholder, initialValue, errorMessage: errorMessage2, onSubmit }) {
|
|
26937
|
-
const [value, setValue] =
|
|
26938
|
-
return /* @__PURE__ */
|
|
27159
|
+
const [value, setValue] = useState5(initialValue ?? "");
|
|
27160
|
+
return /* @__PURE__ */ jsx13(Overlay, {
|
|
26939
27161
|
title,
|
|
26940
27162
|
borderColor: tokens.accent,
|
|
26941
|
-
children: /* @__PURE__ */
|
|
27163
|
+
children: /* @__PURE__ */ jsxs11("box", {
|
|
26942
27164
|
flexDirection: "column",
|
|
26943
27165
|
children: [
|
|
26944
|
-
/* @__PURE__ */
|
|
27166
|
+
/* @__PURE__ */ jsx13("input", {
|
|
26945
27167
|
focused: true,
|
|
26946
27168
|
value: initialValue ?? "",
|
|
26947
27169
|
placeholder,
|
|
26948
27170
|
onInput: (val) => setValue(val),
|
|
26949
27171
|
onSubmit: () => onSubmit(value)
|
|
26950
27172
|
}),
|
|
26951
|
-
errorMessage2 ? /* @__PURE__ */
|
|
27173
|
+
errorMessage2 ? /* @__PURE__ */ jsx13("text", {
|
|
26952
27174
|
fg: tokens.error,
|
|
26953
27175
|
style: { marginTop: 1 },
|
|
26954
27176
|
children: errorMessage2
|
|
26955
|
-
}) : /* @__PURE__ */
|
|
27177
|
+
}) : /* @__PURE__ */ jsx13("text", {
|
|
26956
27178
|
fg: tokens.dim,
|
|
26957
27179
|
style: { marginTop: 1 },
|
|
26958
27180
|
children: "Press Enter to submit, empty to cancel"
|
|
@@ -26967,23 +27189,23 @@ var init_InputModal = __esm(() => {
|
|
|
26967
27189
|
});
|
|
26968
27190
|
|
|
26969
27191
|
// src/tui/components/ChoiceModal.tsx
|
|
26970
|
-
import { useState as
|
|
27192
|
+
import { useState as useState6 } from "react";
|
|
26971
27193
|
import { useKeyboard } from "@opentui/react";
|
|
26972
|
-
import { jsx as
|
|
27194
|
+
import { jsx as jsx14, jsxs as jsxs12 } from "@opentui/react/jsx-runtime";
|
|
26973
27195
|
function ChoiceOptionRow({ option, index, isSelected, onSelect }) {
|
|
26974
27196
|
const tap = useTapHandler(() => onSelect(option.value));
|
|
26975
|
-
return /* @__PURE__ */
|
|
27197
|
+
return /* @__PURE__ */ jsxs12("box", {
|
|
26976
27198
|
flexDirection: "column",
|
|
26977
27199
|
backgroundColor: isSelected ? tokens.selectionBg : undefined,
|
|
26978
27200
|
...tap,
|
|
26979
27201
|
children: [
|
|
26980
|
-
/* @__PURE__ */
|
|
26981
|
-
children: /* @__PURE__ */
|
|
27202
|
+
/* @__PURE__ */ jsx14("text", {
|
|
27203
|
+
children: /* @__PURE__ */ jsx14("span", {
|
|
26982
27204
|
fg: isSelected ? tokens.bright : tokens.fg,
|
|
26983
27205
|
children: `${index + 1}. ${option.label}`
|
|
26984
27206
|
})
|
|
26985
27207
|
}),
|
|
26986
|
-
option.desc && /* @__PURE__ */
|
|
27208
|
+
option.desc && /* @__PURE__ */ jsx14("text", {
|
|
26987
27209
|
fg: tokens.dim,
|
|
26988
27210
|
children: ` ${option.desc}`
|
|
26989
27211
|
})
|
|
@@ -26991,7 +27213,7 @@ function ChoiceOptionRow({ option, index, isSelected, onSelect }) {
|
|
|
26991
27213
|
});
|
|
26992
27214
|
}
|
|
26993
27215
|
function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
|
|
26994
|
-
const [index, setIndex] =
|
|
27216
|
+
const [index, setIndex] = useState6(Math.min(Math.max(initialIndex, 0), options.length - 1));
|
|
26995
27217
|
useKeyboard((key) => {
|
|
26996
27218
|
if (key.name === "escape") {
|
|
26997
27219
|
onCancel();
|
|
@@ -27018,19 +27240,19 @@ function ChoiceModal({ title, options, initialIndex = 0, onSubmit, onCancel }) {
|
|
|
27018
27240
|
onSubmit(option.value);
|
|
27019
27241
|
}
|
|
27020
27242
|
});
|
|
27021
|
-
return /* @__PURE__ */
|
|
27243
|
+
return /* @__PURE__ */ jsx14(Overlay, {
|
|
27022
27244
|
title,
|
|
27023
27245
|
borderColor: tokens.accent,
|
|
27024
|
-
children: /* @__PURE__ */
|
|
27246
|
+
children: /* @__PURE__ */ jsxs12("box", {
|
|
27025
27247
|
flexDirection: "column",
|
|
27026
27248
|
children: [
|
|
27027
|
-
options.map((option, i2) => /* @__PURE__ */
|
|
27249
|
+
options.map((option, i2) => /* @__PURE__ */ jsx14(ChoiceOptionRow, {
|
|
27028
27250
|
option,
|
|
27029
27251
|
index: i2,
|
|
27030
27252
|
isSelected: i2 === index,
|
|
27031
27253
|
onSelect: onSubmit
|
|
27032
27254
|
}, option.value)),
|
|
27033
|
-
/* @__PURE__ */
|
|
27255
|
+
/* @__PURE__ */ jsxs12("text", {
|
|
27034
27256
|
fg: tokens.dim,
|
|
27035
27257
|
style: { marginTop: 1 },
|
|
27036
27258
|
children: [
|
|
@@ -27050,16 +27272,16 @@ var init_ChoiceModal = __esm(() => {
|
|
|
27050
27272
|
});
|
|
27051
27273
|
|
|
27052
27274
|
// src/tui/components/ConfigOverlay.tsx
|
|
27053
|
-
import { useState as
|
|
27275
|
+
import { useState as useState7, useEffect as useEffect5 } from "react";
|
|
27054
27276
|
import { useKeyboard as useKeyboard2 } from "@opentui/react";
|
|
27055
|
-
import { jsx as
|
|
27277
|
+
import { jsx as jsx15, jsxs as jsxs13, Fragment as Fragment2 } from "@opentui/react/jsx-runtime";
|
|
27056
27278
|
function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
27057
|
-
const [config2, setConfig] =
|
|
27058
|
-
const [viewState, setViewState] =
|
|
27059
|
-
const [selectedIndex, setSelectedIndex] =
|
|
27060
|
-
const [windowStart, setWindowStart] =
|
|
27061
|
-
const [editState, setEditState] =
|
|
27062
|
-
|
|
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(() => {
|
|
27063
27285
|
try {
|
|
27064
27286
|
setConfig(loadConfig());
|
|
27065
27287
|
} catch (err) {
|
|
@@ -27095,7 +27317,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27095
27317
|
detailRows.push({ type: "remove_repo" });
|
|
27096
27318
|
}
|
|
27097
27319
|
const activeRows = viewState.type === "main" ? mainRows : detailRows;
|
|
27098
|
-
|
|
27320
|
+
useEffect5(() => {
|
|
27099
27321
|
if (!activeRows || selectedIndex >= activeRows.length) {
|
|
27100
27322
|
let newIdx = activeRows.length - 1;
|
|
27101
27323
|
while (newIdx > 0 && activeRows[newIdx]?.type === "header") {
|
|
@@ -27315,17 +27537,17 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27315
27537
|
hint = "Remove this repository from config";
|
|
27316
27538
|
}
|
|
27317
27539
|
}
|
|
27318
|
-
return /* @__PURE__ */
|
|
27540
|
+
return /* @__PURE__ */ jsxs13(Fragment2, {
|
|
27319
27541
|
children: [
|
|
27320
|
-
/* @__PURE__ */
|
|
27542
|
+
/* @__PURE__ */ jsx15(Overlay, {
|
|
27321
27543
|
title: "Configuration",
|
|
27322
27544
|
borderColor: tokens.border,
|
|
27323
|
-
children: /* @__PURE__ */
|
|
27545
|
+
children: /* @__PURE__ */ jsxs13("box", {
|
|
27324
27546
|
flexDirection: "column",
|
|
27325
27547
|
width: "100%",
|
|
27326
27548
|
height: 24,
|
|
27327
27549
|
children: [
|
|
27328
|
-
/* @__PURE__ */
|
|
27550
|
+
/* @__PURE__ */ jsx15("box", {
|
|
27329
27551
|
flexDirection: "column",
|
|
27330
27552
|
flexGrow: 1,
|
|
27331
27553
|
overflow: "hidden",
|
|
@@ -27333,7 +27555,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27333
27555
|
const i2 = windowStart + idx;
|
|
27334
27556
|
const isSelected = i2 === selectedIndex;
|
|
27335
27557
|
if (row.type === "header") {
|
|
27336
|
-
return /* @__PURE__ */
|
|
27558
|
+
return /* @__PURE__ */ jsx15("text", {
|
|
27337
27559
|
fg: tokens.dim,
|
|
27338
27560
|
style: { marginTop: i2 === 0 ? 0 : 1 },
|
|
27339
27561
|
children: row.label
|
|
@@ -27362,11 +27584,11 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27362
27584
|
labelText = "Remove this repo";
|
|
27363
27585
|
valText = "";
|
|
27364
27586
|
}
|
|
27365
|
-
return /* @__PURE__ */
|
|
27587
|
+
return /* @__PURE__ */ jsxs13("box", {
|
|
27366
27588
|
flexDirection: "row",
|
|
27367
27589
|
width: "100%",
|
|
27368
27590
|
children: [
|
|
27369
|
-
/* @__PURE__ */
|
|
27591
|
+
/* @__PURE__ */ jsxs13("text", {
|
|
27370
27592
|
fg: isSelected ? tokens.bright : tokens.fg,
|
|
27371
27593
|
bg: isSelected ? tokens.selectionBg : undefined,
|
|
27372
27594
|
width: 24,
|
|
@@ -27375,7 +27597,7 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27375
27597
|
labelText
|
|
27376
27598
|
]
|
|
27377
27599
|
}),
|
|
27378
|
-
valText && /* @__PURE__ */
|
|
27600
|
+
valText && /* @__PURE__ */ jsx15("text", {
|
|
27379
27601
|
fg: isSelected ? tokens.accent : tokens.dim,
|
|
27380
27602
|
bg: isSelected ? tokens.selectionBg : undefined,
|
|
27381
27603
|
flexShrink: 1,
|
|
@@ -27385,14 +27607,14 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27385
27607
|
}, i2);
|
|
27386
27608
|
})
|
|
27387
27609
|
}),
|
|
27388
|
-
/* @__PURE__ */
|
|
27610
|
+
/* @__PURE__ */ jsx15("box", {
|
|
27389
27611
|
flexDirection: "row",
|
|
27390
27612
|
border: true,
|
|
27391
27613
|
borderColor: tokens.border,
|
|
27392
27614
|
paddingTop: 1,
|
|
27393
27615
|
marginTop: 1,
|
|
27394
27616
|
minHeight: 3,
|
|
27395
|
-
children: /* @__PURE__ */
|
|
27617
|
+
children: /* @__PURE__ */ jsx15("text", {
|
|
27396
27618
|
fg: tokens.dim,
|
|
27397
27619
|
children: hint
|
|
27398
27620
|
})
|
|
@@ -27400,14 +27622,14 @@ function ConfigOverlay({ onClose, onSaved, onError }) {
|
|
|
27400
27622
|
]
|
|
27401
27623
|
})
|
|
27402
27624
|
}),
|
|
27403
|
-
editState.type === "input" && /* @__PURE__ */
|
|
27625
|
+
editState.type === "input" && /* @__PURE__ */ jsx15(InputModal, {
|
|
27404
27626
|
title: editState.title,
|
|
27405
27627
|
initialValue: editState.currentValue,
|
|
27406
27628
|
placeholder: "Enter value...",
|
|
27407
27629
|
errorMessage: editState.error,
|
|
27408
27630
|
onSubmit: handleInputSubmit
|
|
27409
27631
|
}),
|
|
27410
|
-
editState.type === "confirm_remove" && /* @__PURE__ */
|
|
27632
|
+
editState.type === "confirm_remove" && /* @__PURE__ */ jsx15(ConfirmModal, {
|
|
27411
27633
|
title: "Remove Repo",
|
|
27412
27634
|
message: `Remove repo ${editState.repo} from config?`
|
|
27413
27635
|
})
|
|
@@ -27424,26 +27646,29 @@ var init_ConfigOverlay = __esm(() => {
|
|
|
27424
27646
|
});
|
|
27425
27647
|
|
|
27426
27648
|
// src/tui/components/WarningsOverlay.tsx
|
|
27427
|
-
import { jsx as
|
|
27428
|
-
function WarningsOverlay({ warnings }) {
|
|
27429
|
-
|
|
27649
|
+
import { jsx as jsx16, jsxs as jsxs14 } from "@opentui/react/jsx-runtime";
|
|
27650
|
+
function WarningsOverlay({ warnings, onAcknowledge, onClose }) {
|
|
27651
|
+
const ackTap = useTapHandler(() => onAcknowledge?.());
|
|
27652
|
+
const closeTap = useTapHandler(() => onClose?.());
|
|
27653
|
+
return /* @__PURE__ */ jsxs14(Overlay, {
|
|
27430
27654
|
title: `Warnings (${warnings.length})`,
|
|
27431
27655
|
borderColor: tokens.warning,
|
|
27656
|
+
width: 80,
|
|
27432
27657
|
children: [
|
|
27433
|
-
/* @__PURE__ */
|
|
27658
|
+
/* @__PURE__ */ jsx16("box", {
|
|
27434
27659
|
flexDirection: "column",
|
|
27435
|
-
style: { maxHeight:
|
|
27436
|
-
children: /* @__PURE__ */
|
|
27660
|
+
style: { maxHeight: 28 },
|
|
27661
|
+
children: /* @__PURE__ */ jsx16("scrollbox", {
|
|
27437
27662
|
flexGrow: 1,
|
|
27438
|
-
children: warnings.map((warning, i2) => /* @__PURE__ */
|
|
27663
|
+
children: warnings.map((warning, i2) => /* @__PURE__ */ jsxs14("box", {
|
|
27439
27664
|
flexDirection: "column",
|
|
27440
27665
|
style: { marginBottom: 1 },
|
|
27441
27666
|
children: [
|
|
27442
|
-
/* @__PURE__ */
|
|
27667
|
+
/* @__PURE__ */ jsx16("text", {
|
|
27443
27668
|
fg: tokens.warning,
|
|
27444
27669
|
children: `⚠ ${warning.repoName}`
|
|
27445
27670
|
}),
|
|
27446
|
-
wrapText(warning.message,
|
|
27671
|
+
wrapText(warning.message, 72).map((line, j) => /* @__PURE__ */ jsx16("text", {
|
|
27447
27672
|
fg: tokens.fg,
|
|
27448
27673
|
children: ` ${line}`
|
|
27449
27674
|
}, j))
|
|
@@ -27451,9 +27676,46 @@ function WarningsOverlay({ warnings }) {
|
|
|
27451
27676
|
}, i2))
|
|
27452
27677
|
})
|
|
27453
27678
|
}),
|
|
27454
|
-
/* @__PURE__ */
|
|
27679
|
+
/* @__PURE__ */ jsxs14("box", {
|
|
27680
|
+
flexDirection: "row",
|
|
27681
|
+
gap: 3,
|
|
27682
|
+
style: { marginTop: 2 },
|
|
27683
|
+
justifyContent: "center",
|
|
27684
|
+
children: [
|
|
27685
|
+
/* @__PURE__ */ jsx16("box", {
|
|
27686
|
+
...ackTap,
|
|
27687
|
+
children: /* @__PURE__ */ jsxs14("text", {
|
|
27688
|
+
children: [
|
|
27689
|
+
/* @__PURE__ */ jsx16("span", {
|
|
27690
|
+
fg: tokens.dim,
|
|
27691
|
+
children: "[a] "
|
|
27692
|
+
}),
|
|
27693
|
+
/* @__PURE__ */ jsx16("span", {
|
|
27694
|
+
fg: tokens.success,
|
|
27695
|
+
children: "Acknowledge"
|
|
27696
|
+
})
|
|
27697
|
+
]
|
|
27698
|
+
})
|
|
27699
|
+
}),
|
|
27700
|
+
/* @__PURE__ */ jsx16("box", {
|
|
27701
|
+
...closeTap,
|
|
27702
|
+
children: /* @__PURE__ */ jsxs14("text", {
|
|
27703
|
+
children: [
|
|
27704
|
+
/* @__PURE__ */ jsx16("span", {
|
|
27705
|
+
fg: tokens.dim,
|
|
27706
|
+
children: "[esc] "
|
|
27707
|
+
}),
|
|
27708
|
+
/* @__PURE__ */ jsx16("span", {
|
|
27709
|
+
children: "Close"
|
|
27710
|
+
})
|
|
27711
|
+
]
|
|
27712
|
+
})
|
|
27713
|
+
})
|
|
27714
|
+
]
|
|
27715
|
+
}),
|
|
27716
|
+
/* @__PURE__ */ jsx16("text", {
|
|
27455
27717
|
style: { marginTop: 1, fg: tokens.dim },
|
|
27456
|
-
children: "Press
|
|
27718
|
+
children: "Press a to acknowledge and clear errors"
|
|
27457
27719
|
})
|
|
27458
27720
|
]
|
|
27459
27721
|
});
|
|
@@ -27462,13 +27724,14 @@ var init_WarningsOverlay = __esm(() => {
|
|
|
27462
27724
|
init_Overlay();
|
|
27463
27725
|
init_theme();
|
|
27464
27726
|
init_utils();
|
|
27727
|
+
init_use_tap();
|
|
27465
27728
|
});
|
|
27466
27729
|
|
|
27467
27730
|
// src/tui/hooks/useSpinnerFrame.ts
|
|
27468
|
-
import { useEffect as
|
|
27731
|
+
import { useEffect as useEffect6, useState as useState8 } from "react";
|
|
27469
27732
|
function useSpinnerFrame(active) {
|
|
27470
|
-
const [index, setIndex] =
|
|
27471
|
-
|
|
27733
|
+
const [index, setIndex] = useState8(0);
|
|
27734
|
+
useEffect6(() => {
|
|
27472
27735
|
if (!active)
|
|
27473
27736
|
return;
|
|
27474
27737
|
const id = setInterval(() => setIndex((i2) => (i2 + 1) % FRAMES.length), INTERVAL_MS);
|
|
@@ -27481,11 +27744,393 @@ var init_useSpinnerFrame = __esm(() => {
|
|
|
27481
27744
|
FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
27482
27745
|
});
|
|
27483
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
|
+
|
|
27484
28129
|
// src/tui/components/App.tsx
|
|
27485
|
-
import { useState as
|
|
28130
|
+
import { useState as useState10, useEffect as useEffect8, useMemo, useRef as useRef7, useCallback as useCallback6 } from "react";
|
|
27486
28131
|
import { existsSync as existsSync4 } from "node:fs";
|
|
27487
28132
|
import { useKeyboard as useKeyboard3, useRenderer as useRenderer2, useSelectionHandler, useTerminalDimensions } from "@opentui/react";
|
|
27488
|
-
import { jsx as
|
|
28133
|
+
import { jsx as jsx17, jsxs as jsxs15 } from "@opentui/react/jsx-runtime";
|
|
27489
28134
|
function getWorktreePathFor(repoName, branch) {
|
|
27490
28135
|
try {
|
|
27491
28136
|
const config2 = loadConfig();
|
|
@@ -27497,38 +28142,52 @@ function getWorktreePathFor(repoName, branch) {
|
|
|
27497
28142
|
}
|
|
27498
28143
|
function App({ opts }) {
|
|
27499
28144
|
const renderer = useRenderer2();
|
|
27500
|
-
const { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh } = useWorktrees(opts);
|
|
27501
|
-
const [selectedIndex, setSelectedIndex] =
|
|
27502
|
-
const [modal, setModal] =
|
|
27503
|
-
const [actionMessage, setActionMessage] =
|
|
27504
|
-
const messageTimer =
|
|
27505
|
-
const [ops, setOps] =
|
|
27506
|
-
const [failedLogs, setFailedLogs] =
|
|
27507
|
-
const nextOpId =
|
|
27508
|
-
const [createModal, setCreateModal] =
|
|
27509
|
-
const [createError, setCreateError] =
|
|
27510
|
-
const [createBaseModal, setCreateBaseModal] =
|
|
27511
|
-
const [createBaseError, setCreateBaseError] =
|
|
27512
|
-
const [createDepsChoice, setCreateDepsChoice] =
|
|
27513
|
-
const [pullPrModal, setPullPrModal] =
|
|
27514
|
-
const [pullPrError, setPullPrError] =
|
|
27515
|
-
const [pullForceChoice, setPullForceChoice] =
|
|
27516
|
-
const [renameModal, setRenameModal] =
|
|
27517
|
-
const [renameError, setRenameError] =
|
|
27518
|
-
const [configOpen, setConfigOpen] =
|
|
27519
|
-
const [refreshScopes, setRefreshScopes] =
|
|
27520
|
-
const [filterText, setFilterText] =
|
|
27521
|
-
const [isFiltering, setIsFiltering] =
|
|
27522
|
-
const [selection, setSelection] =
|
|
27523
|
-
const [splitRatio, setSplitRatio] =
|
|
27524
|
-
|
|
27525
|
-
|
|
27526
|
-
|
|
27527
|
-
|
|
27528
|
-
|
|
27529
|
-
|
|
27530
|
-
|
|
27531
|
-
|
|
28145
|
+
const { blocks, loading, refreshing, error: error52, warnings, lastRefreshed, pendingRepos, refresh, clearWarnings } = useWorktrees(opts);
|
|
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) => {
|
|
27532
28191
|
const targets = scope ?? [
|
|
27533
28192
|
...new Set([...blocks.map((b) => b.repoName), ...pendingRepos])
|
|
27534
28193
|
];
|
|
@@ -27539,18 +28198,18 @@ function App({ opts }) {
|
|
|
27539
28198
|
setRefreshScopes([]);
|
|
27540
28199
|
}
|
|
27541
28200
|
}, [blocks, pendingRepos, refresh]);
|
|
27542
|
-
|
|
28201
|
+
useEffect8(() => {
|
|
27543
28202
|
if (error52) {
|
|
27544
28203
|
setModal({ type: "error", message: error52 });
|
|
27545
28204
|
}
|
|
27546
28205
|
}, [error52]);
|
|
27547
|
-
const flash =
|
|
28206
|
+
const flash = useCallback6((message, ms = 3000) => {
|
|
27548
28207
|
if (messageTimer.current)
|
|
27549
28208
|
clearTimeout(messageTimer.current);
|
|
27550
28209
|
setActionMessage(message);
|
|
27551
28210
|
messageTimer.current = setTimeout(() => setActionMessage(undefined), ms);
|
|
27552
28211
|
}, []);
|
|
27553
|
-
const copySelectedText =
|
|
28212
|
+
const copySelectedText = useCallback6((warnOnEmpty) => {
|
|
27554
28213
|
if (isResizing) {
|
|
27555
28214
|
renderer.clearSelection();
|
|
27556
28215
|
return;
|
|
@@ -27580,12 +28239,130 @@ function App({ opts }) {
|
|
|
27580
28239
|
const flatRows = useMemo(() => displayBlocks.flatMap((b) => b.rows.filter((r) => !r.isPendingCreate)), [displayBlocks]);
|
|
27581
28240
|
const totalRows = blocks.flatMap((b) => b.rows).length;
|
|
27582
28241
|
const maxIndex = Math.max(0, flatRows.length - 1);
|
|
27583
|
-
|
|
28242
|
+
useEffect8(() => {
|
|
27584
28243
|
if (selectedIndex > maxIndex && maxIndex >= 0) {
|
|
27585
28244
|
setSelectedIndex(maxIndex);
|
|
27586
28245
|
}
|
|
27587
28246
|
}, [maxIndex, selectedIndex]);
|
|
27588
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]);
|
|
27589
28366
|
const { repoVerbs, rowVerbs } = useMemo(() => {
|
|
27590
28367
|
const rv = new Map;
|
|
27591
28368
|
const nv = new Map;
|
|
@@ -27724,7 +28501,7 @@ function App({ opts }) {
|
|
|
27724
28501
|
args.push("--force");
|
|
27725
28502
|
executeOp(op, args, [repoName]);
|
|
27726
28503
|
};
|
|
27727
|
-
const handleHintClick =
|
|
28504
|
+
const handleHintClick = useCallback6((key) => {
|
|
27728
28505
|
if (key === "c") {
|
|
27729
28506
|
setConfigOpen(true);
|
|
27730
28507
|
return;
|
|
@@ -27876,8 +28653,72 @@ function App({ opts }) {
|
|
|
27876
28653
|
setModal({ type: "confirm_sync", rows: targets });
|
|
27877
28654
|
return;
|
|
27878
28655
|
}
|
|
27879
|
-
|
|
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]);
|
|
27880
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
|
+
}
|
|
27881
28722
|
if (isFiltering) {
|
|
27882
28723
|
if (key.name === "escape") {
|
|
27883
28724
|
setIsFiltering(false);
|
|
@@ -27927,7 +28768,20 @@ function App({ opts }) {
|
|
|
27927
28768
|
return;
|
|
27928
28769
|
}
|
|
27929
28770
|
if (modal.type !== "none") {
|
|
27930
|
-
if (modal.type === "
|
|
28771
|
+
if (modal.type === "warnings") {
|
|
28772
|
+
if (key.name === "a") {
|
|
28773
|
+
clearWarnings();
|
|
28774
|
+
setModal({ type: "none" });
|
|
28775
|
+
return;
|
|
28776
|
+
}
|
|
28777
|
+
if (key.name === "escape" || key.name === "q" || key.name === "enter" || key.name === "return") {
|
|
28778
|
+
setModal({ type: "none" });
|
|
28779
|
+
return;
|
|
28780
|
+
}
|
|
28781
|
+
setModal({ type: "none" });
|
|
28782
|
+
return;
|
|
28783
|
+
}
|
|
28784
|
+
if (modal.type === "error" || modal.type === "help" || modal.type === "history") {
|
|
27931
28785
|
setModal({ type: "none" });
|
|
27932
28786
|
if (modal.type === "error" && error52) {
|
|
27933
28787
|
renderer.destroy();
|
|
@@ -28176,108 +29030,106 @@ function App({ opts }) {
|
|
|
28176
29030
|
setModal({ type: "confirm_sync", rows: targets });
|
|
28177
29031
|
return;
|
|
28178
29032
|
}
|
|
28179
|
-
if (key.name === "
|
|
28180
|
-
|
|
28181
|
-
|
|
28182
|
-
return;
|
|
28183
|
-
const target = targets[0];
|
|
28184
|
-
if (!target)
|
|
29033
|
+
if (key.name === "t") {
|
|
29034
|
+
if (!selectedRow) {
|
|
29035
|
+
flash("No worktree selected");
|
|
28185
29036
|
return;
|
|
28186
|
-
|
|
28187
|
-
|
|
28188
|
-
|
|
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`);
|
|
28189
29041
|
return;
|
|
28190
29042
|
}
|
|
28191
|
-
(
|
|
28192
|
-
|
|
28193
|
-
try {
|
|
28194
|
-
const config2 = await loadConfig();
|
|
28195
|
-
const cmdTemplate = resolveAgentCommand("claude", config2.agents) ?? "claude";
|
|
28196
|
-
const result = await spawnAgentInWorktree(cmdTemplate, target.path, { repoName: target.repoName, branch: target.branch });
|
|
28197
|
-
appendHistory({
|
|
28198
|
-
ts: new Date().toISOString(),
|
|
28199
|
-
source: "terminal",
|
|
28200
|
-
command: "agent",
|
|
28201
|
-
args: ["agent", target.branch, "--repo", target.repoName],
|
|
28202
|
-
durationMs: Date.now() - startedAt,
|
|
28203
|
-
exit: 0
|
|
28204
|
-
});
|
|
28205
|
-
flash(`Agent spawned (${result.mode}${result.session ? ` session ${result.session}` : ""})`, 5000);
|
|
28206
|
-
} catch (e) {
|
|
28207
|
-
appendHistory({
|
|
28208
|
-
ts: new Date().toISOString(),
|
|
28209
|
-
source: "terminal",
|
|
28210
|
-
command: "agent",
|
|
28211
|
-
args: ["agent", target.branch, "--repo", target.repoName],
|
|
28212
|
-
durationMs: Date.now() - startedAt,
|
|
28213
|
-
exit: 1
|
|
28214
|
-
});
|
|
28215
|
-
flash(`Agent failed: ${e.message}`, 5000);
|
|
28216
|
-
}
|
|
28217
|
-
})();
|
|
29043
|
+
handleAddSession();
|
|
29044
|
+
return;
|
|
28218
29045
|
}
|
|
28219
29046
|
});
|
|
28220
29047
|
const rawLeft = Math.floor(totalWidth * splitRatio);
|
|
28221
29048
|
const leftCols = Math.max(20, Math.min(totalWidth - 20 - DIVIDER_WIDTH, rawLeft));
|
|
28222
29049
|
const rightCols = Math.max(20, totalWidth - leftCols - DIVIDER_WIDTH);
|
|
28223
|
-
|
|
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", {
|
|
28224
29065
|
flexDirection: "column",
|
|
28225
29066
|
width: "100%",
|
|
28226
29067
|
height: "100%",
|
|
28227
29068
|
children: [
|
|
28228
|
-
/* @__PURE__ */
|
|
29069
|
+
/* @__PURE__ */ jsxs15("box", {
|
|
28229
29070
|
flexDirection: "row",
|
|
28230
29071
|
width: "100%",
|
|
28231
29072
|
flexGrow: 1,
|
|
28232
29073
|
children: [
|
|
28233
|
-
/* @__PURE__ */
|
|
29074
|
+
/* @__PURE__ */ jsx17("box", {
|
|
28234
29075
|
width: leftCols,
|
|
28235
29076
|
height: "100%",
|
|
28236
29077
|
flexDirection: "column",
|
|
28237
|
-
|
|
29078
|
+
onMouseDown: () => setTerminalFocused(false),
|
|
29079
|
+
children: /* @__PURE__ */ jsx17(WorktreeTable, {
|
|
28238
29080
|
blocks: displayBlocks,
|
|
28239
29081
|
selectedIndex,
|
|
28240
29082
|
selection,
|
|
28241
29083
|
frame: spinnerFrame,
|
|
28242
29084
|
repoVerbs,
|
|
28243
29085
|
rowVerbs,
|
|
28244
|
-
onRowClick: (idx) =>
|
|
29086
|
+
onRowClick: (idx) => {
|
|
29087
|
+
setTerminalFocused(false);
|
|
29088
|
+
setSelectedIndex(idx);
|
|
29089
|
+
},
|
|
28245
29090
|
onToggleSelect: (path37) => setSelection((prev) => toggleSelection(prev, path37))
|
|
28246
29091
|
})
|
|
28247
29092
|
}),
|
|
28248
|
-
/* @__PURE__ */
|
|
29093
|
+
/* @__PURE__ */ jsx17(Divider, {
|
|
28249
29094
|
splitRatio,
|
|
28250
29095
|
totalWidth,
|
|
28251
29096
|
onChange: setSplitRatio,
|
|
28252
29097
|
onDraggingChange: setIsResizing
|
|
28253
29098
|
}),
|
|
28254
|
-
/* @__PURE__ */
|
|
29099
|
+
/* @__PURE__ */ jsx17("box", {
|
|
28255
29100
|
width: rightCols,
|
|
28256
29101
|
height: "100%",
|
|
28257
29102
|
flexDirection: "column",
|
|
28258
|
-
children: /* @__PURE__ */
|
|
28259
|
-
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
|
|
28260
29112
|
})
|
|
28261
29113
|
})
|
|
28262
29114
|
]
|
|
28263
29115
|
}),
|
|
28264
|
-
isFiltering && /* @__PURE__ */
|
|
29116
|
+
isFiltering && /* @__PURE__ */ jsxs15("box", {
|
|
28265
29117
|
flexDirection: "row",
|
|
28266
29118
|
paddingX: 1,
|
|
28267
29119
|
border: true,
|
|
28268
29120
|
borderColor: "magenta",
|
|
28269
29121
|
children: [
|
|
28270
|
-
/* @__PURE__ */
|
|
29122
|
+
/* @__PURE__ */ jsx17("text", {
|
|
28271
29123
|
children: "filter: "
|
|
28272
29124
|
}),
|
|
28273
|
-
/* @__PURE__ */
|
|
29125
|
+
/* @__PURE__ */ jsx17("input", {
|
|
28274
29126
|
focused: true,
|
|
28275
29127
|
placeholder: "Type to filter...",
|
|
28276
29128
|
onInput: (v) => setFilterText(v)
|
|
28277
29129
|
})
|
|
28278
29130
|
]
|
|
28279
29131
|
}),
|
|
28280
|
-
/* @__PURE__ */
|
|
29132
|
+
/* @__PURE__ */ jsx17(Footer, {
|
|
28281
29133
|
loading: loading || refreshing,
|
|
28282
29134
|
lastRefreshed,
|
|
28283
29135
|
errorCount: warnings.length,
|
|
@@ -28288,12 +29140,17 @@ function App({ opts }) {
|
|
|
28288
29140
|
onHintClick: handleHintClick,
|
|
28289
29141
|
onErrorClick: () => setModal({ type: "warnings" })
|
|
28290
29142
|
}),
|
|
28291
|
-
modal.type === "help" && /* @__PURE__ */
|
|
28292
|
-
modal.type === "history" && /* @__PURE__ */
|
|
28293
|
-
modal.type === "warnings" && /* @__PURE__ */
|
|
28294
|
-
warnings
|
|
29143
|
+
modal.type === "help" && /* @__PURE__ */ jsx17(HelpOverlay, {}),
|
|
29144
|
+
modal.type === "history" && /* @__PURE__ */ jsx17(HistoryOverlay, {}),
|
|
29145
|
+
modal.type === "warnings" && /* @__PURE__ */ jsx17(WarningsOverlay, {
|
|
29146
|
+
warnings,
|
|
29147
|
+
onAcknowledge: () => {
|
|
29148
|
+
clearWarnings();
|
|
29149
|
+
setModal({ type: "none" });
|
|
29150
|
+
},
|
|
29151
|
+
onClose: () => setModal({ type: "none" })
|
|
28295
29152
|
}),
|
|
28296
|
-
modal.type === "error" && /* @__PURE__ */
|
|
29153
|
+
modal.type === "error" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28297
29154
|
title: "Error",
|
|
28298
29155
|
message: modal.message,
|
|
28299
29156
|
onConfirm: () => {
|
|
@@ -28311,7 +29168,7 @@ function App({ opts }) {
|
|
|
28311
29168
|
}
|
|
28312
29169
|
}
|
|
28313
29170
|
}),
|
|
28314
|
-
modal.type === "confirm_remove" && /* @__PURE__ */
|
|
29171
|
+
modal.type === "confirm_remove" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28315
29172
|
title: `Remove ${modal.rows.length} Worktree(s)`,
|
|
28316
29173
|
message: (() => {
|
|
28317
29174
|
const count2 = modal.rows.length;
|
|
@@ -28340,7 +29197,7 @@ function App({ opts }) {
|
|
|
28340
29197
|
},
|
|
28341
29198
|
onCancel: () => setModal({ type: "none" })
|
|
28342
29199
|
}),
|
|
28343
|
-
modal.type === "confirm_rebase" && /* @__PURE__ */
|
|
29200
|
+
modal.type === "confirm_rebase" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28344
29201
|
title: `Rebase ${modal.rows.length} Worktree(s)`,
|
|
28345
29202
|
message: `Are you sure you want to fetch and rebase ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`,
|
|
28346
29203
|
onConfirm: () => {
|
|
@@ -28350,7 +29207,7 @@ function App({ opts }) {
|
|
|
28350
29207
|
},
|
|
28351
29208
|
onCancel: () => setModal({ type: "none" })
|
|
28352
29209
|
}),
|
|
28353
|
-
modal.type === "confirm_sync" && /* @__PURE__ */
|
|
29210
|
+
modal.type === "confirm_sync" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28354
29211
|
title: `Sync ${modal.rows.length} Worktree(s)`,
|
|
28355
29212
|
message: `Are you sure you want to sync ${modal.rows.length === 1 ? modal.rows[0]?.branch : modal.rows.length + " worktrees"}?`,
|
|
28356
29213
|
onConfirm: () => {
|
|
@@ -28360,7 +29217,7 @@ function App({ opts }) {
|
|
|
28360
29217
|
},
|
|
28361
29218
|
onCancel: () => setModal({ type: "none" })
|
|
28362
29219
|
}),
|
|
28363
|
-
createModal && /* @__PURE__ */
|
|
29220
|
+
createModal && /* @__PURE__ */ jsx17(InputModal, {
|
|
28364
29221
|
title: `New worktree branch in ${selectedRow?.repoName ?? ""}`,
|
|
28365
29222
|
placeholder: "Branch name (empty to cancel)",
|
|
28366
29223
|
errorMessage: createError,
|
|
@@ -28387,7 +29244,7 @@ function App({ opts }) {
|
|
|
28387
29244
|
setCreateBaseModal({ branch, repoName });
|
|
28388
29245
|
}
|
|
28389
29246
|
}),
|
|
28390
|
-
createBaseModal && /* @__PURE__ */
|
|
29247
|
+
createBaseModal && /* @__PURE__ */ jsx17(InputModal, {
|
|
28391
29248
|
title: `Base ref for ${createBaseModal.branch}`,
|
|
28392
29249
|
placeholder: "origin/main (empty for default main)",
|
|
28393
29250
|
errorMessage: createBaseError,
|
|
@@ -28403,7 +29260,7 @@ function App({ opts }) {
|
|
|
28403
29260
|
setCreateDepsChoice({ branch, repoName, base: base2 || undefined });
|
|
28404
29261
|
}
|
|
28405
29262
|
}),
|
|
28406
|
-
createDepsChoice && /* @__PURE__ */
|
|
29263
|
+
createDepsChoice && /* @__PURE__ */ jsx17(ChoiceModal, {
|
|
28407
29264
|
title: `Dependencies for ${createDepsChoice.branch}`,
|
|
28408
29265
|
options: DEPS_CHOICES,
|
|
28409
29266
|
onSubmit: (choice) => {
|
|
@@ -28413,7 +29270,7 @@ function App({ opts }) {
|
|
|
28413
29270
|
},
|
|
28414
29271
|
onCancel: () => setCreateDepsChoice(null)
|
|
28415
29272
|
}),
|
|
28416
|
-
pullPrModal && /* @__PURE__ */
|
|
29273
|
+
pullPrModal && /* @__PURE__ */ jsx17(InputModal, {
|
|
28417
29274
|
title: `Pull PR into ${selectedRow?.repoName ?? ""}`,
|
|
28418
29275
|
placeholder: "https://github.com/owner/repo/pull/123",
|
|
28419
29276
|
errorMessage: pullPrError,
|
|
@@ -28441,7 +29298,7 @@ function App({ opts }) {
|
|
|
28441
29298
|
setPullForceChoice({ link, repoName });
|
|
28442
29299
|
}
|
|
28443
29300
|
}),
|
|
28444
|
-
pullForceChoice && /* @__PURE__ */
|
|
29301
|
+
pullForceChoice && /* @__PURE__ */ jsx17(ChoiceModal, {
|
|
28445
29302
|
title: `Pull ${pullForceChoice.link.split("/").pop()}?`,
|
|
28446
29303
|
options: [
|
|
28447
29304
|
{ value: "normal", label: "Pull (skip if exists)", desc: "Fails if branch already exists" },
|
|
@@ -28454,7 +29311,7 @@ function App({ opts }) {
|
|
|
28454
29311
|
},
|
|
28455
29312
|
onCancel: () => setPullForceChoice(null)
|
|
28456
29313
|
}),
|
|
28457
|
-
renameModal && selectedRow && /* @__PURE__ */
|
|
29314
|
+
renameModal && selectedRow && /* @__PURE__ */ jsx17(InputModal, {
|
|
28458
29315
|
title: `Rename branch ${selectedRow.branch}`,
|
|
28459
29316
|
initialValue: selectedRow.branch,
|
|
28460
29317
|
placeholder: "New branch name",
|
|
@@ -28479,7 +29336,7 @@ function App({ opts }) {
|
|
|
28479
29336
|
setModal({ type: "confirm_rename", row: target, to });
|
|
28480
29337
|
}
|
|
28481
29338
|
}),
|
|
28482
|
-
modal.type === "confirm_rename" && /* @__PURE__ */
|
|
29339
|
+
modal.type === "confirm_rename" && /* @__PURE__ */ jsx17(ConfirmModal, {
|
|
28483
29340
|
title: "Rename Worktree",
|
|
28484
29341
|
message: [
|
|
28485
29342
|
`${modal.row.branch} → ${modal.to}`,
|
|
@@ -28510,12 +29367,12 @@ function App({ opts }) {
|
|
|
28510
29367
|
},
|
|
28511
29368
|
onCancel: () => setModal({ type: "none" })
|
|
28512
29369
|
}),
|
|
28513
|
-
configOpen && /* @__PURE__ */
|
|
29370
|
+
configOpen && /* @__PURE__ */ jsx17(ConfigOverlay, {
|
|
28514
29371
|
onClose: () => setConfigOpen(false),
|
|
28515
29372
|
onSaved: () => void doRefresh(),
|
|
28516
29373
|
onError: (msg) => setModal({ type: "error", message: msg })
|
|
28517
29374
|
}),
|
|
28518
|
-
failedLogs.length > 0 && /* @__PURE__ */
|
|
29375
|
+
failedLogs.length > 0 && /* @__PURE__ */ jsx17(ActionLogModal, {
|
|
28519
29376
|
title: failedLogs[0].title,
|
|
28520
29377
|
lines: failedLogs[0].lines,
|
|
28521
29378
|
done: true,
|
|
@@ -28529,7 +29386,9 @@ var VERBS, capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1), DEPS_CHOI
|
|
|
28529
29386
|
var init_App = __esm(() => {
|
|
28530
29387
|
init_useWorktrees();
|
|
28531
29388
|
init_WorktreeTable();
|
|
28532
|
-
|
|
29389
|
+
init_TabPane();
|
|
29390
|
+
init_DetailsTab();
|
|
29391
|
+
init_TerminalView();
|
|
28533
29392
|
init_Footer();
|
|
28534
29393
|
init_Divider();
|
|
28535
29394
|
init_HelpOverlay();
|
|
@@ -28542,10 +29401,9 @@ var init_App = __esm(() => {
|
|
|
28542
29401
|
init_ConfigOverlay();
|
|
28543
29402
|
init_WarningsOverlay();
|
|
28544
29403
|
init_utils();
|
|
28545
|
-
init_agents();
|
|
28546
29404
|
init_config();
|
|
28547
|
-
init_history();
|
|
28548
29405
|
init_useSpinnerFrame();
|
|
29406
|
+
init_useTerminalSessions();
|
|
28549
29407
|
VERBS = {
|
|
28550
29408
|
create: "creating worktree",
|
|
28551
29409
|
remove: "deleting",
|
|
@@ -28569,16 +29427,16 @@ var exports_tui = {};
|
|
|
28569
29427
|
__export(exports_tui, {
|
|
28570
29428
|
runTerminal: () => runTerminal
|
|
28571
29429
|
});
|
|
28572
|
-
import { createCliRenderer, TextTableRenderable } from "@opentui/core";
|
|
29430
|
+
import { createCliRenderer, TextTableRenderable, EmbeddedTerminalRenderable } from "@opentui/core";
|
|
28573
29431
|
import { createRoot, extend as extend2 } from "@opentui/react";
|
|
28574
|
-
import { jsx as
|
|
29432
|
+
import { jsx as jsx18 } from "@opentui/react/jsx-runtime";
|
|
28575
29433
|
async function runTerminal(opts) {
|
|
28576
29434
|
const renderer = await createCliRenderer({
|
|
28577
29435
|
exitOnCtrlC: false
|
|
28578
29436
|
});
|
|
28579
29437
|
const root = createRoot(renderer);
|
|
28580
29438
|
try {
|
|
28581
|
-
root.render(/* @__PURE__ */
|
|
29439
|
+
root.render(/* @__PURE__ */ jsx18(App, {
|
|
28582
29440
|
opts
|
|
28583
29441
|
}));
|
|
28584
29442
|
} catch (err) {
|
|
@@ -28589,7 +29447,7 @@ async function runTerminal(opts) {
|
|
|
28589
29447
|
}
|
|
28590
29448
|
var init_tui = __esm(() => {
|
|
28591
29449
|
init_App();
|
|
28592
|
-
extend2({ "text-table": TextTableRenderable });
|
|
29450
|
+
extend2({ "text-table": TextTableRenderable, "embedded-terminal": EmbeddedTerminalRenderable });
|
|
28593
29451
|
});
|
|
28594
29452
|
|
|
28595
29453
|
// node_modules/commander/lib/error.js
|
|
@@ -30928,7 +31786,80 @@ async function runPostCreateSetup(params) {
|
|
|
30928
31786
|
init_deps();
|
|
30929
31787
|
init_forge();
|
|
30930
31788
|
init_owner();
|
|
30931
|
-
|
|
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
|
|
30932
31863
|
var DEPS_STRATEGIES = ["auto", "link", "symlink", "install", "off"];
|
|
30933
31864
|
async function applyDepsStrategy(strategy, wtPath, mainPath, opts) {
|
|
30934
31865
|
if (strategy === "install") {
|