API map
Every call on the backend's app next to its page twin on tiny, one row per operation. A page's tiny.app.X is the backend's app.X, and tiny.win.X (the page's own window) pairs with app.win.X (the main window); app.window(id) handles have the same window methods as app.win minus open, windows and setHideOnClose, and their close() takes no id.
Generated from tiny.d.ts, checked against the live app and tiny objects and line by line against the code — 135 same · 36 page only · 12 backend only · 3 args differ · 2 returns differ.
bridge · windows · menus · tray · hotkeys · dialogs · notifications · app · system · theme · clipboard · store · audio · macos only · events · deprecated
"returns differ" mostly means the page resolves true where the backend returns nothing
Bridge
backend app | page tiny | notes |
|---|---|---|
| — | tiny.api.call(method: string, params?: unknown) → Promise<any> | page only Call a backend api method; resolves with its return value. |
| — | tiny.api.off(event: string, fn: (data: any) => void) → void | page only unhook a handler passed to on() by reference (the tiny.*.on sugar wraps yours, so keep the function those return instead) |
| — | tiny.api.on(event: string, fn: (data: any) => void) → () => void | page only Subscribe to backend push events (app.push / broadcasts). |
app.debug(…)not in tiny.d.ts | — | backend only |
app.done: Promise<{ exit_status: number; }> | — | backend only resolves when the window process ends |
| — | tiny.fetch(url: string, init?: TinyFetchInit) → Promise<Response> | page only Like window.fetch, but the request runs in the backend (a native process) — no CORS, CSP, or mixed-content limits, so the page can reach any origin. |
| — | tiny.fileURL(path: string) → string | page only A correct file:// URL for a disk path on every platform — use for <audio>/<img>/<video> src of backend-provided paths. |
| — | tiny.log(msg: unknown) → Promise<true> | page only print to the backend's console (the tinyjs dev terminal); any JSON value |
| — | tiny.proxyURL(url: string) → string | page only A same-app URL (tiny-media://…) that streams a remote http(s) resource through the native layer with permissive CORS. |
app.push(event: string, data?: unknown) → void | — | backend only push an event to EVERY window (tiny.api.on) — app.win.push reaches main only. |
app.reload(newHtml?: string) → Promise<void> | — | backend only |
app.spawnHidden(args: string[], opts?: any) → any | — | backend only tjs.spawn without the console window a GUI app gets on Windows |
Windows
backend app | page tiny | notes |
|---|---|---|
app.win.center() → voidalso app.center() (main window) | tiny.win.center() → Promise<void> | same |
app.win.close(id?: string) → void | tiny.win.close(id?: string) → Promise<void> | same close a window; no id = this window ('main' quits the app) |
| — | tiny.win.dragOut(opts: TinyDragOutOptions) → Promise<void> | page only drag files out of the window (same as startDrag({ files })) |
app.win.ensureOnScreen() → voidalso app.ensureOnScreen() (main window) | tiny.win.ensureOnScreen() → Promise<void> | same clamp this window onto the nearest screen if it's (nearly) off all |
app.win.eval(js: string) → voidalso app.eval() (main window) | — | backend only |
| — | tiny.win.find(term: string, opts?: { forward?: boolean; matchCase?: boolean; }) → Promise<{ found: boolean; matches?: number; activeMatch?: number; }> | page only Find-in-page: selects and scrolls to the next match; call again to step. |
app.win.fullscreen() → voidalso app.fullscreen() (main window) | tiny.win.fullscreen() → Promise<void> | same toggle |
app.win.getState() → Promise<TinyWinState | null> | tiny.win.getState() → Promise<TinyWinState | null> | same null if the window is already gone |
app.win.hide(opts?: TinyHideOptions) → voidalso app.hide() (main window) | tiny.win.hide(opts?: TinyHideOptions) → Promise<void> | same hides the APP (NSApp hide): focus returns to the previous app — palettes can hide-then-paste with no frontmost tracking. |
app.win.id: string | tiny.win.id: string | same which window this page lives in ('main' or a tiny.win.open id) |
app.win.menu.get(id: string) → Promise<TinyMenuItemState> | tiny.win.menu.get(id: string) → Promise<TinyMenuItemState> | same |
app.win.menu.reset() → void | tiny.win.menu.reset() → Promise<void> | same back to showing the app menu |
app.win.menu.set(menus: TinyMenu[]) → void | tiny.win.menu.set(menus: TinyMenu[]) → Promise<void> | same |
app.win.menu.update(id: string, patch?: { label?: string; checked?: boolean; enabled?: boolean; }) → void | tiny.win.menu.update(id: string, patch?: { label?: string; checked?: boolean; enabled?: boolean; }) → Promise<void> | same patch an item in THIS window's bar only |
app.win.minimize() → voidalso app.minimize() (main window) | tiny.win.minimize() → Promise<void> | same |
| — | tiny.win.onDrop(fn: (paths: string[]) => void) → () => void | page only files dragged onto the window — real filesystem paths |
| — | tiny.win.onState(fn: (state: TinyWindowStateEvent) => void) → () => void | page only fullscreen/maximized/minimized/focused transitions, from any cause. |
app.win.open(id: string, opts?: TinyOpenWindowOptions) → void | tiny.win.open(id: string, opts?: TinyOpenWindowOptions) → Promise<void> | same With "mainWindow": false there is no main window; open named ones on demand. |
app.win.print() → voidalso app.print() (main window) | tiny.win.print() → Promise<void> | same print THIS window's page (its own print panel) |
app.win.printToPDF(path: string) → Promise<{ path: string; }>also app.printToPDF() (main window) | tiny.win.printToPDF(path: string) → Promise<{ path: string; }> | same render the page to a PDF file (vector) -> the path written. |
app.win.push(event: string, data?: unknown) → void | — | backend only main only — app.push broadcasts to every window. |
app.win.restore() → voidalso app.restore() (main window) | tiny.win.restore() → Promise<void> | same |
app.win.setAllSpaces(enabled: boolean) → voidalso app.setAllSpaces() (main window) | tiny.win.setAllSpaces(enabled: boolean) → Promise<void> | same follow the user across every Space + float over fullscreen apps |
app.win.setAlwaysOnTop(enabled: boolean) → voidalso app.setAlwaysOnTop() (main window) | tiny.win.setAlwaysOnTop(enabled: boolean) → Promise<void> | same |
app.win.setChrome(opts: TinyChromeOptions) → voidalso app.setChrome() (main window) | tiny.win.setChrome(opts: TinyChromeOptions) → Promise<void> | same |
app.win.setClickThrough(enabled: boolean) → voidalso app.setClickThrough() (main window) | tiny.win.setClickThrough(enabled: boolean) → Promise<void> | same mouse events pass through to whatever is behind the window |
app.win.setFullscreen(enabled: boolean, opts?: TinyFullscreenOptions) → voidalso app.setFullscreen() (main window) | tiny.win.setFullscreen(enabled: boolean, opts?: TinyFullscreenOptions) → Promise<void> | same |
app.win.setHideOnClose(enabled: boolean) → voidalso app.setHideOnClose() (main window) | tiny.win.setHideOnClose(enabled: boolean) → Promise<void> | same true: the close button hides the window instead of quitting. |
app.win.setLevel(level: TinyWindowLevel) → voidalso app.setLevel() (main window) | tiny.win.setLevel(level: TinyWindowLevel) → Promise<void> | same |
app.win.setMinSize(width: number, height: number) → void | tiny.win.setMinSize(width: number, height: number) → Promise<void> | same floor for user resizes |
app.win.setPosition(x: number, y: number, opts?: TinyPositionOptions) → voidalso app.setPosition() (main window) | tiny.win.setPosition(x: number, y: number, opts?: TinyPositionOptions) → Promise<void> | same top-left origin, screen points (global). |
app.win.setResizable(enabled: boolean) → voidalso app.setResizable() (main window) | tiny.win.setResizable(enabled: boolean) → Promise<void> | same |
app.win.setSize(width: number, height: number, opts?: TinySizeOptions) → voidalso app.setSize() (main window) | tiny.win.setSize(width: number, height: number, opts?: TinySizeOptions) → Promise<void> | same resize the PAGE's box (decorations excluded), top-left anchored — the same units getState().width/height reports back |
app.win.setTitle(title: string) → voidalso app.setTitle() (main window) | tiny.win.setTitle(title: string) → Promise<void> | same |
app.win.setZoom(factor: number) → void | tiny.win.setZoom(factor: number) → Promise<void> | same Native page zoom, 0.25–5 — the WEBVIEW's own (WKWebView pageZoom, WebView2 ZoomFactor, webkit_web_view_set_zoom_level), so it renders crisp and the page keeps laying out in ordinary CSS px with fewer of them. |
app.win.share(opts?: TinyShareOptions) → boolean | tiny.win.share(opts?: TinyShareOptions) → Promise<boolean> | same native share sheet — anchor at the click's clientX/clientY. |
app.win.show(opts?: TinyShowOptions) → voidalso app.show() (main window) | tiny.win.show(opts?: TinyShowOptions) → Promise<void> | same show({ activate: false }) surfaces the window without stealing focus |
app.win.startDrag() → voidalso app.startDrag() (main window) | tiny.win.startDrag(opts?: TinyDragOutOptions) → Promise<void> | args differ No args: drag the window (frameless chrome). |
app.win.startResize(edge?: TinyResizeEdge) → voidalso app.startResize() (main window) | tiny.win.startResize(edge?: TinyResizeEdge) → Promise<void> | same begin a resize from an edge (frameless windows' own grips) |
| — | tiny.win.stopFind() → Promise<void> | page only clear the find selection |
app.win.windows() → Promise<string[]>also app.windows() (main window) | tiny.win.windows() → Promise<string[]> | same |
app.win.zoom() → voidalso app.zoom() (main window) | tiny.win.zoom() → Promise<void> | same |
app.window(id: string) → TinyWindowHandle | — | backend only |
Menus
backend app | page tiny | notes |
|---|---|---|
app.menu.get(id: string) → Promise<TinyMenuItemState> | tiny.menu.get(id: string) → Promise<TinyMenuItemState> | same |
| — | tiny.menu.on(fn: (id: string) => void) → () => void | page only With `"about": "menu"` in tinyjs.json, the macOS About menu item fires here with the reserved id 'about' instead of showing the standard panel. |
| — | tiny.menu.onContext(fn: (id: string) => void) → () => void | page only |
app.menu.set(menus: TinyMenu[]) → void | tiny.menu.set(menus: TinyMenu[]) → Promise<void> | same The APP menu: every window shows it, including windows opened later. |
app.menu.setContext(items: TinyMenuItem[] | null) → void | tiny.menu.setContext(items: TinyMenuItem[] | null) → Promise<void> | same replace the right-click menu; null restores WebKit's default |
app.menu.update(id: string, patch?: { label?: string; checked?: boolean; enabled?: boolean; }) → void | tiny.menu.update(id: string, patch?: { label?: string; checked?: boolean; enabled?: boolean; }) → Promise<void> | same patch a live item without redeclaring the menu — every window's copy of the id moves. |
Tray
backend app | page tiny | notes |
|---|---|---|
| — | tiny.tray.on(fn: (id: string) => void) → () => void | page only |
| — | tiny.tray.onClick(fn: () => void) → () => void | page only |
app.tray.position() → Promise<{ x: number; y: number; width: number; height: number; } | null> | tiny.tray.position() → Promise<{ x: number; y: number; width: number; height: number; } | null> | same the tray icon's rect { x, y, width, height } (top-left) | null. |
app.tray.remove() → void | tiny.tray.remove() → Promise<void> | same |
app.tray.set(spec: TinyTraySpec) → void | tiny.tray.set(spec: TinyTraySpec) → Promise<void> | same |
Hotkeys
backend app | page tiny | notes |
|---|---|---|
| — | tiny.hotkey.on(fn: (id: string) => void) → () => void | page only |
app.hotkey.register(id: string, combo: string) → Promise<boolean> | tiny.hotkey.register(id: string, combo: string) → Promise<boolean> | same true once the system has the hotkey; false if the combo isn't valid or another app holds it (the reason is logged). |
app.hotkey.unregister(id: string) → void | tiny.hotkey.unregister(id: string) → Promise<void> | same |
Dialogs
backend app | page tiny | notes |
|---|---|---|
app.dialog.alert(message: string, opts?: string | { detail?: string; ok?: string; }) → Promise<true> | tiny.dialog.alert(message: string, opts?: string | { detail?: string; ok?: string; }) → Promise<true> | same `alert`/`confirm`/`prompt` are native panels with the message as the headline. |
app.dialog.confirm(message: string, opts?: string | { detail?: string; ok?: string; cancel?: string; }) → Promise<boolean> | tiny.dialog.confirm(message: string, opts?: string | { detail?: string; ok?: string; cancel?: string; }) → Promise<boolean> | same |
app.dialog.openFile(opts?: { types?: string[]; }) → Promise<string | null> | tiny.dialog.openFile(opts?: { types?: string[]; }) → Promise<string | null> | same `types`: extensions without dots (`['md', 'txt']`) limiting what's choosable — allowedContentTypes / COMDLG_FILTERSPEC / GtkFileFilter. |
app.dialog.openFiles(opts?: { types?: string[]; }) → Promise<string[] | null> | tiny.dialog.openFiles(opts?: { types?: string[]; }) → Promise<string[] | null> | same |
app.dialog.pickFolder() → Promise<string | null> | tiny.dialog.pickFolder() → Promise<string | null> | same |
app.dialog.prompt(message: string, opts?: { default?: string; ok?: string; cancel?: string; }) → Promise<string | null> | tiny.dialog.prompt(message: string, opts?: { default?: string; ok?: string; cancel?: string; }) → Promise<string | null> | same |
app.dialog.saveFile(opts?: { types?: string[]; name?: string; }) → Promise<string | null> | tiny.dialog.saveFile(opts?: { types?: string[]; name?: string; }) → Promise<string | null> | same |
Notifications
backend app | page tiny | notes |
|---|---|---|
app.notify(title: string, body?: string, opts?: TinyNotifyOptions) → Promise<boolean> | tiny.notify(title: string, body?: string, opts?: TinyNotifyOptions) → Promise<boolean> | same Desktop notification. |
App
backend app | page tiny | notes |
|---|---|---|
app.attention(opts?: { critical?: boolean; }) → boolean | tiny.app.attention(opts?: { critical?: boolean; }) → Promise<void> | same ask for the user's attention: macOS bounces the Dock icon once (critical: until the app is activated), Windows flashes the taskbar button (critical: longer), Linux sets the urgency hint (X11 only; critical makes no diffe |
app.authenticate(reason?: string) → Promise<boolean> | tiny.app.authenticate(reason?: string) → Promise<boolean> | same Touch ID (or the account-password sheet); false covers cancel |
app.badge(text: string) → boolean | tiny.app.badge(text: string) → Promise<void> | same '' clears the badge |
app.beep() → Promise<boolean> | tiny.app.beep() → Promise<boolean> | same the system alert beep — the one portable sound |
app.captureScreen(screenId?: number) → Promise<TinyCapture> | tiny.app.captureScreen(screenId?: number) → Promise<TinyCapture> | same screenshot a display (id from screens(); default: the first display the OS lists, usually the main one — Linux always captures the whole desktop and ignores the id) — png in the temp dir, caller owns the file; needs the |
app.cookies.get(filter?: { url?: string; name?: string; domain?: string; path?: string; }) → Promise<TinyCookie[]> | — | backend only url keeps what a request to it would carry (domain, path, secure). |
app.cookies.remove(target: { name: string; domain: string; path?: string; } | { url: string; name?: string; }) → Promise<number> | — | backend only one cookie by name + domain (+ path, default '/') — e.g. |
app.cookies.set(cookie: TinyCookieInit) → Promise<true> | — | backend only replaces any cookie with the same name/domain/path |
app.foregroundScreen() → Promise<TinyScreen | null> | tiny.app.foregroundScreen() → Promise<TinyScreen | null> | same the screen of the focused window of ANY app (no permission needed); the cursor's screen when that app shows no window; null on Wayland |
app.frontmostApp() → Promise<TinyFrontmostApp | null> | tiny.app.frontmostApp() → Promise<TinyFrontmostApp | null> | same the active app right now (who focus returns to after win.hide()) |
app.icon(path: string) → boolean | tiny.app.icon(path: string) → Promise<void> | same replace the app icon from a png ('' resets to the bundle icon) |
app.info() → Promise<TinyAppInfo> | tiny.app.info() → Promise<TinyAppInfo> | same like tiny.app.info() (reading app.info.version as an object still works but is deprecated) |
app.keystroke(combo: string) → Promise<TinyKeystrokeResult> | tiny.app.keystroke(combo: string) → Promise<TinyKeystrokeResult> | same post a native keystroke, combo like 'cmd+v' (needs Accessibility) |
app.launchAtLogin.get() → Promise<TinyLoginStatus> | tiny.app.launchAtLogin.get() → Promise<TinyLoginStatus> | same |
app.launchAtLogin.set(enabled: boolean) → Promise<TinyLoginStatus> | tiny.app.launchAtLogin.set(enabled: boolean) → Promise<TinyLoginStatus> | same returns the resulting status |
app.matchScreen(saved: Partial<TinyScreen> | TinyScreenSnapshot) → Promise<TinyScreen | null> | tiny.app.matchScreen(saved: Partial<TinyScreen> | TinyScreenSnapshot) → Promise<TinyScreen | null> | same a saved TinyScreen / TinyScreenSnapshot -> the connected screen it most likely is now (ids don't survive replugs/reboots), or null |
app.mousePosition() → Promise<TinyMousePosition> | tiny.app.mousePosition() → Promise<TinyMousePosition> | same global cursor position + the screen it's on |
app.mouseTracking.start() → Promise<{ ok: true; }> | tiny.app.mouseTracking.start() → Promise<{ ok: true; }> | same |
app.mouseTracking.stop() → boolean | tiny.app.mouseTracking.stop() → Promise<boolean> | same |
app.nowPlaying.clear() → boolean | tiny.app.nowPlaying.clear() → Promise<void> | same |
app.nowPlaying.set(info: TinyNowPlaying) → boolean | tiny.app.nowPlaying.set(info: TinyNowPlaying) → Promise<void> | same also arms the media keys |
| — | tiny.app.onMediaKey(fn: (info: TinyMediaKey) => void) → () => void | page only a hardware media key / Control Center transport fired |
| — | tiny.app.onNotificationAction(fn: (info: TinyNotificationAction) => void) → () => void | page only action button / reply field on a notification was used |
| — | tiny.app.onNotificationClick(fn: (id: string) => void) → () => void | page only |
| — | tiny.app.onOpenFiles(fn: (paths: string[]) => void) → () => void | page only |
| — | tiny.app.onOpenUrl(fn: (url: string) => void) → () => void | page only |
| — | tiny.app.onScreensChanged(fn: (screens: TinyScreen[]) => void) → () => void | page only screens plugged/unplugged/rearranged/rescaled; returns unsubscribe |
app.paste() → Promise<TinyKeystrokeResult> | tiny.app.paste() → Promise<TinyKeystrokeResult> | same keystroke('cmd+v'): paste into the frontmost app (hide first) |
app.paths() → Promise<TinyPaths> | tiny.app.paths() → Promise<TinyPaths> | same standard per-app directories |
app.permissions.check(name: TinyPermissionName) → Promise<TinyPermissionStatus> | tiny.app.permissions.check(name: TinyPermissionName) → Promise<TinyPermissionStatus> | same |
app.permissions.request(name: TinyPermissionName) → Promise<TinyPermissionStatus> | tiny.app.permissions.request(name: TinyPermissionName) → Promise<TinyPermissionStatus> | same also prompts (accessibility opens System Settings at your app) |
app.pickColor() → Promise<string | null> | tiny.app.pickColor() → Promise<string | null> | same system eyedropper — NO screen-recording permission needed; '#rrggbb', or null if the user cancels |
app.playSound(target?: TinySoundName | (string & {})) → Promise<boolean> | tiny.app.playSound(target?: TinySoundName | (string & {})) → Promise<boolean> | same 'info' | 'success' | 'alert' | 'error' (portable — mapped to each OS's nearest sound), a platform sound name ('Glass' macOS, 'SystemHand' Windows, 'complete' Linux — these do NOT port), or an audio file path. |
app.power.allowSleep() → Promise<boolean> | tiny.app.power.allowSleep() → Promise<boolean> | same |
app.power.preventSleep(reason?: string, opts?: { display?: boolean; }) → Promise<boolean> | tiny.app.power.preventSleep(reason?: string, opts?: { display?: boolean; }) → Promise<boolean> | same reason shows in `pmset -g assertions`; display: true also keeps the screen on |
app.presence(mode: "normal" | "menubar") → void | tiny.app.presence(mode: "normal" | "menubar") → Promise<void> | same 'menubar': no Dock icon / taskbar button / app switcher entry |
app.progress(value: number | null) → boolean | tiny.app.progress(value: number | null) → Promise<void> | same progress bar on the app icon / taskbar button: 0..1, null clears |
app.quit() → void | tiny.quit() → Promise<void> | same |
app.say(text: string, opts?: TinySayOptions) → Promise<boolean> | tiny.app.say(text: string, opts?: TinySayOptions) → Promise<boolean> | same speak text; resolves when playback finishes (false if interrupted) |
app.screenAt(x: number, y: number) → Promise<TinyScreen | null> | tiny.app.screenAt(x: number, y: number) → Promise<TinyScreen | null> | same the screen containing a global point; null if none |
app.screenFor(rect: TinyRect) → Promise<TinyScreen | null> | tiny.app.screenFor(rect: TinyRect) → Promise<TinyScreen | null> | same the screen holding most of a global rect; null if it touches none |
app.screens() → Promise<TinyScreen[]> | tiny.app.screens() → Promise<TinyScreen[]> | same every display, same top-left coords as win.setPosition |
app.secrets.delete(key: string) → Promise<boolean> | tiny.app.secrets.delete(key: string) → Promise<boolean> | same |
app.secrets.get(key: string) → Promise<string | null> | tiny.app.secrets.get(key: string) → Promise<string | null> | same |
app.secrets.set(key: string, value: string) → Promise<boolean> | tiny.app.secrets.set(key: string, value: string) → Promise<boolean> | same |
app.setAsDefaultHandler(ext: string) → Promise<"unsupported" | "ok" | "failed"> | tiny.app.setAsDefaultHandler(ext: string) → Promise<"unsupported" | "ok" | "failed"> | same become the default opener for a file extension, or 'folder'. |
app.shell.open(target: string) → Promise<true> | tiny.app.shell.open(target: string) → Promise<true> | same open a URL (any scheme) or file path in the default app |
app.shell.reveal(path: string) → Promise<true> | tiny.app.shell.reveal(path: string) → Promise<true> | same show the file in Finder |
app.shell.trash(path: string) → Promise<true> | tiny.app.shell.trash(path: string) → Promise<true> | same move to the Trash (recoverable — prefer over deleting user files) |
app.spotlight(query: string) → Promise<string[]> | tiny.app.spotlight(query: string) → Promise<string[]> | same find files — up to 100 paths. |
app.stopSpeaking() → boolean | tiny.app.stopSpeaking() → Promise<void> | same |
app.thumbnail(path: string, size?: number) → Promise<TinyThumbnail> | tiny.app.thumbnail(path: string, size?: number) → Promise<TinyThumbnail> | same thumbnail png for ANY path — a content preview where Quick Look has a renderer (images, video, PDF, source files), the document/app/folder ICON otherwise, so this never fails on file type alone (macOS and Windows; Linux |
app.update.check() → Promise<{ available: boolean; current: string; latest: string | null; notes: string | null; }> | tiny.app.update.check() → Promise<{ available: boolean; current: string; latest: string | null; notes: string | null; }> | same notes = release notes from the manifest ("tinyjs publish --notes") |
app.update.install() → Promise<boolean> | tiny.app.update.install() → Promise<true> | same downloads, verifies, swaps the bundle and relaunches |
app.voices() → Promise<TinyVoice[]> | tiny.app.voices() → Promise<TinyVoice[]> | same |
System
backend app | page tiny | notes |
|---|---|---|
| — | tiny.capabilities.can(path: string) → boolean | page only true when the call works here. |
| — | tiny.capabilities.get(path: string) → TinyCapability | page only why not, when it doesn't |
| — | tiny.capabilities.list() → string[] | page only every path that works here, sorted |
| — | tiny.capabilities.onChange(fn: (change: { added: string[]; removed: string[]; }) => void) → () => void | page only fires when the answer changes, including when the first one lands |
| — | tiny.capabilities.ready: Promise<void> | page only resolves once the answer has arrived, or after 3s without one — it never hangs; a late answer fires onChange. |
| — | tiny.capabilities.refresh() → Promise<void> | page only ask the backend again; fires onChange if anything differs |
app.system.architecture() → Promise<string> | tiny.system.architecture() → Promise<string> | same |
app.system.battery() → Promise<TinyBattery | null> | tiny.system.battery() → Promise<TinyBattery | null> | same live machine state |
app.system.capabilities() → Promise<Record<string, string | boolean>> | tiny.system.capabilities() → Promise<Record<string, string | boolean> & { api?: { gated: boolean; denied: string[]; }; }> | returns differ What this machine can actually do — anything ABSENT is supported. |
app.system.idleTime() → Promise<number> | tiny.system.idleTime() → Promise<number> | same seconds since the user's last input, session-wide |
app.system.info() → Promise<TinySystemInfo> | tiny.system.info() → Promise<TinySystemInfo> | same |
app.system.isLinux() → boolean | tiny.system.isLinux() → boolean | same |
app.system.isMacOS() → boolean | tiny.system.isMacOS() → boolean | same |
app.system.isWindows() → boolean | tiny.system.isWindows() → boolean | same |
app.system.locale() → Promise<TinyLocale | null> | tiny.system.locale() → Promise<TinyLocale | null> | same null where the launcher has no locale reader yet (Windows, Linux) |
app.system.missing(ids?: string[] | null) → Promise<TinyRequirement[]> | tiny.system.missing(ids?: string[] | null) → Promise<TinyRequirement[]> | same |
app.system.os() → "macos" | "windows" | "linux" | tiny.system.os() → "macos" | "windows" | "linux" | same synchronous and safe during page setup (the webview's UA is decisive) |
| — | tiny.system.promptMissing(ids?: string[] | null, opts?: { title?: string; ok?: string; cancel?: string; }) → Promise<{ missing: TinyRequirement[]; copied: boolean; }> | page only |
app.system.requirements(ids?: string[] | null, opts?: { refresh?: boolean; }) → Promise<TinyRequirement[]> | tiny.system.requirements(ids?: string[] | null, opts?: { refresh?: boolean; }) → Promise<TinyRequirement[]> | same |
app.system.wifi() → Promise<TinyWifi | null> | tiny.system.wifi() → Promise<TinyWifi | null> | same |
Theme
backend app | page tiny | notes |
|---|---|---|
app.theme.get() → Promise<{ dark: boolean; } | null> | tiny.theme.get() → Promise<{ dark: boolean; } | null> | same |
| — | tiny.theme.on(fn: (dark: boolean) => void) → () => void | page only |
Clipboard
backend app | page tiny | notes |
|---|---|---|
app.clipboard.changeCount() → Promise<number> | tiny.clipboard.changeCount() → Promise<number> | same |
| — | tiny.clipboard.onChange(fn: (info: { changeCount: number; self: boolean; }) => void) → () => void | page only after watch(); self = our own write() caused the change |
app.clipboard.read() → Promise<TinyClipboardData> | tiny.clipboard.read() → Promise<TinyClipboardData> | same |
app.clipboard.unwatch() → true | tiny.clipboard.unwatch() → Promise<void> | same |
app.clipboard.watch(intervalMs?: number) → true | tiny.clipboard.watch(intervalMs?: number) → Promise<void> | same poll for changes every intervalMs (default 500) |
app.clipboard.write(data: TinyClipboardWrite) → boolean | tiny.clipboard.write(data: TinyClipboardWrite) → Promise<void> | same |
Store
backend app | page tiny | notes |
|---|---|---|
app.store.all() → Promise<Record<string, any>> | tiny.store.all() → Promise<Record<string, any>> | same |
app.store.delete(key: string) → Promise<boolean> | tiny.store.delete(key: string) → Promise<boolean> | same Same result and failure rules as set(). |
app.store.get(key: string) → Promise<any> | tiny.store.get(key: string) → Promise<any> | same |
app.store.set(key: string, value: unknown) → Promise<boolean> | tiny.store.set(key: string, value: unknown) → Promise<boolean> | same Resolves true once on disk. |
Audio
backend app | page tiny | notes |
|---|---|---|
app.audio.balance(v: number) → void | tiny.audio.balance(v: number) → Promise<void> | same Stereo balance -1 (left) .. |
app.audio.clear() → void | tiny.audio.clear() → Promise<void> | same no filters, balance back to centre |
app.audio.filter(index: number, patch: Partial<TinyAudioFilter>) → void | tiny.audio.filter(index: number, patch: Partial<TinyAudioFilter>) → Promise<void> | same Retune ONE filter in place — no rebuild, no gap; what a slider drag should call |
app.audio.filters(list: TinyAudioFilter[]) → void | tiny.audio.filters(list: TinyAudioFilter[]) → Promise<void> | same Replace the whole chain. |
| — | tiny.audio.pageChain(ctx: BaseAudioContext) → TinyPageChain | page only The same chain as Web Audio nodes IN THE PAGE — the fallback where the native chain doesn't exist (Windows). |
app.audio.sampler.load(name: string, source: string | ArrayBuffer | ArrayBufferView) → Promise<true> | tiny.audio.sampler.load(name: string, source: string | ArrayBuffer | ArrayBufferView | Blob) → Promise<true> | args differ Decode a sound into the bank. |
app.audio.sampler.master(v: number) → true | tiny.audio.sampler.master(v: number) → Promise<void> | same One master gain over the whole mixer (linear, default 1). |
app.audio.sampler.play(name: string, opts?: { vol?: number; pan?: number; rate?: number; loop?: boolean; }) → Promise<{ id: number; set(patch: { vol?: number; pan?: number; rate?: number; }): true; stop(): true; }> | tiny.audio.sampler.play(name: string, opts?: { vol?: number; pan?: number; rate?: number; loop?: boolean; }) → Promise<TinySamplerVoice> | returns differ Fire a voice. |
app.audio.sampler.stop(id: number) → true | — | backend only stop a voice by id (same as voice.stop()) |
app.audio.sampler.stopAll() → true | tiny.audio.sampler.stopAll() → Promise<void> | same |
app.audio.sampler.unload(name: string) → true | tiny.audio.sampler.unload(name: string) → Promise<void> | same Drop a sound and free its decoded PCM. |
| — | tiny.audioTap.on(fn: (chunk: { pcm: string; sampleRate: number; channels: number; frames: number; t: number; }) => void) → () => void | page only Register a chunk handler. |
| — | tiny.audioTap.start(opts?: { scope?: "app" | "system"; excludeSelf?: boolean; interval?: number; }) → Promise<true> | page only Start tapping. |
| — | tiny.audioTap.stop() → Promise<void> | page only |
macOS only
backend app | page tiny | notes |
|---|---|---|
app.macos.ai.availability() → Promise<TinyAiAvailability> | tiny.macos.ai.availability() → Promise<TinyAiAvailability> | same |
app.macos.ai.generate(prompt: string, opts?: { instructions?: string; }) → Promise<string>app.macos.ai.generate(prompt: string, opts: { instructions?: string; tools: TinyAiTool[]; }) → Promise<{ text: string; calls: TinyAiCall[]; }> | tiny.macos.ai.generate(prompt: string, opts?: { instructions?: string; }) → Promise<string> | args differ offline, no API key. |
app.macos.applescript(source: string) → Promise<string | null> | tiny.macos.applescript(source: string) → Promise<string | null> | same run AppleScript in-process (no osascript spawn; 'automation' TCC); resolves the result as a string, null if it isn't text; rejects with the script error message |
app.macos.moveWindow(pid: number, rect: { x: number; y: number; width: number; height: number; }) → Promise<true> | tiny.macos.moveWindow(pid: number, rect: { x: number; y: number; width: number; height: number; }) → Promise<true> | same move/resize another app's frontmost window (pid from otherWindows) |
app.macos.ocr(path: string) → Promise<TinyOcrResult> | tiny.macos.ocr(path: string) → Promise<TinyOcrResult> | same on-device OCR via Vision (accurate mode); box normalized 0..1 |
app.macos.otherWindows() → Promise<TinyOtherWindow[] | null> | tiny.macos.otherWindows() → Promise<TinyOtherWindow[] | null> | same other apps' on-screen windows (Accessibility); null if not granted |
app.macos.quickLook(paths?: string | string[] | null) → boolean | tiny.macos.quickLook(paths?: string | string[] | null) → Promise<void> | same Quick Look panel for the path(s); no args closes it |
app.macos.recorder.start(opts: { path: string; screenId?: number; }) → Promise<true> | tiny.macos.recorder.start(opts: { path: string; screenId?: number; }) → Promise<true> | same resolves once capture is running; needs the 'screen' permission + macOS 14, rejects with the reason otherwise |
app.macos.recorder.stop() → Promise<TinyRecording> | tiny.macos.recorder.stop() → Promise<TinyRecording> | same |
app.macos.selectedText() → Promise<string | null> | tiny.macos.selectedText() → Promise<string | null> | same text selected in the frontmost app (Accessibility); null if none |
Events
The backend hooks are exports of your backend module, wired once at startup; the page subscribes at runtime and gets back an unsubscribe function.
backend app | page tiny | notes |
|---|---|---|
export function onMenu(id, app) | tiny.menu.on(fn) | menu bar click |
export function onContextMenu(id, app) | tiny.menu.onContext(fn) | custom right-click menu click |
export function onTray(id | null, app) | tiny.tray.on(fn) / tiny.tray.onClick(fn) | tray menu item / bare icon click (one hook on the backend, two on the page) |
export function onHotkey(id, app) | tiny.hotkey.on(fn) | global hotkey pressed |
export function onSystem(kind, value, app) | tiny.theme.on(fn(dark)) | theme / sleep / wake — the page has sugar for theme only; sleep/wake via tiny.api.on |
export function onLocale(info, app) | tiny.api.on('locale', fn) | no page sugar; macOS only for now (no locale reader on Windows/Linux) |
export function onOpenUrl(url, app) | tiny.app.onOpenUrl(fn) | |
export function onOpenFiles(paths, app) | tiny.app.onOpenFiles(fn) | |
export function onNotificationClick(id, app) | tiny.app.onNotificationClick(fn) | |
export function onNotificationAction(info, app) | tiny.app.onNotificationAction(fn) | |
export function onMediaKey(info, app) | tiny.app.onMediaKey(fn) | |
export function onWindowState(info, app) | tiny.win.onState(fn) | broadcast to every window |
export function onWindowClosed(id, app) | tiny.api.on('window-closed', fn) | no page sugar; the page gets { id }, the hook a bare id |
export function onClipboardChange(info, app) | tiny.clipboard.onChange(fn) | both start a watch |
export function onAudioTap(chunk, app) | tiny.audioTap.on(fn) | only a page can start the tap |
export function onUpdateAvailable(info, app) | tiny.api.on('update-available', fn) | no page sugar |
| — | tiny.win.onDrop(fn) | page only; the launcher's drop carries no window id, so every window hears it |
export function onNavigate(info, app) | tiny.api.on('navigate', fn) | pages hear the lifecycle (start/commit/finish/fail/crash); only the backend answers the policy ask |
export function onDownload(info, app) | tiny.api.on('download', fn) | pages hear progress; only the backend answers the policy ask |
export function onWindowOpen(info, app) | tiny.api.on('popup', fn) | backend decides; the page hears 'popup' (no kind field) |
Deprecated backend names
Still work, log a one-line warning the first time each is used, and will be removed in a future release.
| old backend name | use instead |
|---|---|
app.getMenuItem(id: string) → Promise<TinyMenuItemState> | use app.menu.get() |
app.getWinState() → Promise<TinyWinState | null> | use app.win.getState() |
app.openWindow(id: string, opts?: TinyOpenWindowOptions) → void | use app.win.open() |
app.setAudioBalance(v: number) → void | use app.audio.balance() |
app.setAudioFilter(index: number, patch: Partial<TinyAudioFilter>) → void | use app.audio.filter() |
app.setAudioFilters(list: TinyAudioFilter[]) → void | use app.audio.filters() |
app.setContextMenu(items: TinyMenuItem[] | null) → void | use app.menu.setContext() |
app.setMenu(menus: TinyMenu[]) → void | use app.menu.set() |
app.updateMenuItem(id: string, patch?: { label?: string; checked?: boolean; enabled?: boolean; }) → void | use app.menu.update() |
app.win.getMenuItem(id: string) → Promise<TinyMenuItemState> | use menu.get() |
app.win.resetMenu() → void | use menu.reset() |
app.win.setMenu(menus: TinyMenu[]) → void | use menu.set() |
app.win.updateMenuItem(id: string, patch?: { label?: string; checked?: boolean; enabled?: boolean; }) → void | use menu.update() |
app.notify({ title, body, …opts }) | app.notify(title, body, opts) |
app.info.version, app.paths.data, … | (await app.info()).version, (await app.paths()).data |
app.presence(anything but 'normal' | 'menubar') | will throw, as the page call does |