[READ-ONLY] Mirror of https://github.com/autonome/peek-builds. Build mirror of tangled.org/burrito.space/peek — macOS Electron builds on Git
peek-builds docs tile-api.md
13 kB · 355 lines
Markdown
at main

Tile API Reference (Phase 4 — strict surface only) #

This document lists the API surfaces available to tiles after Phase 4 shim removal. All surfaces are injected via contextBridge.exposeInMainWorld('app', api) from apps/desktop/main/tile-preload.cts. Deprecated compat shims are gone; calling any removed name throws Error('[tile-preload] ... removed in Phase 4; use ...').


Always-available (no capability required) #

Surface Description
api.version Tile preload version string ('2.0.0')
api.getTileId() Returns the tile's id string
api.getTileEntry() Returns the tile entry filename
api.initialize() Validates capability token; must be called first. Returns { capabilities }
api.onShutdown(cb) Register a shutdown callback
api.scopes { SELF: 2, GLOBAL: 3 }
api.screen.getPrimaryDisplay() Read-only display info
api.escape.onEscape(cb) Register ESC handler; return { handled: true } to suppress default
api.log(...args) Log to main-process renderer-log handler

Capability-gated surfaces #

pubsub capability #

"pubsub": { "topics": ["..."], "scope": "global" }
Surface Description
api.pubsub.publish(topic, data, scope?) Publish an event
api.pubsub.subscribe(topic, cb, scope?) Subscribe; returns unsubscribe fn

Removed: api.publish / api.subscribe top-level aliases (Phase 4).


commands capability #

"commands": true
Surface Description
api.commands.register(name, handler) Register a command handler
api.commands.register({ name, description, execute, ... }) v1-compatible full-object form
api.commands.unregister(name) Unregister a command

window capability #

"window": true | { "create": true, "query": true, "manage": true, "urls": ["peek://..."] }
Surface Gate Description
api.window.open(url, options?) create Open a new window
api.window.close(id?) manage or self Close a window
api.window.getInfo() query Get current window info
api.window.list({ includeInternal?, includeHidden? }) query List open windows on screen; includeHidden: true also lists hidden ones (a dismissed peek or slide kept alive, a window hidden with hide); app chrome (page chrome overlay, desktop markers, spaces border) is listed only when includeHidden: true is passed
api.window.exists(id) manage Check if window is open
api.window.show(id) manage Show a window
api.window.hide(id) manage Hide a window
api.window.center(id?) manage Center window on display
api.window.centerAll() manage Center all visible windows
api.window.maximize(id?) manage Toggle maximize
api.window.getSeeThrough(id?) query, or none for the calling window Whether this view supports the see-through switch and whether it is on: { success, data: { supported, seeThrough } }
api.window.setSeeThrough(id, on) manage Turn the see-through look on or off for one window. on is explicit, not a toggle. On applies the frosted look and off removes it; neither moves, resizes, maximizes or unmaximizes the window. A view that does not declare seeThrough: true on its manifest tile entry answers { success: false, error } and nothing changes
api.window.getSeeThroughEverywhere() query The app-wide see-through switch: { success, data: { on } }. Stored with the app's other settings, so it survives a restart
api.window.setSeeThroughEverywhere(on) manage Turn the see-through look on or off for every open window whose view declares seeThrough: true, and remember the choice so a declaring window opened later opens that way too. on is explicit, not a toggle. Each window is set through the same path as setSeeThrough, so none of them is moved, resized or maximized; a window whose view does not declare the switch is skipped rather than reported. data.applied counts the windows set. Flipping this resets every declaring window, which ends any per-window override
api.window.maximizeAll() manage Toggle maximize-all mode: maximize every eligible window on the current screen and desktop, or put the ones it maximized back (docs/design/window-rules.md G16)
api.window.fullscreen(id?) manage Toggle fullscreen
api.window.setIgnoreMouseEvents(id, ignore, opts?) manage Click-through toggle
api.window.setVisibleOnAllWorkspaces(id, visible, opts?) manage Pin across Spaces
api.window.getFocusedVisibleWindowId() query Last-focused visible window id
api.window.setOverlayFocusTarget(targetWindowId) manage Set overlay focus target

Removed: api.closeWindow(id?) top-level alias (Phase 4).


datastore capability #

"datastore": { "tables": ["items", "tags", "item_tags", "item_events"] }

Strict key-value scoped storage:

Surface Description
api.datastore.get(table, key) Get a value
api.datastore.set(table, key, value) Set a value
api.datastore.query(table, filter?) Query rows
api.datastore.extractPageContent(url) Extract DOM content from live webContents

Removed: All 30+ v1-compat helpers (addItem, getItem, tagItem, addAddress, etc.) — Phase 4. Hard-fail stubs remain to surface unmigrated callers.


settings capability #

"settings": true | { "readForeign": ["other-tile-id", "*"] }
Surface Description
api.settings.get(key) Get a setting value
api.settings.set(key, value) Set a setting value
api.settings.getExtKey(extId, key) Cross-tile read (requires settings.readForeign)

Removed: api.settings.getKey / api.settings.setKey (Phase 4). api.settings.getExtKey v1-compat fallback removed; strict path only.


shortcuts capability #

"shortcuts": true | { "keys": ["Option+S", ...] }
Surface Description
api.shortcuts.register(shortcut, cb, options?) Register a keyboard shortcut
api.shortcuts.unregister(shortcut, options?) Unregister a shortcut

Phase 4: v1-compat fallback removed. Manifest must declare shortcuts capability; throws if not declared.


context capability #

