experiments in a post-browser web
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 346 lines 11 kB view raw view rendered
1# Tile API Reference (Phase 4 — strict surface only) 2 3This document lists the API surfaces available to tiles after Phase 4 shim removal. 4All surfaces are injected via `contextBridge.exposeInMainWorld('app', api)` from 5`backend/electron/tile-preload.ts`. Deprecated compat shims are gone; calling any 6removed name throws `Error('[tile-preload] ... removed in Phase 4; use ...')`. 7 8--- 9 10## Always-available (no capability required) 11 12| Surface | Description | 13|---|---| 14| `api.version` | Tile preload version string (`'2.0.0'`) | 15| `api.getTileId()` | Returns the tile's id string | 16| `api.getTileEntry()` | Returns the tile entry filename | 17| `api.initialize()` | Validates capability token; must be called first. Returns `{ capabilities }` | 18| `api.onShutdown(cb)` | Register a shutdown callback | 19| `api.scopes` | `{ SELF: 2, GLOBAL: 3 }` | 20| `api.screen.getPrimaryDisplay()` | Read-only display info | 21| `api.escape.onEscape(cb)` | Register ESC handler; return `{ handled: true }` to suppress default | 22| `api.log(...args)` | Log to main-process renderer-log handler | 23 24--- 25 26## Capability-gated surfaces 27 28### `pubsub` capability 29 30```json 31"pubsub": { "topics": ["..."], "scope": "global" } 32``` 33 34| Surface | Description | 35|---|---| 36| `api.pubsub.publish(topic, data, scope?)` | Publish an event | 37| `api.pubsub.subscribe(topic, cb, scope?)` | Subscribe; returns unsubscribe fn | 38 39**Removed:** `api.publish` / `api.subscribe` top-level aliases (Phase 4). 40 41--- 42 43### `commands` capability 44 45```json 46"commands": true 47``` 48 49| Surface | Description | 50|---|---| 51| `api.commands.register(name, handler)` | Register a command handler | 52| `api.commands.register({ name, description, execute, ... })` | v1-compatible full-object form | 53| `api.commands.unregister(name)` | Unregister a command | 54 55--- 56 57### `window` capability 58 59```json 60"window": true | { "create": true, "query": true, "manage": true, "urls": ["peek://..."] } 61``` 62 63| Surface | Gate | Description | 64|---|---|---| 65| `api.window.open(url, options?)` | `create` | Open a new window | 66| `api.window.close(id?)` | `manage` or self | Close a window | 67| `api.window.getInfo()` | `query` | Get current window info | 68| `api.window.list()` | `query` | List open windows | 69| `api.window.exists(id)` | `manage` | Check if window is open | 70| `api.window.show(id)` | `manage` | Show a window | 71| `api.window.hide(id)` | `manage` | Hide a window | 72| `api.window.center(id?)` | `manage` | Center window on display | 73| `api.window.centerAll()` | `manage` | Center all visible windows | 74| `api.window.maximize(id?)` | `manage` | Toggle maximize | 75| `api.window.fullscreen(id?)` | `manage` | Toggle fullscreen | 76| `api.window.setIgnoreMouseEvents(id, ignore, opts?)` | `manage` | Click-through toggle | 77| `api.window.setVisibleOnAllWorkspaces(id, visible, opts?)` | `manage` | Pin across Spaces | 78| `api.window.getFocusedVisibleWindowId()` | `query` | Last-focused visible window id | 79| `api.window.setOverlayFocusTarget(targetWindowId)` | `manage` | Set overlay focus target | 80 81**Removed:** `api.closeWindow(id?)` top-level alias (Phase 4). 82 83--- 84 85### `datastore` capability 86 87```json 88"datastore": { "tables": ["items", "tags", "item_tags", "item_events"] } 89``` 90 91Strict key-value scoped storage: 92 93| Surface | Description | 94|---|---| 95| `api.datastore.get(table, key)` | Get a value | 96| `api.datastore.set(table, key, value)` | Set a value | 97| `api.datastore.query(table, filter?)` | Query rows | 98| `api.datastore.extractPageContent(url)` | Extract DOM content from live webContents | 99 100**Removed:** All 30+ v1-compat helpers (`addItem`, `getItem`, `tagItem`, `addAddress`, etc.) — Phase 4. Hard-fail stubs remain to surface unmigrated callers. 101 102--- 103 104### `settings` capability 105 106```json 107"settings": true | { "readForeign": ["other-tile-id", "*"] } 108``` 109 110| Surface | Description | 111|---|---| 112| `api.settings.get(key)` | Get a setting value | 113| `api.settings.set(key, value)` | Set a setting value | 114| `api.settings.getExtKey(extId, key)` | Cross-tile read (requires `settings.readForeign`) | 115 116**Removed:** `api.settings.getKey` / `api.settings.setKey` (Phase 4). `api.settings.getExtKey` v1-compat fallback removed; strict path only. 117 118--- 119 120### `shortcuts` capability 121 122```json 123"shortcuts": true | { "keys": ["Option+S", ...] } 124``` 125 126| Surface | Description | 127|---|---| 128| `api.shortcuts.register(shortcut, cb, options?)` | Register a keyboard shortcut | 129| `api.shortcuts.unregister(shortcut, options?)` | Unregister a shortcut | 130 131**Phase 4:** v1-compat fallback removed. Manifest must declare `shortcuts` capability; throws if not declared. 132 133--- 134 135### `context` capability 136 137```json 138"context": true | { "read": ["key"], "write": ["key"], "modes": true, "queryWindows": true } 139``` 140 141| Surface | Gate | Description | 142|---|---|---| 143| `api.context.get(key, windowId?)` | `read` allowlist | Read a context value | 144| `api.context.set(key, value, meta?, windowId?)` | `write` allowlist | Write a context value | 145| `api.context.history(key, limit?)` | `read` | Read context history | 146| `api.context.snapshot(windowId?)` | any context cap | Full context snapshot | 147| `api.context.windowsWithValue(key, value)` | `queryWindows` | Windows matching value | 148| `api.context.windowsInSpace(spaceId)` | `queryWindows` | Windows in a space | 149 150--- 151 152### `filesystem` capability 153 154```json 155"filesystem": { "read": ["/allowed/path/"], "write": ["/allowed/path/"] } 156``` 157 158| Surface | Description | 159|---|---| 160| `api.filesystem.read(path)` | Read a file | 161| `api.filesystem.write(path, content)` | Write a file | 162 163--- 164 165### `dialogs` capability 166 167```json 168"dialogs": true | { "types": ["save", "open"] } 169``` 170 171| Surface | Description | 172|---|---| 173| `api.dialogs.save(content, options?)` | Show save dialog | 174| `api.dialogs.open()` | Show open dialog | 175 176**Removed:** `api.files.open/save/readFromPath/writeToPath` (Phase 4) — hard-fail stubs. 177 178--- 179 180### `network` capability 181 182```json 183"network": { "domains": ["https://api.example.com"] } 184``` 185 186| Surface | Description | 187|---|---| 188| `api.network.fetch(url, options?)` | Domain-gated HTTP fetch | 189 190--- 191 192### `theme` capability 193 194```json 195"theme": true 196``` 197 198| Surface | Description | 199|---|---| 200| `api.theme.getInfo()` | Get current theme | 201| `api.theme.onChange(cb)` | Subscribe to theme changes | 202 203--- 204 205### `oauth` capability 206 207```json 208"oauth": true | { "providers": ["youtube", "*"] } 209``` 210 211| Surface | Description | 212|---|---| 213| `api.oauth.startLoopback(options?)` | Start OAuth loopback server | 214| `api.oauth.awaitCallback(port)` | Await OAuth callback | 215 216--- 217 218### `sync` capability (builtin only) 219 220```json 221"sync": true 222``` 223 224| Surface | Description | 225|---|---| 226| `api.sync.syncAll()` | Trigger full sync | 227 228--- 229 230### `features` capability 231 232```json 233"features": true | { "read": true, "install": true, "manage": true, "update": true, "dev": true, "publish": true, "devtools": true, "browse": true, "sources": ["atproto"] } 234``` 235 236See `tile-features-strict.test.ts` for full gate matrix. 237 238--- 239 240### `izui` capability 241 242```json 243"izui": true 244``` 245 246| Surface | Description | 247|---|---| 248| `api.izui.isTransient()` | Is this window transient | 249| `api.izui.getEffectiveMode()` | Current IZUI mode | 250| `api.izui.getState()` | IZUI state object | 251| `api.izui.getPreOverlayFocusTarget()` | Pre-overlay focus target | 252| `api.izui.closeSelf()` | Close this overlay | 253 254--- 255 256### `session` capability 257 258```json 259"session": true 260``` 261 262| Surface | Description | 263|---|---| 264| `api.session.saveSpaceWorkspaces()` | Persist space window layouts | 265 266--- 267 268## Removed v1-compat surfaces (Phase 4) 269 270These names exist on `window.app` but throw `Error('[tile-preload] ... removed in Phase 4; use ...')`: 271 272- `api.publish` → use `api.pubsub.publish` 273- `api.subscribe` → use `api.pubsub.subscribe` 274- `api.closeWindow` → use `api.window.close()` 275- `api.modes.*` → was unused; no replacement 276- `api.files.open/save/readFromPath/writeToPath` → use `api.dialogs.*` / `api.filesystem.*` 277- `api.settings.getKey/setKey` → use `api.settings.get/set` 278- `api.datastore.addItem/getItem/tagItem/...` (30+ methods) → hard-fail stubs 279 280--- 281 282## Phase 5: TILE_STRICT mode and capability-violation events 283 284### `TILE_STRICT=true` env flag 285 286Set `TILE_STRICT=true` (environment variable) when starting the Electron main process to 287enable strict mode. In strict mode, any capability violation in a tile IPC handler **throws** 288an `Error` synchronously instead of logging a warning and returning `{ error }`. 289 290**When to enable:** 291- CI pipelines — catches regressions as hard failures before merge 292- Strict-mode local development — surface violations early while writing new tiles 293 294**When NOT to enable:** 295- Production releases — throw paths in IPC handlers can terminate the promise chain and 296 produce blank tiles if a legitimate configuration edge case hits 297 298**What changes:** 299- Every `tile:*` IPC handler that rejects a capability check (shortcuts, context, dialogs, 300 window, features, settings-foreign, oauth, sync, escape, session, izui, datastore, commands, 301 pubsub, network) now calls `handleViolation()` which throws `new Error(msg)` when 302 `TILE_STRICT === true`. 303- The violation event (`tile:capability-violation`) is still published before the throw. 304- Flag is read from `process.env.TILE_STRICT === 'true'` in `backend/electron/config.ts`. 305 306### `tile:capability-violation` pubsub topic 307 308Whenever a tile IPC handler rejects a request due to a missing or insufficient capability, 309a `tile:capability-violation` event is published on `GLOBAL` scope from the system address. 310 311**Subscribe:** 312```js 313api.pubsub.subscribe('tile:capability-violation', (evt) => { 314 console.warn('Capability violation:', evt); 315}, api.scopes.GLOBAL); 316``` 317 318**Payload shape:** 319```ts 320{ 321 tileId: string | null; // Tile that triggered the violation (null = invalid token) 322 capability: string; // e.g. 'shortcuts', 'window', 'datastore' 323 op: string; // e.g. 'shortcuts:register', 'window:open' 324 token?: string; // Raw token from request (may be undefined) 325 reason: string; // Human-readable rejection reason 326 timestamp: number; // Date.now() at emission time 327} 328``` 329 330**Rate limiting:** The same `(tileId, capability, op)` tuple is coalesced to at most one 331event per 5 seconds. A runaway tile retrying the same operation rapidly emits only one event, 332preventing subscriber flood. 333 334### Static typings — `backend/electron/tile-api.d.ts` 335 336A hand-written `.d.ts` file provides TypeScript autocomplete for the full `window.app` surface. 337It covers all capability-gated surfaces (pubsub, commands, window, datastore, settings, 338shortcuts, context, filesystem, dialogs, network, features, oauth, sync, izui, session, 339screen, escape) plus always-available base API. 340 341Reference it in your tile: 342```ts 343/// <reference path="../../backend/electron/tile-api.d.ts" /> 344``` 345 346Keep it in sync with `tile-preload.ts` + `tile-ipc.ts` when adding new surfaces.