| layout | default |
|---|---|
| title | Agent quickstart |
Read application architecture first. It defines the non-negotiable split between models, focused controllers, and etlua views.
This page is an operating guide for an agent creating or changing a lua-objc app. Read it before writing application code.
Create a product app under apps/ or a framework example under test/:
apps/weather/
init.lua
Model.lua
Controller.lua
views/
Window.etlua
Detail.etlua
Keep init.lua thin. It returns the controller class; it does not create a
window or start the application.
return require("apps.weather.Controller")Put domain queries, validation, formatting inputs, and mutations in
Model.lua. Keep callbacks, navigation, and template refs in Controller.lua.
Put all reusable visual structure in views/*.etlua partials. Controllers may
prepare data and bind refs after rendering, but must not construct view trees.
Use a semantic window split when the app has navigation and primary content:
local ns = require("AppKit")
local xml = require("ui.xml")
local config, refs = xml.renderFile("apps/weather/views/Window.etlua", data, ns)
local window = ns.Window(config)All view trees come from .etlua templates rendered through ui.xml.
Controllers must not call ns view constructors (VStack, Text, List,
Button, and similar); they may create the top-level ns.Window and use
non-view services and operations. The semantic sidebar owns its native
appearance and split geometry. Do not wrap it in a visual-effect material or
manually set pane frames.
Use etlua for declarative structure and Lua for stateful behavior. Render a
file with xml.renderFile(path, data, ns). See XML syntax
for the complete supported tag list.
<Window title="Weather" width="900" height="620" minWidth="760" minHeight="520">
<Toolbar>
<ToolbarItem id="refresh" label="Refresh" icon="arrow.clockwise"
tooltip="Refresh weather" />
</Toolbar>
</Window>Use id="name" on an XML element when the controller needs to attach a
callback or mutate the resulting native view after rendering.
- Stacks add sibling spacing, not outer margins.
- Use
paddingonly when content needs separation from its container. - Give primary tables
flexGrow = 1and useplainorfullWidth. - Use
sourceListonly for navigation sidebars. - Use
insetonly for grouped/settings content. - Let
NSSplitViewand native windows own pane geometry. - Put window-wide actions in the toolbar and row actions near the row.
- Keep numeric layout values in a named table in the controller.
Implement loading, loaded, empty, error, selected, disabled, and long-text
states where the feature supports them. For tables, call showLoading() before
fetching and hideLoading() after rows are available. Do not add sleeps to
simulate progress.
The same apps/<app>/ and test/<app>/ trees run on iOS. The Simulator host is a runtime;
Lua, templates, and assets stream from a Mac packager. After make ios-run,
a save reloads the running app without quitting:
make ios-run ARGS=demo/helloDo not copy Lua into the .app. Do not rebuild the host because a view or
asset changed. See iOS host and hot reload.
make test
./lua-objc --dump-layout=/tmp/layout.xml apps/weather/init.lua
rg -n 'cropped="true"|outsideParent="true"|contentClipped="true"' /tmp/layout.xml
./lua-objc --screenshot=/tmp/weather.png apps/weather/init.luaFor visual changes, inspect small and large window sizes plus light and dark
appearances. Add a fast tests/*.test.lua regression test for every behavior
change. Tests must run headlessly, without windows or pauses.