Skip to content

Commit cfc0095

Browse files
committed
fix(plugin-studio): stabilize pre-issue-6 shell and runtime
1 parent 14b7349 commit cfc0095

12 files changed

Lines changed: 227 additions & 26 deletions

File tree

.dev/.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Plugin Studio future AI provider wiring
2-
# Copy the keys you actually use into .dev/.env and keep real secrets out of Git.
2+
# Copy this file to .dev/.env for local-only use; .dev/.env stays out of Git.
3+
# Add only the keys you actually need and never place live secrets in this template.
34

45
# GitHub Models is the preferred default provider for future shell chat wiring.
56
GITHUB_MODELS_TOKEN=

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ logs/
1313
.env.*
1414
!.env.example
1515
!.dev/.env.example
16+
/.dev/.env
17+
/.dev/.env.*
1618

1719
# Temporary files
1820
*.tmp

plugins/plugin-studio/STATUS.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Plugin Studio Status
2+
3+
Canonical internal implementation-status note for the current checked-in state of `plugin-studio`.
4+
Use this file as the source of truth for what is actually shipped in this branch. The public
5+
[`README.md`](README.md) is ahead of reality in a few places and is owned by issue `#20`.
6+
7+
## Evidence Basis
8+
9+
- Based on the checked-in code under `plugins/plugin-studio/`.
10+
- Backend reality verified with `node --test api/fs.test.js api/validation.test.js` in `plugins/plugin-studio/server` at the time this note was written.
11+
- This note describes implementation status, not intended roadmap scope.
12+
13+
## Shipped Today
14+
15+
### Runtime and backend
16+
17+
- Plugin scaffold, manifest, commands, hook config, and launch scripts exist.
18+
- `scripts/start.sh` enforces `node >= 18` and launches `server/index.js`.
19+
- `server/index.js` serves `app/dist/`, probes for an available localhost port, writes a PID file, applies localhost-only CORS rules, and opens the browser.
20+
- `/api/` returns runtime status used by the frontend health poll.
21+
- `/api/fs/*` is implemented: tree, file read/write/create/delete, rename, and duplicate.
22+
- `/api/validate/*` is implemented for plugin, hook, agent, and health validation.
23+
- The validation engine is native to `plugin-studio`; it is not a runtime wrapper around `plugin-dev`.
24+
25+
### Shell chrome that is real
26+
27+
- The React/Vite app shell exists and loads from `app/dist/`.
28+
- The 4-panel workbench layout is implemented: header, left rail, editor/preview workspace, AI rail, validation rail, and status strip.
29+
- Tree rail mode cycling, workspace mode switching, command palette modal, toasts, runtime status polling, responsive bounds, resize handles, and layout persistence are implemented.
30+
31+
## Placeholder or Demo-Only Today
32+
33+
- The tree, open tabs, editor content, preview content, validation list, command-palette file results, and AI chat content are all hydrated from `app/src/lib/workbench-demo.ts` via `useWorkbenchDemoState.ts`.
34+
- The editor pane is `EditorPlaceholder`, not Monaco, and it does not read or write real files.
35+
- The preview pane is `PreviewPlaceholder`, not a renderer over real active-document content.
36+
- The validation rail UI is not wired to `/api/validate/*`; `RUN ALL` currently triggers demo toast behavior.
37+
- The AI rail is present as shell chrome, but its messages are canned local state; there is no `/api/ai/*` backend.
38+
- The settings affordances are placeholders: the header `SETTINGS` button does not open a real panel, and `--settings` is not implemented end to end.
39+
- `useServerStatus.ts` is the only frontend code currently calling live backend routes.
40+
41+
## Issue Ownership Map
42+
43+
| Issue | Missing capability owned by the issue | Current reality before that issue lands |
44+
| --- | --- | --- |
45+
| `#6` | Real plugin tree wired to `/api/fs/tree`, plus root validity/error states and scale handling | Tree rail UX/chrome is shipped, but `TreePlaceholder` still renders demo groups and demo files |
46+
| `#7` | Monaco editor plus real file load/save wiring through `/api/fs/file` | Editor surface is `EditorPlaceholder` with static demo lines and fixed cursor text |
47+
| `#8` | Real preview rendering for the active document | Preview surface is `PreviewPlaceholder` backed by canned preview blocks |
48+
| `#9` | Validation rail wired to `/api/validate/*`, with running/result states and navigation | Validation backend is shipped, but the rail still uses `DEMO_VALIDATION_ISSUES` |
49+
| `#10` | Truthful `/plugin-studio:open` behavior, path propagation, pre-flight/failure handling, launch UX | Command docs and launcher exist, but the server always opens the base URL; `[path]` and `--settings` are not wired through to the browser app |
50+
| `#11` | QA and integration coverage for shell behavior, runtime smoke paths, viewport matrix, and cross-platform checks | Backend unit tests exist and pass; there is no app smoke suite or documented viewport/integration matrix in the plugin |
51+
| `#12` | Real `/api/ai/*` REST API and request/response contracts | Only provider config preparation exists under `server/lib/ai/`; no AI routes are exposed |
52+
| `#13` | AI chat UI wired to the backend with pending/error states and editor-apply flow | The AI drawer is shell chrome only; chat replies come from local demo state |
53+
| `#18` | Multi-provider runtime abstraction and provider execution order | Provider constants/defaults exist, but there is no runtime implementation behind them |
54+
| `#19` | Real settings UI for provider/runtime configuration and health | `commands/settings.md` exists, but the UI/settings flow is not implemented |
55+
| `#20` | Public README/docs aligned to shipped reality, with screenshot/mockup and no overclaims | The current README still describes Monaco, live validation wiring, scaffolding, and `plugin-dev` dependency posture ahead of implementation |
56+
57+
## Practical Read of the Current State
58+
59+
- `plugin-studio` has real local runtime infrastructure and real backend foundations.
60+
- The user-facing workbench chrome is substantially built.
61+
- Most workbench content is still demo scaffolding until issues `#6`, `#7`, `#8`, `#9`, `#12`, `#13`, `#18`, and `#19` land.
62+
- Public-facing documentation should not be used as implementation truth until `#20` is complete.

