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>