"context": true | { "read": ["key"], "write": ["key"], "modes": true, "queryWindows": true }
Surface Gate Description
api.context.get(key, windowId?) read allowlist Read a context value
api.context.set(key, value, meta?, windowId?) write allowlist Write a context value
api.context.history(key, limit?) read Read context history
api.context.snapshot(windowId?) any context cap Full context snapshot
api.context.windowsWithValue(key, value) queryWindows Windows matching value
api.context.windowsInSpace(spaceId) queryWindows Windows in a space

filesystem capability #

"filesystem": { "read": ["/allowed/path/"], "write": ["/allowed/path/"] }
Surface Description
api.filesystem.read(path) Read a file
api.filesystem.write(path, content) Write a file

dialogs capability #

"dialogs": true | { "types": ["save", "open"] }
Surface Description
api.dialogs.save(content, options?) Show save dialog
api.dialogs.open() Show open dialog

Removed: api.files.open/save/readFromPath/writeToPath (Phase 4) — hard-fail stubs.


network capability #

"network": { "domains": ["https://api.example.com"] }
Surface Description
api.network.fetch(url, options?) Domain-gated HTTP fetch

theme capability #

"theme": true
Surface Description
api.theme.getInfo() Get current theme
api.theme.onChange(cb) Subscribe to theme changes

oauth capability #

"oauth": true | { "providers": ["youtube", "*"] }
Surface Description
api.oauth.startLoopback(options?) Start OAuth loopback server
api.oauth.awaitCallback(port) Await OAuth callback

sync capability (builtin only) #

"sync": true
Surface Description
api.sync.syncAll() Trigger full sync

features capability #

"features": true | { "read": true, "install": true, "manage": true, "update": true, "dev": true, "publish": true, "devtools": true, "browse": true, "sources": ["atproto"] }

See tile-features-strict.test.ts for full gate matrix.


izui capability #

"izui": true
Surface Description
api.izui.isTransient() Is this window transient
api.izui.getEffectiveMode() Current IZUI mode
api.izui.getState() IZUI state object
api.izui.getPreOverlayFocusTarget() Pre-overlay focus target
api.izui.closeSelf() Close this overlay

session capability #

"session": true
Surface Description
api.session.saveWorkspaceSnapshot(key) Persist the current window layout under a workspace key (a named key saves that workspace's own windows, never a cross-workspace pin; default saves everything)
api.session.restoreWorkspaceSnapshot(key) Reopen a workspace's saved window layout
api.session.getWorkspaceSnapshotInfo(key) Read a named workspace's saved window bounds/z-order without restoring anything (refuses the default workspace's own key)
api.session.beginWorkspaceSwitch() Suspend the app's ambient session saves until setActiveWorkspace — bracket a save → close → restore switch
api.session.setActiveWorkspace(key) Name the active workspace, whose row the app's ambient saves (autosave, quit, window-lifecycle) write from now on; ends a switch

Removed v1-compat surfaces (Phase 4) #

These names exist on window.app but throw Error('[tile-preload] ... removed in Phase 4; use ...'):

  • api.publish → use api.pubsub.publish
  • api.subscribe → use api.pubsub.subscribe
  • api.closeWindow → use api.window.close()
  • api.modes.* → was unused; no replacement
  • api.files.open/save/readFromPath/writeToPath → use api.dialogs.* / api.filesystem.*
  • api.settings.getKey/setKey → use api.settings.get/set
  • api.datastore.addItem/getItem/tagItem/... (30+ methods) → hard-fail stubs

Phase 5: TILE_STRICT mode and capability-violation events #

TILE_STRICT=true env flag #

Set TILE_STRICT=true (environment variable) when starting the Electron main process to enable strict mode. In strict mode, any capability violation in a tile IPC handler throws an Error synchronously instead of logging a warning and returning { error }.

When to enable:

  • CI pipelines — catches regressions as hard failures before merge
  • Strict-mode local development — surface violations early while writing new tiles

When NOT to enable:

  • Production releases — throw paths in IPC handlers can terminate the promise chain and produce blank tiles if a legitimate configuration edge case hits

What changes:

  • Every tile:* IPC handler that rejects a capability check (shortcuts, context, dialogs, window, features, settings-foreign, oauth, sync, escape, session, izui, datastore, commands, pubsub, network) now calls handleViolation() which throws new Error(msg) when TILE_STRICT === true.
  • The violation event (tile:capability-violation) is still published before the throw.
  • Flag is read from process.env.TILE_STRICT === 'true' in apps/desktop/main/config.ts.

tile:capability-violation pubsub topic #

Whenever a tile IPC handler rejects a request due to a missing or insufficient capability, a tile:capability-violation event is published on GLOBAL scope from the system address.

Subscribe:

api.pubsub.subscribe('tile:capability-violation', (evt) => {
  console.warn('Capability violation:', evt);
}, api.scopes.GLOBAL);

Payload shape:

{
  tileId: string | null;      // Tile that triggered the violation (null = invalid token)
  capability: string;         // e.g. 'shortcuts', 'window', 'datastore'
  op: string;                 // e.g. 'shortcuts:register', 'window:open'
  token?: string;             // Raw token from request (may be undefined)
  reason: string;             // Human-readable rejection reason
  timestamp: number;          // Date.now() at emission time
}

Rate limiting: The same (tileId, capability, op) tuple is coalesced to at most one event per 5 seconds. A runaway tile retrying the same operation rapidly emits only one event, preventing subscriber flood.

Static typings — apps/desktop/main/tile-api.d.ts #

A hand-written .d.ts file provides TypeScript autocomplete for the full window.app surface. It covers all capability-gated surfaces (pubsub, commands, window, datastore, settings, shortcuts, context, filesystem, dialogs, network, features, oauth, sync, izui, session, screen, escape) plus always-available base API.

Reference it in your tile:

/// <reference path="../../apps/desktop/main/tile-api.d.ts" />

Keep it in sync with tile-preload.ts + tile-ipc.ts when adding new surfaces.