Execute API
Every helper an execute snippet can call, with its arguments and what it returns.
An agent changes a design with the execute tool: a short JavaScript snippet that calls these helpers. Every helper is in scope; nothing is imported. A target is an id, a layer name or the value a helper returned. The execute tool page shows how to use them.
Types
type Id = string
/** What a helper that makes something returns. It also works as its id string: `${ref}`, `ref + ""`. */
interface Ref {
id: Id
kind: string
name: string
[extra: string]: unknown
}
type Target = Id | Ref | { id: Id }
type Targets = Target | Target[]
/** `as` keeps the result for later calls under that name, as a bare assignment does. */
interface Named {
as?: string
}
/**
* Options of the helpers that read TSX. `autofix: true` removes what a design
* cannot keep (`key`, `ref`, event handlers and hooks; a useState value becomes
* a prop with its default) and the result notes say what changed.
*/
interface TsxOptions {
autofix?: boolean
}
interface GetResult {
id: Id
kind: string
name: string
path: string
parent: string | null
children: Id[]
tsx?: string
bounds?: { x: number; y: number; width: number; height: number }
/** With `{bounds: 'all'}`: the canvas box of the target and of every layer under it, by id. */
boxes?: Record<Id, { x: number; y: number; width: number; height: number }>
[extra: string]: unknown
}
interface FoundNode {
id: Id
kind: string
name: string
path: string
}
Globals
page, tsx
/** The current page id. */
declare const page: Id
/** The named TSX blocks of the call, such as tsx.Hero. */
declare const tsx: Record<string, string>
Reading
Tree, Get, Find
declare function Tree(scope?: Target | null, options?: { depth?: number }): string
declare function Get(
target: Target,
options?: { depth?: number; bounds?: boolean | 'all' }
): GetResult
declare function Find(selector: string): FoundNode[]
declare function Find(scope: Target, selector: string): FoundNode[]
Screens, pages and components
AddScreen, AddScreens, Define, Place, AddPage, MakeComponent, FindEmptySpace, ListTemplates, InsertTemplate
declare function AddScreen(
name: string,
tsx: string,
options?: Named &
TsxOptions & {
page?: Target
x?: number
y?: number
width?: number
height?: number
/** A print page: its size (A4 794 × 1123, A5 559 × 794, Letter 816 × 1056) and p-[20mm] on the root. */
paper?: PaperName
modes?: Record<string, string>
screenshot?: boolean | { scale?: number; widths?: number[]; compare?: boolean }
}
): Ref & {
component: string
item: Id
root: Id
page: Id
/** Set when the component name is not the name you gave: "Cover page" becomes CoverPage. */
note?: string
}
declare function AddScreens(
screens: {
name: string
tsx: string
width?: number
height?: number
paper?: PaperName
modes?: Record<string, string>
}[],
options?: TsxOptions & {
page?: Target
gap?: number
screenshot?: boolean | { scale?: number; widths?: number[]; compare?: boolean }
}
): Ref[]
declare function Define(source: string, options?: Named & TsxOptions): string[]
declare function Place(
component: Target,
options?: Named & {
page?: Target
x?: number
y?: number
width?: number
height?: number
props?: Record<string, unknown>
}
): Ref
declare function AddPage(name: string, options?: Named & { kind?: 'canvas' | 'components' }): Ref
declare function MakeComponent(
target: Target,
name: string,
options?: Named
): Ref & { instance: Id | null }
declare function FindEmptySpace(options?: {
page?: Target
width?: number
height?: number
near?: Target
direction?: 'top' | 'right' | 'bottom' | 'left'
gap?: number
}): { x: number; y: number }
declare function ListTemplates(filter?: { kind?: string; query?: string }): unknown[]
declare function InsertTemplate(
id: string,
options?: Named & { page?: Target; x?: number; y?: number }
): Ref & { screens: Id[]; components: string[]; renamed: Record<string, string> }
Changing the tree
Insert, Replace, Update, Move, Copy, Delete, Archive, ProposeOptions, Rounds, AcceptOption, InsertIcon, InsertLogo, SearchLogos
declare function Insert(
parent: Target,
tsx: string,
options?: Named &
TsxOptions & {
index?: number
screenshot?: boolean | { scale?: number; widths?: number[]; compare?: boolean }
}
): Ref
declare function Replace(
target: Target,
tsx: string,
options?: Named & TsxOptions & { force?: boolean }
): Ref
declare function Update(target: Target, changes: Record<string, unknown>): void
declare function Move(target: Target, parent?: Target | null, index?: number): void
declare function Copy(
target: Target,
parent?: Target | null,
options?: Named & { index?: number }
): Ref
declare function Delete(targets: Targets, options?: { force?: boolean }): void
/**
* Moves screens to the Archive page, the page of old ideas, which is made when
* the design has none. Use it in place of Delete when the user keeps the old screen.
*/
declare function Archive(targets: Targets): { page: Ref; screens: Id[] }
/** A round of proposals: a group on the canvas beside its screen, with the prompt and the options. */
interface Round {
id: Id
round: string
name: string
screen: string
prompt: string
status: 'open' | 'accepted'
accepted?: number
options: { option: number; component: string }[]
}
/**
* A new round of options for a screen, beside it. Each option is TSX for the
* whole screen. The screen does not change until AcceptOption.
*/
declare function ProposeOptions(
screen: Target,
options: { prompt: string; options: (string | { tsx: string })[]; name?: string }
): Round
/** The rounds of one screen, or of the design, oldest first. */
declare function Rounds(screen?: Target): Round[]
/** Puts option `option` (1, 2, …) of a round into its screen and marks the round accepted. */
declare function AcceptOption(round: string | Round, option: number): Ref
/**
* In the TSX of Define, AddScreen, Insert, Replace and tsx blocks, write
* <Icon name="rotate-cw" className="size-4" size={16} /> instead: no call is necessary.
*/
declare function InsertIcon(
parent: Target,
name: string,
options?: Named & { className?: string; name?: string; index?: number }
): Promise<Ref>
declare function InsertLogo(
parent: Target,
logo:
| string
| { title: string; route: string | { light: string; dark: string }; wordmark?: unknown },
options?: Named & {
theme?: 'light' | 'dark'
wordmark?: boolean
className?: string
name?: string
index?: number
}
): Promise<Ref>
declare function SearchLogos(
query: string,
options?: { limit?: number }
): Promise<
{
title: string
categories: string[]
route: { light: string; dark: string }
wordmark?: { light: string; dark: string }
url?: string
brandUrl?: string
}[]
>
Styles and components
CopyStyle, MatchStyle, AuditTokens, DefineProp, Patch, AddVariant, UseKit
declare function CopyStyle(from: Target, to: Targets, options?: { parts?: string[] }): Promise<Id[]>
declare function MatchStyle(
targets: Target[],
options?: { parts?: string[] }
): Promise<Record<string, { from: Id; changed: Id[] }>>
declare function AuditTokens(
scope?: Targets,
options?: { apply?: boolean; min?: number }
): unknown[]
declare function DefineProp(
component: Target,
prop: string,
value: string | number | boolean,
options?: { options?: string[] }
): unknown
/** Replaces `find` with `replace` in the component's TSX, as Get(component).tsx prints it, and defines it again. */
declare function Patch(
component: Target,
find: string,
replace: string,
options?: { all?: boolean }
): string[]
declare function AddVariant(
component: Target,
prop: string,
value: string,
classes: string
): { prop: string; options: string[] }
/**
* Defines the common components of a library with its variants and default look:
* Button, Badge, Card, Input, Switch, Checkbox, Tabs and Tab, Avatar, Separator and
* Tooltip, and the tokens they use that the theme does not have. A component the
* design has already stays as it is. Returns the names of the kit's components.
*/
declare function UseKit(kit: 'shadcn' | 'heroui', options?: { only?: string[] }): string[]
States: tooltips, hover, open menus and confirmation steps
AddTooltip, AddState
/**
* A tooltip next to a layer of a screen, absolute in the screen root and placed from
* the layer's box (inside the layer when the host cannot measure). Returns the tooltip.
*/
declare function AddTooltip(
target: Target,
text: string,
options?: Named & {
side?: 'top' | 'bottom' | 'left' | 'right'
gap?: number
className?: string
name?: string
}
): Promise<Ref>
/**
* Copies a screen beside it as the screen "<Screen><State>", such as SettingsDeleteConfirm,
* for a hover, an open menu or a confirmation step. `changes` maps a layer's data-name in
* the copy to an Update patch. Returns the new screen.
*/
declare function AddState(
screen: Target,
state: string,
changes?: Record<string, Record<string, unknown>>,
options?: Named
): Ref
Tokens
GetTokens, SetTokens, ImportTokens
declare function GetTokens(): {
tokens: Record<string, string>
modes: Record<string, Record<string, unknown>>
fonts: unknown[]
}
declare function SetTokens(
values: Record<string, string | number | null>,
options?: { mode?: string }
): void
/**
* The custom properties of a CSS text (a globals.css) as tokens, through
* SetTokens. `:root`, `html` and `@theme` give the base values; `.dark`,
* `[data-theme="dark"]` and `@media (prefers-color-scheme: dark)` give the
* values of the `darkMode` mode ("dark"). `var()` is resolved per mode.
*/
declare function ImportTokens(
css: string,
options?: { darkMode?: string }
): { tokens: Record<string, string>; dark: Record<string, string> }
Output
Screenshot, CompareShot, Export, UploadImage, SearchPhotos, GenerateImage, TraceImage, Lint, CheckAccessibility, ApplyFix, Print, console
/** `state` draws each target with its `hover:` (or `focus:`, `active:`, `disabled:`) classes applied. */
declare function Screenshot(
targets: Targets,
options?: { scale?: number; widths?: number[]; compare?: boolean; state?: ShotState }
): void
declare function CompareShot(
targets: Targets,
options?: { scale?: number; widths?: number[]; state?: ShotState }
): void
type ShotState = 'hover' | 'focus' | 'active' | 'disabled'
/** `svg` gives each screen as SVG layers, in the Knighn app. */
declare function Export(
targets: Targets,
format: 'png' | 'jpg' | 'pdf' | 'svg' | 'tsx' | 'expo',
path?: string | ExportOptions,
options?: ExportOptions
): string | { files: { path: string; code: string }[]; skipped: string[] } | undefined
/** `page` (pdf only): pages of this size, the screen fit to the page width. */
interface ExportOptions {
scale?: number
page?: PaperName | { width: number | string; height: number | string }
}
type PaperName = 'A4' | 'A5' | 'Letter'
declare function UploadImage(source: string): Promise<string>
declare function SearchPhotos(
query: string,
options?: { count?: number; orientation?: 'wide' | 'tall' | 'square' }
): Promise<unknown[]>
declare function GenerateImage(
prompt: string,
options?: { kind?: 'photo' | 'logo'; width?: number; height?: number }
): Promise<string>
declare function TraceImage(
image: Target | string,
options?: Named & {
threshold?: number
detail?: number
smoothness?: number
colour?: string
invert?: boolean
replace?: boolean
name?: string
parent?: Target
}
): Promise<Ref>
/**
* Code checks always, with no render; the layout check (layout/past-bottom)
* only when a renderer gives the bounds in 20 s. `{layout: false}` skips the
* layout check. The response notes say which checks ran and which Lint skipped.
*/
declare function Lint(targets?: Targets, options?: { layout?: boolean }): Promise<unknown[]>
declare function Lint(options: { layout?: boolean }): Promise<unknown[]>
declare function CheckAccessibility(
targets?: Targets,
options?: { level?: 'AA' | 'AAA' }
): Promise<unknown[]>
declare function ApplyFix(findings: unknown): Promise<string[]>
declare function Print(...values: unknown[]): void
declare const console: {
log(...values: unknown[]): void
info(...values: unknown[]): void
debug(...values: unknown[]): void
warn(...values: unknown[]): void
error(...values: unknown[]): void
}
Imports
ImportWebPage, ImportHTML, ImportSimulator, ImportSafari, ImportFile
/**
* A web page as a new screen. The page sees `prefers-color-scheme: dark`
* unless `colorScheme` is "light". A Storybook story link
* (`?path=/story/<id>`) gives the story alone, not the Storybook UI. For a
* page behind a sign-in, give `headers` (sent to the page's origin only) and
* `cookies` (a list, an object of names and values, or "a=1; b=2"); only a
* Mac host (the desktop app or the daemon) takes them, and they are never
* logged. The page's start-up overlays (cookie and consent banners, chat
* widgets, first-run dialogs, pop-ups) are hidden unless `hideOverlays` is false.
*/
declare function ImportWebPage(
input:
| string
| (Named & {
url: string
width?: number
colorScheme?: 'dark' | 'light'
hideOverlays?: boolean
headers?: Record<string, string>
cookies?: CaptureCookie[] | Record<string, string> | string
page?: Target
})
): Promise<{ id?: Id; screens: Id[]; report: unknown }>
/** With no `domain` and no `url`, a cookie is for the page's own address. */
interface CaptureCookie {
name: string
value: string
domain?: string
path?: string
url?: string
secure?: boolean
httpOnly?: boolean
sameSite?: 'Strict' | 'Lax' | 'None'
}
/**
* HTML text and its CSS (in `<style>` elements, `style` attributes or `css`) as
* a new screen. A local HTML file is passed as its text. The HTML sees
* `prefers-color-scheme: dark` unless `colorScheme` is "light". `method` says
* how it ran: "browser" (measured layers) or "headless" (the CSS as Tailwind
* classes). An http or https URL in place of the HTML is captured as
* ImportWebPage does it (a Storybook story link gives the story alone).
*/
declare function ImportHTML(
input:
| string
| (Named & {
html: string
css?: string
width?: number
name?: string
colorScheme?: 'dark' | 'light'
page?: Target
method?: 'browser' | 'headless'
})
): Promise<{ id?: Id; screens: Id[]; report: unknown; method: 'browser' | 'headless' }>
declare function ImportSimulator(
input?: Named & { device?: string; name?: string; page?: Target }
): Promise<{ id?: Id; screens: Id[]; report: unknown }>
/**
* The page open in Safari, as the person sees it, as a new screen: the
* current tab of the front window unless `tab` (from 1) names one. The
* Knighn app on a Mac only.
*/
declare function ImportSafari(
input?: Named & {
tab?: number
width?: number
colorScheme?: 'dark' | 'light'
hideOverlays?: boolean
page?: Target
}
): Promise<{ id?: Id; screens: Id[]; report: unknown }>
/**
* A `.fig`, `.pen` or SVG file as new screens. `file` is an http or https URL,
* a path on the Mac of the host, a data URL, or SVG text. It needs the Knighn
* app or a Mac with Knighn. `format` is for a file whose name has no extension.
*/
declare function ImportFile(
file: string,
options?: Named & { format?: 'fig' | 'pen' | 'svg'; name?: string; page?: Target }
): Promise<{ id?: Id; screens: Id[]; report: unknown }>
The canvas of the person (the Knighn app)
Select
/**
* Selects layers or screens on the canvas after the call's edits, on the page
* that has them. `zoom` fits the view to them. `Select([])` clears the selection.
*/
declare function Select(targets: Targets, options?: { zoom?: boolean }): { selected: Id[] }
Guidance
GetAppState, ReadSkill, Search, GetStyle, GetErrors, ResolveError, LinkTo, OpenProject
declare function GetAppState(): Promise<Record<string, unknown>>
declare function ReadSkill(path?: string): string
declare function Search(
query: string,
options?: {
kinds?: string | string[]
limit?: number
cursor?: string
/** Search only inside this page, screen, component or layer. */
scope?: Target
/** Only ids, kinds and titles in each hit. */
compact?: boolean
}
): unknown
declare function GetStyle(name?: string): unknown
declare function GetErrors(options?: {
limit?: number
since?: number
runtime?: string
}): Promise<unknown[]>
declare function ResolveError(id: string): Promise<void>
/**
* Links that open a place in the Knighn app: "settings/agents", "import/web", "design/<file id>".
* On a Mac, give `mac` (knighn://go/…) first; give `web` only when the person does not have the app.
*/
declare function LinkTo(place: string): {
place: string
title: string
where: string
mac: string
ios: string
web: string
use: string
}
/**
* Opens a code project folder in a tab of the Knighn app. It needs the app on the Mac.
* With no app, pass the folder as `project` to execute: execute({ project: "<path>", input: "return await Screens()" }).
*/
declare function OpenProject(path: string): Promise<{ opened: string }>
The user's designs (the hosted server)
ArchiveDesign, UnarchiveDesign, RenameDesign, DuplicateDesign, ShareDesign, ListVersions, RestoreVersion
/** Archives a design: Home and agents leave it out of their lists. Only when the user asks. */
declare function ArchiveDesign(
fileId: string
): Promise<{ id: string; name: string; archived: boolean }>
/** Brings an archived design back to the lists. */
declare function UnarchiveDesign(
fileId: string
): Promise<{ id: string; name: string; archived: boolean }>
/** Gives a design a new name. */
declare function RenameDesign(fileId: string, name: string): Promise<{ id: string; name: string }>
/** Makes a copy of a design on the account, and returns the copy. */
declare function DuplicateDesign(fileId: string): Promise<{ id: string; name: string }>
/** Turns the public link of a design on (the default) or off. Only when the user asks. */
declare function ShareDesign(
fileId: string,
on?: boolean
): Promise<{ id: string; name: string; shared: boolean; url?: string }>
/** The older versions of a design, newest first. */
declare function ListVersions(fileId: string): Promise<
{
id: string
savedAt: number
reason: string
size: number
name?: string
pinned: boolean
}[]
>
/** Makes an older version the newest version of a design. */
declare function RestoreVersion(
fileId: string,
versionId: string
): Promise<{ id: string; name: string; version: number }>
The run
Commit, Undo, Redo, RevertTo, AllowLargeChange, DryRun
/** Keeps the steps so far: when a later step fails, they are applied and the rest is rolled back. */
declare function Commit(name?: string): void
/** Undoes the last `count` execute calls of this session (1 by default). */
declare function Undo(count?: number): { undone: string[] }
/** Makes the change of the last `count` calls that Undo undid again (1 by default). */
declare function Redo(count?: number): { redone: string[] }
/** Undoes every change made after Commit(name), in this call or an earlier one. */
declare function RevertTo(name: string): { reverted: string }
/** Lets this call delete screens or change more than 200 existing layers. */
declare function AllowLargeChange(): void
/** Runs the snippet and reports what it would change, without applying it. */
declare function DryRun(): void
Comments (a design on Knighn: the hosted server, or the app)
ListComments, AddComment, ReplyToComment, ResolveComment
interface DesignComment {
id: string
/** A name, and never an email address. */
author: string
text: string
resolved: boolean
createdAt: number
/** The name of the page that shows the pin. */
page: string
/** The id of the layer or screen in this call, or null for a pin on the page. */
layer: string | null
/** The pin's point on the page, when the pin is not on a layer. */
point?: { x: number; y: number }
/** The pin was on a layer that the design does not have now. */
layerGone?: true
replies: { author: string; text: string; createdAt: number }[]
}
/** The comments of the design, oldest first. `{resolved: false}` gives only the open ones. */
declare function ListComments(options?: { resolved?: boolean }): Promise<DesignComment[]>
/** Puts a comment on a screen or a layer, or at a point on a page. */
declare function AddComment(
target: Target | { page?: string; x: number; y: number },
text: string
): Promise<DesignComment>
/** Adds a reply to a comment. */
declare function ReplyToComment(id: string, text: string): Promise<DesignComment>
/** Resolves a comment, or with `false` opens it again. */
declare function ResolveComment(id: string, resolved?: boolean): Promise<DesignComment>