Build your first app¶
An app is a small, persistent UI you open again and again, such as a habit tracker, a dashboard, or a board. You describe it to the Lucidos Agent in chat, and it builds a working version in seconds. You then change it by talking. This tutorial walks through that loop end to end.
Prerequisite
A running Lucidos workspace with an LLM provider configured. See the Quickstart.
1. Describe what you want¶
Open a chat and say what the app is for, in plain language:
"Make me a habit tracker: a few daily habits I can check off, and it should remember what I checked each day."
The agent first decides whether an app is the right answer. It answers a one-off question ("summarize today's emails") directly or saves the result to an artifact. An app is for a recurring UI you come back to. If your request leaves the scope open, expect a question or two, typically:
- What's the smallest version that's useful? (scope)
- What should it remember between visits? (storage)
After you answer, it starts building.
2. It scaffolds a working version¶
The agent creates the smallest thing that demonstrates the idea: a working
index.html plus a manifest.json (the app's name, description, and icon). The
app shows up in your Apps panel and opens in the Canvas (the right-hand side
on desktop, a swipe away on mobile).
Every new app follows the Lucidos look by default:
- Inherits your theme. It follows your light/dark setting automatically. The
scaffold loads the SDK assets in order (
/api/v1/sdk-prefs.js, then/api/v1/sdk-iframe.css, then/api/v1/sdk.js) and callslucidos.ui.applyPreferences()+lucidos.ui.watchPreferences(). - Uses theme variables, never hardcoded colors:
var(--bg-primary),var(--text-primary),var(--accent),var(--border-color), … so it looks right in both modes. - Reuses Lucidos's component classes:
.action-btn(with the additive.action-btn-confirm/.action-btn-danger/.action-btn-secondaryvariants),.list-row,.label, and others, so controls match the host shell. - Sizes in
rem, so it respects your font-size / UI-scale preference. - Works on your phone. The layout starts as one column and adds columns where there is room. Rows of controls wrap, and wide tables stack into cards. The agent keeps the top-right corner free for the exit button Lucidos draws when you send an app fullscreen.
A minimal app the agent might start from looks like this:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Habit Tracker</title>
<script src="/api/v1/sdk-prefs.js"></script>
<link rel="stylesheet" href="/api/v1/sdk-iframe.css">
<script src="/api/v1/sdk.js"></script>
</head>
<body>
<div class="panel">
<button class="action-btn">Add habit</button>
</div>
<script>
lucidos.ui.applyPreferences(); // apply your theme on load
lucidos.ui.watchPreferences(); // re-apply when you change it
// app code…
</script>
</body>
</html>
3. Where the files live¶
An app is a set of files in your workspace under data/apps/<id>/:
data/apps/habit-tracker/
manifest.json # user-facing name, description, icon (NOT in the LLM's context)
index.html # the UI
knowhow/ # optional: how-to docs the agent loads when the app is active
intents/ # optional: stable statements of what you want
scripts/ # optional: helper code
triggers/ # optional: app-specific automation
The app's code lives here. Its data lives under data/artifacts/<id>/,
for example data/artifacts/habit-tracker/data.json. Keep them apart: the code
is versioned source, and the data is your changing state.
The app persists state through the SDK:
// Paths are relative to data/, so app data is "artifacts/<id>/...".
await lucidos.data.write('artifacts/habit-tracker/data.json', JSON.stringify(state));
const raw = await lucidos.data.read('artifacts/habit-tracker/data.json'); // returns a STRING
const state = JSON.parse(raw);
To call an external service, the app uses lucidos.proxy(name).fetch(path, init).
The credential stays server-side.
4. Iterate by talking¶
To change the app, say what you want:
"Add a weekly streak count at the top."
For small edits, the agent changes the files directly. The open app reloads automatically when the agent's turn finishes. Ask for small, visible changes that you can react to each round.
For heavier work, such as a multi-file refactor or a new feature, the agent can spawn an app coding-agent thread. It works in an isolated worktree narrowed to that one app folder. The result is a reviewable change that you Apply when you are happy with it. While the thread runs, you can preview the in-flight version.
5. The finishing bar¶
Before calling an app done, the agent checks that it:
- works in both light and dark
- has no hardcoded colors
- has no
pxsizing beyond1pxborders - uses real component classes instead of plain
<button>s - has one clear focal point and generous whitespace
The agent treats a cheap-looking app as a defect.
Common pitfalls¶
- Storing data in
apps/<id>/. App data belongs inartifacts/<id>/; the app folder is source. - Forgetting the
artifacts/prefix inlucidos.data.*paths. They're relative todata/, so it'sartifacts/<id>/data.json, not<id>/data.json. - Hardcoding colors or sizing in
px. Both ignore the user's theme and scale. - Using the browser's
alert/confirm/promptor a native<select>. Uselucidos.ui.toast/confirm/prompt/Selectinstead. The host shell renders them themed, above the app. - Creating an app for a one-shot. If you only want the answer once, ask in chat.
Going deeper¶
The engine guides are the source of truth for this page:
- Building an App (
system-knowhow/building-an-app.md): when an app is the right fit, the visual-quality bar, and how to iterate. - JS SDK (
system-knowhow/js-sdk.md): the fulllucidos.*surface (data,proxy,ui,events), theme variables, and component classes.