# Ripple - AI/LLM Documentation ## Overview Ripple is a TypeScript-first UI framework and runtime for `.tsrx` files. TSRX is the shared source language; Ripple is the target that provides fine-grained reactivity, DOM rendering, server modules, hydration, context, portals, and reactive collections. When answering Ripple questions: - Prefer `.tsrx` component examples. - Return direct JSX for single-root components: `function Component(props) { return
; }`. - Use JSX statement containers (`@{...}`) when TypeScript setup belongs next to the rendered output. - Use JSX text for static text and `{expr}` for dynamic values. - When a statement container mixes TypeScript setup with rendered output, put setup first and finish it with one JSX element, JSX fragment, or JSX control-flow expression. It cannot finish with a bare expression container, and script statements cannot appear after the final output. - Use `@if`, `@for`, `@switch`, and `@try` for rendered control flow. Their bodies are implicit statement containers and must use `{...}` blocks. - Keep runtime API guidance Ripple-specific, but keep syntax guidance aligned with TSRX. For target-neutral TSRX syntax, see https://tsrx.dev/llms.txt. ## Install ```bash npm install ripple @ripple-ts/vite-plugin ``` ```ts // vite.config.ts import { defineConfig } from 'vite'; import ripple from '@ripple-ts/vite-plugin'; export default defineConfig({ plugins: [ripple()], }); ``` Mount client apps with `mount()`. ```ts import { mount } from 'ripple'; import { App } from './App.tsrx'; mount(App, { target: document.getElementById('root'), props: { title: 'Hello world' }, }); ``` Use `hydrate()` for server-rendered HTML that should become interactive. ```ts import { hydrate } from 'ripple'; import { App } from './App.tsrx'; hydrate(App, { target: document.getElementById('root'), }); ``` ## Components Components are TypeScript functions. Return a JSX element directly when there is one root, use a fragment for real multiple-child output, and use a JSX statement container (`@{...}`) when setup statements belong next to the UI. ```tsrx export function Button({ text, onClick }: { text: string; onClick: () => void }) { return ; } export function App() { return } ``` If output needs multiple siblings, text, or expression containers after setup, wrap that output in a fragment. Plain text between tags is JSX text, not JavaScript. ```tsrx export function LiteralText() { return
x = 123
; } ``` ## Reactivity Create reactive state with `track()` and lazy destructuring. Reading a lazy binding subscribes to it; assigning to it updates the tracked value. ```tsrx import { effect, track, type Tracked } from 'ripple'; export function Counter() @{ let &[count, countTracked] = track(0); let &[double] = track(() => count * 2); effect(() => { console.log('count changed', count); }); <>

Count: {count}

Double: {double}

} function CounterValue({ count }: { count: Tracked }) { return

Shared value: {count.value}

; } ``` You can also keep the tracked object and read or write `.value` directly. ```tsrx import { track } from 'ripple'; export function Counter() @{ const count = track(0); } ``` Use `untrack(fn)` when an effect or derived value needs to read something without subscribing to it. ## Reactive Collections Use Ripple collection classes when collection operations should update rendered output. ```tsrx import { RippleArray, RippleMap, RippleObject, RippleSet } from 'ripple'; export function Inventory() @{ const products = new RippleArray( { id: 1, name: 'Jacket' }, { id: 2, name: 'Boots' }, ); const prices = new RippleMap([[1, 120], [2, 95]]); const selected = new RippleSet(); const totals = new RippleObject({ added: 0 }); <>
    @for (const product of products; key product.id) {
  • {product.name}: ${prices.get(product.id)}
  • }

Selected: {selected.size + totals.added}

} ``` Available collection APIs include `new RippleArray(...)`, `RippleArray.from(...)`, `RippleArray.of(...)`, `new RippleObject(...)`, `new RippleMap(...)`, and `new RippleSet(...)`. ## Template Control Flow Rendered control flow uses directive expressions. ```tsrx import { track } from 'ripple'; export function StatusBadge() @{ let &[status] = track<'idle' | 'loading' | 'done'>('idle'); <> @switch (status) { @case 'loading': {

Loading...

} @case 'done': {

Done

} @default: {

Idle

} } } ``` Use `@for (... of ...)` for rendered lists. Filter the iterable before rendering when some items should be skipped, and use `@empty { ... }` for the no-items fallback. Direct `continue`, `break`, and `return` statements are not allowed in `@for` template loop bodies, and are also invalid inside `@if` template branches. `@switch` cases use `@case` and `@default` clauses with isolated `{...}` blocks. They do not fall through, and do not use `break` or `return`. ```tsrx export function UserList({ users }: { users: User[] }) @{ const visibleUsers = users.filter((user) => !user.hidden);
    @for (const user of visibleUsers; index i; key user.id) {
  • {i + 1}. {user.name}
  • } @empty {
  • No users
  • }
} ``` Use ordinary TypeScript `return` for true component exits in setup code. ```tsrx export function Profile({ user }: { user: User | null }) @{ if (!user) { return null; }

{user.name}

} ``` `@try` provides pending and error UI. ```tsrx export function ProfileBoundary() @{ @try { } @pending {

Loading...

} @catch (error, reset) {

Error: {error.message}

} } ``` ## Events And Refs Events use JSX-style handler props. DOM refs can be callback refs, tracked refs, or local variables. ```tsrx import { track } from 'ripple'; export function SearchBox() @{ let &[query] = track(''); let input: HTMLInputElement | undefined; <> } ``` ## Dynamic Components And Elements Use the dynamic tag syntax `<{expression}>` when the element tag or component constructor is chosen at runtime. The expression can be a string tag name, a component, or a tracked variable holding either. The closing tag repeats the same expression: ``. Ripple host elements use `class`, not React's `className`. ```tsrx import { track } from 'ripple'; type Tag = 'section' | 'article'; function Summary() { return

Summary

; } function Details() { return
Details
; } export function DynamicPanel() @{ let &[tag] = track('section' as Tag); let &[Body] = track(() => Summary); <> <{tag} class="panel"> <{Body} /> } ``` The tag expression must resolve to an element name: an identifier, member access, static string, or a runtime expression composed of those. Calls, spreads, string concatenation, and string interpolation are not valid tag names. Do not use removed dynamic tag syntax such as `<@tag />`, `<@Component />`, or the imported `Dynamic` component with an `is` prop. Use `<{tag}>` instead. ## Styles ` } ``` Use `:global(...)` to opt out of scoping. Module-scope style expressions expose scoped class names that can be passed as props. ```tsrx const styles = ; export function Badge() { return New; } ``` ## Context Use `new Context(defaultValue?)`, `set(value)`, and `get()` to share state through the component tree. ```tsrx import { Context, track, type Tracked } from 'ripple'; const ThemeContext = new Context>(); export function App() @{ let &[theme, themeTracked] = track('light'); ThemeContext.set(themeTracked); <> } function ThemeLabel() @{ const theme = ThemeContext.get();

Theme: {theme.value}

} ``` ## Portals Use `Portal` to render content outside the normal DOM position. ```tsrx import { Portal, track } from 'ripple'; export function ModalDemo() @{ let &[open] = track(false); <> @if (open) { } } ``` ## Server Modules `module server { ... }` declares server-only exports in a `.tsrx` module. Import from `server` in the same file to call those exports from client code through the Ripple server integration. ```tsrx module server { export async function saveName(name: string) { return { ok: true, name }; } } import { saveName } from server; import { track } from 'ripple'; export function Form() @{ let &[name] = track(''); let &[saved] = track(''); const submit = async () => { const result = await saveName(name); saved = result.name; }; <> (name = event.currentTarget.value)} />

Saved: {saved}

} ``` ## Raw HTML Use `innerHTML={trustedHtml}` for trusted markup. Do not pass untrusted strings to raw HTML props. ```tsrx export function Article({ markup }: { markup: string }) { return
; } ``` ## Do And Do Not Do: - Use `.tsrx` for component files. - Return single-root components directly in examples. - Use `@{...}` when setup appears next to rendered output, and finish the statement container with one JSX element, JSX fragment, or JSX control-flow expression. - Use JSX text for literal children. - Use `@for` for rendered lists and `@if` for conditional rendering. - Use Ripple collections for reactive collection state. Do not: - Use quoted-string text children as examples. - Use raw `if`, `for`, `switch`, or `try` as rendered control flow. - Treat plain text inside a template as JavaScript; it is JSX text. - Put JavaScript expressions inside `