Project tabs
Everything a project tab does with your Next.js, Vite, TanStack Start or Expo app.
Home and the tab bar
- There are no modes. The tab bar shows every tab: design tabs and project tabs together.
- Home has a Projects section beside the designs: the recent projects (each local project has the mark "This Mac"), Open Project and Clone from GitHub on the desktop, and the projects in the shared folders of each paired Mac. On the web it says how to share a project when no Mac opens projects.
- The link
knighn://go/code/openand the web address/go/code/openshow Home's Projects section.
Open a project
- Open a project folder on this Mac. (File > Open Project…, Cmd+Shift+O, Home. Desktop only.)
- Open a project on another paired Mac. (File > Open Project on Another Mac…, the cards of Home's Recent designs.) Only the apps Asset Asserter ships on that Mac show; Show All Mac Projects in Home's menu lists every project.
- Open a project from the command line:
knighn open [folder]. In an SSH session it opens in the app on the other Mac (--on <host>names it). - Clone one of your GitHub repositories with your own
gh, then open it. (File > Clone from GitHub…, Home. Desktop only.) You can filter the repositories and pick the folder. - Open a project from a link:
knighn://project?path=<folder>. - Open a project that is in a shared folder of a paired Mac. It opens as a local folder on that Mac itself, else as a remote project. (Home, web and desktop.)
- Knighn finds the framework, its version, the package manager, the app folder of a monorepo, the workspace packages, the Tailwind version and shadcn/ui.
- Knighn starts the project's own dev server. The screens show their last snapshots at once while it starts, with a small bar at the foot of the canvas for a failure or a missing install; with no snapshot yet, the canvas shows the state while it starts. (Opening the project…)
- Install the packages when
node_modulesis missing. (Install Dependencies on the canvas, Project > Install Dependencies.) - Start, retry or restart the dev server. The canvas shows the server's state only when it fails, with Retry there. (Start and Retry on the canvas, Project > Restart Dev Server.)
- Knighn does not change the project's files to add its source tags. Its runtime files go to
node_modules/.knighn/. - A Next.js 16 project can run its own
next devand Knighn's at the same time: Knighn's server builds in.next/knighn, reads its own tsconfig that extends the project's, and puts backnext-env.d.tswhen it is ready. Knighn does not use the project'sdevscript, so a script with a fixed port or a wrapper such as portless does not stop it. - An app that sends a page opened in the browser to a shell of its own (for example a top bar that shows each page in a frame) shows each route as itself: the canvas frames load the page as a frame, and the pictures do the same.
- Each project has settings outside the repository: Snap to Tokens, the sample values of dynamic routes, the viewports, mock data and the hidden routes.
- The host stops the dev server when the tab closes or the app quits.
Frameworks
- Next.js 16 with the App Router: Turbopack by default, webpack when necessary.
- Vite with React, with React Router or TanStack Router for the routes.
- TanStack Start with file routes.
- Routes written in code: TanStack Router
createRoutetrees (with layout routes and parents in other files) and React Router route objects or<Route>elements (createBrowserRouter,createHashRouter,useRoutes,createRoutesFromElements,<Routes>), with nested paths and index routes. Knighn reads them and does not run them. - Expo with Expo Router: the app runs on the web for its snapshots.
- An Expo app that is iOS only: Knighn turns the web platform on for its own dev server and supplies
react-domandreact-native-webfrom a cache. - An Expo app with native views (
@expo/uiand others): each native view shows as a web stand-in in the look of iOS. - SF Symbols in an Expo app: the Mac draws each real symbol, in the colour of the text.
- An Expo app with React Native Storybook: one story shows alone on the Storybook route, also when that route is behind a sign-in.
- Screens are drawn from the UI code first, with the project's own stories and mock data (Storybook or Forkbook previews) when it has them, so a screen shows what its code draws.
- Expo screens look as on iOS 26: the navigation bar with large titles, the tab bar, sheets, maps and native controls.
- Sign-in gates are open, so a screen behind a login shows its signed-in form.
- A page reaches no backend: requests for data are refused, and the screen shows its mock data.
- Knighn reads
metro.config.tsand amainthat is a file of the app. - Monorepos: bun, npm and yarn workspaces and
pnpm-workspace.yaml.apps/webis the first choice. - An Expo app in
ios/beside a web app is a project of its own, with the name "name (iOS)". - Tailwind 4 (
@theme) and Tailwind 3 (tailwind.config.*) themes, with the project's breakpoints.
The screens
A project tab shows a project as one design: one still screen for each page, on the editor's own canvas, layers panel and properties panel. Selection, hover, marquee, zoom and the keys are the editor's.
- One screen for each route, each scene, each shared component with its stories and its prop grid (the components of a folder named
ui, such assrc/uiorsrc/components/ui, and of shadcn/ui first, then the components used more than once; not those intests/,fixtures/,examples/or stories, or of an Expo app beside a web app; at most 60), and each other story of the project's Storybook stories (with its args as props). Shared components draw alone in every framework, Expo too. The routes come first, in the order of the scenes file'srouteslist and then in file order. - Each screen has a fixed title,
Group / Name, not its URL. The group comes from the scenes file, else from the first segment of a route with more than one (/settings/profileis in Settings;/and/pricingare in Pages). The name comes from the scenes file, else from the route's component (PricingPageis Pricing), else from the last segment. A scene with nogroupis in the group its name starts with ("Settings: Account" is Account in Settings), else in Scenes. - Each route shows at the project's viewports: 1440 px, or 390 px for an Expo app, by default. Choose them in the Viewports menu of the project bar (Project > Viewports…): the usual widths from 375 to 1920 px, This screen (the size of the Knighn window) and a width that you type. With one viewport, its screen fills the canvas at 100 %. Use Default Sizes sets the one default width again. Knighn keeps the viewports in the project settings, outside the repository.
- A project can choose its own viewports in the scenes file:
viewports: ['screen']orviewports: [375, 1440]. Knighn takes them when you have not chosen viewports for the project on this Mac. The Knighn repository uses This screen. - Each screen has a fixed height: the height of its viewport (900 px for a desktop, 844 px for a phone, 1024 px for a tablet) or the height in the layout file. A component or story screen is 480 by 320 px (390 px wide in an Expo app, in a React Native root on the app's background).
- One fixed layout: a group starts on the row where it fits, else on a new row, with the same widths and gaps each time.
- The layout file
knighn/layout.knighn.jsonin the app folder keeps the place, width and height of each screen, their order, and the view. It is plain JSON that you can commit, so the canvas opens the same on each Mac and after a restart. Knighn writes it when the screens or the view change, and does not commit it for you. - The first view shows the first row of screens at a readable scale, clear of the project bar.
- Knighn takes a snapshot of each screen's page (its HTML with its styles, after the page settles), makes it a design (each element with its styles as Tailwind classes, its source tag and the components it is in kept) and shows all the screens as one design.
- A layer with no source in the project (an element that a package or the browser made) is locked. A click on it selects the nearest parent that has a source.
- The last snapshot of each screen shows at once when the project opens, before the dev server is ready. Knighn keeps the snapshots in its cache on the Mac (
~/Library/Caches/Knighn/frame-pictures/). The host takes a snapshot after the page settles: the fonts, the images, the lazy parts (no Suspense fallback shows) and the requests are done, and the page neither changed nor loaded a file for half a second, at most 15 s. - When the dev server is ready, the host takes the snapshots the screens lack, one at a time, and each screen shows as it comes. A page that was still at work at its first snapshot (the dev server's first build) is taken again a few times. A page that does not settle does not take the place of a good snapshot. A broken page gives no snapshot.
- A screen whose page does not build, throws or draws nothing, and has no good snapshot yet, is drawn by reading its code instead (no code of the project runs): its JSX with the project's own classes and styles, its components drawn in place with their props, React Native and
@expo/uiviews in their iOS look, and data only the running app has as placeholder text. Each element keeps its source tag, so an edit goes to the code, and the screen's live snapshot takes its place once the page draws.Screens()gives such a screen withsource: "static". - When files change, Knighn takes new snapshots of the screens whose route reaches a changed file. A snapshot that is the same as the last changes nothing. The screens with a new snapshot follow the code; the other screens keep your edits.
- In each snapshot, animations and transitions end at once,
prefers-reduced-motionisreduce, the caret is hidden and the page starts at the top. The dev server's badge and error overlay do not show, nor do the app's start-up overlays: its onboarding or first-run dialog, coach marks, cookie and consent banners, chat widgets and newsletter or "use the app" pop-ups. (Project > Hide Start-Up Overlays, on by default.) - A dynamic route (
/f/$fileId,[id]) shows with a sample value: the one you typed, else theparamsof the route in the scenes file'srouteslist, else its stories', else a guess from its types. A route with no value does not show. - Routes that only redirect (a page whose only element is
<Navigate>or<Redirect>, or that callsredirect()and draws nothing) are hidden on the first open. The Screens tab hides a route with its eye button and lists the hidden routes, each with a button that shows it again. The list is kept for the project. - The Screens tab lists every screen by group, like Storybook's sidebar. A click brings the screen into view. (Left column.)
- The Map brings every screen into view. (View > Map, and the grid button in the project bar.)
- Mock data (Mock data in the project bar, on by default, kept for each project): each page's address has
knighnMock=1, and before the app's code runs the page getsglobalThis.__knighnMock = true(the app reads it to use its own mock data and never wait on its live backend), the date stays at 1 October 2026 10:00 in the page's time zone, andMath.random,crypto.getRandomValuesandcrypto.randomUUIDgive the same numbers on each load. - Option-click a screen: its live page from the dev server runs over that screen, at its place and size, so you can use the real app. Esc or a click on the canvas closes it.
- The properties panel shows where the selected layer is in the code (file:line:column), and for the top layer of a component use, where that use is. Show source in the … menu of the panel's header hides or shows it.
- A route with stories (Storybook or Forkbook, such as
screens/home.stories.tsx) draws its first story, with the story's own mock data; its other stories (Loading, Empty, Not found…) are its states, each a screen on the States page (Pages / Home · Loading). An app with stories never reaches its real backend. - States found in the code, for a route with no stories: for each route, Knighn reads the JSX conditions of the files the route reaches. A test about loading (
isLoading,status === 'pending'), an error (error,isError), an empty list (items.length === 0) or a user (user,isSignedIn) gives a state: Loading, Error, Empty, Signed in or Signed out. Each state is one more screen on the States page, at the first viewport (Pages / Home · Loading). A state keeps its place when the code changes. Its page loads with those conditions forced to the side of the state, and with the conditions that would hide it (loading, an error) forced off. No story or fixture is necessary. The screens follow the code when files change. - iPhone screens show no status bar.
- Simulator pictures (an Expo app that Asset Asserter builds): each screen that draws a story shows the picture of that story from aa's Forkbook walk on the Mac mini's simulator (the real app, SwiftUI too), else aa's screenshot of its route, over its layers; a screen you select shows its layers. When the code of a screen changes, aa walks its stories again; a walk that aa runs on its own shows as its pictures come. Project > Walk Screen on Simulator and Walk All Screens on Simulator take new pictures.
- The project bar and the panel tabs wrap onto a second row in a narrow window.
Scenes
- A scene is a screen that has no URL, such as a dialog, a panel, a menu or a tab: a start path and the steps that bring the page to that state. People and agents write scenes in the scenes file.
- Scenes are in
knighn/scenes.knighn.tsxin the app folder, as plain data. Knighn reads the file and does not run it. You can write it by hand. - A step is a click, a fill, a select, a check, a key on an element, a hover, a drag, a right-click (
contextmenu, for a context menu), a key on the page (Meta+KeyK) or a wait for an element or a time. - Each scene is a screen after the routes. The host loads the start path, plays the steps and takes the snapshot.
- The scenes file can give each scene a
group, and has an optionalrouteslist:{ route, group?, name?, hidden?, params? }for each route, in the order the routes show.paramsgives a dynamic route its sample values, such as{ fileId: 'demo' }. A route withhidden: true(a link that only opens a place, a task page) is hidden on the first open;Screens()with no routes leaves it out too. An optionalviewportslist gives the project's frame sizes (see the viewports above). Screens()andknighn screensdraw each scene after the routes.
Edits
- You edit the screens as a design. Each edit changes the design at once (one undo step) and the same edit goes to the code of a duplicate of the project: a plain copy of its files in Knighn's cache, made from the work tree as it is (uncommitted changes too) the first time the project opens. A moment after the last edit (
applySoon), the changes go into the project's own files as one undo step. There is no Apply step. Undo and redo change the duplicate too. - The changes go in as a patch: changes made to the files in another way stay. Undo takes the last change back out of the files. Discard takes the project's files again and puts the screens back as the code has them.
- When a project file changes outside Knighn, the duplicate takes the change. A pending edit that no longer fits stays pending as a conflict: the list marks it with its lines, Show selects the layers those lines wrote, every other change still goes in, and Apply (or Cmd+S) tries the conflicts again. The duplicate and its pending changes stay when the project closes and opens again.
- Each edit changes only the characters it must. The rest of the file stays the same, byte for byte.
- An edit of a layer changes the JSX that wrote it, so the same layer on every screen and in every item of a list changes with it.
- Edits the code takes: classes (the properties panel, a resize as
w-andh-, the padding and gap handles, and the auto layout controls: direction, gap, padding, alignment and wrap), text, delete, a move in a row or column (a drag in a flex or grid parent, the arrow keys, the layers panel), a move into another parent (the layers panel) and a copy (Alt-drag, Duplicate). A copy goes right after its layer, as in the code. A list item moves, is copied or goes in its list data. - The editor's own tools write the code too, through the duplicate: Wrap in Auto Layout puts the layers' JSX in a new
<div>with the frame's classes; Create Component makes a new component file from the layers' JSX and uses it in their place; Detach Instance puts a component's JSX in place of its use; a new name for the top layer of a use renames the component, its export and every use; a new frame on the canvas is a new page (a route file with a starter component, where the router allows it), and its frame stays until the edit is in the project's files or Discard. - With the top layer of a use selected, the properties panel shows the use's props and cva() variants (a menu for a union, a switch for a boolean, a field for a string or number; a prop the use writes as code shows
{code}and is refused); a change writes the prop at the use. An icon of Lucide, Heroicons, Phosphor, react-icons or Tabler shows an Icon field: type another icon of the same library to swap it. A click on a place in the code copies itsfile:line:column. - The Components tab of the left column lists the project's components by folder with their uses. A click selects every use on the screens; the chevron shows the props with their types and the variants grid, and a variant with a use selects it.
- A free move (a layer placed at a position,
absolutewithleftandtop) is refused: a layer moves only by its order in a row or column, or by its margin. - A class goes in at the base, unless the source has a class of the same property under a breakpoint that applies at the screen's width (
lg:p-8on a 1440 px screen): then under that breakpoint. tailwind-merge with the project's tokens decides which class a new class replaces. - Classes are read from a string,
cn(),clsx(),twMerge(),twJoin(),classNames(),cx()and the base or a variant of acva()in the same file. - Text goes where it is written: the JSX, a string constant (also in another file), an i18n message (next-intl, react-i18next,
<Trans>: Knighn marks the same key in the other languages with[translate]and adds a message that is missing), or the item of a.map()list. One text among other children (Templates <Menu />) is written in place. - Components work like the editor's instances. The top layer of a component use (
<Card />) is the use: its classes, a move, a copy or a delete change that use only (theclassNameat the call site). A layer inside the component changes the component's JSX, so every use changes. A double-click goes inside a use, as the editor enters a group: then the edits of its top layer change the component too, until the selection leaves it. Edit Component (the properties panel, Project > Edit Component, or Alt held) does the same. - Text that comes from a prop is written at the use: the prop's text (
title="Welcome"), or the use's own text forchildren, through each component that passes the prop on. - With Snap to Tokens on, a size or space goes in as the near token (
w-[312px]isw-78). It is on by default. (Project > Snap to Tokens.) - An edit the code cannot take (for example text from a ternary) is refused with a short note, and the layer snaps back.
- When the host refuses an edit of the project's own files, Knighn sends the change to Claude Code with the source location, the file excerpt and pictures before and after.
- Push the React files of a design into a project that is open in another tab, with a diff of each file. (File > Push to Project…, the Project tab of the Design to Code dialog. Desktop.)
Designs in the project
- A project with a
knighn/folder of designs (knighn init,knighn pull <file id>) shows them in the Designs tab of the left column. A click opens the design in its own tab. (Desktop app, a project on this Mac.) - The design tab saves to the folder's TSX files, and a change to the files (from you, an agent or
knighn dev) shows in the open design.
Components and usages
- Find Usages finds every use of the selected layer's component; a click on a use selects its layer on its screen. (Project > Find Usages, the Usages tab.)
- Show Everywhere selects every use of the selected layer's component on every screen. (Project > Show Everywhere.)
- The Components tab lists the states found in each component's code (its file and what it imports), with the conditions each state forces. (Left column, the chevron of a component.)
- A component's screen takes its props from its Storybook stories, else its most common use, and every required prop that is still missing gets a sample from its TypeScript type: readable text by the prop's name (a title, a person's name, a button label), a picture that works offline for
srcandimage, sensible numbers (a rating of 4, a price of 24), three items for a list built from the item type, an object's required fields filled in, the first value of a union or enum, a function that does nothing for a callback and a short text for a React node. A React Nativestyleis left out. No Storybook is necessary. - A component that reads a provider still draws alone: when the app depends on Convex (queries stay loading, no one is signed in), convex-helpers' query cache, TanStack Query or React Native's safe area, the screen has that provider, and inside it the project's own
knighn/preview.knighn.tsx. A component that still fails shows its error on its screen.
Design system and tokens
- The System tab has two views: Tokens and Lint. (Left column.)
- Tokens lists each token by kind, with a value field, a dark value field and the number of classes that use it. You can search them.
- A token change writes the Tailwind 4
@theme, the:rootor.darkvalue that it reads (shadcn/ui), or the Tailwind 3 config. - Add a new token.
- Rename a token. Knighn also changes every class and every
var()that uses it, as one undo step. - Remove a token whose declaration is in the project.
- Tokens of the project show as token chips in the properties panel (for example
bg-primary). - Lint finds hard-coded values that a token gives, such as
bg-[#18181b],p-[16px],rounded-[8px]andtext-[14px]. (System > Lint: Check, Fix, Fix All. Project > Design System > Check Hard-Coded Values and Fix Hard-Coded Values.) - Lint reads
.ts,.tsx,.jsand.jsxfiles. With Tailwind 4 it also reads inlinestyleobjects and CSS Modules. - Lint offers Tailwind's own colours (such as
zinc-900) when no project token is near. - Consistency Audit loads each route and compares elements of the same kind across the pages: side panels, headings, text fields, buttons, cards, empty states and list rows. It compares the width, height, text size, weight and colour, border or shadow, corner radius, padding and gap. Each element that is not like most of its kind becomes a proposal with a class change toward the common value (a project token when one matches), and shows Now and Proposed beside the canvas. (Project > Design System > Consistency Audit.)
- Export Review writes one HTML page to share: each class or text proposal with its sentence, its file and a picture before and after, then each route before and after. (Project > Design System > Export Review….)
Builds (Asset Asserter)
- An Expo app has a Builds section in the properties panel. It shows the app's builds on the Asset Asserter panel (Dev, Dev sim, Prod), what Ship does with the code on disk and why, the files that are not in a commit, and the newest jobs. (Right column > Builds.)
- Ship, Build (choose the profile), OTA to testflight or production (with a message), Start Metro and Dev loop. Each one runs as a job card, then a message says what the panel did. (Builds section.)
- The project host runs your own
aaCLI on the app folder. With no panel key in Settings > Connections > Asset Asserter,aauses its own login (aa login). On a project of another Mac,aaof that Mac runs. - When the panel does not know the app folder, the tab says so. Add the app on the panel, or run
aa rescan.
Agents in the editor
- The project's chat runs Claude Code in the project folder, with its own file tools and the Knighn MCP for the canvas. Codex runs in a
workspace-writesandbox. - The agent sees your selection: the file and line, the classes, the props and a picture.
- Ask Claude Code for a change to the selection. (Project > Ask Claude Code….)
- Propose Changes: the agent proposes changes and writes nothing. (Agent menu in the project bar, Project > Agent > Propose Changes….)
- The compare view shows each screen twice, Now on the left and Proposed on the right, with the proposals applied in a real render. It covers the canvas while it is open. (The "proposed" menu in the project bar, Compare in the Proposals tab.)
- One screen for each route: all the proposals of that screen show together in its Proposed frame.
- The compare view uses the project's viewports, the first one at the start. With more than one viewport, its bar has a button for each.
- Slider: one frame with a divider that you drag between Now and Proposed. Toggle: one frame that switches when you click it or press Space. (The compare view's bar.)
- Highlight Changes outlines only the parts of the Proposed frame that look different. (The compare view's bar.)
- The Proposals section lists each proposal as one plain sentence, under its screen. Show Code shows the classes and the file changes. (The Proposals section of the properties panel.)
- Each proposal has a number and a pin on its layer on the screens. Hover on the pin or the row lights the other; a click shows its screen.
- Clear a proposal's check box to remove it from the Proposed frame. The frame changes at once.
- A proposal can be one change across many files (for example, one panel width in four files). You accept or reject it as one item.
- A proposal that changes a file in a different way (a component swap, a new token, a prop) renders in a temporary copy of the project, with its own dev server.
- Accept writes the proposal as one undo step. Accept and Reject are on each row, and Accept and Reject for the chosen proposals of the screen are on the compare view's bar. Accept All and Reject All. (Proposals tab, the "proposed" menu.)
- After each agent turn, the screens outline each changed layer with the classes taken away and added. (Project > Agent > Last Turn's Changes.)
- Send a design to the project's agent as a spec. (The Agent tab of the Design to Code dialog.)
Deploy
- Deploy the project to Vercel with your own
vercelCLI and login. (Project > Deploy > Deploy Preview, Project > Deploy > Deploy to Production.) - The project must be linked with
vercel link(.vercel/project.jsonin the app folder or the project root). - A job card shows the deploy while Vercel builds; Cancel stops it. When it is done, a toast gives the address with Open: the deployment's own address for a preview, the production domain for production.
- When Vercel is not installed, not signed in or not linked, the card says what to run.
Agents and MCP
- With a project tab open,
executehas the project helpers and not the design API. A command reaches the tab whendocument_idnames it, when the call comes from its chat, or when the tab shows. GetSelection()gives the selected elements with their sourcefile:line:column, classes, text, props, owners and a picture.Screenshot(route, { width })gives one picture.Propose(proposals)shows proposals for you to compare and accept or reject: a class or text change{ at, add, remove, breakpoint, text, note }, a file patch{ file, find, replace, note }(or{ file, text, hash }for the whole file), or a group{ title, note, edits, patches, route, at }that you accept or reject as one item.GetAppState()gives the project in brief and the number of proposals. With no project it lists the projects and what project tabs support.OpenProject(path)opens a project in a tab of the app.Deploy({ production })deploys the project to Vercel and gives{ url, deploymentUrl, production, inspectUrl }. It makes a preview unlessproductionis true. It works with a tab and with a folder path.- With no open tab, give the folder as
project("." or an absolute path). Knighn installs the packages if necessary, starts the dev server and runs the call. No app and no browser is necessary. Screens({ routes, states, widths, out, restart, hideOverlays, scenes, mock })draws each route as a PNG file, with no limit on the number. With nowidths, it uses the widths of the project's viewports, else 1440 px (390 px for an Expo app). The pictures hide the app's start-up overlays;hideOverlays: falsekeeps them.Screenshot()hides them too.mock: truedraws each page with mock data and still, after it settles, as the screens are.Screens()then draws each scene ofknighn/scenes.knighn.tsxat the same widths: all when you give no routes, none withscenes: false, or the scenes you name. A scene has the sourcesceneand its name as the state. The start-up overlays go before its steps, so a dialog that the steps open stays. When a step fails, the picture shows where the scene stopped, with the reason.Screens()(andknighn screens) also draws the shared components alone, after the routes and scenes, each as tall as it is:components: falsefor none, or name them (--components Button,Card).Screens()fills dynamic routes from sample data: the route'sparamsin the scenes file, then the Storybook stories of its component, then a guess from the route's types. An optional catch-all ([[...slug]]) is left empty by default. Each story of a route is a state:states: "all"draws every one, or name them.Screens({ states: "found" })draws the states found in the code of each route (sourcefound);states: "all"draws them after the stories.States({ routes })lists them with the conditions each forces.Audit({ routes, widths, propose })runs the consistency audit and gives{ pages, groups, findings, proposals }; its changes show as proposals unlessproposeis false.ExportReview({ out, width, routes })writes the review page of the proposals that wait and gives{ path, proposals, pictures, missing }.Watch()andChanges(cursor, { wait })give only the screens that the changed files reach.waitholds the call until a file changes.AssetAsserter.status()gives the builds of an Expo app on the Asset Asserter panel: the Dev, Dev sim and Prod cells, what Ship does with the code on disk, and the newest jobs. It gives null when the panel does not know the app folder.AssetAsserter.ship(),build(profile),publishUpdate(channel, message),startMetro()anddev()runaaon your Mac. A store build or an OTA update only goes in the queue of the panel.- A failure is JSON with the cause, the log and the exact next call (
{ error, log, retry }). searchhas the kindproject. The agent skill has a "project tabs" guide.- The hosted connector (mcp.knighn.com) takes
projectin place offileId: a tab's id, "open" for the tab announced last, or a folder path on the Mac. - On the hosted connector, a folder path runs on your Mac with no editor when the folder is in a shared folder and "Allow remote agent runs" is on. A long call continues for up to eight minutes.
CLI
knighn edit <files or globs…> --select <query>changes JSX without the app: classes (--classes,--remove,--breakpoint), text (--text), props (--prop,--unset-prop),--deleteand--duplicate.- The query finds elements by line, tag, class, id, prop, text,
:nth(), descendant, child (>) and a comma list. knighn edithas--all(more than one match),--dry-run,--rootand--json.- When Knighn has the project open,
knighn editgoes through it: the change is one undo step there.--no-hostwrites the files directly. knighn init,knighn pull <design>,knighn push [design]andknighn devkeep designs in the project'sknighn/folder as React source (see the README).knighn devsets the project up on its first run and holds a save with an error until it is fixed.knighn zed [folder | file:line]opens the project or a file in Zed; from an SSH session it opens Zed on the Mac you came from, over SSH to this one.knighn drift [folder]runs the visual tests in Terminal and starts the dev server when no--urlis given. Options:--update,--strict,--filter,--app,--json.- A visual test is a
*.knighn.spec.tsfile that you write: a route (or a scene,scene: 'Settings') and its widths, withmock: truefor the page with mock data, still, after it settles. A spec that is there keeps its comments and layout. The baselines go in__knighn__/beside the spec, in git. knighn screens [folder] --out dirdraws each route and each scene as a PNG file. Options:--routes,--states(names,allorfound),--widths,--scenes(all,noneor names),--mock(mock data and still pages, as the screens are),--restart,--keep-overlays,--json.bun run check --driftadds the visual tests of the sample project and of the Knighn app's mock screens to the check of this repository.bun run release publishruns the mock screens' visual tests first and warns when a screen drifted.
Remote: the web and another Mac
- Project tabs work in the web app and in the desktop app on another Mac. The project and its dev server run on the Mac that has them, through the Mac daemon and Convex, with no open port.
- Turn on "Allow remote agent runs" and share a folder on the Mac that has the projects. (Settings > Remote.)
- The screens are snapshots the Mac takes. When this device is on the same Tailscale network, the live page of Option-click and the compare view load from the Mac's
tailscale serveHTTPS address; else the compare view shows pictures. - The project's chat runs Claude Code or Codex on that Mac and reads the canvas of this tab.
- Project > Open in Zed opens the selected layer's source line, or the project, in Zed. A project on another Mac opens over SSH (
zed://ssh/<user>@<mac>/<path>), so Zed edits it there and the screens follow each save. - A remote host stops after two idle hours. A Mac that does not start the host in 90 s fails the session with the reason.
- The iOS app has no project tabs. Its
code/openlink opens the app's first screen.
Known limits
Frameworks and pages:
- The Next.js
pages/router is not read. Only App Routerpagefiles are routes. - A Next.js project whose config sets its own
adapterPathgets no source tags. - A component that needs data from the server, or a provider Knighn does not give, shows its error instead of a static picture.
- Expo shows as the web app. Native views are stand-ins, not the real views.
- React Router and TanStack Router in code: only literal paths are routes.
- A React Server Component owner has its name only, with no source.
The screens:
globalThis.__knighnMockis set in the browser only. A Next.js or TanStack Start server render sees no mock data, so the first render of a page on the server can differ from the client's (React renders again on the client and can report a hydration mismatch).- A snapshot keeps the page as it was at that time: a canvas becomes a picture, and a video shows its poster.
- A broken page is known only by a page that draws nothing. The dev server's error overlay does not count: Vite sends one module's error to every page of the server, and Next.js keeps every issue (a hydration warning too) in its own overlay. A page that shows an error message of its own gives its snapshot.
- Open Project, Clone from GitHub and Push to Project are on the desktop only.
Edits:
- A class list from a variable, a ternary
classNameor a template with an expression refuses. Classes under a condition are read only. - A prop edit does not replace an expression. A style value that is an expression is not replaced.
- Move, delete and duplicate need a direct JSX child. A
.map()result and a condition branch refuse. - A move into another parent of a layer that repeats is refused. A nudge of an absolutely placed layer is refused as a free move.
- A screen that takes a new snapshot shows the code: an edit that is still a conflict on it no longer shows there until it fits.
- List data from a prop, a fetch or a package refuses. Re-exported
.map()data and re-exported styled components are not followed. - A cross-file move adds props that the callers of the new component do not pass yet.
- i18n marks use a
[translate]prefix, in JSON files only. - Token lint in
styleobjects and CSS Modules is Tailwind 4 only.
Checks:
- Visual tests do not hide start-up overlays, so a baseline shows the page as it is.
- A start-up overlay that comes more than 4 seconds after the page loads stays in a snapshot. Knighn hides overlays with rules (dialogs, fixed layers over the page, banners with cookie or app words, known consent managers and chat widgets); a full-screen app root, a sticky header, a fixed navigation bar and a bottom tab bar stay.
Agents:
GetSelection()gives nothing when no tab has the project open.- Hosted
executewithprojecttakes nodryRunand does not try again. - The daemon's
appjob does not list project tabs. The project link needs the desktop app signed in to the same account. - A call on a folder through the hosted connector stops after eight minutes.
Remote:
- A project tab from a signed-in web editor and from the desktop app on another Mac is not yet checked live with the UI (open in
checklist.md). - The iOS app has no project tabs.