feat(ui): replace subcommands with interactive menu
/remote-control now opens a select menu with Turn on/off, Configure URL, and Status instead of relying on subcommands. Adds ability to stop the server. Shows current URL in the Configure URL menu item and in the input dialog title.
This commit is contained in:
parent
ee3341d20c
commit
7080cdc34f
22
README.md
22
README.md
|
|
@ -6,22 +6,12 @@
|
||||||
pi install https://github.com/goofansu/pi-remote-control
|
pi install https://github.com/goofansu/pi-remote-control
|
||||||
```
|
```
|
||||||
|
|
||||||
## Setup
|
## Usage
|
||||||
|
|
||||||
### 1. Configure your public URL
|
Run `/remote-control` to open the menu:
|
||||||
|
|
||||||
Tell the extension the base URL your proxy/tunnel exposes:
|
- **Turn on / Turn off** — start or stop the server
|
||||||
|
- **Configure URL** — set the public base URL your proxy/tunnel exposes (saved to `~/.pi/agent/remote-control.json`)
|
||||||
|
- **Status** — show the QR code and connection URL (only when server is running)
|
||||||
|
|
||||||
```
|
On first use, you'll be prompted to configure the URL before the server starts.
|
||||||
/remote-control config
|
|
||||||
```
|
|
||||||
|
|
||||||
Enter something like `http://pi.myhost` or `https://pi.example.com`. This is saved to `~/.pi/agent/remote-control.json`.
|
|
||||||
|
|
||||||
### 2. Start the server
|
|
||||||
|
|
||||||
```
|
|
||||||
/remote-control
|
|
||||||
```
|
|
||||||
|
|
||||||
This starts the server on a random localhost port, generates an auth token, and displays a QR code + URL. Open the URL on any device.
|
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,10 @@ async function configureRemoteControlUI(ctx: ExtensionContext): Promise<void> {
|
||||||
if (!ctx.hasUI) return;
|
if (!ctx.hasUI) return;
|
||||||
|
|
||||||
const current = (await readRemoteControlConfig()).publicBaseUrl ?? "";
|
const current = (await readRemoteControlConfig()).publicBaseUrl ?? "";
|
||||||
const raw = await ctx.ui.input("Remote-control public base URL", current || "e.g. http://pi.sgponte");
|
const title = current
|
||||||
|
? `Public base URL (current: ${current})`
|
||||||
|
: "Public base URL";
|
||||||
|
const raw = await ctx.ui.input(title, "e.g. http://pi.myhost");
|
||||||
if (raw === undefined) return;
|
if (raw === undefined) return;
|
||||||
|
|
||||||
let value: string;
|
let value: string;
|
||||||
|
|
@ -1215,29 +1218,13 @@ export default function remoteControl(pi: ExtensionAPI) {
|
||||||
|
|
||||||
// ── /remote-control command ───────────────────────────────────────────────
|
// ── /remote-control command ───────────────────────────────────────────────
|
||||||
|
|
||||||
pi.registerCommand("remote-control", {
|
async function showConnectionInfo(ctx: ExtensionContext): Promise<void> {
|
||||||
description: "Start localhost-only remote control server for use behind a port-forwarding proxy",
|
if (!server) return;
|
||||||
handler: async (args, ctx) => {
|
|
||||||
if (!ctx.hasUI) return;
|
|
||||||
const subcommand = args.trim().toLowerCase();
|
|
||||||
if (subcommand === "config") {
|
|
||||||
await configureRemoteControlUI(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = await readRemoteControlConfig();
|
const config = await readRemoteControlConfig();
|
||||||
const publicBaseUrl = config.publicBaseUrl?.trim();
|
const publicBaseUrl = config.publicBaseUrl?.trim();
|
||||||
if (!publicBaseUrl) {
|
if (!publicBaseUrl) return;
|
||||||
ctx.ui.notify("Set the public URL first with /remote-control config", "warning");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start server on first invocation
|
|
||||||
if (!server) {
|
|
||||||
server = await startServer(pi, ctx);
|
|
||||||
server.onClientChange(() => updateStatus(ctx));
|
|
||||||
updateStatus(ctx);
|
|
||||||
}
|
|
||||||
const url = buildRemoteControlUrl(publicBaseUrl, server.port, server.token);
|
const url = buildRemoteControlUrl(publicBaseUrl, server.port, server.token);
|
||||||
|
|
||||||
// Generate QR code
|
// Generate QR code
|
||||||
|
|
@ -1271,6 +1258,53 @@ export default function remoteControl(pi: ExtensionAPI) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pi.registerCommand("remote-control", {
|
||||||
|
description: "Remote control — start/stop server, configure, show connection info",
|
||||||
|
handler: async (args, ctx) => {
|
||||||
|
if (!ctx.hasUI) return;
|
||||||
|
|
||||||
|
const isRunning = !!server;
|
||||||
|
const config = await readRemoteControlConfig();
|
||||||
|
const currentUrl = config.publicBaseUrl?.trim();
|
||||||
|
|
||||||
|
const configLabel = currentUrl ? `Configure URL (${currentUrl})` : "Configure URL (not set)";
|
||||||
|
const menuItems = [
|
||||||
|
isRunning ? "Turn off" : "Turn on",
|
||||||
|
configLabel,
|
||||||
|
...(isRunning ? ["Status"] : []),
|
||||||
|
];
|
||||||
|
|
||||||
|
const choice = await ctx.ui.select("Remote control", menuItems);
|
||||||
|
if (choice === undefined) return;
|
||||||
|
|
||||||
|
if (choice === "Turn on") {
|
||||||
|
const publicBaseUrl = currentUrl;
|
||||||
|
if (!publicBaseUrl) {
|
||||||
|
ctx.ui.notify("Set the public URL first — opening config…", "warning");
|
||||||
|
await configureRemoteControlUI(ctx);
|
||||||
|
// Re-check after config
|
||||||
|
const updated = await readRemoteControlConfig();
|
||||||
|
if (!updated.publicBaseUrl?.trim()) return;
|
||||||
|
}
|
||||||
|
server = await startServer(pi, ctx);
|
||||||
|
server.onClientChange(() => updateStatus(ctx));
|
||||||
|
updateStatus(ctx);
|
||||||
|
ctx.ui.notify("Remote-control server started", "info");
|
||||||
|
await showConnectionInfo(ctx);
|
||||||
|
} else if (choice === "Turn off") {
|
||||||
|
if (server) {
|
||||||
|
await server.stop();
|
||||||
|
server = undefined;
|
||||||
|
ctx.ui.setStatus("remote-control", undefined);
|
||||||
|
ctx.ui.notify("Remote-control server stopped", "info");
|
||||||
|
}
|
||||||
|
} else if (choice === configLabel) {
|
||||||
|
await configureRemoteControlUI(ctx);
|
||||||
|
} else if (choice === "Status") {
|
||||||
|
await showConnectionInfo(ctx);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue