Skip to content

The stack

Typillar builds real web apps, not a proprietary format you can only run here. This page says exactly what the code is, so you know what you’re getting before you open the repo.

HTML, CSS, and Svelte 5. The entry document is always index.html, and it mounts one root component. Everything else is components, modules, and styles the agent writes itself.

Components use runes$state, $derived, $props, $effect — and never the older Svelte 4 store idioms. Runes only work inside .svelte and .svelte.js files, so reactive state that’s shared across components lives in a .svelte.js module.

Styles go in a component’s own <style> block, where they’re scoped to that component. Resets and shared design tokens live in styles.css, linked from index.html.

Nothing bundles. There’s no npm install, no package.json, no toolchain to configure or keep current. Each .svelte file is compiled individually, on the server, when your app is previewed and when it ships.

One consequence worth knowing: a file that doesn’t compile is never saved. The compiler error goes back to the agent, which fixes it and tries again. Broken code doesn’t reach your repository.

  • svelte — the framework runtime.
  • router — a small built-in router, described below.
  • your own files, by relative path.
  • a short shelf of vendored libraries, by their real package name.

There is no npm and no CDN. Whatever else the app needs, the agent writes.

A handful of libraries are worth more than anything an agent would write from scratch — a rich text editor is thousands of hours of browser-quirk work, and a hand-rolled HTML sanitizer is a security bug waiting to happen. Those are vendored into the platform and importable by name:

Import What it is
marked render markdown to HTML
dompurify sanitize HTML before it reaches innerHTML
chart.js canvas charts
quill rich text editor
import { marked } from 'marked';
import DOMPurify from 'dompurify';
el.innerHTML = DOMPurify.sanitize(marked(post.body));

They are served from your app’s own origin, never a CDN. An app that doesn’t import one pays nothing for it — the bytes only ship when the code asks for them, and any stylesheet the library needs is linked for you.

This stays deliberately short. Your app has no package.json, so it has no dependency that can rot, get yanked, or ship a supply-chain surprise: the shelf is pinned and reviewed on our side, and everything else the agent writes.

Apps with more than one view import the built-in router rather than hand-rolling hash parsing:

import { route, navigate, match, isActive } from 'router';
  • route is reactive — read route.path.
  • navigate('/about') moves in code.
  • match('/users/:id') returns a params object, or null.
  • isActive(to, exact?) powers class:active={isActive('/about')}.

Plain <a href="#/about"> links just work, with no reload. Routing is hash-based by design, so a preview served from a subpath behaves exactly like the app on its own domain.

Your app talks to typed capability endpoints — /api/_data for data, /api/_files for files, and so on — that run in your Cloudflare account. The agent turns them on as it needs them. See Capabilities.