@hienlh/ppm 0.9.94 → 0.9.96
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/.opencode/.env.example +98 -0
- package/.opencode/skills/ads-management/scripts/.env.example +13 -0
- package/.opencode/skills/ai-multimodal/.env.example +230 -0
- package/.opencode/skills/cip-design/.env.example +6 -0
- package/.opencode/skills/devops/.env.example +76 -0
- package/.opencode/skills/docs-seeker/.env.example +15 -0
- package/.opencode/skills/elevenlabs/.env.example +3 -0
- package/.opencode/skills/marketing-dashboard/.env.example +15 -0
- package/.opencode/skills/marketing-dashboard/app/.env.example +2 -0
- package/.opencode/skills/marketing-dashboard/server/.env.example +2 -0
- package/.opencode/skills/mcp-management/scripts/dist/analyze-tools.js +70 -0
- package/.opencode/skills/mcp-management/scripts/dist/cli.js +160 -0
- package/.opencode/skills/mcp-management/scripts/dist/mcp-client.js +183 -0
- package/.opencode/skills/payment-integration/scripts/.env.example +20 -0
- package/.opencode/skills/sequential-thinking/.env.example +8 -0
- package/CHANGELOG.md +10 -0
- package/bun.lock +6 -0
- package/dist/web/assets/{chat-tab-DQNdrUvL.js → chat-tab-tR7SZsdu.js} +3 -3
- package/dist/web/assets/{code-editor-B4XNYHnl.js → code-editor-Bhz8AgLm.js} +2 -2
- package/dist/web/assets/{conflict-editor-BcsRDSCw.js → conflict-editor-D3DGzqKK.js} +1 -1
- package/dist/web/assets/{database-viewer-CfzAAtm3.js → database-viewer-Bzp7hS_D.js} +1 -1
- package/dist/web/assets/{diff-viewer-DgA9z9Ux.js → diff-viewer-D9q6BT1y.js} +1 -1
- package/dist/web/assets/extension-webview-wYrm8m63.js +3 -0
- package/dist/web/assets/{index-B4mGNywE.js → index-B6g2V7M2.js} +2 -2
- package/dist/web/assets/{markdown-renderer-sIjU5LtB.js → markdown-renderer-DoKKqxdV.js} +1 -1
- package/dist/web/assets/{port-forwarding-tab-BMXnuRuI.js → port-forwarding-tab-DpdNm9h7.js} +1 -1
- package/dist/web/assets/{postgres-viewer-B6Wj5xiN.js → postgres-viewer-ArOzys-1.js} +1 -1
- package/dist/web/assets/{settings-tab-BKQo79HU.js → settings-tab-BMkj25Ox.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-CytNesG3.js → sqlite-viewer-BcID_uP5.js} +1 -1
- package/dist/web/assets/{terminal-tab-DjfxKMSB.js → terminal-tab-ChXkY0qu.js} +1 -1
- package/dist/web/index.html +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/cli/commands/stop.ts +31 -0
- package/src/server/index.ts +68 -17
- package/src/services/autostart-generator.ts +1 -1
- package/src/services/autostart-register.ts +34 -29
- package/src/web/components/extensions/extension-webview.tsx +24 -31
- package/dist/web/assets/extension-webview-gHGB2Nw2.js +0 -3
package/src/server/index.ts
CHANGED
|
@@ -347,23 +347,46 @@ export async function startServer(options: {
|
|
|
347
347
|
await ensureCloudflared();
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
//
|
|
350
|
+
// ── Try starting via system service manager (crash recovery) ────────
|
|
351
|
+
// If autostart was previously enabled, start via systemd/launchd so the OS
|
|
352
|
+
// monitors the supervisor and restarts it on crash (Restart=always).
|
|
353
|
+
let startedViaService = false;
|
|
354
|
+
{
|
|
355
|
+
const { getAutoStartStatus } = await import("../services/autostart-register.ts");
|
|
356
|
+
const autoStatus = getAutoStartStatus();
|
|
357
|
+
if (autoStatus.enabled && !autoStatus.running) {
|
|
358
|
+
if (process.platform === "linux") {
|
|
359
|
+
// Update service file in case config changed (port, share, etc.)
|
|
360
|
+
const { enableAutoStart } = await import("../services/autostart-register.ts");
|
|
361
|
+
await enableAutoStart({ port, host, share: !!options.share, configPath: options.config, profile: options.profile });
|
|
362
|
+
startedViaService = true;
|
|
363
|
+
} else if (process.platform === "darwin") {
|
|
364
|
+
const { enableAutoStart } = await import("../services/autostart-register.ts");
|
|
365
|
+
await enableAutoStart({ port, host, share: !!options.share, configPath: options.config, profile: options.profile });
|
|
366
|
+
startedViaService = true;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ── Spawn supervisor directly (fallback or first run) ────────────────
|
|
351
372
|
const isCompiledBin = isCompiledBinary();
|
|
352
373
|
const logFile = resolve(ppmDir, "ppm.log");
|
|
353
374
|
const logFd = openSync(logFile, "a");
|
|
354
375
|
const supervisorScript = resolve(import.meta.dir, "..", "services", "supervisor.ts");
|
|
355
376
|
|
|
356
|
-
const superviseArgs = [
|
|
357
|
-
"__supervise__", String(port), host,
|
|
358
|
-
options.config ?? "", options.profile ?? "",
|
|
359
|
-
];
|
|
360
|
-
if (options.share) superviseArgs.push("--share");
|
|
361
|
-
// Strip trailing empty args (before --share flag)
|
|
362
|
-
while (superviseArgs.length > 1 && superviseArgs[superviseArgs.length - 1] === "") superviseArgs.pop();
|
|
363
|
-
|
|
364
377
|
let supervisorPid: number;
|
|
365
378
|
|
|
366
|
-
if (
|
|
379
|
+
if (startedViaService) {
|
|
380
|
+
// Supervisor was started by systemd/launchd — read PID from status.json
|
|
381
|
+
supervisorPid = 0; // will be read from status.json below
|
|
382
|
+
} else if (process.platform === "win32") {
|
|
383
|
+
const superviseArgs = [
|
|
384
|
+
"__supervise__", String(port), host,
|
|
385
|
+
options.config ?? "", options.profile ?? "",
|
|
386
|
+
];
|
|
387
|
+
if (options.share) superviseArgs.push("--share");
|
|
388
|
+
while (superviseArgs.length > 1 && superviseArgs[superviseArgs.length - 1] === "") superviseArgs.pop();
|
|
389
|
+
|
|
367
390
|
const bunExe = process.execPath.replace(/\\/g, "\\\\");
|
|
368
391
|
const logEscaped = logFile.replace(/\\/g, "\\\\");
|
|
369
392
|
const errLog = logFile.replace(/\.log$/, ".err.log").replace(/\\/g, "\\\\");
|
|
@@ -388,6 +411,13 @@ export async function startServer(options: {
|
|
|
388
411
|
process.exit(1);
|
|
389
412
|
}
|
|
390
413
|
} else {
|
|
414
|
+
const superviseArgs = [
|
|
415
|
+
"__supervise__", String(port), host,
|
|
416
|
+
options.config ?? "", options.profile ?? "",
|
|
417
|
+
];
|
|
418
|
+
if (options.share) superviseArgs.push("--share");
|
|
419
|
+
while (superviseArgs.length > 1 && superviseArgs[superviseArgs.length - 1] === "") superviseArgs.pop();
|
|
420
|
+
|
|
391
421
|
const cmd = isCompiledBin
|
|
392
422
|
? [process.execPath, ...superviseArgs]
|
|
393
423
|
: [process.execPath, "run", supervisorScript, ...superviseArgs];
|
|
@@ -405,26 +435,30 @@ export async function startServer(options: {
|
|
|
405
435
|
let serverPid: number | null = null;
|
|
406
436
|
while (Date.now() - startWait < 10_000) {
|
|
407
437
|
await Bun.sleep(500);
|
|
408
|
-
// Check supervisor is still alive
|
|
409
|
-
try { process.kill(supervisorPid, 0); } catch {
|
|
410
|
-
console.error(" ✗ Supervisor exited immediately after start.");
|
|
411
|
-
console.error(" Check logs: ppm logs");
|
|
412
|
-
process.exit(1);
|
|
413
|
-
}
|
|
414
438
|
// Check if server PID appeared in status.json
|
|
415
439
|
try {
|
|
416
440
|
const data = JSON.parse(readFileSync(statusFile, "utf-8"));
|
|
417
441
|
if (data.pid && data.supervisorPid) {
|
|
442
|
+
// Update supervisorPid if started via service (was 0 initially)
|
|
443
|
+
if (!supervisorPid) supervisorPid = data.supervisorPid;
|
|
418
444
|
serverPid = data.pid;
|
|
419
445
|
break;
|
|
420
446
|
}
|
|
421
447
|
} catch {}
|
|
448
|
+
// Check supervisor is still alive (skip if PID unknown from service start)
|
|
449
|
+
if (supervisorPid) {
|
|
450
|
+
try { process.kill(supervisorPid, 0); } catch {
|
|
451
|
+
console.error(" ✗ Supervisor exited immediately after start.");
|
|
452
|
+
console.error(" Check logs: ppm logs");
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
422
456
|
}
|
|
423
457
|
|
|
424
458
|
if (!serverPid) {
|
|
425
459
|
console.error(" ✗ Server did not start within 10 seconds.");
|
|
426
460
|
console.error(" Check logs: ppm logs");
|
|
427
|
-
try { process.kill(supervisorPid); } catch {}
|
|
461
|
+
if (supervisorPid) { try { process.kill(supervisorPid); } catch {} }
|
|
428
462
|
process.exit(1);
|
|
429
463
|
}
|
|
430
464
|
|
|
@@ -456,6 +490,23 @@ export async function startServer(options: {
|
|
|
456
490
|
qr.generate(shareUrl, { small: true });
|
|
457
491
|
}
|
|
458
492
|
|
|
493
|
+
// Auto-enable system service (systemd/launchd) for boot resilience
|
|
494
|
+
try {
|
|
495
|
+
const { getAutoStartStatus, enableAutoStart } = await import("../services/autostart-register.ts");
|
|
496
|
+
const status = getAutoStartStatus();
|
|
497
|
+
if (!status.enabled) {
|
|
498
|
+
const autoConfig = {
|
|
499
|
+
port, host,
|
|
500
|
+
share: !!options.share,
|
|
501
|
+
configPath: options.config,
|
|
502
|
+
profile: options.profile,
|
|
503
|
+
};
|
|
504
|
+
// skipStart: supervisor is already running from direct spawn above
|
|
505
|
+
await enableAutoStart(autoConfig, { skipStart: true });
|
|
506
|
+
console.log(` ✓ Auto-restart enabled (${status.platform}). Disable: ppm autostart disable`);
|
|
507
|
+
}
|
|
508
|
+
} catch {}
|
|
509
|
+
|
|
459
510
|
console.log(` Commands:`);
|
|
460
511
|
console.log(` ppm restart Reload config (keeps tunnel URL)`);
|
|
461
512
|
console.log(` ppm stop Stop server & tunnel`);
|
|
@@ -60,34 +60,37 @@ function removeMetadata(): void {
|
|
|
60
60
|
|
|
61
61
|
// ─── macOS ──────────────────────────────────────────────────────────────
|
|
62
62
|
|
|
63
|
-
async function enableMacOS(config: AutoStartConfig): Promise<string> {
|
|
63
|
+
async function enableMacOS(config: AutoStartConfig, opts?: { skipStart?: boolean }): Promise<string> {
|
|
64
64
|
const plistPath = getPlistPath();
|
|
65
65
|
const plistDir = dirname(plistPath);
|
|
66
66
|
|
|
67
67
|
if (!existsSync(plistDir)) mkdirSync(plistDir, { recursive: true });
|
|
68
68
|
writeFileSync(plistPath, generatePlist(config));
|
|
69
69
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const result = Bun.spawnSync({
|
|
78
|
-
cmd: ["launchctl", "bootstrap", `gui/${process.getuid!()}`, plistPath],
|
|
79
|
-
stdout: "pipe", stderr: "pipe",
|
|
80
|
-
});
|
|
70
|
+
// Skip loading if supervisor is already running from direct spawn
|
|
71
|
+
if (!opts?.skipStart) {
|
|
72
|
+
// Unload first if already loaded (ignore errors)
|
|
73
|
+
Bun.spawnSync({
|
|
74
|
+
cmd: ["launchctl", "bootout", `gui/${process.getuid!()}`, plistPath],
|
|
75
|
+
stdout: "ignore", stderr: "ignore",
|
|
76
|
+
});
|
|
81
77
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
cmd: ["launchctl", "load", plistPath],
|
|
78
|
+
// Load the agent
|
|
79
|
+
const result = Bun.spawnSync({
|
|
80
|
+
cmd: ["launchctl", "bootstrap", `gui/${process.getuid!()}`, plistPath],
|
|
86
81
|
stdout: "pipe", stderr: "pipe",
|
|
87
82
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
83
|
+
|
|
84
|
+
if (result.exitCode !== 0) {
|
|
85
|
+
// Fallback to legacy syntax
|
|
86
|
+
const legacy = Bun.spawnSync({
|
|
87
|
+
cmd: ["launchctl", "load", plistPath],
|
|
88
|
+
stdout: "pipe", stderr: "pipe",
|
|
89
|
+
});
|
|
90
|
+
if (legacy.exitCode !== 0) {
|
|
91
|
+
const err = legacy.stderr.toString().trim();
|
|
92
|
+
throw new Error(`launchctl load failed: ${err}`);
|
|
93
|
+
}
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
96
|
|
|
@@ -146,7 +149,7 @@ function statusMacOS(): AutoStartStatus {
|
|
|
146
149
|
|
|
147
150
|
// ─── Linux ──────────────────────────────────────────────────────────────
|
|
148
151
|
|
|
149
|
-
async function enableLinux(config: AutoStartConfig): Promise<string> {
|
|
152
|
+
async function enableLinux(config: AutoStartConfig, opts?: { skipStart?: boolean }): Promise<string> {
|
|
150
153
|
const servicePath = getServicePath();
|
|
151
154
|
const serviceDir = dirname(servicePath);
|
|
152
155
|
|
|
@@ -171,11 +174,13 @@ async function enableLinux(config: AutoStartConfig): Promise<string> {
|
|
|
171
174
|
throw new Error(`systemctl enable failed: ${enable.stderr.toString().trim()}`);
|
|
172
175
|
}
|
|
173
176
|
|
|
174
|
-
// Start
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
// Start (skip if supervisor is already running from direct spawn)
|
|
178
|
+
if (!opts?.skipStart) {
|
|
179
|
+
Bun.spawnSync({
|
|
180
|
+
cmd: ["systemctl", "--user", "start", "ppm.service"],
|
|
181
|
+
stdout: "ignore", stderr: "ignore",
|
|
182
|
+
});
|
|
183
|
+
}
|
|
179
184
|
|
|
180
185
|
// Enable lingering so service runs at boot without login
|
|
181
186
|
Bun.spawnSync({
|
|
@@ -312,11 +317,11 @@ function statusWindows(): AutoStartStatus {
|
|
|
312
317
|
|
|
313
318
|
// ─── Public API ─────────────────────────────────────────────────────────
|
|
314
319
|
|
|
315
|
-
/** Enable auto-start for the current platform */
|
|
316
|
-
export async function enableAutoStart(config: AutoStartConfig): Promise<string> {
|
|
320
|
+
/** Enable auto-start for the current platform. skipStart=true registers without starting (when supervisor is already running). */
|
|
321
|
+
export async function enableAutoStart(config: AutoStartConfig, opts?: { skipStart?: boolean }): Promise<string> {
|
|
317
322
|
const platform = process.platform;
|
|
318
|
-
if (platform === "darwin") return enableMacOS(config);
|
|
319
|
-
if (platform === "linux") return enableLinux(config);
|
|
323
|
+
if (platform === "darwin") return enableMacOS(config, opts);
|
|
324
|
+
if (platform === "linux") return enableLinux(config, opts);
|
|
320
325
|
if (platform === "win32") return enableWindows(config);
|
|
321
326
|
throw new Error(`Auto-start not supported on ${platform}`);
|
|
322
327
|
}
|
|
@@ -54,54 +54,47 @@ export function ExtensionWebview({ metadata }: ExtensionWebviewProps) {
|
|
|
54
54
|
const rawHtml = panel?.html ?? "";
|
|
55
55
|
const html = injectVscodeApiShim(rawHtml);
|
|
56
56
|
|
|
57
|
-
//
|
|
58
|
-
|
|
57
|
+
// Track which project was last dispatched to prevent duplicate dispatches
|
|
58
|
+
const prevProjectRef = useRef<string | null>(null);
|
|
59
|
+
|
|
60
|
+
// On reload: resolve project path and dispatch command once.
|
|
61
|
+
// No retry — if it fails, user closes tab and reopens to retry.
|
|
62
|
+
// Skip if project-sync effect already dispatched for this project
|
|
63
|
+
// (panel is briefly undefined during dispose→recreate transition).
|
|
59
64
|
useEffect(() => {
|
|
60
65
|
if (panel || !viewType) return;
|
|
61
|
-
//
|
|
66
|
+
// If we already dispatched for this project (via project-sync effect),
|
|
67
|
+
// don't dispatch again — the panel is just temporarily missing.
|
|
68
|
+
if (projectName && projectName === prevProjectRef.current) return;
|
|
62
69
|
if (projectName) prevProjectRef.current = projectName;
|
|
63
70
|
const command = viewType.includes(".") ? viewType : `${viewType}.view`;
|
|
64
71
|
let cancelled = false;
|
|
65
|
-
let resolvedArgs: unknown[] | null = null;
|
|
66
72
|
|
|
67
|
-
async function
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
resolvedArgs = [];
|
|
73
|
+
async function dispatch() {
|
|
74
|
+
let args: unknown[] = [];
|
|
75
|
+
if (projectName) {
|
|
76
|
+
try {
|
|
77
|
+
const res = await fetch("/api/projects");
|
|
78
|
+
const json = await res.json() as { ok: boolean; data?: { name: string; path: string }[] };
|
|
79
|
+
const match = json.data?.find((p) => p.name === projectName);
|
|
80
|
+
if (match) args = [match.path];
|
|
81
|
+
} catch {}
|
|
77
82
|
}
|
|
78
|
-
return resolvedArgs;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function attempt() {
|
|
82
|
-
const args = await resolveArgs();
|
|
83
83
|
if (cancelled) return;
|
|
84
84
|
window.dispatchEvent(new CustomEvent("ext:command:execute", {
|
|
85
85
|
detail: { command, args },
|
|
86
86
|
}));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
//
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
}, 500);
|
|
93
|
-
const retryTimer = setInterval(() => {
|
|
94
|
-
if (!cancelled) attempt();
|
|
95
|
-
}, 2_000);
|
|
96
|
-
|
|
97
|
-
return () => { cancelled = true; clearTimeout(initialTimer); clearInterval(retryTimer); };
|
|
89
|
+
// Short delay to let WS connect after page load
|
|
90
|
+
const timer = setTimeout(dispatch, 500);
|
|
91
|
+
return () => { cancelled = true; clearTimeout(timer); };
|
|
98
92
|
}, [panel, viewType, projectName]);
|
|
99
93
|
|
|
100
94
|
// When panel exists, ensure correct project is loaded.
|
|
101
95
|
// On mount: dispatch command so extension can reload if project differs.
|
|
102
96
|
// On project switch: dispatch command with new project path.
|
|
103
97
|
// Extension deduplicates same-project calls (noop if already correct).
|
|
104
|
-
const prevProjectRef = useRef<string | null>(null);
|
|
105
98
|
useEffect(() => {
|
|
106
99
|
if (!panel || !viewType || !projectName) return;
|
|
107
100
|
// Skip if we already dispatched for this project
|
|
@@ -168,10 +161,10 @@ export function ExtensionWebview({ metadata }: ExtensionWebviewProps) {
|
|
|
168
161
|
};
|
|
169
162
|
}, []);
|
|
170
163
|
|
|
171
|
-
// Timeout: if panel doesn't appear within
|
|
164
|
+
// Timeout: if panel doesn't appear within 5s, show error
|
|
172
165
|
useEffect(() => {
|
|
173
166
|
if (panel) { setTimedOut(false); return; }
|
|
174
|
-
const timer = setTimeout(() => setTimedOut(true),
|
|
167
|
+
const timer = setTimeout(() => setTimedOut(true), 5_000);
|
|
175
168
|
return () => clearTimeout(timer);
|
|
176
169
|
}, [panel]);
|
|
177
170
|
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import{t as r}from"./tab-store-DZbiYk7y.js";import{t as i}from"./extension-store-3yZYn07W.js";import{A as a}from"./index-B4mGNywE.js";var o=e(n(),1),s=t(),c=`<script>
|
|
2
|
-
function acquireVsCodeApi(){return{postMessage:function(m){window.parent.postMessage(m,"*")},getState:function(){try{return JSON.parse(sessionStorage.getItem("vscode-state")||"null")}catch{return null}},setState:function(s){sessionStorage.setItem("vscode-state",JSON.stringify(s));return s}}}
|
|
3
|
-
<\/script>`;function l(e){if(!e)return e;let t=e.indexOf(`<head>`);return t===-1?c+e:e.slice(0,t+6)+c+e.slice(t+6)}function u({metadata:e}){let t=e?.panelId,n=e?.viewType,c=r(e=>e.currentProject)||e?.projectName||void 0,[u,d]=(0,o.useState)(!1),f=i(e=>{if(t&&e.webviewPanels[t])return e.webviewPanels[t];if(n){let t=n.includes(`.`)?n:`${n}.view`;return Object.values(e.webviewPanels).find(e=>e.viewType===n||e.viewType===t)}}),p=f?.id??t,m=(0,o.useRef)(null),h=f?.html??``,g=l(h);(0,o.useEffect)(()=>{if(f||!n)return;c&&(_.current=c);let e=n.includes(`.`)?n:`${n}.view`,t=!1,r=null;async function i(){if(r)return r;if(!c)return[];try{let e=(await(await fetch(`/api/projects`)).json()).data?.find(e=>e.name===c);r=e?[e.path]:[]}catch{r=[]}return r}async function a(){let n=await i();t||window.dispatchEvent(new CustomEvent(`ext:command:execute`,{detail:{command:e,args:n}}))}let o=setTimeout(()=>{t||a()},500),s=setInterval(()=>{t||a()},2e3);return()=>{t=!0,clearTimeout(o),clearInterval(s)}},[f,n,c]);let _=(0,o.useRef)(null);(0,o.useEffect)(()=>{if(!f||!n||!c||c===_.current)return;_.current=c;let e=n.includes(`.`)?n:`${n}.view`;(async()=>{try{let t=(await(await fetch(`/api/projects`)).json()).data?.find(e=>e.name===c);t&&window.dispatchEvent(new CustomEvent(`ext:command:execute`,{detail:{command:e,args:[t.path]}}))}catch{}})()},[f,n,c]);let v=e?.extensionId,y=i(e=>{if(v&&e.activationErrors[v])return e.activationErrors[v];if(n){for(let[t,r]of Object.entries(e.activationErrors))if(t===`ext-${n}`)return r}}),b=(0,o.useCallback)(()=>{if(d(!1),!n)return;let e=n.includes(`.`)?n:`${n}.view`;(async()=>{try{let t=(await(await fetch(`/api/projects`)).json()).data?.find(e=>e.name===c),n=t?[t.path]:[];window.dispatchEvent(new CustomEvent(`ext:command:execute`,{detail:{command:e,args:n}}))}catch{}})()},[n,c]),x=(0,o.useRef)(null);return(0,o.useEffect)(()=>{x.current=p??null},[p]),(0,o.useEffect)(()=>()=>{let e=x.current;e&&(i.getState().removeWebviewPanel(e),window.dispatchEvent(new CustomEvent(`ext:webview:close`,{detail:{panelId:e}})))},[]),(0,o.useEffect)(()=>{if(f){d(!1);return}let e=setTimeout(()=>d(!0),1e4);return()=>clearTimeout(e)},[f]),(0,o.useEffect)(()=>{if(!p)return;let e=e=>{m.current&&e.source===m.current.contentWindow&&window.dispatchEvent(new CustomEvent(`ext:webview:send`,{detail:{panelId:p,message:e.data}}))};return window.addEventListener(`message`,e),()=>window.removeEventListener(`message`,e)},[p]),(0,o.useEffect)(()=>{if(!p)return;let e=e=>{let{panelId:t,message:n}=e.detail;t===p&&m.current?.contentWindow?.postMessage(n,`*`)};return window.addEventListener(`ext:webview:message`,e),()=>window.removeEventListener(`ext:webview:message`,e)},[p]),!f||!h?(0,s.jsx)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-sm text-text-subtle`,children:u?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(`span`,{className:`text-destructive font-medium`,children:`Extension failed to load`}),y&&(0,s.jsx)(`span`,{className:`text-xs text-muted-foreground max-w-md text-center`,children:y}),(0,s.jsx)(`button`,{onClick:b,className:`text-xs text-primary hover:underline`,children:`Retry`})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(a,{className:`size-5 animate-spin`}),(0,s.jsx)(`span`,{children:`Loading extension...`})]})}):(0,s.jsx)(`div`,{className:`h-full w-full relative`,children:(0,s.jsx)(`iframe`,{ref:m,srcDoc:g,sandbox:`allow-scripts`,className:`w-full h-full border-0 bg-white dark:bg-zinc-900`,title:f.title},p)})}export{u as ExtensionWebview};
|