fix: hello world plugin

This commit is contained in:
simosmik
2026-03-05 12:18:38 +00:00
parent c9694f9eb6
commit c9465a64be
3 changed files with 136 additions and 3 deletions

View File

@@ -0,0 +1,133 @@
/**
* Hello World plugin — module entry point.
*
* The host calls mount(container, api) when the plugin tab is activated and
* unmount(container) when it is torn down.
*
* api shape:
* api.context — current PluginContext snapshot
* api.onContextChange(cb) → unsubscribe — called whenever theme/project/session changes
* api.rpc(method, path, body?) → Promise — proxied to this plugin's server subprocess
*/
export function mount(container, api) {
// ── Build DOM ──────────────────────────────────────────────────────────────
container.innerHTML = `
<div id="hw-root" style="
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
padding: 24px;
height: 100%;
box-sizing: border-box;
">
<h1 style="font-size: 1.4rem; margin: 0 0 8px;">Hello from a plugin!</h1>
<p style="font-size: 0.9rem; opacity: 0.7; margin: 0 0 20px;">
This tab is rendered by a plain ES module — no iframe.
</p>
<h2 style="font-size: 1rem; margin: 0 0 6px; opacity: 0.8;">Context</h2>
<dl style="font-size: 0.85rem; opacity: 0.7; margin: 0 0 24px;">
<dt style="font-weight: 600; margin-top: 6px;">Theme</dt>
<dd id="hw-theme" style="margin-left: 12px;">—</dd>
<dt style="font-weight: 600; margin-top: 6px;">Project</dt>
<dd id="hw-project" style="margin-left: 12px;">—</dd>
<dt style="font-weight: 600; margin-top: 6px;">Session</dt>
<dd id="hw-session" style="margin-left: 12px;">—</dd>
</dl>
<h2 style="font-size: 1rem; margin: 0 0 6px; opacity: 0.8;">Server RPC</h2>
<p style="font-size: 0.85rem; opacity: 0.7; margin: 0 0 10px;">
Calls this plugin's Node.js server subprocess via the RPC bridge.
</p>
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
<button id="hw-btn-hello">GET /hello</button>
<button id="hw-btn-echo">POST /echo</button>
</div>
<pre id="hw-result" style="
margin-top: 12px;
padding: 12px;
border-radius: 6px;
font-size: 0.8rem;
overflow-x: auto;
white-space: pre-wrap;
">Click a button to make an RPC call…</pre>
</div>
`;
// ── Theme helper ──────────────────────────────────────────────────────────
function applyTheme(theme) {
const root = container.querySelector('#hw-root');
if (!root) return;
const isDark = theme === 'dark';
root.style.background = isDark ? '#1a1a2e' : '#ffffff';
root.style.color = isDark ? '#e0e0e0' : '#1a1a2e';
const pre = container.querySelector('#hw-result');
if (pre) pre.style.background = isDark ? '#2a2a3e' : '#f0f0f4';
}
// ── Render context values ─────────────────────────────────────────────────
function renderContext(ctx) {
const t = container.querySelector('#hw-theme');
const p = container.querySelector('#hw-project');
const s = container.querySelector('#hw-session');
if (t) t.textContent = ctx.theme || '—';
if (p) p.textContent = ctx.project ? ctx.project.name : '(none)';
if (s) s.textContent = ctx.session ? (ctx.session.title || ctx.session.id) : '(none)';
applyTheme(ctx.theme);
}
// Apply initial context
renderContext(api.context);
// Subscribe to future changes
const unsubscribe = api.onContextChange(renderContext);
// ── Button styles ─────────────────────────────────────────────────────────
container.querySelectorAll('button').forEach((btn) => {
Object.assign(btn.style, {
padding: '8px 16px',
border: '1px solid currentColor',
borderRadius: '6px',
background: 'transparent',
color: 'inherit',
cursor: 'pointer',
fontSize: '0.85rem',
opacity: '0.8',
});
btn.addEventListener('mouseenter', () => { btn.style.opacity = '1'; });
btn.addEventListener('mouseleave', () => { btn.style.opacity = '0.8'; });
});
// ── RPC buttons ───────────────────────────────────────────────────────────
const resultEl = container.querySelector('#hw-result');
container.querySelector('#hw-btn-hello').addEventListener('click', async () => {
resultEl.textContent = 'Loading…';
try {
const data = await api.rpc('GET', '/hello');
resultEl.textContent = JSON.stringify(data, null, 2);
} catch (err) {
resultEl.textContent = `Error: ${err.message}`;
}
});
container.querySelector('#hw-btn-echo').addEventListener('click', async () => {
resultEl.textContent = 'Loading…';
try {
const data = await api.rpc('POST', '/echo', { greeting: 'Hello from the plugin module!' });
resultEl.textContent = JSON.stringify(data, null, 2);
} catch (err) {
resultEl.textContent = `Error: ${err.message}`;
}
});
// Store unsubscribe so unmount can clean up
container._hwUnsubscribe = unsubscribe;
}
export function unmount(container) {
if (typeof container._hwUnsubscribe === 'function') {
container._hwUnsubscribe();
delete container._hwUnsubscribe;
}
container.innerHTML = '';
}

View File

@@ -5,9 +5,9 @@
"description": "A minimal example plugin that demonstrates the plugin API.",
"author": "Claude Code UI",
"icon": "Puzzle",
"type": "iframe",
"type": "module",
"slot": "tab",
"entry": "index.html",
"entry": "index.js",
"server": "server.js",
"permissions": []
}