orca/examples/plugins/hello-orca/panel.html

126 lines
4.2 KiB
HTML

<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<style>
/* The host injects Orca design tokens as CSS custom properties, so
panels can match the app without any access to it. */
body {
margin: 0;
padding: 12px;
font-family: system-ui, sans-serif;
font-size: 13px;
color: var(--foreground, #ddd);
background: var(--background, transparent);
}
h1 {
font-size: 14px;
margin: 0 0 8px;
}
button {
display: block;
margin: 6px 0;
padding: 6px 10px;
border: 1px solid var(--border, #555);
border-radius: 6px;
background: var(--secondary, #2a2a2a);
color: var(--foreground, #ddd);
cursor: pointer;
font: inherit;
}
select {
margin: 6px 0;
max-width: 100%;
}
.status {
margin-top: 10px;
min-height: 1.4em;
color: var(--muted-foreground, #999);
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>Hello Orca 👋</h1>
<p>Panel + worker command + events, gated by consent.</p>
<button id="ctx">Read workspace context</button>
<select id="terms" hidden></select>
<button id="send" hidden>Type "echo hi from plugin" into selected terminal</button>
<button id="notify">Show a notification</button>
<p class="status" id="status"></p>
<script>
'use strict'
var seq = 0
var pending = {}
var statusEl = document.getElementById('status')
var termsEl = document.getElementById('terms')
function call(action, params) {
return new Promise(function (resolve) {
var requestId = 'req-' + ++seq
pending[requestId] = resolve
// The panel frame is an opaque origin, so '*' is the only usable
// targetOrigin; the host verifies the sending window instead.
window.parent.postMessage(
{ type: 'orca-panel-action', requestId: requestId, action: action, params: params },
'*'
)
})
}
window.addEventListener('message', function (event) {
var data = event.data
if (!data || data.type !== 'orca-panel-action-result') return
var resolve = pending[data.requestId]
if (!resolve) return
delete pending[data.requestId]
resolve(data)
})
document.getElementById('ctx').addEventListener('click', function () {
call('workspace.readContext').then(function (result) {
if (!result.ok) {
statusEl.textContent = 'context failed: ' + (result.error || result.errorCode)
return
}
if (!result.value) {
statusEl.textContent = 'no focused worktree'
return
}
statusEl.textContent = result.value.displayName + ' on branch ' + result.value.branch
termsEl.innerHTML = ''
result.value.terminals.forEach(function (terminal) {
var option = document.createElement('option')
option.value = terminal.id
option.textContent = terminal.id
termsEl.appendChild(option)
})
termsEl.hidden = result.value.terminals.length === 0
document.getElementById('send').hidden = result.value.terminals.length === 0
})
})
document.getElementById('send').addEventListener('click', function () {
// Explicit terminal id — the API has no "active terminal" target.
call('terminal.sendText', {
terminalId: termsEl.value,
text: 'echo hi from plugin',
enter: true
}).then(function (result) {
statusEl.textContent = result.ok
? 'typed (accepted=' + result.value.accepted + ')'
: 'send failed: ' + (result.error || result.errorCode)
})
})
document.getElementById('notify').addEventListener('click', function () {
call('notifications.show', { title: 'Hello from the panel' }).then(function (result) {
statusEl.textContent = result.ok
? 'notification delivered=' + result.value.delivered
: 'notify failed: ' + (result.error || result.errorCode)
})
})
</script>
</body>
</html>