mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-03-10 08:27:40 +00:00
134 lines
5.6 KiB
JavaScript
134 lines
5.6 KiB
JavaScript
/**
|
|
* 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 = '';
|
|
}
|