Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extensions/cate.kitchensink/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
"readyPath": "/health",
"portEnv": "PORT"
},
"cateApi": ["storage", "editor", "canvas", "theme", "ui", "workspace.read", "agent"]
"cateApi": ["storage", "editor", "canvas", "theme", "ui", "workspace.read", "agent", "browser"]
}
39 changes: 39 additions & 0 deletions extensions/cate.kitchensink/src/public/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,44 @@ function initAgent(): void {
})
}

// --- browser ----------------------------------------------------------------

// Drive Cate's browser panels through window.cate.browser (needs the `browser`
// scope). These panels hold the user's real logged-in session, so this is a
// deliberately small demo: open a URL, and snapshot the focused panel.
function initBrowser(): void {
if (!window.cate) return
const out = byId('browser-out')

byId('browser-open').addEventListener('click', async () => {
const url = byId<HTMLInputElement>('browser-url').value.trim()
if (!url) return
out.textContent = 'opening…'
try {
const res = await cate.browser.open({ url })
out.textContent = 'opened ' + JSON.stringify(res, null, 2)
log('browser.open ->', res)
} catch (err) {
out.textContent = 'failed: ' + String(err)
log('browser.open failed:', String(err))
}
})

byId('browser-snapshot').addEventListener('click', async () => {
out.textContent = 'snapshotting…'
try {
const snap = await cate.browser.snapshot()
out.textContent =
`${snap.title} — ${snap.url}\n${snap.refs.length} refs\n` +
JSON.stringify(snap.refs.slice(0, 20), null, 2)
log('browser.snapshot ->', snap)
} catch (err) {
out.textContent = 'failed: ' + String(err)
log('browser.snapshot failed:', String(err))
}
})
}

initBridge()
initNotes()
initStorageApi()
Expand All @@ -334,3 +372,4 @@ initHttp()
initWs()
initRoundtrip()
initAgent()
initBrowser()
37 changes: 37 additions & 0 deletions extensions/cate.kitchensink/src/public/cate-host.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,22 @@ interface CatePanel {
setTitle(title: string): Promise<void>
}

/** One interactable element in an accessibility `snapshot()`. `ref` is opaque and
* only valid for the snapshot it came from. */
interface CateBrowserRef {
ref: string
role: string
name: string
value?: string
}

/** Accessibility snapshot of a browser panel, from `cate.browser.snapshot()`. */
interface CateBrowserSnapshot {
url: string
title: string
refs: CateBrowserRef[]
}

interface CateHost {
/** API version int, for feature detection. */
version(): Promise<number>
Expand Down Expand Up @@ -95,6 +111,27 @@ interface CateHost {
/** Abort the in-flight turn of this extension's session. */
cancel(): Promise<unknown>
}
/** Drive Cate's browser panels (requires the `browser` scope). These panels
* hold the user's real, logged-in browser session, so treat the access as
* sensitive. `panelId` targets a panel; omit it for the focused one. */
browser: {
/** To enumerate open browser panels, use `cate.panel.list()`. */
open(opts: { url: string; panelId?: string }): Promise<{ panelId: string; url: string }>
reload(opts?: { panelId?: string }): Promise<{ ok: true }>
/** Capture a screenshot; returns a host filesystem path in the OS temp dir
* (a webview guest can't read it directly; a server-backed extension can). */
screenshot(opts?: { panelId?: string }): Promise<{ path: string }>
snapshot(opts?: { panelId?: string }): Promise<CateBrowserSnapshot>
click(opts: { ref: string; panelId?: string }): Promise<{ ok: true }>
type(opts: { ref: string; text: string; panelId?: string }): Promise<{ ok: true }>
/** Resolve once the panel stops loading (`timeoutMs` defaults to 5000,
* capped at 8000). Rejects in-band with `still-loading`. */
wait(opts?: { panelId?: string; timeoutMs?: number }): Promise<{ url: string; title: string; loading: false }>
/** Press a named key (Enter, Tab, Escape, Backspace, Delete, Space, arrows,
* PageUp/PageDown, Home, End) as TRUSTED input, so Enter submits forms.
* With `ref` the element is focused first. */
press(opts: { key: string; ref?: string; panelId?: string }): Promise<{ ok: true }>
}
storage: CateHostStorage
}

Expand Down
10 changes: 10 additions & 0 deletions extensions/cate.kitchensink/src/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ <h2>server to cate (CATE_API)</h2>
<pre id="roundtrip-out"></pre>
</section>

<section>
<h2>browser</h2>
<div class="row">
<input id="browser-url" type="text" value="https://example.com" />
<button id="browser-open">open</button>
<button id="browser-snapshot">snapshot</button>
</div>
<pre id="browser-out"></pre>
</section>

<section>
<h2>agent</h2>
<div class="row">
Expand Down
Loading