plugins/plugin-studio/app/dist/assets/index-CzoK_wUE.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

plugins/plugin-studio/app/dist/assets/index-DZlFiGLb.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/plugin-studio/app/dist/assets/index-B1O-rc9_.js renamed to plugins/plugin-studio/app/dist/assets/index-DsqAusaJ.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/plugin-studio/app/dist/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<title>Plugin Studio</title>
7-
<script type="module" crossorigin src="/assets/index-B1O-rc9_.js"></script>
8-
<link rel="stylesheet" crossorigin href="/assets/index-CzoK_wUE.css">
7+
<script type="module" crossorigin src="/assets/index-DsqAusaJ.js"></script>
8+
<link rel="stylesheet" crossorigin href="/assets/index-DZlFiGLb.css">
99
</head>
1010
<body>
1111
<div id="root"></div>

plugins/plugin-studio/app/src/components/shell/WorkspaceHeader.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@ export function WorkspaceHeader({
2121
<ContextBar activeDocument={activeDocument} />
2222

2323
<div className="workspace-controls">
24-
<div className="mode-switch" role="tablist" aria-label="Workspace mode">
24+
<div className="mode-switch" role="group" aria-label="Workspace mode">
2525
{VIEW_MODES.map((mode) => (
2626
<button
2727
key={mode}
2828
type="button"
29-
role="tab"
30-
aria-selected={workspaceMode === mode}
29+
aria-pressed={workspaceMode === mode}
3130
className={`mode-btn ${workspaceMode === mode ? 'active' : ''}`}
3231
onClick={() => onSetWorkspaceMode(mode)}
3332
>

plugins/plugin-studio/app/src/index.css

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
--radius-l: 12px;
4343

4444
--shadow-float: 0 12px 32px -8px rgba(0, 0, 0, 0.5);
45+
--scrollbar-size: 8px;
46+
--scrollbar-thin-inset: 2px;
47+
--scrollbar-hover-inset: 1px;
4548
}
4649

4750
/* Base Reset */
@@ -82,10 +85,48 @@ button:focus-visible, input:focus-visible, .search-trigger:focus-within {
8285
}
8386

8487
/* SCROLLBARS */
85-
::-webkit-scrollbar { width: 6px; height: 6px; }
88+
::-webkit-scrollbar { width: var(--scrollbar-size); height: var(--scrollbar-size); }
8689
::-webkit-scrollbar-track { background: transparent; }
87-
::-webkit-scrollbar-thumb { background: var(--border-ui); border-radius: 10px; }
88-
::-webkit-scrollbar-thumb:hover { background: var(--text-dim); }
90+
::-webkit-scrollbar-thumb {
91+
background: var(--border-ui);
92+
border-radius: 999px;
93+
border: var(--scrollbar-thin-inset) solid transparent;
94+
background-clip: padding-box;
95+
transition: background-color 0.18s ease, border-width 0.18s ease;
96+
}
97+
::-webkit-scrollbar-thumb:hover {
98+
background: var(--text-dim);
99+
border-width: 0;
100+
}
101+
102+
.tree-content,
103+
.editor-surface,
104+
.preview-surface,
105+
.ai-content,
106+
.val-content,
107+
.cmd-results {
108+
scrollbar-width: thin;
109+
scrollbar-color: var(--border-ui) transparent;
110+
}
111+
112+
.tree-content:hover,
113+
.editor-surface:hover,
114+
.preview-surface:hover,
115+
.ai-content:hover,
116+
.val-content:hover,
117+
.cmd-results:hover {
118+
scrollbar-width: auto;
119+
scrollbar-color: var(--text-dim) transparent;
120+
}
121+
122+
.tree-content:hover::-webkit-scrollbar-thumb,
123+
.editor-surface:hover::-webkit-scrollbar-thumb,
124+
.preview-surface:hover::-webkit-scrollbar-thumb,
125+
.ai-content:hover::-webkit-scrollbar-thumb,
126+
.val-content:hover::-webkit-scrollbar-thumb,
127+
.cmd-results:hover::-webkit-scrollbar-thumb {
128+
border-width: var(--scrollbar-hover-inset);
129+
}
89130

90131
/* --- HEADER --- */
91132
.studio-header {
@@ -197,9 +238,11 @@ button:focus-visible, input:focus-visible, .search-trigger:focus-within {
197238
.tree-content { flex: 1; overflow-y: auto; padding: 8px 0; }
198239
.tree-item {
199240
display: flex; align-items: center; gap: 8px; padding: 6px 16px; width: 100%;
200-
color: var(--text-muted); cursor: pointer; transition: 0.1s;
241+
color: var(--text-muted); cursor: pointer;
242+
transition: background-color 0.1s ease, color 0.1s ease, transform 0.16s cubic-bezier(0.16, 1, 0.3, 1);
201243
}
202244
.tree-item:hover { background: var(--panel-bg-alt); color: var(--text-main); }
245+
.tree-item:not(.folder):hover { transform: translateX(4px); }
203246
.tree-item.active { background: rgba(94, 194, 209, 0.05); color: var(--accent-cyan); font-weight: 500; }
204247
.tree-item.folder { font-weight: 600; font-size: 11px; color: var(--text-strong); margin-top: 4px; padding-top: 8px; padding-bottom: 8px; letter-spacing: 0.02em; cursor: default; }
205248
.tree-item.folder:hover { background: none; color: var(--text-strong); }
@@ -437,8 +480,8 @@ button:focus-visible, input:focus-visible, .search-trigger:focus-within {
437480
.cmd-palette-backdrop {
438481
position: fixed; inset: 0; background: rgba(0, 0, 0, 0.4);
439482
backdrop-filter: blur(8px); z-index: 999;
440-
display: flex; justify-content: center; align-items: flex-start;
441-
padding-top: 12vh; opacity: 0; pointer-events: none; transition: opacity 0.2s;
483+
display: flex; justify-content: center; align-items: center;
484+
padding: 24px; opacity: 0; pointer-events: none; transition: opacity 0.2s;
442485
}
443486
.cmd-palette-backdrop[data-open="true"] { opacity: 1; pointer-events: auto; }
444487
.cmd-palette-modal {

plugins/plugin-studio/server/api/fs.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,16 +88,21 @@ export function classifyFile(components, rel, absPath) {
8888
// Internal helpers
8989
// ---------------------------------------------------------------------------
9090

91-
function withTimeout(promise) {
91+
export function withTimeout(promise, timeoutMs = TIMEOUT_MS) {
92+
let timer = null;
93+
9294
return Promise.race([
93-
promise,
94-
new Promise((_, reject) =>
95-
setTimeout(
95+
Promise.resolve(promise),
96+
new Promise((_, reject) => {
97+
timer = setTimeout(
9698
() => reject(Object.assign(new Error('Operation timed out'), { code: 'ETIMEDOUT' })),
97-
TIMEOUT_MS,
98-
).unref(),
99-
),
100-
]);
99+
timeoutMs,
100+
);
101+
if (typeof timer?.unref === 'function') timer.unref();
102+
}),
103+
]).finally(() => {
104+
if (timer) clearTimeout(timer);
105+
});
101106
}
102107

103108
async function walk(rootDir, dir, components) {

0 commit comments

Comments
 (0)