@joeldmtz/prtl 0.1.2
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.d.ts +10 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +106 -0
- package/dist/format.d.ts +5 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +57 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +3 -0
- package/dist/prompt.d.ts +2 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +8 -0
- package/dist/tui/App.d.ts +10 -0
- package/dist/tui/App.d.ts.map +1 -0
- package/dist/tui/App.js +121 -0
- package/dist/tui/index.d.ts +6 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +16 -0
- package/package.json +35 -0
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
export interface CliIO {
|
|
3
|
+
write(text: string): void;
|
|
4
|
+
error(text: string): void;
|
|
5
|
+
isInteractive: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const createProgram: (io?: CliIO) => Command;
|
|
8
|
+
export declare const defaultIO: () => CliIO;
|
|
9
|
+
export declare const runCli: (argv?: string[], io?: CliIO) => Promise<void>;
|
|
10
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,MAAM,WAAW,KAAK;IACpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;CACxB;AAOD,eAAO,MAAM,aAAa,GAAI,KAAI,KAAmB,KAAG,OA6EvD,CAAC;AAEF,eAAO,MAAM,SAAS,QAAO,KAI3B,CAAC;AAEH,eAAO,MAAM,MAAM,GAAU,OAAM,MAAM,EAAiB,EAAE,KAAK,KAAK,KAAG,OAAO,CAAC,IAAI,CAmBpF,CAAC"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { deleteTarget, listSessions, openTarget, resolveTarget } from "@joeldmtz/prtl-core";
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { formatHumanList, formatOperationResult } from "./format.js";
|
|
4
|
+
import { confirm } from "./prompt.js";
|
|
5
|
+
import { renderInteractive } from "./tui/index.js";
|
|
6
|
+
const providerOption = (value) => {
|
|
7
|
+
if (value === "herdr" || value === "tmux")
|
|
8
|
+
return value;
|
|
9
|
+
throw new Error("provider must be herdr or tmux");
|
|
10
|
+
};
|
|
11
|
+
export const createProgram = (io = defaultIO()) => {
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name("prtl")
|
|
15
|
+
.description("Portal for Herdr and tmux sessions")
|
|
16
|
+
.version("0.1.0")
|
|
17
|
+
.showHelpAfterError()
|
|
18
|
+
.exitOverride();
|
|
19
|
+
program
|
|
20
|
+
.command("interactive")
|
|
21
|
+
.aliases(["i", "select"])
|
|
22
|
+
.description("Open the interactive session selector")
|
|
23
|
+
.action(() => renderInteractive());
|
|
24
|
+
program
|
|
25
|
+
.command("list")
|
|
26
|
+
.alias("ls")
|
|
27
|
+
.description("List Herdr and tmux sessions")
|
|
28
|
+
.option("--json", "Output normalized prtl JSON")
|
|
29
|
+
.option("--provider <provider>", "Filter provider: herdr|tmux", providerOption)
|
|
30
|
+
.action(async (options) => {
|
|
31
|
+
const result = await listSessions({ provider: options.provider, timeoutMs: 900 });
|
|
32
|
+
io.write(options.json ? `${JSON.stringify(result, null, 2)}\n` : formatHumanList(result));
|
|
33
|
+
});
|
|
34
|
+
program
|
|
35
|
+
.command("open <name>")
|
|
36
|
+
.alias("o")
|
|
37
|
+
.description("Open a session or pane by name")
|
|
38
|
+
.option("--provider <provider>", "Filter provider: herdr|tmux", providerOption)
|
|
39
|
+
.action(async (name, options) => {
|
|
40
|
+
const listed = await listSessions({ provider: options.provider, timeoutMs: 900 });
|
|
41
|
+
const resolved = resolveTarget(listed.items, name);
|
|
42
|
+
if (resolved.type === "ambiguous" && io.isInteractive) {
|
|
43
|
+
renderInteractive({ items: resolved.matches, query: name });
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const result = resolved.type === "resolved" ? await openTarget(name, { items: listed.items }) : resolved;
|
|
47
|
+
io.write(formatOperationResult(result));
|
|
48
|
+
if (result.type === "not_found" || result.type === "ambiguous" || result.type === "failed")
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
});
|
|
51
|
+
program
|
|
52
|
+
.command("delete <name>")
|
|
53
|
+
.aliases(["rm", "del"])
|
|
54
|
+
.description("Delete a session or pane by name")
|
|
55
|
+
.option("--provider <provider>", "Filter provider: herdr|tmux", providerOption)
|
|
56
|
+
.option("--yes", "Skip confirmation prompt")
|
|
57
|
+
.action(async (name, options) => {
|
|
58
|
+
const listed = await listSessions({ provider: options.provider, timeoutMs: 900 });
|
|
59
|
+
const resolved = resolveTarget(listed.items, name);
|
|
60
|
+
if (resolved.type !== "resolved") {
|
|
61
|
+
io.write(formatOperationResult(resolved));
|
|
62
|
+
process.exitCode = 1;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!options.yes) {
|
|
66
|
+
if (!io.isInteractive) {
|
|
67
|
+
io.error("Refusing to delete without --yes in noninteractive mode.\n");
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const accepted = await confirm(`Delete ${resolved.target.provider} ${resolved.target.id}?`);
|
|
72
|
+
if (!accepted) {
|
|
73
|
+
io.write("Delete cancelled.\n");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const result = await deleteTarget(name, { items: listed.items });
|
|
78
|
+
io.write(formatOperationResult(result));
|
|
79
|
+
if (result.type === "failed")
|
|
80
|
+
process.exitCode = 1;
|
|
81
|
+
});
|
|
82
|
+
return program;
|
|
83
|
+
};
|
|
84
|
+
export const defaultIO = () => ({
|
|
85
|
+
write: (text) => process.stdout.write(text),
|
|
86
|
+
error: (text) => process.stderr.write(text),
|
|
87
|
+
isInteractive: Boolean(process.stdin.isTTY && process.stdout.isTTY)
|
|
88
|
+
});
|
|
89
|
+
export const runCli = async (argv = process.argv, io) => {
|
|
90
|
+
if (argv.length <= 2) {
|
|
91
|
+
renderInteractive();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const program = createProgram(io);
|
|
95
|
+
try {
|
|
96
|
+
await program.parseAsync(argv);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error instanceof Error &&
|
|
100
|
+
"code" in error &&
|
|
101
|
+
error.code === "commander.helpDisplayed") {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
};
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { PrtlListResult, PrtlPane, ProviderOperationResult } from "@joeldmtz/prtl-core";
|
|
2
|
+
export declare const describeTarget: (item: PrtlPane) => string;
|
|
3
|
+
export declare const formatHumanList: (result: PrtlListResult) => string;
|
|
4
|
+
export declare const formatOperationResult: (result: ProviderOperationResult) => string;
|
|
5
|
+
//# sourceMappingURL=format.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,cAAc,EACd,QAAQ,EACR,uBAAuB,EAExB,MAAM,qBAAqB,CAAC;AAI7B,eAAO,MAAM,cAAc,GAAI,MAAM,QAAQ,KAAG,MAW/C,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,QAAQ,cAAc,KAAG,MAuCxD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,QAAQ,uBAAuB,KAAG,MAevE,CAAC"}
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const providerLabel = (item) => item.provider.toUpperCase();
|
|
2
|
+
export const describeTarget = (item) => {
|
|
3
|
+
const hierarchy = item.provider === "herdr"
|
|
4
|
+
? [item.workspace, item.tab, item.name ?? item.id]
|
|
5
|
+
: [item.session, item.tab, item.name ?? item.id];
|
|
6
|
+
const context = hierarchy.filter(Boolean).join(" > ");
|
|
7
|
+
const agent = item.agent?.name
|
|
8
|
+
? ` agent=${item.agent.name}${item.agent.status ? `:${item.agent.status}` : ""}`
|
|
9
|
+
: "";
|
|
10
|
+
const cwd = item.cwd ? ` cwd=${item.cwd}` : "";
|
|
11
|
+
return `${providerLabel(item)} ${item.id} ${context}${agent}${cwd}`.trim();
|
|
12
|
+
};
|
|
13
|
+
export const formatHumanList = (result) => {
|
|
14
|
+
const lines = [];
|
|
15
|
+
lines.push(`prtl ${result.version} · ${result.items.length} panes · ${result.generatedAt}`);
|
|
16
|
+
for (const [provider, status] of Object.entries(result.metadata.providers)) {
|
|
17
|
+
const source = status.source ? ` via ${status.source}` : "";
|
|
18
|
+
lines.push(`${provider.toUpperCase()}: ${status.available ? "available" : "unavailable"}${source}`);
|
|
19
|
+
}
|
|
20
|
+
const grouped = result.items.reduce((groups, item) => {
|
|
21
|
+
groups[item.provider] ??= [];
|
|
22
|
+
groups[item.provider].push(item);
|
|
23
|
+
return groups;
|
|
24
|
+
}, {});
|
|
25
|
+
for (const [provider, items] of Object.entries(grouped)) {
|
|
26
|
+
lines.push("");
|
|
27
|
+
lines.push(provider.toUpperCase());
|
|
28
|
+
for (const item of items) {
|
|
29
|
+
const hierarchy = item.provider === "herdr"
|
|
30
|
+
? `${item.workspace ?? "?"} > ${item.tab ?? "?"}`
|
|
31
|
+
: `${item.session ?? "?"} > ${item.tab ?? "?"}`;
|
|
32
|
+
const label = item.name ?? item.id;
|
|
33
|
+
const agent = item.agent?.name
|
|
34
|
+
? ` · ${item.agent.name}${item.agent.status ? ` ${item.agent.status}` : ""}`
|
|
35
|
+
: "";
|
|
36
|
+
const detail = item.command ?? item.cwd ?? "";
|
|
37
|
+
lines.push(` ${item.focused ? "*" : " "} ${item.id} ${hierarchy} > ${label}${agent}${detail ? ` · ${detail}` : ""}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return `${lines.join("\n")}\n`;
|
|
41
|
+
};
|
|
42
|
+
export const formatOperationResult = (result) => {
|
|
43
|
+
switch (result.type) {
|
|
44
|
+
case "opened":
|
|
45
|
+
return `Opened ${describeTarget(result.target)}\n`;
|
|
46
|
+
case "deleted":
|
|
47
|
+
return `Deleted ${describeTarget(result.target)}\n`;
|
|
48
|
+
case "failed":
|
|
49
|
+
return `Failed ${describeTarget(result.target)}: ${result.error}\n`;
|
|
50
|
+
case "ambiguous":
|
|
51
|
+
return `Ambiguous target '${result.query}'. Matches:\n${result.matches.map((item) => ` - ${describeTarget(item)}`).join("\n")}\n`;
|
|
52
|
+
case "not_found":
|
|
53
|
+
return `No target found for '${result.query}'.\n`;
|
|
54
|
+
default:
|
|
55
|
+
return "Unknown operation result.\n";
|
|
56
|
+
}
|
|
57
|
+
};
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":""}
|
package/dist/main.js
ADDED
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,GAAU,SAAS,MAAM,KAAG,OAAO,CAAC,OAAO,CAO9D,CAAC"}
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type PrtlPane } from "@joeldmtz/prtl-core";
|
|
2
|
+
import React from "react";
|
|
3
|
+
interface AppProps {
|
|
4
|
+
initialItems?: PrtlPane[];
|
|
5
|
+
initialQuery?: string;
|
|
6
|
+
onOpen?: (item: PrtlPane, items: PrtlPane[]) => Promise<void> | void;
|
|
7
|
+
}
|
|
8
|
+
export declare const App: ({ initialItems, initialQuery, onOpen }: AppProps) => React.ReactElement;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=App.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,QAAQ,EACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAA8B,MAAM,OAAO,CAAC;AAMnD,UAAU,QAAQ;IAChB,YAAY,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACtE;AAgBD,eAAO,MAAM,GAAG,GAAI,wCAA6C,QAAQ,KAAG,KAAK,CAAC,YAyLjF,CAAC"}
|
package/dist/tui/App.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { deleteTarget, filterItems, listSessions, openTarget } from "@joeldmtz/prtl-core";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { Box, Text, useApp, useInput } from "ink";
|
|
5
|
+
import TextInput from "ink-text-input";
|
|
6
|
+
const truncate = (value, max) => {
|
|
7
|
+
if (!value)
|
|
8
|
+
return "";
|
|
9
|
+
return value.length > max ? `${value.slice(0, Math.max(0, max - 1))}…` : value;
|
|
10
|
+
};
|
|
11
|
+
const statusLine = (result) => {
|
|
12
|
+
if (!result)
|
|
13
|
+
return "loading providers";
|
|
14
|
+
return Object.entries(result.metadata.providers)
|
|
15
|
+
.map(([provider, status]) => `${provider}:${status.available ? (status.source ?? "ok") : "down"}`)
|
|
16
|
+
.join(" ");
|
|
17
|
+
};
|
|
18
|
+
export const App = ({ initialItems, initialQuery = "", onOpen }) => {
|
|
19
|
+
const { exit } = useApp();
|
|
20
|
+
const columns = process.stdout.columns ?? 80;
|
|
21
|
+
const rows = process.stdout.rows ?? 24;
|
|
22
|
+
const [result, setResult] = useState(initialItems
|
|
23
|
+
? {
|
|
24
|
+
version: "0.1.0",
|
|
25
|
+
generatedAt: new Date().toISOString(),
|
|
26
|
+
items: initialItems,
|
|
27
|
+
metadata: { providers: {} }
|
|
28
|
+
}
|
|
29
|
+
: undefined);
|
|
30
|
+
const [query, setQuery] = useState(initialQuery);
|
|
31
|
+
const [selected, setSelected] = useState(0);
|
|
32
|
+
const [mode, setMode] = useState("list");
|
|
33
|
+
const [message, setMessage] = useState();
|
|
34
|
+
const load = async () => {
|
|
35
|
+
setMessage(undefined);
|
|
36
|
+
setResult(await listSessions({ timeoutMs: 900 }));
|
|
37
|
+
setSelected(0);
|
|
38
|
+
};
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (!initialItems)
|
|
41
|
+
void load();
|
|
42
|
+
}, []);
|
|
43
|
+
const items = filterItems(result?.items ?? [], query);
|
|
44
|
+
const current = items[Math.min(selected, Math.max(0, items.length - 1))];
|
|
45
|
+
const isWide = columns >= 100;
|
|
46
|
+
const isNarrow = columns < 70;
|
|
47
|
+
const visibleRows = Math.max(4, rows - 8);
|
|
48
|
+
const start = Math.max(0, Math.min(selected - Math.floor(visibleRows / 2), Math.max(0, items.length - visibleRows)));
|
|
49
|
+
const visibleItems = items.slice(start, start + visibleRows);
|
|
50
|
+
useInput((input, key) => {
|
|
51
|
+
if (mode === "search") {
|
|
52
|
+
if (key.escape || key.return)
|
|
53
|
+
setMode("list");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (mode === "metadata") {
|
|
57
|
+
if (key.escape || input === "q" || input === "m")
|
|
58
|
+
setMode("list");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (mode === "delete") {
|
|
62
|
+
if (key.escape || input === "n" || input === "q")
|
|
63
|
+
setMode("list");
|
|
64
|
+
if (input === "y" && current) {
|
|
65
|
+
void deleteTarget(current.id, { items }).then((deleted) => {
|
|
66
|
+
setMessage(deleted.type === "deleted" ? `Deleted ${current.id}` : deleted.type);
|
|
67
|
+
setMode("list");
|
|
68
|
+
void load();
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (input === "q" || key.escape)
|
|
74
|
+
exit();
|
|
75
|
+
if (input === "/")
|
|
76
|
+
setMode("search");
|
|
77
|
+
if (input === "r")
|
|
78
|
+
void load();
|
|
79
|
+
if (input === "m" && current)
|
|
80
|
+
setMode("metadata");
|
|
81
|
+
if (input === "d" && current)
|
|
82
|
+
setMode("delete");
|
|
83
|
+
if (key.downArrow || input === "j")
|
|
84
|
+
setSelected((value) => Math.min(items.length - 1, value + 1));
|
|
85
|
+
if (key.upArrow || input === "k")
|
|
86
|
+
setSelected((value) => Math.max(0, value - 1));
|
|
87
|
+
if (key.return && current) {
|
|
88
|
+
if (onOpen) {
|
|
89
|
+
void Promise.resolve(onOpen(current, items));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
void openTarget(current.id, { items }).then((opened) => {
|
|
93
|
+
if (opened.type === "opened")
|
|
94
|
+
exit();
|
|
95
|
+
else
|
|
96
|
+
setMessage(opened.type === "failed" ? opened.error : opened.type);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
const renderRow = (item, index) => {
|
|
101
|
+
const absolute = start + index;
|
|
102
|
+
const hierarchy = item.provider === "herdr"
|
|
103
|
+
? `${item.workspace ?? "?"} > ${item.tab ?? "?"}`
|
|
104
|
+
: `${item.session ?? "?"} > ${item.tab ?? "?"}`;
|
|
105
|
+
const label = item.name ?? item.id;
|
|
106
|
+
const agent = item.agent?.name
|
|
107
|
+
? `${item.agent.name}${item.agent.status ? ` ${item.agent.status}` : ""}`
|
|
108
|
+
: item.command;
|
|
109
|
+
return (_jsxs(Box, { children: [_jsxs(Text, { color: absolute === selected ? "cyan" : undefined, children: [absolute === selected ? "›" : " ", " "] }), _jsxs(Text, { color: item.provider === "herdr" ? "magenta" : "green", children: [item.provider.toUpperCase(), " "] }), _jsxs(Text, { children: [truncate(hierarchy, isNarrow ? 20 : 34), " "] }), _jsxs(Text, { bold: true, children: [truncate(label, isNarrow ? 16 : 22), " "] }), !isNarrow && _jsx(Text, { dimColor: true, children: truncate(agent, 28) })] }, `${item.provider}:${item.id}`));
|
|
110
|
+
};
|
|
111
|
+
const detail = current ? (_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, width: isWide ? Math.floor(columns * 0.35) : undefined, children: [_jsx(Text, { bold: true, children: "Details" }), _jsxs(Text, { children: ["Provider: ", current.provider] }), _jsxs(Text, { children: ["Source: ", current.metadata.source] }), _jsxs(Text, { children: ["ID: ", current.id] }), _jsxs(Text, { children: ["Name: ", current.name ?? "-"] }), _jsxs(Text, { children: ["CWD: ", truncate(current.cwd, 46) || "-"] }), _jsxs(Text, { children: ["Command: ", current.command ?? "-"] }), _jsxs(Text, { children: ["Agent: ", current.agent?.name ?? "-"] })] })) : null;
|
|
112
|
+
if (mode === "metadata" && current) {
|
|
113
|
+
const rawKeys = current.metadata.raw && typeof current.metadata.raw === "object"
|
|
114
|
+
? Object.keys(current.metadata.raw).join(", ")
|
|
115
|
+
: typeof current.metadata.raw;
|
|
116
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, color: "cyan", children: "prtl metadata" }), _jsxs(Text, { children: ["Provider: ", current.provider] }), _jsxs(Text, { children: ["Source: ", current.metadata.source] }), _jsxs(Text, { children: ["Collected: ", current.metadata.collectedAt] }), _jsxs(Text, { children: ["ID: ", current.id] }), _jsxs(Text, { children: ["Raw: ", truncate(rawKeys, columns - 6)] }), _jsx(Text, { dimColor: true, children: "Esc/q/m back" })] }));
|
|
117
|
+
}
|
|
118
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsx(Text, { bold: true, color: "cyan", children: "prtl \u00B7 sessions portal" }), _jsx(Text, { dimColor: true, children: statusLine(result) })] }), _jsxs(Box, { children: [_jsx(Text, { children: "Search: " }), mode === "search" ? (_jsx(TextInput, { value: query, onChange: setQuery })) : (_jsx(Text, { children: query || "-" }))] }), _jsxs(Box, { flexDirection: isWide ? "row" : "column", children: [_jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", paddingX: 1, flexGrow: 1, children: [result ? visibleItems.map(renderRow) : _jsx(Text, { children: "Loading Herdr and tmux\u2026" }), result && items.length === 0 && _jsx(Text, { dimColor: true, children: "No targets match this search." })] }), isWide
|
|
119
|
+
? detail
|
|
120
|
+
: current && (_jsx(Text, { dimColor: true, children: truncate(`${current.id} ${current.cwd ?? current.command ?? ""}`, columns - 2) }))] }), mode === "delete" && current && _jsxs(Text, { color: "red", children: ["Delete ", current.id, "? y/N"] }), message && _jsx(Text, { color: "yellow", children: message }), _jsx(Text, { dimColor: true, children: "Enter open \u00B7 d delete \u00B7 m metadata \u00B7 r refresh \u00B7 / search \u00B7 q quit" })] }));
|
|
121
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tui/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEhE,eAAO,MAAM,iBAAiB,GAAI,UAAU;IAAE,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,IAiBpF,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { render } from "ink";
|
|
3
|
+
import { App } from "./App.js";
|
|
4
|
+
import { openTarget } from "@joeldmtz/prtl-core";
|
|
5
|
+
export const renderInteractive = (options) => {
|
|
6
|
+
let unmount = () => undefined;
|
|
7
|
+
const instance = render(_jsx(App, { initialItems: options?.items, initialQuery: options?.query, onOpen: async (item, items) => {
|
|
8
|
+
unmount();
|
|
9
|
+
const result = await openTarget(item.id, { items });
|
|
10
|
+
if (result.type === "failed") {
|
|
11
|
+
process.stderr.write(`${result.error}\n`);
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
}
|
|
14
|
+
} }));
|
|
15
|
+
unmount = () => instance.unmount();
|
|
16
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@joeldmtz/prtl",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"prtl": "dist/main.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"bun": ">=1.0.0"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@joeldmtz/prtl-core": "0.1.2",
|
|
19
|
+
"commander": "latest",
|
|
20
|
+
"ink": "latest",
|
|
21
|
+
"ink-text-input": "latest",
|
|
22
|
+
"react": "latest",
|
|
23
|
+
"string-width": "latest"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc -p tsconfig.json",
|
|
27
|
+
"prepublishOnly": "bun ../../scripts/resolve-workspace.ts",
|
|
28
|
+
"postpublish": "bun ../../scripts/restore-workspace.ts"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/bun": "latest",
|
|
32
|
+
"@types/react": "latest",
|
|
33
|
+
"ink-testing-library": "latest"
|
|
34
|
+
}
|
|
35
|
+
}
|