<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yuuichi Eguchi</title>
    <description>The latest articles on DEV Community by Yuuichi Eguchi (@yuu1ch13).</description>
    <link>https://dev.to/yuu1ch13</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3637286%2F75ace802-b15d-4291-9a5a-bcb54d78a051.jpeg</url>
      <title>DEV Community: Yuuichi Eguchi</title>
      <link>https://dev.to/yuu1ch13</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yuu1ch13"/>
    <language>en</language>
    <item>
      <title>Seven rounds of AI code review in one day, 2,048 tests green, and the feature had never started once</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Thu, 15 Oct 2026 12:55:40 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/seven-rounds-of-ai-code-review-in-one-day-2048-tests-green-and-the-feature-had-never-started-once-1lb8</link>
      <guid>https://dev.to/yuu1ch13/seven-rounds-of-ai-code-review-in-one-day-2048-tests-green-and-the-feature-had-never-started-once-1lb8</guid>
      <description>&lt;p&gt;Since March 2026 I have been building &lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;Calyx&lt;/a&gt;, a native macOS terminal for running and supervising coding agents in parallel. I work on it alone, and Claude Code writes most of the code. As of October 7, 2026 the repository has 880 commits (752 excluding merges) and 110 release tags, v0.1.0 through v0.46.1.&lt;/p&gt;

&lt;p&gt;I run development under one self-imposed rule: every finding from the review subagent gets fixed. This post reads seven months of commit history back and pulls out what that review found and what it did not. There is no control group. Nothing here was built twice, once with review and once without, so this is not a measurement of how well AI review works. It is a record with numbers, and the last section separates what the record supports from what it does not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;

&lt;p&gt;In my current setup, Claude Code subagents write all of the code. The main conversation does not write code; it plans, approves and coordinates. Every change goes through the same stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Plan in Plan Mode. I approve the plan.&lt;/li&gt;
&lt;li&gt;From a clean tree, &lt;code&gt;git worktree add&lt;/code&gt; a worktree. A test-writing subagent writes the tests and confirms they fail before any implementation exists.&lt;/li&gt;
&lt;li&gt;Implement. A test-running subagent confirms everything passes.&lt;/li&gt;
&lt;li&gt;A review subagent reviews the worktree's diff. Commit in the worktree. I approve.&lt;/li&gt;
&lt;li&gt;Merge into main, then delete the worktree and the branch.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two rules sit on top. Every review finding is fixed, none silently skipped, and I am asked only when a finding is genuinely unclear. And symptoms are never patched: when a defect shows up, the fix goes back to the primary source and addresses the root cause.&lt;/p&gt;

&lt;p&gt;Those rules live in my local configuration, not in the repository, and the file has no history. I cannot show when each rule took effect. The oldest trace in a commit message is &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/7ec033794" rel="noopener noreferrer"&gt;&lt;code&gt;7ec033794&lt;/code&gt;&lt;/a&gt; on June 22, which fixed even suggestion-level findings and gave the reason as "ALL @code-reviewer findings MUST be fixed".&lt;/p&gt;

&lt;p&gt;What I can use as a record is the commit bodies. Twenty-nine non-merge commits identify themselves in the subject line as responses to review or audit findings. I counted subjects containing "review", "audit" or "batch N", then removed the unrelated ones (the diff-review feature, "preview", README updates) and the comment-label cleanup discussed later. Some review fixes never say so in the subject (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/5f3606b94" rel="noopener noreferrer"&gt;&lt;code&gt;5f3606b94&lt;/code&gt;&lt;/a&gt; is one), so 29 is a floor. Many of those bodies also record how many unit tests (the CalyxTests suite) ran and how many failed at that point.&lt;/p&gt;

&lt;h2&gt;
  
  
  July 5: seven rounds
&lt;/h2&gt;

&lt;p&gt;Calyx has persistent sessions: terminal sessions that survive quitting the app. A small Rust daemon owns each shell's PTY, and each pane runs a client that attaches to the daemon. I built it in phases. Phase 3, which integrated the daemon into the app, was merged into main at 00:51 on July 5.&lt;/p&gt;

&lt;p&gt;That afternoon and evening, the persistent-session code went through review after review. These are the rounds whose numbers made it into commit subjects:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Round&lt;/th&gt;
&lt;th&gt;Commit&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;CalyxTests&lt;/th&gt;
&lt;th&gt;Diff (lines)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/77e50735d" rel="noopener noreferrer"&gt;&lt;code&gt;77e50735d&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;13:41&lt;/td&gt;
&lt;td&gt;2019 run, 0 failed&lt;/td&gt;
&lt;td&gt;+1419 / -101&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/b7ad88913" rel="noopener noreferrer"&gt;&lt;code&gt;b7ad88913&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;15:51&lt;/td&gt;
&lt;td&gt;2028 run, 0 failed&lt;/td&gt;
&lt;td&gt;+1479 / -212&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/d5ae8c9cd" rel="noopener noreferrer"&gt;&lt;code&gt;d5ae8c9cd&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;17:25&lt;/td&gt;
&lt;td&gt;2032 run, 0 failed&lt;/td&gt;
&lt;td&gt;+916 / -215&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/a941b245d" rel="noopener noreferrer"&gt;&lt;code&gt;a941b245d&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;18:23&lt;/td&gt;
&lt;td&gt;2036 run, 0 failed&lt;/td&gt;
&lt;td&gt;+536 / -64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/f1868a8a5" rel="noopener noreferrer"&gt;&lt;code&gt;f1868a8a5&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;19:08&lt;/td&gt;
&lt;td&gt;2042 run, 0 failed&lt;/td&gt;
&lt;td&gt;+906 / -192&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/3c32054da" rel="noopener noreferrer"&gt;&lt;code&gt;3c32054da&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;20:03&lt;/td&gt;
&lt;td&gt;2047 run, 0 failed&lt;/td&gt;
&lt;td&gt;+1252 / -144&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/1948d43a6" rel="noopener noreferrer"&gt;&lt;code&gt;1948d43a6&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;20:35&lt;/td&gt;
&lt;td&gt;2048 run, 0 failed&lt;/td&gt;
&lt;td&gt;+224 / -105&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Only rounds 4, 5, 7, 9, 11, 13 and 15 (and round 18, early the next morning) appear in commits. The commits do not say what happened in the other rounds.&lt;/p&gt;

&lt;p&gt;The findings were real: reentrancy in the quit-confirmation flow, events dropped while the confirmation modal was up, cancellation not reaching child processes. Each round added a few tests and nothing ever failed. The fixes did not shrink steadily (rounds 5 and 13 were each larger than the round before), but round 15 came in at +224/-105 lines.&lt;/p&gt;

&lt;p&gt;Through all seven rounds, a persistent pane had never once started inside the app.&lt;/p&gt;

&lt;p&gt;The symptom had been visible from the beginning: panes were created and destroyed at full speed, and the window flashed. At 21:45, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/c842b18f1" rel="noopener noreferrer"&gt;&lt;code&gt;c842b18f1&lt;/code&gt;&lt;/a&gt; fixed that flashing as a reconnect bug. The reconnect attempt counter was reset to zero the moment a replacement pane was created, so the backoff never grew and the retries never stopped. After that fix the retries were bounded, but the panes still died instantly.&lt;/p&gt;

&lt;p&gt;The cause turned up only when the app was actually run and the error printed in the pane was read. At 23:00, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/0040b282e" rel="noopener noreferrer"&gt;&lt;code&gt;0040b282e&lt;/code&gt;&lt;/a&gt; fixed one problem: the &lt;code&gt;HOME=&amp;lt;root&amp;gt;&lt;/code&gt; word at the front of the pane command was being executed as a path relative to the pane's working directory. The fix kept &lt;code&gt;exec&lt;/code&gt; as the first word and moved the &lt;code&gt;HOME&lt;/code&gt; assignment into arguments to &lt;code&gt;/usr/bin/env&lt;/code&gt;. It locked a "first word is &lt;code&gt;exec&lt;/code&gt;" contract into the tests, and 2,057 tests passed. Nineteen minutes later, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/cd89aab20" rel="noopener noreferrer"&gt;&lt;code&gt;cd89aab20&lt;/code&gt;&lt;/a&gt; recorded this from the running app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bash: line 0: exec: exec: not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On macOS, the ghostty in the submodule (v1.3.1) runs a shell-form command by wrapping it as &lt;code&gt;/usr/bin/login -flp &amp;lt;user&amp;gt; /bin/bash --noprofile --norc -c "exec -l &amp;lt;command&amp;gt;"&lt;/code&gt; (&lt;code&gt;execCommand&lt;/code&gt; in &lt;code&gt;ghostty/src/termio/Exec.zig&lt;/code&gt;). Calyx's command already started with its own &lt;code&gt;exec&lt;/code&gt;, so the result was a double &lt;code&gt;exec&lt;/code&gt;, and bash went looking for a program literally named &lt;code&gt;exec&lt;/code&gt;. The commit body says the Phase 3 form had never worked inside the app at all, and that this was the real root of the pane churn that had been there from the start.&lt;/p&gt;

&lt;p&gt;The tests did execute the synthesized command and checked the environment the child process saw. They ran it through a plain &lt;code&gt;/bin/sh -c&lt;/code&gt;, without ghostty's &lt;code&gt;exec -l&lt;/code&gt; wrapper, and without that wrapper a leading &lt;code&gt;exec&lt;/code&gt; of your own is perfectly fine. ghostty's source sits in the repository as a submodule, but the review was reading the worktree's diff. Today the tests run the command through &lt;code&gt;/bin/sh -c "exec &amp;lt;command&amp;gt;"&lt;/code&gt;, emulating ghostty's wrapping, and the whole story is kept in exactly one place, a test file header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;//  This also RETIRES the entire ghostty-exec-wrapping saga the old&lt;/span&gt;
&lt;span class="c1"&gt;//  HomeStampTests file fought through, in three steps:&lt;/span&gt;
&lt;span class="c1"&gt;//  - A bare `HOME=&amp;lt;root&amp;gt;` first word resolves as a cwd-relative&lt;/span&gt;
&lt;span class="c1"&gt;//    path, field-verified broken as `bash: .../HOME=/tmp/cxpane: No such&lt;/span&gt;
&lt;span class="c1"&gt;//    file or directory`.&lt;/span&gt;
&lt;span class="c1"&gt;//  - A redundant leading `exec` is PATH-searched as a literal&lt;/span&gt;
&lt;span class="c1"&gt;//    nonexistent program, field-verified broken as `bash: line 0: exec:&lt;/span&gt;
&lt;span class="c1"&gt;//    exec: not found`.&lt;/span&gt;
&lt;span class="c1"&gt;//  - Routing through `/usr/bin/env` finally makes the `HOME=`&lt;/span&gt;
&lt;span class="c1"&gt;//    word an actual env assignment rather than a literal argv word.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;code&gt;CalyxTests/Sessions/SessionCommandSynthesizerRuntimeStateDirFlagsTests.swift&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Persistent sessions first shipped in v0.29.0 on July 7, which includes the fix. No user ever received the broken form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixes that introduced defects
&lt;/h2&gt;

&lt;p&gt;In several places, the commit that fixed a finding produced the next round's finding. The clearest case is a small helper that carries Swift task cancellation through to a child process.&lt;/p&gt;

&lt;p&gt;Round 11 (&lt;code&gt;f1868a8a5&lt;/code&gt;) added a lock-guarded bridge to &lt;code&gt;SystemCommandRunner&lt;/code&gt; so that polling a hung daemon would stop piling up orphaned processes. Round 13 (&lt;code&gt;3c32054da&lt;/code&gt;) adopted the same bridge for Git subprocesses and added a second bridge to propagate cancellation into time-bounded daemon queries. Round 15 (&lt;code&gt;1948d43a6&lt;/code&gt;) fixed what those two rounds had introduced. The Git adoption was a copy, so two byte-identical classes lived in two files. The branch for "already cancelled before launch" called &lt;code&gt;process.terminate()&lt;/code&gt; directly and never set the flag that prevents a double terminate. And the time-bound bridge could fire &lt;code&gt;onTimeout&lt;/code&gt; twice if cancellation arrived before registration. The file that exists today explains why it is one file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Shared by SystemCommandRunner.runInternal and GitService.run --&lt;/span&gt;
&lt;span class="c1"&gt;// was two byte-identical `private final class CancellationBridge`&lt;/span&gt;
&lt;span class="c1"&gt;// copies, one per file, consolidated here so a future fix to the&lt;/span&gt;
&lt;span class="c1"&gt;// terminate-once discipline only has to land in one place.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;code&gt;Calyx/Helpers/ProcessCancellationBridge.swift&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;The LSP feature shows the same shape three times in a little over half an hour on June 22. At 09:40, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/b649a9336" rel="noopener noreferrer"&gt;&lt;code&gt;b649a9336&lt;/code&gt;&lt;/a&gt; fixed seven issues the reviewer found after fix batches 1 to 3. The first was a race between &lt;code&gt;stop()&lt;/code&gt; and &lt;code&gt;startLSP()&lt;/code&gt;; the fix made the teardown re-read the bridge after pending startup work drained. Twenty minutes later, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/2eeb6d366" rel="noopener noreferrer"&gt;&lt;code&gt;2eeb6d366&lt;/code&gt;&lt;/a&gt; fixed the fact that this re-read could also tear down the new bridge installed by a subsequent &lt;code&gt;start()&lt;/code&gt;. Twelve minutes after that, &lt;code&gt;7ec033794&lt;/code&gt; fixed the test for that fix, which would have passed against the old code too: an &lt;code&gt;await&lt;/code&gt; between &lt;code&gt;stop()&lt;/code&gt; and the injection let the teardown run first and bail out harmlessly, so the test could not see the regression it was written for.&lt;/p&gt;

&lt;p&gt;One introduced defect stayed hidden for three months. LSP session shutdown waits for pending persistence work, with a two-second cap. Item 6 of &lt;code&gt;b649a9336&lt;/code&gt; added that cap using two unstructured Tasks, a drain observer and a timer, with only the first to arrive waking the caller. Its comment explained the choice: &lt;code&gt;Task.value&lt;/code&gt; does not honour cancellation, so a naive TaskGroup race-and-cancel would still wait for the drain.&lt;/p&gt;

&lt;p&gt;Two days later, on June 24, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/cc80ebbd3" rel="noopener noreferrer"&gt;&lt;code&gt;cc80ebbd3&lt;/code&gt;&lt;/a&gt;, the response to an audit that surfaced about 70 issues, rewrote that code. To stop the losing timer from living for two seconds while holding &lt;code&gt;self&lt;/code&gt;, it switched to a &lt;code&gt;withTaskGroup&lt;/code&gt; race followed by &lt;code&gt;cancelAll()&lt;/code&gt;. The same diff deleted the comment warning that this shape cannot enforce the bound, and replaced it with one explaining that shutdown latency stays bounded by whichever child wins. The cap was not restored until &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/7edef08f1" rel="noopener noreferrer"&gt;&lt;code&gt;7edef08f1&lt;/code&gt;&lt;/a&gt; on September 26. The current comment reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;        &lt;span class="c1"&gt;// Bounded wait: cap the drain at 2 seconds. `Task.value` does not&lt;/span&gt;
        &lt;span class="c1"&gt;// honour cancellation, so a structured race (`withTaskGroup`)&lt;/span&gt;
        &lt;span class="c1"&gt;// cannot enforce the bound: the group implicitly awaits every&lt;/span&gt;
        &lt;span class="c1"&gt;// child, including a cancelled `task.value` child whose persist&lt;/span&gt;
        &lt;span class="c1"&gt;// worker is stuck in cancellation-blind work (e.g. decoding a&lt;/span&gt;
        &lt;span class="c1"&gt;// huge file).&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;code&gt;Calyx/Features/LSP/LSPSession.swift&lt;/code&gt;, excerpt)&lt;/p&gt;

&lt;p&gt;The regression test's comment records that with the &lt;code&gt;withTaskGroup&lt;/code&gt; version, a 2,000,000-entry persistence file kept shutdown busy for about 21 seconds. The same comment suggests why it went unnoticed for three months: there is no test hook for the persistence worker's timing, so the regression test manufactures slowness by writing a real 1,000,000-entry JSON file. The June 24 diff contained the deleted warning, but no test could say which of the two comments was right.&lt;/p&gt;

&lt;p&gt;On July 6, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/87d77f130" rel="noopener noreferrer"&gt;&lt;code&gt;87d77f130&lt;/code&gt;&lt;/a&gt; swept a batch of fixes (H1 to H11) to the experimental daemon handoff feature and found four defects those fixes had introduced. In one, an adopted session was left Running permanently, impossible to kill or reattach.&lt;/p&gt;

&lt;p&gt;All of these are concurrency or lifecycle code. Fixes there close a problem by adding a state or a path, and the added path lands in the next round's diff, which is where most of these were caught. The two-second cap was different. No test could create its condition, so which of the two explanations held was decided only by whoever wrote the comment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What review caught
&lt;/h2&gt;

&lt;p&gt;Review also caught defects that had slipped past the tests. Round 9 (&lt;code&gt;a941b245d&lt;/code&gt;) found that the production snapshot builder never saved &lt;code&gt;tab.sessionRefs&lt;/code&gt;. The completeness tests exercised a different snapshot path that production did not use, and according to the commit, persistent sessions therefore never survived a real save and relaunch. Round 11 (&lt;code&gt;f1868a8a5&lt;/code&gt;) made the production builder delegate to the tested path, so there are no longer two builders to drift apart.&lt;/p&gt;

&lt;p&gt;On August 7, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/b7a8aa9c1" rel="noopener noreferrer"&gt;&lt;code&gt;b7a8aa9c1&lt;/code&gt;&lt;/a&gt; fixed a close path that set the "termination confirmed" flag without checking whether the Quick Terminal was keeping the app alive. Every other close path did that check. Trusting the flag, each window believed the app was quitting: it detached persistent sessions instead of killing them, and the last window skipped its pre-teardown save.&lt;/p&gt;

&lt;p&gt;The test in &lt;code&gt;7ec033794&lt;/code&gt; that could not fail belongs in the same group. The production snapshot path versus the path the tests checked, one close path versus the other close paths, a test versus the regression it is meant to catch: each is a mismatch between two things inside the repository, and reading both side by side tends to find it.&lt;/p&gt;

&lt;p&gt;An audit also overturned an earlier declaration that the work was done. &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/c372964be" rel="noopener noreferrer"&gt;&lt;code&gt;c372964be&lt;/code&gt;&lt;/a&gt; on June 26 fixed 14 findings and one regression from a second-pass audit, and its body says an earlier "all bugs fixed" claim was premature. The first symptom it names is that Calyx launched from Finder could not start a pyright installed through NVM. PATH augmentation used a hardcoded list, and the code that spawned language servers ignored even that augmented environment. This one is also an audit catching a defect that depends on something outside the repository, the environment the app is launched in.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was found outside review
&lt;/h2&gt;

&lt;p&gt;Like the &lt;code&gt;exec&lt;/code&gt; story, several defects surfaced only when the real thing was run.&lt;/p&gt;

&lt;p&gt;On August 19, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/067fa4b4f" rel="noopener noreferrer"&gt;&lt;code&gt;067fa4b4f&lt;/code&gt;&lt;/a&gt; fixed Codex rows in the sidebar that stayed idle forever after Codex exited. Calyx never installed a SessionEnd hook for Codex, on a premise written in the config manager's own doc comment: "Codex has no SessionEnd hook". For Codex 0.148.0 that was false. The binary's hook event enum lists SessionEnd, and writing the hook into a scratch &lt;code&gt;CODEX_HOME&lt;/code&gt; fires it. The same experiment disproved a second recorded premise, that Codex strips Calyx's environment variables before running hooks.&lt;/p&gt;

&lt;p&gt;On August 21, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/85d84a255" rel="noopener noreferrer"&gt;&lt;code&gt;85d84a255&lt;/code&gt;&lt;/a&gt; captured a real &lt;code&gt;opencode run&lt;/code&gt; that delegated to a subagent and found that &lt;code&gt;session.deleted&lt;/code&gt; never fires at all. The code that retired child sessions on &lt;code&gt;session.deleted&lt;/code&gt; had never run in practice, and the sets the comments said were cleaned on that same signal were never cleaned either.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/5f3606b94" rel="noopener noreferrer"&gt;&lt;code&gt;5f3606b94&lt;/code&gt;&lt;/a&gt; on June 27 started with a pyright crash I hit in real use. Calyx advertised &lt;code&gt;refreshSupport: true&lt;/code&gt; but never registered the matching handler, and according to the commit, pyright treats the resulting &lt;code&gt;-32601&lt;/code&gt; reply as a fatal protocol violation. A sweep for the same class of bug found 25 of them. After those fixes, a 148-cell check running 70 tools against pyright and rust-analyzer passed in full. LSP fixes still continued afterward: &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/15a4a03fa" rel="noopener noreferrer"&gt;&lt;code&gt;15a4a03fa&lt;/code&gt;&lt;/a&gt; in August, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/bc177c7f9" rel="noopener noreferrer"&gt;&lt;code&gt;bc177c7f9&lt;/code&gt;&lt;/a&gt; and &lt;code&gt;7edef08f1&lt;/code&gt; in September.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/bcedbc4e3" rel="noopener noreferrer"&gt;&lt;code&gt;bcedbc4e3&lt;/code&gt;&lt;/a&gt; on July 7 is the commit tagged v0.29.0. Eight production call sites referenced test seams that exist only in Debug builds, so the Release configuration did not compile. The commit notes that 80 hours of Debug-only verification never tripped it.&lt;/p&gt;

&lt;p&gt;In each of these, the fact that decided correctness lived outside the diff: how another program actually behaves (ghostty, Codex, OpenCode, pyright), or the build configuration. In the Codex case the wrong premise was written into a code comment; in the OpenCode case a comment described cleanup that never happened; and &lt;code&gt;0040b282e&lt;/code&gt; locked a wrong premise into the tests as a contract. A premise written into comments or tests is likely to become the yardstick a diff review checks consistency against, rather than something it checks. Checking the premise itself took running the other program.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a shrinking review diff measures
&lt;/h2&gt;

&lt;p&gt;July 5 is one feature on one day, so it is worth checking whether the same shape shows up elsewhere. The MCP Apps host landed at 14:48 on September 24 in &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/bbff0a049" rel="noopener noreferrer"&gt;&lt;code&gt;bbff0a049&lt;/code&gt;&lt;/a&gt;: 249 files, 49,027 lines added. The commits for review passes 2 through 4 followed the same day, each smaller than the last:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pass&lt;/th&gt;
&lt;th&gt;Commit&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Files changed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/de6df4d14" rel="noopener noreferrer"&gt;&lt;code&gt;de6df4d14&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;16:13&lt;/td&gt;
&lt;td&gt;36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/27a1dd857" rel="noopener noreferrer"&gt;&lt;code&gt;27a1dd857&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;19:36&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/9e224b836" rel="noopener noreferrer"&gt;&lt;code&gt;9e224b836&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;22:18&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Pass 4 was edge conditions: a negative pane width should yield a zero dock width, and the dock divider's hit area must not overlap a split divider's. In a little over two hours after it, three more fixes landed. Two of them matter here. At 00:05, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/c098c4f0d" rel="noopener noreferrer"&gt;&lt;code&gt;c098c4f0d&lt;/code&gt;&lt;/a&gt; fixed the open-link consent prompt showing only the tail of a long URL: the text view measured itself before it had a width and stayed scrolled to the bottom. At 00:40, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/cac63bc88" rel="noopener noreferrer"&gt;&lt;code&gt;cac63bc88&lt;/code&gt;&lt;/a&gt; worked around Swift 6.2.3 aborting the Release build (&lt;code&gt;-O&lt;/code&gt;, whole-module) with a SIL ownership error inside the compiler.&lt;/p&gt;

&lt;p&gt;Both show up as soon as you put a long URL on screen or run a Release build, and neither came out of four review passes. A shrinking review diff tells you the reviewer is finding less in the diff. It does not tell you the feature is done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traces the workflow left behind
&lt;/h2&gt;

&lt;p&gt;Fixing every finding also left marks in the code itself. On August 20, &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/1c783299d" rel="noopener noreferrer"&gt;&lt;code&gt;1c783299d&lt;/code&gt;&lt;/a&gt; removed 496 labels spread across 86 files. They named review rounds, task IDs and internal spec documents that are not in the repository, so the next reader had no way to follow them. Sentences that existed only to carry a label were rewritten to state, in the present tense, the invariant the code or test actually holds. The commit verifies the cleanup touched nothing else: with string literals masked and comment lines stripped, every file is byte-identical to before.&lt;/p&gt;

&lt;p&gt;For a subagent writing a fix, the natural explanation is "which finding this fixes". That context exists only in the review conversation; the next person reading the code does not have it. My reading is that 496 labels piled up because that mismatch played out on every single fix.&lt;/p&gt;

&lt;p&gt;Workflow state also ended up in commit messages. The body of &lt;a href="https://github.com/yuuichieguchi/Calyx/commit/a3f729d57" rel="noopener noreferrer"&gt;&lt;code&gt;a3f729d57&lt;/code&gt;&lt;/a&gt; on July 5 says: "NOT yet code-reviewed; review before merging to main". And the round 5 and round 7 commits each include removing em dashes from lines the previous round had added. In rounds 5 and 7, the fixes from the previous round had added new violations for the next round to clean up.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this record supports, and what it does not
&lt;/h2&gt;

&lt;p&gt;First, what it cannot tell you. With no control, I cannot say how many defects review prevented overall, or whether it was worth the time. I cannot say whether human review would have done differently. I cannot say how faithfully the tests-fail-first step was followed: of 752 non-merge commits, only seven mention the RED step in their subject or body. And because the rules file has no history, I cannot say when each rule took effect.&lt;/p&gt;

&lt;p&gt;Within the record, three things hold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Neither an all-green unit test suite nor review fixes that end up small showed that the feature worked. The same shape appears for persistent sessions on July 5 and for MCP Apps on September 24.&lt;/li&gt;
&lt;li&gt;Fixes to concurrency and lifecycle code introduced new defects. Most were caught in the next round. The one whose condition no test could create stayed for three months.&lt;/li&gt;
&lt;li&gt;In the examples above, mismatches between two things inside the repository were caught by review. Apart from the Finder PATH defect the audit picked up, defects whose deciding fact lived outside the repository were caught by running the real thing, not by another review round.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practices the record suggests
&lt;/h2&gt;

&lt;p&gt;If I set up the same workflow again, the record points to these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a change integrates with an external program, run it in the app once before stacking review rounds. The July 5 defect would very likely have shown up in the first error message a pane printed.&lt;/li&gt;
&lt;li&gt;When a comment states a premise about an external program, record how and against which version it was checked. &lt;code&gt;067fa4b4f&lt;/code&gt; checked Codex 0.148.0's binary and a scratch config. An unchecked premise is an open question, not a comment.&lt;/li&gt;
&lt;li&gt;Run tests through the wrapping the external program actually applies.&lt;/li&gt;
&lt;li&gt;Build Release early, with the exact invocation the release script uses.&lt;/li&gt;
&lt;li&gt;Do not treat a claim as established until a test can create its condition. Time bounds are the typical case.&lt;/li&gt;
&lt;li&gt;Record "all findings fixed" as the state of the review, and do not read it as the state of the feature.&lt;/li&gt;
&lt;li&gt;Keep comments to invariants the next reader can follow. Round numbers and finding IDs belong in the commit or the review record.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The end-to-end tests had their own share of places that passed while checking nothing; that is a separate post.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Calyx
&lt;/h2&gt;

&lt;p&gt;Calyx is a native macOS terminal (MIT) for running and supervising coding agents in parallel: Claude Code, Codex, OpenCode, Hermes, Grok and pi. It has an approval inbox that collects permission prompts in one place, agent status driven by each CLI's own hooks, the persistent sessions described above, command history that agents can read, and in-terminal diff review with line comments sent back to the agent. It requires macOS 26 or later on Apple Silicon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; calyx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;https://github.com/yuuichieguchi/Calyx&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://help.getcalyx.app" rel="noopener noreferrer"&gt;https://help.getcalyx.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every commit cited here is in the public repository, so you can read the messages and diffs yourself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codereview</category>
      <category>testing</category>
      <category>swift</category>
    </item>
    <item>
      <title>What were my AI-written UI tests actually proving? Green XCUITests over a broken macOS terminal</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Thu, 08 Oct 2026 14:57:38 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/what-were-my-ai-written-ui-tests-actually-proving-green-xcuitests-over-a-broken-macos-terminal-3i76</link>
      <guid>https://dev.to/yuu1ch13/what-were-my-ai-written-ui-tests-actually-proving-green-xcuitests-over-a-broken-macos-terminal-3i76</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;Calyx&lt;/a&gt; is a native macOS terminal for running and supervising coding agents in parallel. Most of its code is written by Claude Code subagents, and the tests are no exception: I run a TDD loop where one agent writes a failing test and another writes the implementation. The UI tests are XCUITest, and the &lt;code&gt;CalyxUITests&lt;/code&gt; target has more than 130 test methods as I write this.&lt;/p&gt;

&lt;p&gt;The suite was green many times while the product was broken. The clearest case was the switch in Settings that enables persistent sessions. Clicking it did nothing, and every persistent-session E2E test passed. On the same day I found out that the UI tests had left 47 shells running against the session daemon on my own Mac.&lt;/p&gt;

&lt;p&gt;This post groups those failures by kind. Each one starts with something specific to Calyx, but they all come back to two questions: what does this assertion actually observe, and what does this test leave behind on the developer's machine? It is written for people who have agents writing their tests, and for anyone writing E2E tests for a macOS app.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Where can a test observe a terminal?
&lt;/h2&gt;

&lt;p&gt;XCUITest observes an app through accessibility. Buttons and labels show up as elements; the text in a Calyx pane does not. Calyx renders the terminal on the GPU with Ghostty's engine (libghostty), and the glyphs on screen never become accessibility elements.&lt;/p&gt;

&lt;p&gt;The first persistent-session E2E test ran straight into this. A &lt;strong&gt;persistent session&lt;/strong&gt; keeps the shell in a daemon outside Calyx, so you can quit the app and come back to the same shell after relaunch. The first version of the test echoed a marker string in a pane and, after relaunch, looked for it with &lt;code&gt;app.staticTexts&lt;/code&gt;. That assertion could not pass even with a fully working session (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/c01be27e0" rel="noopener noreferrer"&gt;&lt;code&gt;c01be27e0&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The first fix moved the observation into a file. The pane's shell writes its own PID to a file; after the app restarts, the restored pane writes it again. Equal PIDs mean the daemon kept the same shell alive and the restored pane reattached to it.&lt;/p&gt;

&lt;p&gt;That version still typed into the pane, and &lt;code&gt;typeText&lt;/code&gt; sends key events to whatever app is actually frontmost on the system, not to the app under test. When the app under test loses focus, the keystrokes land in the developer's own terminal. I saw that happen twice, and about an hour later the test was rewritten to type nothing at all (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/62564941c" rel="noopener noreferrer"&gt;&lt;code&gt;62564941c&lt;/code&gt;&lt;/a&gt;). Section 4 has the story of the leak.&lt;/p&gt;

&lt;p&gt;The rewritten test observes the daemon's ledger. Persistent sessions are owned by a daemon called &lt;code&gt;calyx-session&lt;/code&gt;, which records its sessions in a ledger file at &lt;code&gt;$HOME/.calyx/state/sessions.json&lt;/code&gt;. The test reads the ledger before and after the restart and asserts that the same session id is still Running with the same PID and creation time. It also asserts that &lt;code&gt;attached_clients&lt;/code&gt; is at least 1 on both sides of the restart. The daemon keeps a session alive with zero clients attached, so matching ids and PIDs alone would not prove that the restored pane reattached.&lt;/p&gt;

&lt;p&gt;The test reads the ledger as a file rather than asking the CLI, and there is a reason for that too. The earlier version spawned &lt;code&gt;calyx-session ls --all --json&lt;/code&gt; from the test runner, and the output was always empty, even while the daemon was healthy (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/2bb1b24a2" rel="noopener noreferrer"&gt;&lt;code&gt;2bb1b24a2&lt;/code&gt;&lt;/a&gt;). The UI test runner is itself App-Sandboxed; &lt;code&gt;codesign -d --entitlements&lt;/code&gt; on it showed the sandbox enabled with only a read-only file exception. A sandboxed process cannot connect to the daemon's Unix domain socket. The same query run outside the sandbox succeeded, so the problem was on the runner's side. Writes are restricted too: a write from the runner to &lt;code&gt;/tmp&lt;/code&gt; failed, and because it was written with &lt;code&gt;try?&lt;/code&gt;, it failed silently.&lt;/p&gt;

&lt;p&gt;For a terminal app, the most reliable thing an E2E test can observe turned out to be the state the product writes to disk. Screen text is invisible to accessibility, the socket is out of reach, and typed keys can leak. A file the runner can read was the one channel none of those constraints touched.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Launch arguments and a switch that did nothing
&lt;/h2&gt;

&lt;p&gt;According to the July 7, 2026 commit that fixed them (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/1a17867c1" rel="noopener noreferrer"&gt;&lt;code&gt;1a17867c1&lt;/code&gt;&lt;/a&gt;), the four switches in Settings &amp;gt; Sessions were all dead. They surfaced through user reports and a deliberate sweep for UI that looks functional but is not. The switches had no target and no action, and nothing seeded them from the stored value. Clicking one wrote nothing; reopening Settings showed it off again. There was no way to turn persistent sessions on from the UI.&lt;/p&gt;

&lt;p&gt;Every persistent-session E2E test still passed, because every one of them turned the feature on with a launch argument before starting the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;additionalLaunchArguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"-calyx.session.persistentSessionsEnabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"YES"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;-key value&lt;/code&gt; launch argument goes into the UserDefaults argument domain, which is read ahead of the app's own persistent domain. What these tests could check was "what happens when persistent sessions are on", not "can a user turn persistent sessions on". The switch's wiring was not on any test's path.&lt;/p&gt;

&lt;p&gt;It was also easy to miss by hand. An &lt;code&gt;NSSwitch&lt;/code&gt; flips its own on-screen state when clicked whether or not anything is wired to it, so at the moment you click, it looks like it worked.&lt;/p&gt;

&lt;p&gt;The E2E test written for the fix clicks the real switch, quits through the app menu, relaunches, and reads the switch back. It deliberately avoids the launch argument: the argument domain would shadow every read of that key for the life of the process, so the test could never tell whether &lt;code&gt;set()&lt;/code&gt; had taken effect. The relaunch matters because the Settings window controller is a per-process singleton. Close and reopen the window in the same process and you get the same &lt;code&gt;NSSwitch&lt;/code&gt; instance you just clicked, which shows "on" whether or not anything was saved. After a relaunch, a fresh controller builds its state from what is actually on disk, and "looked like it worked" becomes distinguishable from "was saved".&lt;/p&gt;

&lt;p&gt;A commit that evening (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/1cab009aa" rel="noopener noreferrer"&gt;&lt;code&gt;1cab009aa&lt;/code&gt;&lt;/a&gt;) closed the gaps found by an audit that classified every assertion in all 20 UI test classes by what it actually proves. Ten of them would have let through a defect a user would notice within a second. A few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The group-switching test checked that group rows existed. It did not check that the active group changed, which is to say that the visible set of tabs changed.&lt;/li&gt;
&lt;li&gt;Of the seven switches in Settings, one had a click, quit, relaunch, and read-back E2E test. The other six had none.&lt;/li&gt;
&lt;li&gt;The search test checked that the search bar appeared. It now types a real query and asserts the match count and that Find Next moves.&lt;/li&gt;
&lt;li&gt;The test for pressing Enter in the Compose box checked that the box cleared. It did not check that the text reached the terminal and ran.&lt;/li&gt;
&lt;li&gt;The restore-after-restart tests looked only at ledger fields. They now also assert the window count and the restored tab's visible title.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these tests existed, was green, and checked something. What they checked was not what the user sees. A red-first TDD loop does not catch this. The launch-argument E2E tests fail without the flag and pass with it, so they go from red to green exactly as the process asks. Red-first guarantees that an assertion reacts to something; it says nothing about whether that something is visible to a user.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. When the test's actions are not the user's actions
&lt;/h2&gt;

&lt;p&gt;The launch arguments in section 2 were a case of the test reaching a state by a path no user takes. The same gap showed up in the actions themselves, such as dragging and quitting.&lt;/p&gt;

&lt;p&gt;After moving to Xcode 27 and macOS 27, &lt;code&gt;press(forDuration:thenDragTo:)&lt;/code&gt; stopped delivering any mouse events to the app (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/516a9afe1" rel="noopener noreferrer"&gt;&lt;code&gt;516a9afe1&lt;/code&gt;&lt;/a&gt;). I instrumented &lt;code&gt;mouseDown&lt;/code&gt; and &lt;code&gt;mouseDragged&lt;/code&gt; on the tab view and added an app-wide &lt;code&gt;NSEvent&lt;/code&gt; local monitor; nothing fired. &lt;code&gt;click(forDuration:thenDragTo:)&lt;/code&gt;, the same click-hold-and-drag gesture through a different XCTest code path, did deliver events.&lt;/p&gt;

&lt;p&gt;A test that asserts a reorder happens will fail when the drag never arrives. The problem was the test pointing the other way. &lt;code&gt;test_tapStillWorksAfterDrag&lt;/code&gt; asserts that a drag shorter than the 5pt threshold does not reorder anything and the tab stays where it was. With no drag delivered, no reorder could happen, so the test passed without exercising anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// `click(forDuration:thenDragTo:)`, not `press(forDuration:&lt;/span&gt;
&lt;span class="c1"&gt;// thenDragTo:)` -- see test_dragTabBarTab_reordersCorrectly's own&lt;/span&gt;
&lt;span class="c1"&gt;// comment. Using the broken `press` variant here made this&lt;/span&gt;
&lt;span class="c1"&gt;// assertion pass vacuously (no drag was ever delivered to assert&lt;/span&gt;
&lt;span class="c1"&gt;// "no reorder" against).&lt;/span&gt;
&lt;span class="n"&gt;startCoordinate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forDuration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;thenDragTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;nearbyCoordinate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An assertion that something does not happen also holds when the input never arrived. If you keep a test like that, pair it with a test that drives the same input and asserts that the expected thing does happen. When the pair goes red, you learn that the input is not getting through.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;app.terminate()&lt;/code&gt; takes a path no user takes either (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/8d292b778" rel="noopener noreferrer"&gt;&lt;code&gt;8d292b778&lt;/code&gt;&lt;/a&gt;). It sends SIGTERM, which skips AppKit's termination flow entirely. Calyx saves its session snapshot synchronously in &lt;code&gt;applicationWillTerminate&lt;/code&gt;, and on a test-driven quit that never ran. After relaunch there was nothing to restore, and the daemon's ledger showed a third, fresh session. This time the product was right and the test's way of quitting was wrong. Mid-test quits now go through the app's own menu, the same path as a real quit. The &lt;code&gt;terminate()&lt;/code&gt; in &lt;code&gt;tearDown&lt;/code&gt; stays, since cleanup does not need the save.&lt;/p&gt;

&lt;p&gt;Two more cases where the test assumed something the real environment did not provide. When a pane appears on screen, the shell the daemon started may not be reading its PTY yet. A keystroke sent in that window is silently dropped, and the test failed against a healthy pane (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/68b9fcf86" rel="noopener noreferrer"&gt;&lt;code&gt;68b9fcf86&lt;/code&gt;&lt;/a&gt;). And initializing Sparkle, the auto-update framework, at launch put its first-run "Check for updates automatically?" dialog in front of the app under test, where it swallowed every keystroke XCUITest sent (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/0b9a60da9" rel="noopener noreferrer"&gt;&lt;code&gt;0b9a60da9&lt;/code&gt;&lt;/a&gt;). Under the &lt;code&gt;--uitesting&lt;/code&gt; launch argument, Calyx now skips updater setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. What the test harness left on my machine
&lt;/h2&gt;

&lt;p&gt;Running the E2E suite kept leaving things on my Mac. Here are three, in the order they happened. None of them showed up in the test results; the damage piled up on the developer's side instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keystrokes in my real terminal
&lt;/h3&gt;

&lt;p&gt;According to a July 5 commit (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/a3f729d57" rel="noopener noreferrer"&gt;&lt;code&gt;a3f729d57&lt;/code&gt;&lt;/a&gt;), an earlier E2E run had crashed partway through, and the &lt;code&gt;echo&lt;/code&gt; marker the test was typing went into a real Ghostty window I had open. &lt;code&gt;typeText&lt;/code&gt; sends keys to the frontmost app; when the app under test disappears, whatever is behind it receives them.&lt;/p&gt;

&lt;p&gt;The first defense had two layers. The first was a separate bundle ID. A dedicated &lt;code&gt;DebugUITesting&lt;/code&gt; build configuration gives the Calyx built for UI tests its own bundle identifier, &lt;code&gt;com.calyx.terminal.e2e&lt;/code&gt;. According to the comment in &lt;code&gt;project.yml&lt;/code&gt;, LaunchServices had previously been able to confuse a locally installed production Calyx with the UI-test build, letting keystrokes leak into whichever one it treated as the real app. The second layer was a guard that re-checks the frontmost app's bundle ID before every keystroke.&lt;/p&gt;

&lt;p&gt;Later the same day, the persistent-session test moved to the no-typing design from section 1 anyway. If the test types nothing, nothing can leak.&lt;/p&gt;

&lt;h3&gt;
  
  
  47 zombie shells
&lt;/h3&gt;

&lt;p&gt;According to the July 7 commit that fixed it (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/3e1aea2b8" rel="noopener noreferrer"&gt;&lt;code&gt;3e1aea2b8&lt;/code&gt;&lt;/a&gt;), the real daemon under my own &lt;code&gt;~/.calyx&lt;/code&gt; had accumulated 47 shells in a single day. The cause was the UI tests' base class. Its &lt;code&gt;setUp()&lt;/code&gt; launched the app with no UserDefaults isolation and no &lt;code&gt;HOME&lt;/code&gt; override. The app under test's defaults domain, &lt;code&gt;com.calyx.terminal.e2e&lt;/code&gt;, was shared by every run, and during that period it had persistent sessions turned on. Every time a tab-creation or tab-renaming test launched the app, the app read that persistent sessions were enabled and created a session in my real daemon. The bundle-ID split from the previous section separated the test build from the production app; it did nothing to separate one test run from the next.&lt;/p&gt;

&lt;p&gt;The fix gives every test its own UserDefaults suite, named with a fresh UUID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;suiteName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.calyx.tests.e2e.CalyxUITestCase-&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="kt"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuidString&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;defaultsSuiteName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;suiteName&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;launchEnvironment&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"CALYX_UITEST_DEFAULTS_SUITE"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;suiteName&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the app sees this environment variable, it uses that suite. After the fix, a run of &lt;code&gt;TabManagementUITests&lt;/code&gt; added zero sessions to the real ledger.&lt;/p&gt;

&lt;p&gt;You might expect that overriding &lt;code&gt;HOME&lt;/code&gt; with a temp directory would be enough. In this project it was not, and I confirmed that in two places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;UserDefaults.standard&lt;/code&gt; goes through &lt;code&gt;cfprefsd&lt;/code&gt; and turned out to be tied to the real macOS account, not to &lt;code&gt;HOME&lt;/code&gt;. The first version of the Settings switch test changed &lt;code&gt;HOME&lt;/code&gt; for every test and was still reading and writing my real e2e defaults domain.&lt;/li&gt;
&lt;li&gt;Commands running in a pane did not inherit the &lt;code&gt;HOME&lt;/code&gt; the app was launched with. &lt;code&gt;ps aux&lt;/code&gt; on a live run showed each pane's command started through &lt;code&gt;login -flp &amp;lt;username&amp;gt; ...&lt;/code&gt;, which rebuilds the shell's environment for the real user. A bare &lt;code&gt;calyx-session&lt;/code&gt; run in a pane from a test would operate on my daemon, not the test's. So every &lt;code&gt;calyx-session&lt;/code&gt; call a test runs in a pane passes flags that name its state directories explicitly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even with a per-test suite, some code went around it. Three of the seven switches from section 2 (smooth scrolling and two LSP settings) read and write &lt;code&gt;UserDefaults.standard&lt;/code&gt; directly, without going through the isolation suite. Their test saves those three keys before it runs and restores them afterwards.&lt;/p&gt;

&lt;p&gt;Moving &lt;code&gt;HOME&lt;/code&gt; into a temp directory caused a problem of its own (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/bd79c9482" rel="noopener noreferrer"&gt;&lt;code&gt;bd79c9482&lt;/code&gt;&lt;/a&gt;). The daemon's socket lives at &lt;code&gt;$HOME/.calyx/run/sessiond.sock&lt;/code&gt;, and with &lt;code&gt;HOME&lt;/code&gt; under &lt;code&gt;NSTemporaryDirectory()&lt;/code&gt; that path came to 131 bytes. The path in a macOS &lt;code&gt;sockaddr_un&lt;/code&gt; holds at most 104 bytes, so every &lt;code&gt;calyx-session attach&lt;/code&gt; failed instantly and the pane just kept reconnecting. Test &lt;code&gt;HOME&lt;/code&gt; directories now live at &lt;code&gt;/tmp/cxe2e-&amp;lt;first 8 characters of a UUID&amp;gt;-h&lt;/code&gt;, which keeps the socket path at 46 bytes at most.&lt;/p&gt;

&lt;h3&gt;
  
  
  92,287 lock files
&lt;/h3&gt;

&lt;p&gt;According to the August 20 commit that fixed it (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/b4e3bf393" rel="noopener noreferrer"&gt;&lt;code&gt;b4e3bf393&lt;/code&gt;&lt;/a&gt;), 92,287 lock files had accumulated under my &lt;code&gt;~/Library/Application Support&lt;/code&gt;. When Calyx edits an agent CLI's configuration file, it takes an exclusive lock on a lock file named after a hash of the config path. It never deletes those lock files, on purpose. If you delete a &lt;code&gt;flock&lt;/code&gt; lock file after use, another process can recreate the path as a new inode, and two processes each end up believing they hold "the" lock. A real user has a handful of config paths, so they end up with a handful of lock files.&lt;/p&gt;

&lt;p&gt;The unit tests, though, write to a fresh UUID temp path in every test. Every write minted a new hash and left another file behind in the real directory. The files arrived in bursts that matched test-suite runs, not any configuration activity.&lt;/p&gt;

&lt;p&gt;The fix puts the lock directory under the test host's own temp directory whenever the code runs inside the test host. I did not do it as a per-test override, because any write creates a lock file, and a single test that forgot to opt in would bring the leak back. In the current code this redirect lives in one place, &lt;code&gt;CalyxPathRoot.testRoot&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What typeText actually sends
&lt;/h2&gt;

&lt;p&gt;Some tests still type into panes, and &lt;code&gt;typeText&lt;/code&gt; tripped me up repeatedly.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;typeText&lt;/code&gt; does not send characters. It presses, under the test runner's keyboard layout, the key that produces each character, and the pane turns that key back into a character under its own layout. When the command-log E2E test typed a marker containing an underscore, the pane received &lt;code&gt;=&lt;/code&gt;, and the marker never matched (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/c883f2fe5" rel="noopener noreferrer"&gt;&lt;code&gt;c883f2fe5&lt;/code&gt;&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Quotes broke too. Typing &lt;code&gt;sh '&amp;lt;path&amp;gt;&lt;/code&gt; arrived in the pane as &lt;code&gt;sh ;Su/Users/...&lt;/code&gt;, and &lt;code&gt;sh&lt;/code&gt;, handed a mangled argument, started an interactive shell instead of running the script. With an IME such as Japanese input active, keystrokes become input to the IME's composition, and Return commits the composition instead of reaching the terminal.&lt;/p&gt;

&lt;p&gt;Pasting avoids layouts and brings its own problem. On a loaded machine, Return can arrive while the paste is still being delivered and land inside the bracketed paste, and then the command never runs. That happened even with the short one-line &lt;code&gt;sh '&amp;lt;path&amp;gt;'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The current setup combines three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For the duration of each test, switch the input source to ABC (or US if ABC is not installed) and restore the original afterwards (&lt;code&gt;InputSourceGuard&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Write the command itself to a script file and type only &lt;code&gt;sh &amp;lt;path&amp;gt;&lt;/code&gt; into the pane with &lt;code&gt;typeText&lt;/code&gt;. Before typing, check that the path contains only characters that need no quoting.&lt;/li&gt;
&lt;li&gt;When a test types a command directly, route it through a helper that accepts only layout-invariant characters:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;typeIntoPane&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;line&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CharacterSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;charactersIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 /.;-&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unicodeScalars&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allSatisfy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;XCTFail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pane text contains a character that is not layout-invariant: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;debugDescription&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;typeText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second change broke another test's assumption (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/9ba33d3e0" rel="noopener noreferrer"&gt;&lt;code&gt;9ba33d3e0&lt;/code&gt;&lt;/a&gt;). The focus-movement test tagged each pane by running &lt;code&gt;export PANE=...&lt;/code&gt; and read the tag back with &lt;code&gt;echo $PANE&lt;/code&gt;. Once commands ran from a script file inside a child &lt;code&gt;sh&lt;/code&gt; (&lt;a href="https://github.com/yuuichieguchi/Calyx/commit/23999ef0a" rel="noopener noreferrer"&gt;&lt;code&gt;23999ef0a&lt;/code&gt;&lt;/a&gt;, September 9), the &lt;code&gt;export&lt;/code&gt; ended with the child shell and never reached the pane's interactive shell. &lt;code&gt;echo $PANE&lt;/code&gt; always came back empty, and the test failed on main until I fixed it on September 26. The tag is now the pane's tty, written to a file and compared there.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Rules for letting an agent run the tests
&lt;/h2&gt;

&lt;p&gt;I develop Calyx with Claude Code running inside Calyx. When the agent runs the UI tests, the Calyx I am working in is running on the same machine, next to the app under test. That combination gets two rules in my local setup. Neither is in the public repository; both are my own machine's configuration.&lt;/p&gt;

&lt;p&gt;The first is a hook that forbids concurrent &lt;code&gt;xcodebuild test&lt;/code&gt; runs. A Claude Code PreToolUse hook inspects every Bash command, and for &lt;code&gt;xcodebuild ... test&lt;/code&gt; it creates &lt;code&gt;/tmp/.calyx-test-lock&lt;/code&gt; with &lt;code&gt;mkdir&lt;/code&gt;. &lt;code&gt;mkdir&lt;/code&gt; is atomic, so a failure means another run holds the lock. In that case the hook checks with &lt;code&gt;pgrep&lt;/code&gt; whether a test process is really running, and if so, it denies the command. The denial reason tells the agent not to run tests in parallel, not to kill the other run, and to wait for it to finish before re-running the same command. If no test process is running, the hook treats the lock as stale, removes it, and takes it.&lt;/p&gt;

&lt;p&gt;The second rule, written into the skill that runs the tests, is that the agent never quits Calyx on its own. The UI suite is best run with no Calyx open, but &lt;code&gt;pgrep&lt;/code&gt; cannot tell the Calyx in &lt;code&gt;/Applications&lt;/code&gt; from a debug build in DerivedData. A running Calyx is almost certainly the terminal I am working in, and quitting it would destroy every shell in it along with the scrollback. So the agent stops and asks, and I quit it myself with Cmd-Q. This step is never handed to a subagent, because a subagent cannot stop partway and ask a human.&lt;/p&gt;

&lt;p&gt;The test code follows the same principle. An instance of the app under test left behind by an aborted run blocks the next run's automation connection, so &lt;code&gt;CalyxUITestCase&lt;/code&gt; terminates such instances before launching. It only touches instances whose bundle lives under a &lt;code&gt;/DebugUITesting/&lt;/code&gt; build directory, never the Calyx in &lt;code&gt;/Applications&lt;/code&gt; or a plain Debug build.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. A checklist for the next macOS E2E suite
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;For each assertion, write down what the user should be seeing when it passes. An assertion you cannot describe that way only proves that an element exists.&lt;/li&gt;
&lt;li&gt;Test settings by operating the real control, not a launch argument, then restart the process and read the value back.&lt;/li&gt;
&lt;li&gt;Pair every "nothing happens" test with a test that drives the same input and asserts that something does happen.&lt;/li&gt;
&lt;li&gt;Quit mid-test through the app's menu. &lt;code&gt;app.terminate()&lt;/code&gt; is SIGTERM.&lt;/li&gt;
&lt;li&gt;Terminal contents are not in the accessibility tree, so observe the state the product writes to disk. The UI test runner is sandboxed: it cannot reach your sockets, but it can read files.&lt;/li&gt;
&lt;li&gt;Do not treat a &lt;code&gt;HOME&lt;/code&gt; override as isolation. Isolate UserDefaults with a per-test suite, and isolate commands that run in a pane with explicit flags.&lt;/li&gt;
&lt;li&gt;If test &lt;code&gt;HOME&lt;/code&gt; directories live in a temp directory, do the arithmetic on your Unix socket paths against the 104-byte limit.&lt;/li&gt;
&lt;li&gt;For any code that deliberately never deletes a file, check where it writes when a test calls it.&lt;/li&gt;
&lt;li&gt;Give &lt;code&gt;typeText&lt;/code&gt; only layout-invariant characters. Put long commands in a file, and switch the input source to ABC for the duration of the test.&lt;/li&gt;
&lt;li&gt;If an agent runs your tests, enforce the no-concurrent-runs rule with a hook, and write "never quit the developer's app" into the agent's instructions as a step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having agents write tests makes the number of tests grow fast. The ten holes from section 2 were sitting inside that growing pile of green tests. What found them was not more tests; it was going back through the existing assertions one at a time and asking what each of them observes.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Calyx
&lt;/h2&gt;

&lt;p&gt;Calyx is a native macOS terminal (Swift 6, AppKit and SwiftUI, MIT) for running and supervising coding agents in parallel: an approval inbox for Claude Code and Codex permission prompts and for Grok (always-approve mode) and pi tool calls, an agent status sidebar driven by each CLI's own hooks, in-terminal diff review with line comments sent back to the agent, MCP tools that let agents read command output and language-server results, and the persistent sessions that came up throughout this post. It requires macOS 26 or later on Apple Silicon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; calyx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;https://github.com/yuuichieguchi/Calyx&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://help.getcalyx.app" rel="noopener noreferrer"&gt;https://help.getcalyx.app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>swift</category>
      <category>macos</category>
      <category>ai</category>
    </item>
    <item>
      <title>What six coding-agent CLIs actually expose: hooks, permissions, subagents, and MCP config compared</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Mon, 14 Sep 2026 14:56:01 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/what-six-coding-agent-clis-actually-expose-hooks-permissions-subagents-and-mcp-config-compared-2dhn</link>
      <guid>https://dev.to/yuu1ch13/what-six-coding-agent-clis-actually-expose-hooks-permissions-subagents-and-mcp-config-compared-2dhn</guid>
      <description>&lt;p&gt;Calyx is a macOS terminal for running and supervising coding agents in parallel: Claude Code, Codex, OpenCode, Hermes, Grok, and pi. Supporting six CLIs meant writing Calyx into each one's configuration and speaking whatever event mechanism it has. This post compares what each CLI exposes for four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Where it reads MCP servers and hooks from, and in what format&lt;/li&gt;
&lt;li&gt;How a hook or plugin gets called, and with which events&lt;/li&gt;
&lt;li&gt;Whether an external program can answer a permission prompt, and what happens when it does not&lt;/li&gt;
&lt;li&gt;What it reports about subagents&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Versions are pinned where Calyx's code pins them: Claude Code 2.1.251, Codex 0.148.0, OpenCode 1.18.18. Hermes, Grok, and pi are described as of mid-September 2026. Every claim comes from the integration code listed under Source at the end. Where a claim rests on a vendor's documentation rather than observed behavior, the text says so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Claude Code 2.1.251&lt;/th&gt;
&lt;th&gt;Codex 0.148.0&lt;/th&gt;
&lt;th&gt;OpenCode 1.18.18&lt;/th&gt;
&lt;th&gt;Hermes&lt;/th&gt;
&lt;th&gt;Grok&lt;/th&gt;
&lt;th&gt;pi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MCP config&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.claude.json&lt;/code&gt;, JSON&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.codex/config.toml&lt;/code&gt;, TOML&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;opencode.json&lt;/code&gt;, JSON&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;config.yaml&lt;/code&gt;, YAML&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;config.toml&lt;/code&gt;, TOML&lt;/td&gt;
&lt;td&gt;none; the extension speaks JSON-RPC itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Env var syntax Calyx writes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;${VAR:-}&lt;/code&gt; (default required)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;env_http_headers&lt;/code&gt; mapping&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{env:VAR}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;${VAR}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;${VAR:-}&lt;/code&gt; in any string field&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;process.env&lt;/code&gt; in TypeScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Event transport&lt;/td&gt;
&lt;td&gt;command hook, stdin JSON&lt;/td&gt;
&lt;td&gt;command hook, stdin JSON&lt;/td&gt;
&lt;td&gt;plugin callback (Bun)&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;command hook, stdin JSON, camelCase&lt;/td&gt;
&lt;td&gt;extension API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Events&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;5 + gate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocking event&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PermissionRequest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PermissionRequest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;none used&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PreToolUse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tool_call&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response schema&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;hookSpecificOutput.decision.behavior&lt;/code&gt; and friends&lt;/td&gt;
&lt;td&gt;same envelope, &lt;code&gt;allow&lt;/code&gt;/&lt;code&gt;deny&lt;/code&gt; only, rejects extras&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;flat &lt;code&gt;{decision, reason}&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;flat &lt;code&gt;{decision, reason}&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No answer means&lt;/td&gt;
&lt;td&gt;CLI's own prompt&lt;/td&gt;
&lt;td&gt;CLI's own prompt&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;deny&lt;/td&gt;
&lt;td&gt;deny&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Calyx unreachable means&lt;/td&gt;
&lt;td&gt;CLI's own prompt&lt;/td&gt;
&lt;td&gt;CLI's own prompt&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;td&gt;Grok's own pipeline&lt;/td&gt;
&lt;td&gt;the call runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subagents&lt;/td&gt;
&lt;td&gt;state + current tool&lt;/td&gt;
&lt;td&gt;lifecycle&lt;/td&gt;
&lt;td&gt;lifecycle (synthesized)&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;state + current tool&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session id field&lt;/td&gt;
&lt;td&gt;&lt;code&gt;session_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;session_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;four spellings&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;&lt;code&gt;sessionId&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;from &lt;code&gt;ctx.sessionManager&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How Calyx talks to a CLI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The server&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One local HTTP server, bound to 127.0.0.1.&lt;/li&gt;
&lt;li&gt;Ports 41830 through 41839 are tried in order, then a kernel-assigned port.&lt;/li&gt;
&lt;li&gt;Every route authenticates with a bearer token: 32 random bytes, hex encoded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The routes that matter here&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/mcp&lt;/code&gt;: the MCP tools an agent calls (command history, messaging other agents, and so on).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/agent-event&lt;/code&gt;: state pings (session started, tool call started, turn finished).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/approval-request&lt;/code&gt;: a permission request; the connection stays open until a human answers it in Calyx or the request expires.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What each CLI needs from Calyx&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The port and token.&lt;/li&gt;
&lt;li&gt;A way to say which pane it runs in.&lt;/li&gt;
&lt;li&gt;A way to report state.&lt;/li&gt;
&lt;li&gt;Optionally, a way to hand over permission decisions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pane identity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Calyx sets &lt;code&gt;CALYX_SURFACE_ID&lt;/code&gt; (the Ghostty surface) in the shell's environment before the CLI starts.&lt;/li&gt;
&lt;li&gt;In persistent-session panes it also sets &lt;code&gt;CALYX_SESSION_ID&lt;/code&gt; (the session, which survives surface re-creation).&lt;/li&gt;
&lt;li&gt;Each CLI gets those values into an HTTP header in its own way (section 2).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How the port and token travel&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MCP config is static: the URL and token are written as literals when the integration is enabled. Calyx keeps a running server alive across re-enables rather than restarting it, so the literals stay valid.&lt;/li&gt;
&lt;li&gt;Hook and plugin traffic is dynamic: the shell scripts read &lt;code&gt;~/Library/Application Support/Calyx/agent-endpoint.json&lt;/code&gt; on every invocation, and the JavaScript and TypeScript pieces cache it by modification time. A restart or a rotated token never leaves a hook pointing at a dead port.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Configuration files
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CLI&lt;/th&gt;
&lt;th&gt;Files Calyx writes&lt;/th&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;How Calyx edits it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.claude.json&lt;/code&gt; (MCP), &lt;code&gt;~/.claude/settings.json&lt;/code&gt; (hooks)&lt;/td&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;Key-level merge. Calyx's own hook entries are recognized by the script filename in &lt;code&gt;command&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.codex/config.toml&lt;/code&gt; (MCP and hooks)&lt;/td&gt;
&lt;td&gt;TOML&lt;/td&gt;
&lt;td&gt;MCP: replace the &lt;code&gt;[mcp_servers.calyx-ipc]&lt;/code&gt; section. Hooks: a &lt;code&gt;# BEGIN CALYX AGENT HOOKS&lt;/code&gt; / &lt;code&gt;# END CALYX AGENT HOOKS&lt;/code&gt; block at the end of the file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt; (MCP), &lt;code&gt;~/.config/opencode/AGENTS.md&lt;/code&gt; (instructions), &lt;code&gt;~/.config/opencode/plugins/calyx-agent-monitor.js&lt;/code&gt; (events)&lt;/td&gt;
&lt;td&gt;JSON, Markdown, JavaScript&lt;/td&gt;
&lt;td&gt;JSON: key-level merge. AGENTS.md: an HTML-comment marker block. Plugin: a file Calyx owns outright&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hermes&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.hermes/config.yaml&lt;/code&gt; (MCP)&lt;/td&gt;
&lt;td&gt;YAML&lt;/td&gt;
&lt;td&gt;A &lt;code&gt;# BEGIN CALYX IPC&lt;/code&gt; / &lt;code&gt;# END CALYX IPC&lt;/code&gt; block spliced under &lt;code&gt;mcp_servers:&lt;/code&gt; at the file's own indentation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.grok/config.toml&lt;/code&gt; (MCP), &lt;code&gt;~/.grok/hooks/calyx.json&lt;/code&gt; (hooks)&lt;/td&gt;
&lt;td&gt;TOML, JSON&lt;/td&gt;
&lt;td&gt;TOML: replace the &lt;code&gt;[mcp_servers.calyx-ipc]&lt;/code&gt; section. Hooks: a file Calyx owns outright&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.pi/agent/extensions/calyx.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;One file Calyx owns outright. No MCP entry and no hook entry, because pi has neither an MCP client nor a hook system&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Files Calyx owns outright
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;OpenCode's plugin directory, Grok's hooks directory, and pi's extensions directory each give Calyx a file of its own.&lt;/li&gt;
&lt;li&gt;Install is a write, removal is a delete. No markers, no merge.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  JSON (Claude Code, OpenCode)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Both Claude Code files and &lt;code&gt;opencode.json&lt;/code&gt; round-trip through a JSON parser.&lt;/li&gt;
&lt;li&gt;Calyx touches one key for MCP (&lt;code&gt;mcpServers.calyx-ipc&lt;/code&gt; or &lt;code&gt;mcp.calyx-ipc&lt;/code&gt;) and only its own entries under &lt;code&gt;hooks&lt;/code&gt;. Everything else comes back unchanged in meaning.&lt;/li&gt;
&lt;li&gt;If a hook event's existing value has a shape Calyx does not recognize, Calyx skips that event rather than overwriting it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  TOML (Codex, Grok)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;TOML has no primitive for "replace my entry inside your array".&lt;/li&gt;
&lt;li&gt;Codex MCP: the entry is a named table, so Calyx finds &lt;code&gt;[mcp_servers.calyx-ipc]&lt;/code&gt; and replaces everything up to the next table header.&lt;/li&gt;
&lt;li&gt;Codex hooks: &lt;code&gt;[[hooks.PreToolUse]]&lt;/code&gt; is an array of tables with no name to search for, so Calyx appends a comment-delimited block at the end of the file.&lt;/li&gt;
&lt;li&gt;Codex also writes to the file, and in practice it has placed its own tables between Calyx's markers. On removal Calyx extracts every table that is not positively its own (an exact &lt;code&gt;[[hooks.&amp;lt;Event&amp;gt;]]&lt;/code&gt; plus &lt;code&gt;[[hooks.&amp;lt;Event&amp;gt;.hooks]]&lt;/code&gt; pair whose command names a Calyx script) and re-appends it outside the block, verbatim.&lt;/li&gt;
&lt;li&gt;Grok MCP: same section replacement, with one stricter rule. A &lt;code&gt;[mcp_servers.calyx-ipc.headers]&lt;/code&gt; sub-table is always treated as part of Calyx's section, even with no parent header in scope, because two definitions of one TOML table are a parse error that costs the user every other MCP server in the file.&lt;/li&gt;
&lt;li&gt;Grok's MCP entry carries no ownership marker. The entry name is Calyx's own, so a hand-written &lt;code&gt;[mcp_servers.calyx-ipc]&lt;/code&gt; is adopted and rewritten into the managed shape rather than duplicated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  YAML (Hermes)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Calyx edits &lt;code&gt;config.yaml&lt;/code&gt; without a YAML parser, with a regex splice in two cases.&lt;/li&gt;
&lt;li&gt;No top-level &lt;code&gt;mcp_servers:&lt;/code&gt; key: the block is appended at the end carrying its own parent.&lt;/li&gt;
&lt;li&gt;Key present: only the &lt;code&gt;calyx-ipc:&lt;/code&gt; child is inserted, indented by the unit the file already uses for its other children.&lt;/li&gt;
&lt;li&gt;Refused rather than guessed: an inline &lt;code&gt;mcp_servers: {}&lt;/code&gt;, tab indentation, and control characters in a scalar all raise an error.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. MCP registration
&lt;/h2&gt;

&lt;p&gt;Every entry points at &lt;code&gt;http://127.0.0.1:&amp;lt;port&amp;gt;/mcp&lt;/code&gt; with &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; and two identity headers, &lt;code&gt;X-Calyx-Surface-ID&lt;/code&gt; and &lt;code&gt;X-Calyx-Session-ID&lt;/code&gt;. What differs is how a config file says "this header's value is this environment variable at connect time".&lt;/p&gt;

&lt;h3&gt;
  
  
  Claude Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"calyx-ipc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:41830/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer &amp;lt;token&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"X-Calyx-Surface-ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${CALYX_SURFACE_ID:-}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"X-Calyx-Session-ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${CALYX_SESSION_ID:-}"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Claude Code interpolates &lt;code&gt;${VAR}&lt;/code&gt; inside string values.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;:-&lt;/code&gt; empty default is required. A &lt;code&gt;${VAR}&lt;/code&gt; reference to an undefined variable fails the parse of the whole &lt;code&gt;~/.claude.json&lt;/code&gt;, which breaks MCP for every other terminal the user runs Claude Code in.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Codex
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.calyx-ipc]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://127.0.0.1:41830/mcp"&lt;/span&gt;
&lt;span class="py"&gt;http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"Authorization"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer &amp;lt;token&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;"X-Calyx-Agent-Kind"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"codex"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;env_http_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;"X-Calyx-Surface-ID"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CALYX_SURFACE_ID"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;"X-Calyx-Session-ID"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CALYX_SESSION_ID"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Codex takes the variable name rather than a placeholder string: &lt;code&gt;env_http_headers&lt;/code&gt; maps a header name to an environment variable name, next to the literal &lt;code&gt;http_headers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Measured on Codex 0.148.0: when the variable is unset, Codex omits the header entirely rather than sending it empty.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  OpenCode
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"calyx-ipc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"remote"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:41830/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer &amp;lt;token&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"X-Calyx-Surface-ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{env:CALYX_SURFACE_ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"X-Calyx-Session-ID"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{env:CALYX_SESSION_ID}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"X-Calyx-Agent-Kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"opencode"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Calyx writes a third syntax, &lt;code&gt;{env:VAR}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The transport is called &lt;code&gt;remote&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Hermes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;mcp_servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;calyx-ipc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://127.0.0.1:41830/mcp"&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;token&amp;gt;"&lt;/span&gt;
      &lt;span class="na"&gt;X-Calyx-Surface-ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${CALYX_SURFACE_ID}"&lt;/span&gt;
      &lt;span class="na"&gt;X-Calyx-Session-ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${CALYX_SESSION_ID}"&lt;/span&gt;
      &lt;span class="na"&gt;X-Calyx-Agent-Kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hermes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Calyx writes &lt;code&gt;${VAR}&lt;/code&gt; without a default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Grok
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.calyx-ipc]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://127.0.0.1:41830/mcp"&lt;/span&gt;

&lt;span class="nn"&gt;[mcp_servers.calyx-ipc.headers]&lt;/span&gt;
&lt;span class="py"&gt;Authorization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Bearer &amp;lt;token&amp;gt;"&lt;/span&gt;
&lt;span class="py"&gt;X-Calyx-Agent-Kind&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"grok"&lt;/span&gt;
&lt;span class="py"&gt;X-Calyx-Session-ID&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${CALYX_SESSION_ID:-}"&lt;/span&gt;
&lt;span class="py"&gt;X-Calyx-Surface-ID&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"${CALYX_SURFACE_ID:-}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Grok expands &lt;code&gt;${VAR}&lt;/code&gt; and &lt;code&gt;${VAR:-default}&lt;/code&gt; inside every string field of every &lt;code&gt;[mcp_servers.*]&lt;/code&gt; table, so no mapping table is needed.&lt;/li&gt;
&lt;li&gt;Headers go in a sub-table.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  pi
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;pi has no MCP client.&lt;/li&gt;
&lt;li&gt;The extension speaks JSON-RPC to &lt;code&gt;/mcp&lt;/code&gt; directly with &lt;code&gt;fetch&lt;/code&gt; and sends one &lt;code&gt;initialize&lt;/code&gt; at session start to bind the pane.&lt;/li&gt;
&lt;li&gt;Calyx's tools reach the model through a single dispatcher tool named &lt;code&gt;calyx&lt;/code&gt; that takes &lt;code&gt;{tool, args}&lt;/code&gt;. One tool instead of one per IPC tool, because pi puts every registered tool's name and description into the system prompt on every turn.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The agent kind header
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Codex, OpenCode, Hermes, and Grok entries carry &lt;code&gt;X-Calyx-Agent-Kind&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Claude Code's entry omits it. The hook routes default to &lt;code&gt;claude-code&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;/mcp&lt;/code&gt; route has no default kind, so a generic MCP client that has the URL and token cannot manufacture an agent row in the sidebar just by connecting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Event reporting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Three transports
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Transport&lt;/th&gt;
&lt;th&gt;CLIs&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Command hook, JSON on stdin&lt;/td&gt;
&lt;td&gt;Claude Code, Codex, Grok&lt;/td&gt;
&lt;td&gt;The CLI runs a program, writes the event as JSON to its stdin, reads its stdout. Calyx installs one shell script, &lt;code&gt;calyx-agent-hook&lt;/code&gt;, that reads the endpoint file, guards on the pane identity variables, and forwards stdin verbatim with &lt;code&gt;curl --data-binary @-&lt;/code&gt; to &lt;code&gt;/agent-event&lt;/code&gt;. Codex and Grok pass their name as an argument (&lt;code&gt;codex&lt;/code&gt;, &lt;code&gt;grok&lt;/code&gt;); with no argument the script assumes Claude Code. Every path ends in &lt;code&gt;exit 0&lt;/code&gt;, so a failed POST never breaks the user's hook chain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plugin API callback&lt;/td&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;OpenCode auto-loads any JavaScript file in &lt;code&gt;~/.config/opencode/plugins/&lt;/code&gt; under its Bun runtime, with no entry in &lt;code&gt;opencode.json&lt;/code&gt;. The plugin exports a function that receives the working directory and returns an &lt;code&gt;event&lt;/code&gt; handler, which maps OpenCode's events onto the command-hook event names and posts them with &lt;code&gt;fetch&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extension API&lt;/td&gt;
&lt;td&gt;pi&lt;/td&gt;
&lt;td&gt;A TypeScript file in &lt;code&gt;~/.pi/agent/extensions/&lt;/code&gt; exports a function that receives pi's extension API and registers handlers with &lt;code&gt;pi.on(event, handler)&lt;/code&gt;. Same posting shape as the OpenCode plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Hermes&lt;/td&gt;
&lt;td&gt;Calyx registers no hooks for Hermes. A Hermes row comes from the MCP connection itself; its working, blocked, and idle state comes from classifying the pane's on-screen text, the same way Calyx treats any agent it only knows through an MCP connection. The pane's own exit signal marks the row finished, when Command Tracking is on and the pane runs an interactive zsh or fish&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Events per CLI
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CLI&lt;/th&gt;
&lt;th&gt;Events Calyx registers&lt;/th&gt;
&lt;th&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SessionStart&lt;/code&gt;, &lt;code&gt;UserPromptSubmit&lt;/code&gt;, &lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, &lt;code&gt;Notification&lt;/code&gt; (matcher &lt;code&gt;permission_prompt&lt;/code&gt;), &lt;code&gt;Stop&lt;/code&gt;, &lt;code&gt;SessionEnd&lt;/code&gt;, &lt;code&gt;PermissionRequest&lt;/code&gt;, &lt;code&gt;SubagentStart&lt;/code&gt;, &lt;code&gt;SubagentStop&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;The same minus &lt;code&gt;Notification&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;session.created&lt;/code&gt;, &lt;code&gt;tool.execute.before&lt;/code&gt;, &lt;code&gt;tool.execute.after&lt;/code&gt;, &lt;code&gt;permission.asked&lt;/code&gt;, &lt;code&gt;permission.replied&lt;/code&gt;, &lt;code&gt;session.idle&lt;/code&gt;, &lt;code&gt;session.deleted&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hermes&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SessionStart&lt;/code&gt;, &lt;code&gt;UserPromptSubmit&lt;/code&gt;, &lt;code&gt;PreToolUse&lt;/code&gt;, &lt;code&gt;PostToolUse&lt;/code&gt;, &lt;code&gt;PostToolUseFailure&lt;/code&gt;, &lt;code&gt;Stop&lt;/code&gt;, &lt;code&gt;StopFailure&lt;/code&gt;, &lt;code&gt;StopCancelled&lt;/code&gt;, &lt;code&gt;SessionEnd&lt;/code&gt;, &lt;code&gt;Notification&lt;/code&gt; (matcher &lt;code&gt;permission_prompt&lt;/code&gt;), &lt;code&gt;SubagentStart&lt;/code&gt;, &lt;code&gt;SubagentStop&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pi&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;session_start&lt;/code&gt;, &lt;code&gt;before_agent_start&lt;/code&gt;, &lt;code&gt;tool_execution_end&lt;/code&gt;, &lt;code&gt;agent_end&lt;/code&gt;, &lt;code&gt;session_shutdown&lt;/code&gt;, plus &lt;code&gt;tool_call&lt;/code&gt; for the gate&lt;/td&gt;
&lt;td&gt;5 + 1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Per-CLI notes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Codex&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SessionEnd&lt;/code&gt; must be registered for a session to settle as finished. Without it the last event is &lt;code&gt;Stop&lt;/code&gt;, which only means the turn ended.&lt;/li&gt;
&lt;li&gt;Hook entries are TOML arrays of tables. The command is a TOML literal string (single quotes), so the double quotes around the script path need no escaping. A single quote in the script path is rejected up front.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;OpenCode&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1.18.18's plugin vocabulary has no process-exit event. &lt;code&gt;session.deleted&lt;/code&gt; fires only when a session is actually deleted, so the pane's own exit signal marks the row finished, when Command Tracking is on and the pane runs an interactive zsh or fish.&lt;/li&gt;
&lt;li&gt;The session identifier arrives under four spellings depending on the event: &lt;code&gt;sessionID&lt;/code&gt;, &lt;code&gt;session_id&lt;/code&gt;, &lt;code&gt;sessionId&lt;/code&gt;, &lt;code&gt;info.id&lt;/code&gt;. An event whose session cannot be resolved is dropped rather than guessed, because a guess could land on the wrong pane's row.&lt;/li&gt;
&lt;li&gt;The working directory comes from the plugin's own &lt;code&gt;directory&lt;/code&gt; argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Grok&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Payload keys are camelCase (&lt;code&gt;hookEventName&lt;/code&gt;, &lt;code&gt;sessionId&lt;/code&gt;, &lt;code&gt;toolName&lt;/code&gt;), event values are snake_case (&lt;code&gt;pre_tool_use&lt;/code&gt;), and the hook file uses PascalCase keys (&lt;code&gt;PreToolUse&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;/bin/sh&lt;/code&gt; hook script cannot rewrite JSON, so the script forwards Grok's payload untouched and Calyx's server normalizes it. Detection is on key presence: &lt;code&gt;hookEventName&lt;/code&gt; present, &lt;code&gt;hook_event_name&lt;/code&gt; absent.&lt;/li&gt;
&lt;li&gt;Mapping: &lt;code&gt;post_tool_use_failure&lt;/code&gt; to &lt;code&gt;PostToolUse&lt;/code&gt; (a failed call still means the turn is running); &lt;code&gt;stop_failure&lt;/code&gt; and &lt;code&gt;stop_cancelled&lt;/code&gt; to &lt;code&gt;Stop&lt;/code&gt;; &lt;code&gt;subagent_end&lt;/code&gt; accepted as an alias of &lt;code&gt;subagent_stop&lt;/code&gt;; &lt;code&gt;notification&lt;/code&gt; to &lt;code&gt;PermissionRequest&lt;/code&gt; only when &lt;code&gt;notificationType&lt;/code&gt; is &lt;code&gt;permission_prompt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Grok fires an observe-only &lt;code&gt;stop&lt;/code&gt; at teardown whose &lt;code&gt;reason&lt;/code&gt; is &lt;code&gt;channel_closed&lt;/code&gt; or &lt;code&gt;shutdown&lt;/code&gt;. A &lt;code&gt;stop&lt;/code&gt; whose reason is not &lt;code&gt;end_turn&lt;/code&gt; passes through unmapped.&lt;/li&gt;
&lt;li&gt;Grok defaults &lt;code&gt;Stop&lt;/code&gt; and &lt;code&gt;SubagentStop&lt;/code&gt; hooks to 600 seconds because those gates commonly run builds. Calyx pins its fire-and-forget entries there to 10 seconds to keep them off the turn's critical path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;pi&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event payloads carry neither a session identifier nor a working directory. The extension reads the session id from &lt;code&gt;ctx.sessionManager.getSessionId()&lt;/code&gt; and the directory from &lt;code&gt;ctx.cwd&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Permission prompts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Which event blocks
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CLI&lt;/th&gt;
&lt;th&gt;Blocking event&lt;/th&gt;
&lt;th&gt;When Calyx gates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PermissionRequest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every request the CLI raises&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PermissionRequest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every request the CLI raises&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;none used&lt;/td&gt;
&lt;td&gt;Never; &lt;code&gt;permission.asked&lt;/code&gt; and &lt;code&gt;permission.replied&lt;/code&gt; are forwarded as state only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hermes&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;Never; no hooks registered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PreToolUse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only under &lt;code&gt;bypassPermissions&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pi&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tool_call&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every tool call&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Claude Code and Codex&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PermissionRequest&lt;/code&gt; fires only when a tool call actually needs approval. &lt;code&gt;PreToolUse&lt;/code&gt; fires for every tool call, so gating on it would ask about calls the CLI was going to run anyway.&lt;/li&gt;
&lt;li&gt;The approval entry has a 600 second timeout and no &lt;code&gt;async&lt;/code&gt; flag. A separate 5 second entry carries the state ping; Claude Code's version of that entry additionally sets &lt;code&gt;async&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Grok&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grok has no &lt;code&gt;PermissionRequest&lt;/code&gt; event. &lt;code&gt;PreToolUse&lt;/code&gt; is its only blocking event, and it runs before Grok's own permission system rather than in place of it: a hook deny stops the call, but a hook allow, or no answer, falls through to Grok's own deny-or-ask rules, its remembered grants, and its in-pane prompt.&lt;/li&gt;
&lt;li&gt;Under Grok's &lt;code&gt;default&lt;/code&gt;, &lt;code&gt;auto&lt;/code&gt;, and &lt;code&gt;plan&lt;/code&gt; modes (names from Grok's hook documentation), a Calyx prompt would ask a question Grok is about to ask again, and Calyx's allow could not grant anything. Only under &lt;code&gt;bypassPermissions&lt;/code&gt; does the call run without anyone asking, so that is the one mode where Calyx gates.&lt;/li&gt;
&lt;li&gt;The hook entry carries no matcher and fires for every tool call in every mode. The filtering happens server-side on the payload's &lt;code&gt;permissionMode&lt;/code&gt;, which is why the hook script forwards stdin verbatim and carries no branch of its own.&lt;/li&gt;
&lt;li&gt;Both handlers are registered on &lt;code&gt;PreToolUse&lt;/code&gt;, state ping first, because Grok runs handlers in config order until one returns deny, and a state ping must not sit behind a gate that can block for minutes.&lt;/li&gt;
&lt;li&gt;Grok's default hook timeout is 5 seconds, so the gate entry writes &lt;code&gt;timeout: 600&lt;/code&gt; explicitly. Otherwise a gate waiting on a human would be killed at 5 seconds and fail open.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;pi&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;pi ships no permission prompt of its own; its &lt;code&gt;docs/usage.md&lt;/code&gt; says it intentionally does not include permission popups.&lt;/li&gt;
&lt;li&gt;The extension registers a &lt;code&gt;tool_call&lt;/code&gt; handler, the one place where returning &lt;code&gt;{ block: true, reason }&lt;/code&gt; stops a call. Nothing behind that gate would ever ask the user, so Calyx gates every pi tool call, and a pi request cannot be dismissed the way a Claude Code or Codex request can.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;OpenCode&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;permission.asked&lt;/code&gt; and &lt;code&gt;permission.replied&lt;/code&gt; are forwarded as state (&lt;code&gt;PermissionRequest&lt;/code&gt;, then &lt;code&gt;PostToolUse&lt;/code&gt;), so the row shows blocked while the prompt is open. OpenCode's own prompt answers the question.&lt;/li&gt;
&lt;li&gt;A session with an unanswered &lt;code&gt;permission.asked&lt;/code&gt; suppresses &lt;code&gt;session.idle&lt;/code&gt;, so an idle event arriving ahead of the reply cannot overwrite a blocked row with idle.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Response schemas
&lt;/h3&gt;

&lt;p&gt;Claude Code and Codex read a &lt;code&gt;hookSpecificOutput&lt;/code&gt; envelope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hookSpecificOutput"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hookEventName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PermissionRequest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"behavior"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allow"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Claude Code accepts the widest vocabulary under &lt;code&gt;decision&lt;/code&gt;: &lt;code&gt;behavior: "allow"&lt;/code&gt;; &lt;code&gt;allow&lt;/code&gt; with &lt;code&gt;updatedPermissions&lt;/code&gt;; &lt;code&gt;behavior: "deny"&lt;/code&gt; with &lt;code&gt;message&lt;/code&gt;; &lt;code&gt;deny&lt;/code&gt; with &lt;code&gt;message&lt;/code&gt; and &lt;code&gt;interrupt: true&lt;/code&gt;; &lt;code&gt;allow&lt;/code&gt; with &lt;code&gt;updatedInput&lt;/code&gt; for the &lt;code&gt;AskUserQuestion&lt;/code&gt; tool.&lt;/li&gt;
&lt;li&gt;Codex reads the same envelope but only &lt;code&gt;allow&lt;/code&gt;, or &lt;code&gt;deny&lt;/code&gt; with &lt;code&gt;message&lt;/code&gt;. It rejects the entire response if &lt;code&gt;updatedPermissions&lt;/code&gt;, &lt;code&gt;updatedInput&lt;/code&gt;, or &lt;code&gt;interrupt&lt;/code&gt; is present, so the Codex path never emits them.&lt;/li&gt;
&lt;li&gt;The field is &lt;code&gt;decision.behavior&lt;/code&gt;. The older &lt;code&gt;hookSpecificOutput.permissionDecision&lt;/code&gt; shape from &lt;code&gt;PreToolUse&lt;/code&gt; is not recognized by either CLI under &lt;code&gt;PermissionRequest&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Grok and pi read a flat object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"decision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Denied from the Calyx approval inbox."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Timeouts and silence
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hook-based approval paths have three timers, and their order is the invariant: the server gives up at 570 seconds, curl at 585, the hook entry at 600. Each inner layer fails before the layer outside it can.&lt;/li&gt;
&lt;li&gt;pi's extension has the inner two: the server's 570 seconds and its own fetch deadline at 585.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CLI&lt;/th&gt;
&lt;th&gt;Nobody answers&lt;/th&gt;
&lt;th&gt;Calyx unreachable&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;Empty body; the CLI's own prompt takes over&lt;/td&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;Empty body; the CLI's own prompt takes over&lt;/td&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;Explicit deny&lt;/td&gt;
&lt;td&gt;Grok's own pipeline runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pi&lt;/td&gt;
&lt;td&gt;Explicit deny&lt;/td&gt;
&lt;td&gt;The handler returns nothing and the call runs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;For Claude Code and Codex there is no &lt;code&gt;"ask"&lt;/code&gt; value in the vocabulary. Silence is the ask.&lt;/li&gt;
&lt;li&gt;The approval script treats curl's connection-refused exit as silent, so a stale endpoint file after a crash leaves the hook inert instead of turning every prompt into an error. &lt;code&gt;curl --fail&lt;/code&gt; routes any non-2xx response through the same silent path rather than printing it to the CLI as if it were a decision.&lt;/li&gt;
&lt;li&gt;Grok and pi have no fallback prompt in the situations where Calyx gates, so an unanswered request resolves to an explicit deny with its own reason text: "No approval was given in the Calyx approval inbox before the request expired."&lt;/li&gt;
&lt;li&gt;pi fails open when Calyx is unreachable because pi turns a thrown error into a block, and an unreachable Calyx must not become a pi that cannot run any tool at all.&lt;/li&gt;
&lt;li&gt;On no path, ever, does the approval script print &lt;code&gt;allow&lt;/code&gt; on its own.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cross-CLI bleed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Grok reads &lt;code&gt;~/.claude/settings.json&lt;/code&gt; for hooks by default, so Calyx's Claude Code hook entries also fire inside a Grok session. The approval script's stdout would become Grok's decision in a vocabulary Grok does not recognize.&lt;/li&gt;
&lt;li&gt;Both scripts exit immediately when invoked as &lt;code&gt;claude-code&lt;/code&gt; while Grok's &lt;code&gt;GROK_HOOK_EVENT&lt;/code&gt; variable is set.&lt;/li&gt;
&lt;li&gt;The user's own &lt;code&gt;[compat.*]&lt;/code&gt; settings in Grok's config are never touched.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Subagents
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CLI&lt;/th&gt;
&lt;th&gt;What arrives&lt;/th&gt;
&lt;th&gt;What the sidebar can show&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Claude Code&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SubagentStart&lt;/code&gt; and &lt;code&gt;SubagentStop&lt;/code&gt;, plus &lt;code&gt;agent_id&lt;/code&gt; on every event fired inside a subagent, including &lt;code&gt;PreToolUse&lt;/code&gt; and &lt;code&gt;PostToolUse&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Child rows with state and the current tool (the command, file path, or URL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codex&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SubagentStart&lt;/code&gt; and &lt;code&gt;SubagentStop&lt;/code&gt; carry &lt;code&gt;agent_id&lt;/code&gt;. Per Codex's documentation, the other hooks inside a subagent reuse the parent session's id&lt;/td&gt;
&lt;td&gt;Child rows with lifecycle state only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenCode&lt;/td&gt;
&lt;td&gt;No subagent events. &lt;code&gt;session.created&lt;/code&gt; carries &lt;code&gt;info.parentID&lt;/code&gt; for a child session; the plugin synthesizes &lt;code&gt;SubagentStart&lt;/code&gt;, rewrites that session's later events to carry &lt;code&gt;agent_id&lt;/code&gt; in place of &lt;code&gt;session_id&lt;/code&gt;, and posts &lt;code&gt;SubagentStop&lt;/code&gt; on &lt;code&gt;session.idle&lt;/code&gt; or &lt;code&gt;session.deleted&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Child rows with lifecycle state only; the synthesized child events carry no tool fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hermes&lt;/td&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;No child rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SubagentStart&lt;/code&gt; and &lt;code&gt;SubagentStop&lt;/code&gt; plus &lt;code&gt;subagentId&lt;/code&gt;. &lt;code&gt;subagent_start&lt;/code&gt; carries the parent's &lt;code&gt;sessionId&lt;/code&gt; and names the child only through &lt;code&gt;subagentId&lt;/code&gt;; every later child-scoped event carries the child's own &lt;code&gt;sessionId&lt;/code&gt; and no &lt;code&gt;subagentId&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Child rows with state and the current tool, read from camelCase &lt;code&gt;toolName&lt;/code&gt; and &lt;code&gt;toolInput&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pi&lt;/td&gt;
&lt;td&gt;Nothing&lt;/td&gt;
&lt;td&gt;No child rows&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code: a session started with &lt;code&gt;claude --agent foo&lt;/code&gt; sends &lt;code&gt;agent_type&lt;/code&gt; without &lt;code&gt;agent_id&lt;/code&gt; on its main thread. Deciding "this is a subagent event" from &lt;code&gt;agent_type&lt;/code&gt; would freeze that session's parent row forever. The only reliable signal is the presence of &lt;code&gt;agent_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Grok: a subagent has its own session, so its teardown arrives as &lt;code&gt;SessionEnd&lt;/code&gt; rather than &lt;code&gt;SubagentStop&lt;/code&gt;, and both names have to retire a child row. Session ids and working directories are stripped from child events so a later reattach never offers &lt;code&gt;grok --resume &amp;lt;child&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Guards outside Calyx
&lt;/h2&gt;

&lt;p&gt;All six integrations live in the user's global configuration, so they run in every session of that CLI, not only in Calyx.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shell scripts: exit 0 immediately when neither &lt;code&gt;CALYX_SURFACE_ID&lt;/code&gt; nor &lt;code&gt;CALYX_SESSION_ID&lt;/code&gt; is set. A &lt;code&gt;claude&lt;/code&gt; started in Terminal.app passes through the hook without contacting anything.&lt;/li&gt;
&lt;li&gt;OpenCode plugin and pi extension: register nothing at all when the variables are absent. For pi that matters, because a registered gate with no server behind it would block every tool call.&lt;/li&gt;
&lt;li&gt;Grok: the hooks directory is auto-trusted, and Grok merges every &lt;code&gt;.json&lt;/code&gt; file in it, so Calyx's file has a dedicated name that cannot collide with a user's own hook file. On a launch-time resync, a file at that path that names no Calyx script is treated as somebody else's and left alone.&lt;/li&gt;
&lt;li&gt;herdr: an agent multiplexer that can run inside a Calyx pane. Every shell it spawns inherits that pane's &lt;code&gt;CALYX_SURFACE_ID&lt;/code&gt;, so the value is non-empty but wrong for that shell. The scripts and the pi extension check &lt;code&gt;HERDR_PANE_ID&lt;/code&gt;, the one variable that reliably means "this shell is inside a herdr-managed pane", and stand down. herdr reports those agents to Calyx over its own Unix socket, a separate integration outside this post.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pin versions in code comments. Codex's header behavior and OpenCode's event vocabulary are facts of one measured version, and Claude Code's &lt;code&gt;PermissionRequest&lt;/code&gt; schema replaced an older &lt;code&gt;PreToolUse&lt;/code&gt; one. The comment that says "measured on 0.148.0" tells you which assumptions to re-check after an upgrade.&lt;/li&gt;
&lt;li&gt;"Hook" is three transports, not one. Stdin JSON, a plugin callback, and an extension API differ in where the process boundary is, and that decides what can go wrong: a shell script cannot transform JSON, a Bun plugin has no process-exit event, an extension's thrown error becomes a block.&lt;/li&gt;
&lt;li&gt;Decide what silence means before writing a gate. Where the CLI has its own prompt, an empty answer hands the question back. Where it does not, an empty answer is a decision, and it had better be deny.&lt;/li&gt;
&lt;li&gt;Do not derive subagent identity from the field that looks right. &lt;code&gt;agent_type&lt;/code&gt; looks like the signal in Claude Code; &lt;code&gt;agent_id&lt;/code&gt; is. &lt;code&gt;sessionId&lt;/code&gt; looks like the child's id in Grok's &lt;code&gt;subagent_start&lt;/code&gt;; it is the parent's.&lt;/li&gt;
&lt;li&gt;Guard every place the hook will run that is not your app. Global configuration means every session, including sessions started inside a tool that inherited your environment variables from a parent shell.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Source
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;https://github.com/yuuichieguchi/Calyx&lt;/a&gt; (MIT)&lt;/li&gt;
&lt;li&gt;Install: &lt;code&gt;brew install --cask calyx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>openai</category>
      <category>cli</category>
    </item>
    <item>
      <title>What it takes to embed libghostty in a Swift app: six months of lessons</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Mon, 14 Sep 2026 13:25:14 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/what-it-takes-to-embed-libghostty-in-a-swift-app-six-months-of-lessons-1m64</link>
      <guid>https://dev.to/yuu1ch13/what-it-takes-to-embed-libghostty-in-a-swift-app-six-months-of-lessons-1m64</guid>
      <description>&lt;p&gt;Six months ago I started &lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;Calyx&lt;/a&gt;, a native macOS terminal for running and supervising coding agents, on top of libghostty, the embeddable core of Ghostty (1.3.1 as I write this). Since then I have tagged 103 releases, and every one of them since v0.20.4 in mid-April runs on the same libghostty build. Almost all of the work happened above the FFI line.&lt;/p&gt;

&lt;p&gt;This post is the list of things I wish someone had written down before I started. It is not a tutorial. It is the set of surprises, in the order I would have wanted to hear about them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The build is easy. The coupling is not.
&lt;/h2&gt;

&lt;p&gt;Producing the framework is one command inside the ghostty checkout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zig build &lt;span class="nt"&gt;-Demit-xcframework&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;-Dxcframework-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;native &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Demit-macos-app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="nt"&gt;-Doptimize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ReleaseFast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of those flags, and the toolchain that runs the command, cost me real time.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-Doptimize=ReleaseFast&lt;/code&gt; was missing from my build recipe for the first month. Nothing broke. The terminal simply rendered heavy workloads, like image previews in yazi, noticeably slower than stock Ghostty.app. A user reported it. Rebuilding with ReleaseFast took &lt;code&gt;libghostty_zcu.o&lt;/code&gt; from 33 MB to 8.9 MB and removed every debug panic helper. A missing optimizer flag in a build script is a performance bug that no test catches.&lt;/p&gt;

&lt;p&gt;The Zig version is pinned by ghostty, not by you. &lt;code&gt;build.zig.zon&lt;/code&gt; declares &lt;code&gt;minimum_zig_version&lt;/code&gt;, and every ghostty update can move it. I keep several Zig versions installed side by side and assert the expected one in the update script. Treat the Zig toolchain as part of the submodule, not as part of your machine.&lt;/p&gt;

&lt;p&gt;Three more build facts that are worth knowing up front:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The xcframework you get with &lt;code&gt;-Dxcframework-target=native&lt;/code&gt; is arm64 only. The static archive is about 141 MB in ReleaseFast. I check it into the repo through git-lfs so that building Calyx never requires Zig, and the release build passes &lt;code&gt;ARCHS=arm64&lt;/code&gt; explicitly. Calyx is Apple Silicon only as a direct consequence.&lt;/li&gt;
&lt;li&gt;Xcode 26 no longer ships a &lt;code&gt;metallib&lt;/code&gt; binary, and ghostty's build looks for one. The fix is a two-line script on &lt;code&gt;PATH&lt;/code&gt;: &lt;code&gt;exec xcrun metal "$@"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Treat the &lt;code&gt;ghostty/&lt;/code&gt; submodule as read-only. I corrupted my repository twice by running git commands inside it. Now the update procedure has a human fetch the new tag, and every automated step refuses to touch that directory.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. The C API is small. The runtime config is the whole contract.
&lt;/h2&gt;

&lt;p&gt;libghostty exposes an app handle, a surface handle, a config handle, and one struct that defines how your host behaves: &lt;code&gt;ghostty_runtime_config_s&lt;/code&gt;. It has &lt;code&gt;userdata&lt;/code&gt;, a boolean for selection clipboard support, and six callbacks. In Swift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;runtimeConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghostty_runtime_config_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ghostty_runtime_config_s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Unmanaged&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;passUnretained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toOpaque&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nv"&gt;supports_selection_clipboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;wakeup_cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghosttyWakeupCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;action_cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghosttyActionCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;read_clipboard_cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghosttyReadClipboardCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;confirm_read_clipboard_cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghosttyConfirmReadClipboardCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;write_clipboard_cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghosttyWriteClipboardCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;close_surface_cb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghosttyCloseSurfaceCallback&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implement all six. The action callback alone carries 65 distinct action tags in the current header, from &lt;code&gt;set_title&lt;/code&gt; to &lt;code&gt;progress_report&lt;/code&gt;, so it is effectively the event bus of your application.&lt;/p&gt;

&lt;p&gt;Two structural decisions I would repeat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every C call lives in one file. Calyx has a &lt;code&gt;GhosttyFFI&lt;/code&gt; enum used as a namespace, 417 lines of thin static wrappers, and nothing else in the app calls &lt;code&gt;ghostty_*&lt;/code&gt; functions directly (with two functions I still call from the app delegate and owe myself a refactor for). When the header changes, one file changes.&lt;/li&gt;
&lt;li&gt;Do not use a bridging header. The xcframework ships a module map, so &lt;code&gt;import GhosttyKit&lt;/code&gt; is enough. If you also import &lt;code&gt;ghostty.h&lt;/code&gt; from a bridging header you get &lt;code&gt;error: redefinition of module 'GhosttyKit'&lt;/code&gt;. The one-line bridging header still sitting in my repo, no longer wired into the build, is a fossil from learning that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Swift 6 strict concurrency meets C callbacks
&lt;/h2&gt;

&lt;p&gt;Calyx is Swift 6.2 with strict concurrency, and every stateful type in the bridge is &lt;code&gt;@MainActor&lt;/code&gt; (the FFI namespace, the pure event translators, and the Metal layer subclass are nonisolated). C callbacks do not know what an actor is, so the boundary needs a few idioms.&lt;/p&gt;

&lt;p&gt;The callbacks themselves are file-level functions, never closures and never methods, so they can be passed as &lt;code&gt;@convention(c)&lt;/code&gt; pointers. &lt;code&gt;userdata&lt;/code&gt; carries the host object across the boundary both ways with &lt;code&gt;Unmanaged&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;appController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="nv"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UnsafeMutableRawPointer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;GhosttyAppController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;Unmanaged&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;GhosttyAppController&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromOpaque&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;takeUnretainedValue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is that the two important callbacks arrive differently, so they need different hops.&lt;/p&gt;

&lt;p&gt;The wakeup callback is called from an arbitrary thread and returns nothing, so it hops asynchronously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified from the source.&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;ghosttyWakeupCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;userdata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UnsafeMutableRawPointer&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;userdata&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;nonisolated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;ud&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userdata&lt;/span&gt;
    &lt;span class="kt"&gt;DispatchQueue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;GhosttyAppController&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ud&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The action callback is called on the main thread during &lt;code&gt;tick()&lt;/code&gt; and must return a &lt;code&gt;Bool&lt;/code&gt;, so it cannot be dispatched. It enters the main actor synchronously:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;ghosttyActionCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghostty_app_t&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghostty_target_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                   &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ghostty_action_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;nonisolated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unsafe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;safeApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;safeTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;safeAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kt"&gt;MainActor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assumeIsolated&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;GhosttyActionRouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;safeApp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;safeTarget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;safeAction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That asymmetry (async for a void callback, &lt;code&gt;assumeIsolated&lt;/code&gt; for one with a return value) is the concrete lesson. Everything else follows from it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In the bridge, &lt;code&gt;nonisolated(unsafe)&lt;/code&gt; is confined to C pointer bindings and to the handles you must free in &lt;code&gt;deinit&lt;/code&gt;, because &lt;code&gt;deinit&lt;/code&gt; is nonisolated. I wrote that rule down after I caught myself using it as a general escape hatch elsewhere in the app, where a few uses still remain.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@preconcurrency import AppKit&lt;/code&gt; where an AppKit protocol conformance needs it. &lt;code&gt;NSTextInputClient&lt;/code&gt; on the surface view is the one that forced it.&lt;/li&gt;
&lt;li&gt;Copy C strings into Swift strings inside the callback. The memory is not yours after you return.&lt;/li&gt;
&lt;li&gt;Keep C types out of &lt;code&gt;NotificationCenter&lt;/code&gt; userInfo dictionaries. Calyx converts enums to Swift values before posting. The one exception is the clipboard read confirmation, which has to carry libghostty's request pointer to the window controller that presents the sheet and completes the request later; the plain clipboard read completes in place inside the callback.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. The action callback's return value is not a formality
&lt;/h2&gt;

&lt;p&gt;Calyx fans every action out through &lt;code&gt;NotificationCenter&lt;/code&gt; under a &lt;code&gt;com.calyx.ghostty.*&lt;/code&gt; prefix, 37 names so far, so that windows, tabs, the agent sidebar, and the session layer can subscribe without the bridge knowing about them. That part is ordinary. The part that bit me is the &lt;code&gt;Bool&lt;/code&gt; you return.&lt;/p&gt;

&lt;p&gt;Returning &lt;code&gt;false&lt;/code&gt; tells libghostty that the action was not performed. For a keybind declared with the &lt;code&gt;performable:&lt;/code&gt; prefix, libghostty then acts as though no binding existed and the raw key sequence reaches the shell; for an ordinary keybind the event stays consumed either way. So for actions you deliberately do not implement, the return value depends on why:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified: the source has one case per action, each with its reason.&lt;/span&gt;
&lt;span class="c1"&gt;// GTK-only or touch-only actions: consumed on purpose, nothing to do.&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;GHOSTTY_ACTION_TOGGLE_TAB_OVERVIEW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="kt"&gt;GHOSTTY_ACTION_SHOW_GTK_INSPECTOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="kt"&gt;GHOSTTY_ACTION_SHOW_ON_SCREEN_KEYBOARD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of the 65 tags in the header, Calyx handles most, returns &lt;code&gt;false&lt;/code&gt; for 11 it has not implemented (8 listed explicitly and 3 that fall through to the default case), returns &lt;code&gt;true&lt;/code&gt; for 3 that only make sense on GTK or touch platforms, and still has 2 stubs (key sequences and mouse-over-link). Keep that table honest in your own router. A &lt;code&gt;true&lt;/code&gt; on an unimplemented action silently eats a performable keybind.&lt;/p&gt;

&lt;p&gt;Two timing traps in the same callback:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GHOSTTY_ACTION_INITIAL_SIZE&lt;/code&gt; and &lt;code&gt;CELL_SIZE&lt;/code&gt; fire synchronously from inside &lt;code&gt;ghostty_surface_new&lt;/code&gt;, before your window has registered any observers for its first surface. Calyx writes those values straight onto the view as properties and reads them back after the constructor returns (the cell size is also posted as a notification for the subscribers that exist later).&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;close_surface&lt;/code&gt; callback can re-enter your own teardown path synchronously. Any "should this kill the process" decision that lives in that path needs to be reentrancy-safe. I moved mine into a separate policy object after the second time it surprised me.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Rendering under Liquid Glass
&lt;/h2&gt;

&lt;p&gt;libghostty renders with Metal into a view you hand it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;mutableConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platform_tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;GHOSTTY_PLATFORM_MACOS&lt;/span&gt;
&lt;span class="n"&gt;mutableConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;macos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ghostty_platform_macos_s&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;nsview&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Unmanaged&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;passUnretained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;surfaceView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toOpaque&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The engine creates its own Metal device, command queues, and pipeline. Your &lt;code&gt;CAMetalLayer&lt;/code&gt; only provides the drawable. Three settings on that layer matter: &lt;code&gt;displaySyncEnabled = true&lt;/code&gt; (ghostty drives its own vsync through CVDisplayLink), &lt;code&gt;pixelFormat = .bgra8Unorm&lt;/code&gt;, and &lt;code&gt;isOpaque = false&lt;/code&gt;. That last one is the whole Liquid Glass story. Calyx does not tint the terminal with glass. The window is &lt;code&gt;isOpaque = false&lt;/code&gt; with a clear background, the SwiftUI root applies a &lt;code&gt;.glassEffect&lt;/code&gt; sheet, and the terminal is a transparent Metal layer sitting on top of it. The glass is behind the text, not in front of it.&lt;/p&gt;

&lt;p&gt;Two things follow from that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ghostty's &lt;code&gt;background-opacity&lt;/code&gt; only affects the default background. Cells that a TUI paints itself (status bars, panels) stay opaque unless you also set &lt;code&gt;background-opacity-cells&lt;/code&gt;. Calyx exposes that as a switch.&lt;/li&gt;
&lt;li&gt;Reduce Transparency has to be honored at every layer, not just the root: the root background becomes the window color, the atmosphere gradient renders nothing, and the terminal host layer becomes opaque. It took three separate branches to make the window fully opaque.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One SwiftUI lesson that has nothing to do with Metal and everything to do with hosting a live PTY: never let the terminal subtree sit inside an &lt;code&gt;if&lt;/code&gt;/&lt;code&gt;else&lt;/code&gt;. SwiftUI's &lt;code&gt;_ConditionalContent&lt;/code&gt; tears down and rebuilds whichever branch was active every time the condition flips, and "rebuild" means destroying and recreating live terminal surfaces. The root view shape in Calyx is fixed, and the conditions live in modifiers.&lt;/p&gt;

&lt;p&gt;Scrolling is also yours. libghostty reports &lt;code&gt;total&lt;/code&gt;, &lt;code&gt;offset&lt;/code&gt;, and &lt;code&gt;len&lt;/code&gt; through &lt;code&gt;GHOSTTY_ACTION_SCROLLBAR&lt;/code&gt;. Calyx puts an &lt;code&gt;NSScrollView&lt;/code&gt; with an empty document view on top of the surface, sized to the scrollback, and overrides &lt;code&gt;hitTest&lt;/code&gt; so only the scroller itself intercepts clicks. Smooth trackpad scrolling then has to mirror ghostty's own scroll accumulator to compute the sub-row pixel offset, and mouse events have to be compensated for that offset. The relevant file in ghostty is &lt;code&gt;Surface.zig&lt;/code&gt;; I keep the line numbers in a comment because I have had to re-read it more than once.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Config: libghostty loads it, you watch it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ghostty_config_load_default_files&lt;/code&gt; reads &lt;code&gt;~/.config/ghostty/config&lt;/code&gt; for you, &lt;code&gt;ghostty_config_load_recursive_files&lt;/code&gt; follows its &lt;code&gt;config-file&lt;/code&gt; includes, and &lt;code&gt;ghostty_config_open_path&lt;/code&gt; tells you which file it used. Reading the user's config is free. Two things are not.&lt;/p&gt;

&lt;p&gt;Hot reload is entirely the host's job. There is no file watcher in libghostty. Calyx uses &lt;code&gt;DispatchSourceFileSystemObject&lt;/code&gt; on the config path and its parent directory (so that editors which replace the file are caught), debounces into a reload coordinator with a monotonic generation counter, and keeps the last-known-good config when a reload fails to parse. It does not yet watch included files.&lt;/p&gt;

&lt;p&gt;Overriding is done by load order, not by editing. Calyx loads the user's files first, then its own glass preset from &lt;code&gt;~/.config/calyx/&lt;/code&gt;, then a runtime file that mirrors the Settings sliders, and only then calls &lt;code&gt;ghostty_config_finalize&lt;/code&gt;. Earlier versions wrote managed blocks into the user's ghostty config. That was a mistake, and the current version carries a cleanup routine that removes those blocks if it finds them. Do not write into a file the user thinks of as theirs.&lt;/p&gt;

&lt;p&gt;The list of keys Calyx overrides is a single array in code, rendered verbatim in the Settings window and asserted by tests, so the UI cannot drift from the behavior. Today it is &lt;code&gt;background-opacity&lt;/code&gt;, &lt;code&gt;background-blur&lt;/code&gt;, &lt;code&gt;background-opacity-cells&lt;/code&gt;, &lt;code&gt;font-codepoint-map&lt;/code&gt;, and, for non-Ghostty color presets, &lt;code&gt;foreground&lt;/code&gt; (computed from the luminance of the chrome tint so text stays legible against glass).&lt;/p&gt;

&lt;p&gt;Keybinds go both directions. A Ghostty keybind action reaches your action router like anything else, and &lt;code&gt;ghostty_surface_binding_action&lt;/code&gt; lets you invoke a keybind action by its string name from the host. Global keybinds need a CGEvent tap, gated on &lt;code&gt;ghostty_app_has_global_keybinds&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. The terminal tells you almost enough
&lt;/h2&gt;

&lt;p&gt;This is the section I would have paid for. libghostty surfaces a lot of terminal protocol as actions, and each one has a catch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OSC 7 (working directory)&lt;/strong&gt; arrives as &lt;code&gt;GHOSTTY_ACTION_PWD&lt;/code&gt;. It is the right source for "open a new split in the same directory", and it is also the only reliable cwd signal an agent sidebar can get without hooks of your own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Desktop notifications&lt;/strong&gt; (OSC 9 and OSC 777) arrive with a title and body. Decode them with &lt;code&gt;String(validatingCString:)&lt;/code&gt; so invalid UTF-8 degrades to an empty string instead of trapping, and sanitize bidi override characters and C0/C1 controls before the text reaches &lt;code&gt;UNUserNotificationCenter&lt;/code&gt;. Calyx also suppresses the banner when the tab is active and the window is key, and bounces the Dock only for the first unread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Progress reports&lt;/strong&gt; (OSC 9;4) arrive as a struct with a state (&lt;code&gt;remove&lt;/code&gt;, &lt;code&gt;set&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;indeterminate&lt;/code&gt;, &lt;code&gt;pause&lt;/code&gt;) and an &lt;code&gt;int8_t&lt;/code&gt; percentage, or &lt;code&gt;-1&lt;/code&gt; when none was reported. Calyx currently collapses that to a boolean for the agent status view and does not read the percentage yet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;COMMAND_FINISHED&lt;/code&gt;&lt;/strong&gt; only fires when the shell has ghostty's shell integration loaded. libghostty resolves its resources directory once at engine init, from &lt;code&gt;GHOSTTY_RESOURCES_DIR&lt;/code&gt; if it is set and otherwise by climbing from the running executable looking for a terminfo sentinel, and then injects the integration scripts into the shells it spawns. Two things follow for an embedder. Bundle the &lt;code&gt;terminfo&lt;/code&gt; and &lt;code&gt;shell-integration&lt;/code&gt; directories where that search finds them, or set the variable yourself before the engine initializes; Calyx now does both, and overwrites an inherited value on purpose, because the bundled scripts are version-matched to the embedded engine while a standalone Ghostty's are not. And any shell you spawn yourself gets no injection at all. Calyx's persistent-session daemon spawns its own shells, so it has to forward the variable and source the integration itself; until I did that in July, commands in persistent sessions never produced this action, and nothing told me why. Once it fires there is a second catch: a command whose OSC 133;D carried no exit code still arrives as &lt;code&gt;0&lt;/code&gt;, because ghostty reads the option with a default of zero before building the action, so success and "unknown" are indistinguishable. Calyx keeps its own zsh and fish hooks for the command log and uses the action only as a fallback signal for the shells those hooks do not cover.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;SHOW_CHILD_EXITED&lt;/code&gt;&lt;/strong&gt; on macOS gave me an &lt;code&gt;exit_code&lt;/code&gt; of &lt;code&gt;0&lt;/code&gt; every time in testing; only the duration field (spelled &lt;code&gt;timetime_ms&lt;/code&gt; in the header) was usable. When you need to know whether a process really died, ask something that knows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reading text back&lt;/strong&gt; through &lt;code&gt;ghostty_surface_read_text&lt;/code&gt; takes a selection with a coordinate tag and an &lt;code&gt;exact&lt;/code&gt;, &lt;code&gt;top_left&lt;/code&gt;, or &lt;code&gt;bottom_right&lt;/code&gt; mode. In the current embedded API an &lt;code&gt;exact&lt;/code&gt; y coordinate is clamped to the active grid's row count, not the history-inclusive total, so you cannot address a specific row deep in scrollback. What works: ask for the whole &lt;code&gt;screen&lt;/code&gt; region with &lt;code&gt;top_left&lt;/code&gt; and &lt;code&gt;bottom_right&lt;/code&gt;, then slice in Swift. It is a full dump every time, and for a captured command output that is acceptable. A row-ranged read would be the right upstream addition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor click-to-move&lt;/strong&gt; is implemented in ghostty as arrow-key steps over cells, so on lines of full-width text (Japanese, for example) the landing position can be off. I document it rather than fight it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mouse-over-link&lt;/strong&gt; is still a stub in Calyx. Cmd-click opening works through the &lt;code&gt;OPEN_URL&lt;/code&gt; action; the hover status label does not exist yet. I mention it because it is easy to imply more coverage than you have.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Persistent sessions: the PTY moves out, the engine stays
&lt;/h2&gt;

&lt;p&gt;Calyx's opt-in persistent sessions survive app quit and crashes, and they can live on SSH hosts. The design that made this work with libghostty is simple to state: the shell's PTY lives in a small Rust daemon, and libghostty renders a client process that attaches to it. The seam is one field in the surface config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withCString&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cmdCStr&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;mutableConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
    &lt;span class="n"&gt;mutableConfig&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmdCStr&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of the user's shell, the surface runs &lt;code&gt;calyx-session attach --create&lt;/code&gt;. libghostty still owns a PTY, the one running the attach client, and the daemon owns the real one. The host also passes the already-laid-out grid size to the attach command, otherwise the embedded engine's bootstrap grid becomes the new shell's initial PTY size.&lt;/p&gt;

&lt;p&gt;Two things about this were not obvious.&lt;/p&gt;

&lt;p&gt;The daemon embeds ghostty too. To replay a session's screen state to a reattaching client, the daemon keeps a VT terminal per session, and that terminal is ghostty's own VT engine, built as a static library through a tiny Zig shim that path-depends on the same submodule. One submodule, two consumers, two toolchains: an arm64 xcframework for the app and a per-target static library for the Rust daemon, including a musl target for remote payloads.&lt;/p&gt;

&lt;p&gt;The daemon cannot be a child of the app. A double-forked daemon inherits the spawning app's jetsam coalition, and once the app quits, the daemon's process context can no longer reach opendirectoryd. From that point &lt;code&gt;getpwuid&lt;/code&gt; fails for every shell it spawns, and users lose their username: &lt;code&gt;%n&lt;/code&gt; in zsh, &lt;code&gt;$USER&lt;/code&gt;, &lt;code&gt;$LOGNAME&lt;/code&gt;. The fix is a launchd LaunchAgent, loaded on demand, with &lt;code&gt;NumberOfFiles&lt;/code&gt; raised to 4096 because the default soft limit of 256 is not enough for a daemon holding a PTY master, a wake pipe, and a history fd per session. And launchd's job environment is minimal: &lt;code&gt;HOME&lt;/code&gt;, &lt;code&gt;PATH&lt;/code&gt;, &lt;code&gt;SHELL&lt;/code&gt;, &lt;code&gt;USER&lt;/code&gt;, and a few XPC variables, but no &lt;code&gt;TERM&lt;/code&gt;, no &lt;code&gt;LANG&lt;/code&gt;, and no &lt;code&gt;GHOSTTY_*&lt;/code&gt;, so the attach path has to re-supply all of that per session.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Shipping it
&lt;/h2&gt;

&lt;p&gt;Three release-engineering lessons, all of which cost a broken release.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sparkle must be re-signed inside out.&lt;/strong&gt; Flat re-signing of the framework binary passed &lt;code&gt;codesign --verify&lt;/code&gt; and failed Gatekeeper. The sequence that works is XPC services, then nested apps, then standalone executables, then the framework, then the outer app again, because re-signing anything inside invalidates the outer seal. The release script also whitelists the framework's root entries and fails on anything unexpected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cargo binaries are only ad-hoc signed.&lt;/strong&gt; The outer app's signature does not re-sign nested Mach-O files, so the Rust daemon needs an explicit Developer ID signature with hardened runtime and a timestamp, or notarization rejects the bundle. The Linux payloads for remote hosts are ELF; &lt;code&gt;codesign&lt;/code&gt; cannot sign them and seals them as plain resources, which is fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Re-zip after stapling, then sign the zip.&lt;/strong&gt; Notarize the zip, staple the ticket to the app, create the final zip again, and only then run Sparkle's &lt;code&gt;sign_update&lt;/code&gt;. The EdDSA signature must describe the artifact people download.&lt;/p&gt;

&lt;p&gt;And one that I learned from Homebrew's source, not from my own. &lt;code&gt;ditto -c -k --keepParent&lt;/code&gt; stores extended attributes as AppleDouble &lt;code&gt;._&lt;/code&gt; entries next to each file. Xcode-built binaries carry &lt;code&gt;com.apple.provenance&lt;/code&gt;, so my zips contain 169 of them. Extract that zip with Info-ZIP's &lt;code&gt;unzip&lt;/code&gt; and the &lt;code&gt;._&lt;/code&gt; files land inside the bundle, the resource seal fails, and Gatekeeper calls the app damaged. Extract it with &lt;code&gt;ditto -x -k&lt;/code&gt; and everything is fine. Homebrew checks &lt;code&gt;zipinfo&lt;/code&gt; for &lt;code&gt;._&lt;/code&gt; entries and switches to &lt;code&gt;ditto&lt;/code&gt; when it finds them, which is the only reason &lt;code&gt;brew install --cask calyx&lt;/code&gt; works. If you distribute zips, know how your users extract them.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. What I would tell you before you start
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pin the Zig toolchain to ghostty's &lt;code&gt;minimum_zig_version&lt;/code&gt; and keep old versions around. Assert it in your update script.&lt;/li&gt;
&lt;li&gt;Build with &lt;code&gt;-Doptimize=ReleaseFast&lt;/code&gt; from day one and check the archive size.&lt;/li&gt;
&lt;li&gt;Check the xcframework into LFS so contributors and CI never need Zig.&lt;/li&gt;
&lt;li&gt;Centralize every C call in one file. Import the module; do not use a bridging header.&lt;/li&gt;
&lt;li&gt;Make the bridge &lt;code&gt;@MainActor&lt;/code&gt;, hop wakeups asynchronously, enter actions with &lt;code&gt;assumeIsolated&lt;/code&gt;, and confine &lt;code&gt;nonisolated(unsafe)&lt;/code&gt; to pointer bindings and &lt;code&gt;deinit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Return &lt;code&gt;false&lt;/code&gt; from the action callback only when you really want a performable keybind to reach the shell.&lt;/li&gt;
&lt;li&gt;Expect &lt;code&gt;INITIAL_SIZE&lt;/code&gt; and &lt;code&gt;CELL_SIZE&lt;/code&gt; before your observers exist.&lt;/li&gt;
&lt;li&gt;Bundle ghostty's resources where its search finds them, or set &lt;code&gt;GHOSTTY_RESOURCES_DIR&lt;/code&gt; in-process before engine init, and source the shell integration yourself in any shell you spawn.&lt;/li&gt;
&lt;li&gt;Treat exit codes from the engine as hints, not facts.&lt;/li&gt;
&lt;li&gt;Verify your release artifact the way your users extract it, not the way you do.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About Calyx
&lt;/h2&gt;

&lt;p&gt;Calyx is a native macOS terminal (Swift 6.2, AppKit and SwiftUI, MIT) for running and supervising coding agents in parallel: an approval inbox for Claude Code and Codex permission prompts and for Grok and pi tool calls, an agent status sidebar driven by each CLI's own hooks, in-terminal diff review with line comments sent back to the agent, MCP tools that let agents read command output and language-server results, and the persistent sessions described above. It requires macOS 26 on Apple Silicon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; calyx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;https://github.com/yuuichieguchi/Calyx&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://help.getcalyx.app" rel="noopener noreferrer"&gt;https://help.getcalyx.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to Mitchell Hashimoto and the Ghostty contributors for making the engine embeddable in the first place. Everything above is a list of details; the fact that a solo developer can ship a GPU-rendered, VT-correct terminal as one component of a larger app is the headline.&lt;/p&gt;

</description>
      <category>ghostty</category>
      <category>swift</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Calyx vs cmux: Choosing the Right Ghostty-Based Terminal for macOS 26</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Sun, 08 Mar 2026 15:56:53 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/calyx-vs-cmux-choosing-the-right-ghostty-based-terminal-for-macos-26-28e7</link>
      <guid>https://dev.to/yuu1ch13/calyx-vs-cmux-choosing-the-right-ghostty-based-terminal-for-macos-26-28e7</guid>
      <description>&lt;h2&gt;
  
  
  The Ghostty Terminal Race
&lt;/h2&gt;

&lt;p&gt;With the rise of AI coding agents in 2026, terminals are no longer just text windows -- they're command centers. Two Ghostty-based macOS terminals have emerged as frontrunners: &lt;strong&gt;cmux&lt;/strong&gt; and &lt;strong&gt;Calyx&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Both leverage Ghostty's GPU-accelerated rendering, but they take very different approaches to what a modern terminal should be. Let's break down why Calyx might be the better choice for your daily workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is cmux?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/manaflow-ai/cmux" rel="noopener noreferrer"&gt;cmux&lt;/a&gt; by manaflow-ai is a terminal built for AI coding agents. It features vertical tabs, notification rings, split panes, a socket API, and deep integration with tools like Claude Code, Codex, and Aider.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Calyx?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;Calyx&lt;/a&gt; is a macOS 26+ native terminal that prioritizes daily-driver polish: a beautiful Liquid Glass UI, robust session management, intuitive tab organization, and security-conscious browser integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Head-to-Head Comparison
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Native macOS 26 Design
&lt;/h3&gt;

&lt;p&gt;Calyx is built from the ground up for macOS 26's &lt;strong&gt;Liquid Glass&lt;/strong&gt; design language. Glass Material is applied to the sidebar, tab bar, command palette, and browser toolbar, making Calyx feel like a first-class citizen on your Mac. It also respects the "Reduce Transparency" accessibility setting, automatically falling back to a solid UI for users who need it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Far32z3u83se7uo8dn75n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Far32z3u83se7uo8dn75n.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;cmux uses a functional UI but doesn't adopt macOS 26's design system. If you care about visual consistency with the rest of your OS, Calyx wins here.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Tab Groups vs Vertical Tabs
&lt;/h3&gt;

&lt;p&gt;cmux offers vertical tabs with useful metadata like git branch, PR status, and listening ports in the sidebar.&lt;/p&gt;

&lt;p&gt;Calyx takes tab management further with &lt;strong&gt;tab groups&lt;/strong&gt;. Organize tabs by project or context with 10 color presets. Groups are collapsible in the sidebar, support tab reordering within groups, and show tab counts per group. Double-click a group header to rename it, close individual tabs or an entire group directly from the sidebar. Working on a full-stack app? Group your frontend, backend, and infrastructure tabs separately. With dozens of tabs open, you'll always know where you are.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Split Panes: Stability Matters
&lt;/h3&gt;

&lt;p&gt;Both terminals support split panes, but stability differs significantly.&lt;/p&gt;

&lt;p&gt;cmux has &lt;a href="https://github.com/manaflow-ai/cmux/issues/435" rel="noopener noreferrer"&gt;known rendering issues&lt;/a&gt; where terminal panes fail to render after closing splits. Keyboard shortcuts also reportedly break after opening browser tabs.&lt;/p&gt;

&lt;p&gt;Calyx's splits are backed by an &lt;strong&gt;immutable binary tree data structure&lt;/strong&gt; (&lt;code&gt;SplitTree&lt;/code&gt;) with horizontal/vertical splitting, drag-to-resize with a 50px minimum pane guarantee, equalize operation, and spatial focus navigation (left/right/up/down). Tab switching uses only setFocus + refresh, keeping the render loop stable by design. No phantom blank panes, no lost keyboard input.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Command Palette
&lt;/h3&gt;

&lt;p&gt;Hit &lt;code&gt;Cmd+Shift+P&lt;/code&gt; in Calyx and you get a VS Code-style command palette with &lt;strong&gt;fuzzy search&lt;/strong&gt; across 15 built-in commands. The scoring system combines word-start bonus, consecutive match bonus, exact match bonus, and prefix match bonus for accurate results. Command usage frequency is tracked, so your most-used commands rise to the top over time.&lt;/p&gt;

&lt;p&gt;The shortcut system is built on a low-level NSEvent monitor that detects IME compose state. &lt;strong&gt;CJK input never triggers shortcuts accidentally&lt;/strong&gt;. Text fields and text views are also properly excluded.&lt;/p&gt;

&lt;p&gt;cmux doesn't have a command palette.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Session Persistence
&lt;/h3&gt;

&lt;p&gt;Quit Calyx and reopen it. Your &lt;strong&gt;tabs, tab groups, split layouts, browser URLs, and window positions&lt;/strong&gt; are fully restored.&lt;/p&gt;

&lt;p&gt;Under the hood, Calyx implements atomic file writes (temp file + rename), automatic backup rotation, 2-second debounced auto-save, schema versioning (currently v3) with migration support, crash loop detection (max 3 recovery attempts), and window frame clamping to screen bounds. If the primary session file is corrupted, it automatically recovers from the backup.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Notification System
&lt;/h3&gt;

&lt;p&gt;Both terminals support notifications, but Calyx emphasizes security and reliability.&lt;/p&gt;

&lt;p&gt;Calyx relays OSC notifications to &lt;code&gt;UNUserNotificationCenter&lt;/code&gt; with per-tab unread badges (circle in sidebar, capsule in tab bar, capped at 99+). It implements &lt;strong&gt;rate limiting&lt;/strong&gt; (max 5 per second per tab), Dock icon bounce on first unread, and automatic badge clearing on window focus.&lt;/p&gt;

&lt;p&gt;Notification text is sanitized with bidi override removal, control character filtering, zero-width character removal, newline normalization, NFC normalization, and truncation at 256 grapheme clusters, preventing malicious terminal sequences from becoming attack vectors.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Browser Integration That Works
&lt;/h3&gt;

&lt;p&gt;Both terminals include a built-in browser. However, cmux has reported issues with &lt;a href="https://github.com/manaflow-ai/cmux/issues/180" rel="noopener noreferrer"&gt;HTTP connections being blocked&lt;/a&gt; and &lt;a href="https://github.com/manaflow-ai/cmux/issues/251" rel="noopener noreferrer"&gt;forced dark mode on all pages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Calyx's browser uses a &lt;strong&gt;non-persistent WebKit data store&lt;/strong&gt; for privacy, with back/forward/reload navigation, a loading indicator, error banners, and JavaScript dialog support (alert, confirm, prompt).&lt;/p&gt;

&lt;p&gt;Security is comprehensive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;URL scheme validation (only http/https/about at top-level)&lt;/li&gt;
&lt;li&gt;Subresource scheme blocking (javascript, data)&lt;/li&gt;
&lt;li&gt;Redirect depth limiting (max 10)&lt;/li&gt;
&lt;li&gt;Download filename sanitization (null byte stripping, dangerous character replacement, dangerous extension blocking, 255-char limit)&lt;/li&gt;
&lt;li&gt;MIME type blocking for dangerous executables&lt;/li&gt;
&lt;li&gt;Symlink traversal prevention on download paths&lt;/li&gt;
&lt;li&gt;Automatic macOS Quarantine attribute on downloaded files&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  8. Licensing
&lt;/h3&gt;

&lt;p&gt;Calyx's MIT license places no restrictions on commercial use or modification. cmux's AGPL requires source code disclosure if you provide the software as a network service, a potential blocker for enterprise adoption.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Git Source Control
&lt;/h3&gt;

&lt;p&gt;Calyx includes a &lt;strong&gt;built-in Git source control view&lt;/strong&gt; directly in the sidebar. Switch to the "Changes" mode and you get a complete overview of your repository's state without leaving the terminal.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Working Changes&lt;/strong&gt;: Files grouped into Staged / Unstaged / Untracked sections with status badges (M/A/D/R/C/?/U/T)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit Graph&lt;/strong&gt;: Color-coded branch lane visualization with commit history. Click to expand and see changed files per commit. Infinite scroll pagination for large repositories&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline Diff Viewer&lt;/strong&gt;: Click any file to open a diff tab with color-coded additions (green), deletions (red), and hunk headers (cyan). Dual old/new line number gutter. Binary file detection and automatic truncation for diffs over 1MB or 50,000 lines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Diff Review Comments&lt;/strong&gt;: Click the &lt;code&gt;+&lt;/code&gt; button next to any line number to add an inline comment. Submit Review sends the formatted review directly to a Claude Code or Codex terminal tab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No more switching to VS Code just to review what Claude Code changed. It's all inside Calyx.&lt;/p&gt;

&lt;p&gt;cmux doesn't have this feature.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. AI Agent IPC (Inter-Process Communication)
&lt;/h3&gt;

&lt;p&gt;Calyx includes a &lt;strong&gt;built-in MCP server&lt;/strong&gt; that enables AI agent instances (Claude Code, Codex CLI) to communicate with each other across tabs and panes.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/Xty0ad9gGcM"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Just run "Enable AI Agent IPC" from the command palette, and every agent instance in Calyx can discover peers, send messages, broadcast announcements, and check for incoming messages via 7 MCP tools. Config is auto-written to &lt;code&gt;~/.claude.json&lt;/code&gt; and &lt;code&gt;~/.codex/config.toml&lt;/code&gt; when the respective tool is installed. Thanks to the MCP spec &lt;code&gt;instructions&lt;/code&gt; field, agents automatically register as peers upon connecting.&lt;/p&gt;

&lt;p&gt;Calyx's MCP-based IPC is purpose-built for multi-agent collaboration across Claude Code and Codex.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Scriptable Browser
&lt;/h3&gt;

&lt;p&gt;cmux pioneered browser automation with 80+ CLI commands for controlling WKWebView. Calyx now matches this with &lt;strong&gt;25 CLI commands&lt;/strong&gt; for browser scripting: snapshot accessibility trees, click elements, fill forms, evaluate JavaScript, take screenshots, wait for conditions, inspect DOM attributes, list links and inputs, check visibility, hover, and scroll.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;calyx&lt;/code&gt; CLI binary is bundled inside the app. Run &lt;strong&gt;Install CLI to PATH&lt;/strong&gt; from the command palette to make it available system-wide.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;calyx browser list
calyx browser snapshot &lt;span class="nt"&gt;--tab-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
calyx browser get-text h1 &lt;span class="nt"&gt;--tab-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
calyx browser click a &lt;span class="nt"&gt;--tab-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
calyx browser &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s1"&gt;'document.title'&lt;/span&gt; &lt;span class="nt"&gt;--tab-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
calyx browser get-attribute a href         &lt;span class="c"&gt;# Get element attribute&lt;/span&gt;
calyx browser get-links                    &lt;span class="c"&gt;# List all links (JSON)&lt;/span&gt;
calyx browser get-inputs                   &lt;span class="c"&gt;# List form inputs (JSON)&lt;/span&gt;
calyx browser is-visible &lt;span class="s1"&gt;'#el'&lt;/span&gt;             &lt;span class="c"&gt;# Check visibility&lt;/span&gt;
calyx browser hover &lt;span class="s1"&gt;'#menu'&lt;/span&gt;                &lt;span class="c"&gt;# Hover element&lt;/span&gt;
calyx browser scroll down &lt;span class="nt"&gt;--amount&lt;/span&gt; 500     &lt;span class="c"&gt;# Scroll page/element&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Element refs (&lt;code&gt;@e1&lt;/code&gt;, &lt;code&gt;@e2&lt;/code&gt;) from snapshots can be used as selectors in subsequent commands, enabling reliable DOM automation even on dynamic pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature Matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Calyx&lt;/th&gt;
&lt;th&gt;cmux&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Liquid Glass UI&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tab Groups (color-coded)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (vertical tabs only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split Panes&lt;/td&gt;
&lt;td&gt;Yes, stable&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/manaflow-ai/cmux/issues/435" rel="noopener noreferrer"&gt;Rendering bugs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Command Palette&lt;/td&gt;
&lt;td&gt;Yes (fuzzy search + frequency)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session Persistence&lt;/td&gt;
&lt;td&gt;Yes (schema v3 + crash recovery)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser Integration&lt;/td&gt;
&lt;td&gt;Yes (security validation + downloads)&lt;/td&gt;
&lt;td&gt;No HTTP, forced dark mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notifications&lt;/td&gt;
&lt;td&gt;Yes (rate-limited + sanitized)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CJK Input Handling&lt;/td&gt;
&lt;td&gt;Yes (IME-aware shortcuts)&lt;/td&gt;
&lt;td&gt;Unknown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-Window&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Git Source Control&lt;/td&gt;
&lt;td&gt;Yes (commit graph + diff viewer)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Agent IPC (MCP)&lt;/td&gt;
&lt;td&gt;Yes (7 tools, auto-register, Claude Code + Codex)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scriptable Browser&lt;/td&gt;
&lt;td&gt;Yes (25 CLI commands)&lt;/td&gt;
&lt;td&gt;Yes (80+ CLI commands)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;AGPL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;cmux is a solid terminal with a sharp focus on AI agent workflows.&lt;/p&gt;

&lt;p&gt;But as a &lt;strong&gt;daily-driver terminal&lt;/strong&gt;, Calyx offers a more polished and reliable experience: a beautiful native Liquid Glass UI, intuitive color-coded tab groups, stable split panes backed by immutable data structures, a fuzzy-search command palette with frequency learning, crash-resilient session persistence, a scriptable browser with 25 CLI commands, rate-limited and sanitized notifications, proper CJK input handling, and a permissive MIT license.&lt;/p&gt;

&lt;p&gt;If you're on macOS 26, give &lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;Calyx&lt;/a&gt; a try.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/yuuichieguchi/Calyx" rel="noopener noreferrer"&gt;Calyx on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/manaflow-ai/cmux" rel="noopener noreferrer"&gt;cmux on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>terminal</category>
      <category>ghostty</category>
      <category>devtools</category>
    </item>
    <item>
      <title>d3motap3 — A Multi-Source Video Recorder for macOS That Captures Phone + PC Screens Simultaneously</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Wed, 04 Mar 2026 12:49:31 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/d3motap3-a-multi-source-video-recorder-for-macos-that-captures-phone-pc-screens-simultaneously-3a4c</link>
      <guid>https://dev.to/yuu1ch13/d3motap3-a-multi-source-video-recorder-for-macos-that-captures-phone-pc-screens-simultaneously-3a4c</guid>
      <description>&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;When recording tool demos, I often needed to capture a phone screen and a PC screen at the same time. In practice, that meant recording them separately and syncing them up in post to make it look simultaneous.&lt;/p&gt;

&lt;p&gt;Another pain point was re-recording every time I made a typo or missed a step. Doing that over and over was genuinely frustrating.&lt;/p&gt;

&lt;p&gt;I built d3motap3 to solve both of these problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What It Does
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Capture Up to 2 Sources at Once
&lt;/h3&gt;

&lt;p&gt;Phone and display, terminal and webcam — any combination in side-by-side or picture-in-picture.&lt;/p&gt;

&lt;p&gt;There are 7 source types to choose from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Full display&lt;/li&gt;
&lt;li&gt;Individual window&lt;/li&gt;
&lt;li&gt;Screen region&lt;/li&gt;
&lt;li&gt;Webcam&lt;/li&gt;
&lt;li&gt;Built-in terminal&lt;/li&gt;
&lt;li&gt;iOS device (USB)&lt;/li&gt;
&lt;li&gt;Android device (USB)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  YAML Scripted Automation
&lt;/h3&gt;

&lt;p&gt;Define your demo steps in YAML and it records itself. Each step waits for the previous one to finish, so timing is always perfect. Define it once, get a perfect recording every time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terminal_input&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;install"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wait_for_output&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;added&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;.*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;packages"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;terminal_input&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;start"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wait_for_output&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Server&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;running"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Timeline Editor
&lt;/h3&gt;

&lt;p&gt;Edit your recordings with the built-in editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clip splitting&lt;/li&gt;
&lt;li&gt;Transitions (fade / dissolve / wipe)&lt;/li&gt;
&lt;li&gt;Text overlays with animation&lt;/li&gt;
&lt;li&gt;Audio track import and editing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  AI Script Generation
&lt;/h3&gt;

&lt;p&gt;Describe your demo in plain text and get a YAML script generated via Claude API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in Terminal Rendered in Rust
&lt;/h3&gt;

&lt;p&gt;The built-in terminal isn't a screenshot — it's rendered directly by Rust via PTY → alacritty VT parser → cosmic-text. The terminal itself becomes a capture source, so there's no need to window-capture an external terminal app.&lt;/p&gt;

&lt;h3&gt;
  
  
  3 Layouts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Single — one source, full frame&lt;/li&gt;
&lt;li&gt;Side-by-Side — two sources next to each other (adjustable split ratio)&lt;/li&gt;
&lt;li&gt;Picture-in-Picture — a small overlay on top of the main source (adjustable position and size)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Platform
&lt;/h2&gt;

&lt;p&gt;macOS only (uses ScreenCaptureKit). Distributed as a signed and notarized DMG.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/yuuichieguchi/d3motap3" rel="noopener noreferrer"&gt;https://github.com/yuuichieguchi/d3motap3&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Download: &lt;a href="https://github.com/yuuichieguchi/d3motap3/releases" rel="noopener noreferrer"&gt;https://github.com/yuuichieguchi/d3motap3/releases&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>rust</category>
      <category>electron</category>
    </item>
    <item>
      <title>Approve Claude Code Permission Requests from Your Phone</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Sun, 22 Feb 2026 06:43:06 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/approve-claude-code-permission-requests-from-your-phone-47nd</link>
      <guid>https://dev.to/yuu1ch13/approve-claude-code-permission-requests-from-your-phone-47nd</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Claude Code asks for permission before running tools like &lt;code&gt;Bash&lt;/code&gt;, &lt;code&gt;Write&lt;/code&gt;, and &lt;code&gt;Edit&lt;/code&gt;. This is a great safety feature — but it means you have to be sitting at your terminal to press "y".&lt;/p&gt;

&lt;p&gt;Step away for coffee? Claude Code stalls until you get back.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/yuuichieguchi/claude-remote-approver" rel="noopener noreferrer"&gt;claude-remote-approver&lt;/a&gt;&lt;/strong&gt; sends each permission request as a push notification to your phone via &lt;a href="https://ntfy.sh" rel="noopener noreferrer"&gt;ntfy.sh&lt;/a&gt;. Tap &lt;strong&gt;Approve&lt;/strong&gt; or&lt;br&gt;
  &lt;strong&gt;Deny&lt;/strong&gt;, and Claude Code continues immediately — no terminal required.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Claude Code
    │
    │  PermissionRequest hook (stdin JSON)
    ▼
  claude-remote-approver hook
    │
    ├──POST──▶ ntfy.sh/          ──push──▶  Phone (ntfy app)
    │                                                 │
    │                                           Approve / Deny tap
    │                                                 │
    └──SSE───▶ ntfy.sh/-response  ◀──POST──┘
    │
    │  stdout JSON: { "behavior": "allow" } or { "behavior": "deny" }
    ▼
  Claude Code continues or stops
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It uses &lt;a href="https://ntfy.sh" rel="noopener noreferrer"&gt;ntfy.sh&lt;/a&gt;, an open-source HTTP-based pub-sub notification service. No account required. The topic name is a 128-bit random string, so only you can receive the notifications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup (3 Steps)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Install the ntfy app on your phone
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy" rel="noopener noreferrer"&gt;Android (Google Play)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://apps.apple.com/app/ntfy/id1625396347" rel="noopener noreferrer"&gt;iOS (App Store)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Install and run setup on your PC
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; claude-remote-approver
  claude-remote-approver setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Scan the QR code
&lt;/h3&gt;

&lt;p&gt;The setup command prints a QR code in your terminal. Scan it with the ntfy app to subscribe.&lt;/p&gt;

&lt;p&gt;Important: Start a new Claude Code session after setup. The hook is loaded at session startup, so existing sessions won't pick it up.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Feels
&lt;/h2&gt;

&lt;p&gt;Once set up, just use Claude Code normally. When a permission request fires:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your phone buzzes with a notification showing the tool name and a summary (e.g., Bash: npm install express)&lt;/li&gt;
&lt;li&gt;Tap Approve or Deny&lt;/li&gt;
&lt;li&gt;Claude Code continues instantly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you don't respond within the timeout (default: 120 seconds), the request is automatically denied. Fail-closed by design.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sample notification
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffealihe2r3u0xwr4pdz8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffealihe2r3u0xwr4pdz8.jpeg" alt=" " width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  claude-remote-approver &lt;span class="nb"&gt;test&lt;/span&gt;      &lt;span class="c"&gt;# Send a test notification&lt;/span&gt;
  claude-remote-approver status    &lt;span class="c"&gt;# Show current config&lt;/span&gt;
  claude-remote-approver disable   &lt;span class="c"&gt;# Temporarily disable&lt;/span&gt;
  claude-remote-approver &lt;span class="nb"&gt;enable&lt;/span&gt;    &lt;span class="c"&gt;# Re-enable&lt;/span&gt;
  claude-remote-approver uninstall &lt;span class="c"&gt;# Remove everything&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Topic names are generated with crypto.randomBytes(16) (128 bits of entropy)&lt;/li&gt;
&lt;li&gt;Config file is created with permission 0600 (owner-only)&lt;/li&gt;
&lt;li&gt;For sensitive work, you can self-host ntfy instead of using the public server&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/yuuichieguchi/claude-remote-approver" rel="noopener noreferrer"&gt;https://github.com/yuuichieguchi/claude-remote-approver&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://npmjs.com/package/claude-remote-approver" rel="noopener noreferrer"&gt;https://npmjs.com/package/claude-remote-approver&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Give it a try and let me know what you think!&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>vibecoding</category>
      <category>ai</category>
    </item>
    <item>
      <title>Constela: Built-in a11y Checks, Plugin System &amp; CSS Transitions</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Sun, 08 Feb 2026 07:30:25 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/constela-built-in-a11y-checks-plugin-system-css-transitions-16dd</link>
      <guid>https://dev.to/yuu1ch13/constela-built-in-a11y-checks-plugin-system-css-transitions-16dd</guid>
      <description>&lt;h2&gt;
  
  
  What's New in Constela
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://constela.dev" rel="noopener noreferrer"&gt;Constela&lt;/a&gt; is a JSON-based declarative UI framework — you write your entire UI in JSON, and the compiler + runtime handles the rest. We're shipping three features that make Constela apps more accessible, extensible, and polished.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Compile-Time Accessibility Checks
&lt;/h2&gt;

&lt;p&gt;Accessibility shouldn't be an afterthought. Constela now checks your JSON programs for common a11y issues &lt;strong&gt;at compile time&lt;/strong&gt; and reports them as warnings.&lt;/p&gt;

&lt;h3&gt;
  
  
  7 Built-in Rules
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;What it catches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;IMG_NO_ALT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Images without &lt;code&gt;alt&lt;/code&gt; text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BUTTON_NO_LABEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Buttons with no text or &lt;code&gt;aria-label&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ANCHOR_NO_LABEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Links with no text or &lt;code&gt;aria-label&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INPUT_NO_LABEL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inputs without a label&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;HEADING_SKIP&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skipped heading levels (h1 → h3)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POSITIVE_TABINDEX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Positive &lt;code&gt;tabindex&lt;/code&gt; values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DUPLICATE_ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Duplicate &lt;code&gt;id&lt;/code&gt; attributes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are &lt;strong&gt;warnings&lt;/strong&gt;, not errors — your code still compiles, so you can adopt them gradually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myProgram&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Plugin System
&lt;/h2&gt;

&lt;p&gt;Want to use custom functions in your JSON DSL? Now you can.&lt;/p&gt;

&lt;h3&gt;
  
  
  Define a Plugin
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ConstelaPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@constela/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myPlugin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConstelaPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-analytics&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;globalFunctions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;trackEvent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;formatCurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`$&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;myPlugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Register It
&lt;/h3&gt;

&lt;p&gt;Add to &lt;code&gt;constela.config.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"my-analytics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/plugins/local-plugin"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it — your functions are now available in the DSL.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safety features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atomic registration (all-or-nothing)&lt;/li&gt;
&lt;li&gt;Prototype pollution protection (&lt;code&gt;__proto__&lt;/code&gt;, &lt;code&gt;constructor&lt;/code&gt;, &lt;code&gt;prototype&lt;/code&gt; blocked)&lt;/li&gt;
&lt;li&gt;Works in both client and server rendering&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. CSS Class-Based Transitions
&lt;/h2&gt;

&lt;p&gt;Conditional rendering (&lt;code&gt;if&lt;/code&gt; nodes) now supports smooth enter/exit animations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"condition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"showToast"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"then"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"div"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Saved!"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slide-in"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enterActive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slide-in-active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"exit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slide-out"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"exitActive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slide-out-active"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;250&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pair it with CSS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.slide-in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-20px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.slide-in-active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;250ms&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.slide-out&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.slide-out-active&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt; &lt;span class="m"&gt;250ms&lt;/span&gt; &lt;span class="n"&gt;ease&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;translateY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;-20px&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The runtime handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enter flow:&lt;/strong&gt; create element → apply &lt;code&gt;enter&lt;/code&gt; → next frame: swap to &lt;code&gt;enterActive&lt;/code&gt; → cleanup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exit flow:&lt;/strong&gt; apply &lt;code&gt;exit&lt;/code&gt; → next frame: swap to &lt;code&gt;exitActive&lt;/code&gt; → remove element&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast toggling:&lt;/strong&gt; cancels in-progress animations gracefully&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSR:&lt;/strong&gt; transitions are ignored server-side (no changes needed)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @constela/core @constela/compiler @constela/runtime @constela/start
npx constela dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the &lt;a href="https://constela.dev/docs/language-spec" rel="noopener noreferrer"&gt;Language Specification&lt;/a&gt; for the full DSL reference, or browse the &lt;a href="https://constela.dev/examples" rel="noopener noreferrer"&gt;Examples&lt;/a&gt; to see these features in action.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/yuuichieguchi/constela" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://constela.dev" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://constela.dev/examples" rel="noopener noreferrer"&gt;Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>constela</category>
      <category>json</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Constela New Features - Islands Architecture, Realtime Features, and New UI Components</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Sat, 07 Feb 2026 12:55:44 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/constela-new-features-islands-architecture-realtime-features-and-new-ui-components-12p7</link>
      <guid>https://dev.to/yuu1ch13/constela-new-features-islands-architecture-realtime-features-and-new-ui-components-12p7</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We're excited to announce new features for Constela, with 7 major new capabilities that significantly improve performance and developer experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Theme System (CSS variables, light/dark/system modes)&lt;/li&gt;
&lt;li&gt;Islands Architecture (6 hydration strategies)&lt;/li&gt;
&lt;li&gt;Streaming SSR (3 flush strategies)&lt;/li&gt;
&lt;li&gt;Realtime Features (SSE, optimistic updates, state binding)&lt;/li&gt;
&lt;li&gt;New UI Components (DatePicker, Calendar, Tree, Accordion, DataTable, VirtualScroll, Chart)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feature 1: Theme System
&lt;/h2&gt;

&lt;p&gt;We've introduced a CSS variable-based theme system with support for light/dark/system modes and shadcn/ui-compatible color tokens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"theme"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"colors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(220 90% 56%)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"primary-foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(0 0% 100%)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(0 0% 100%)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(222 47% 11%)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"muted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(210 40% 96%)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"border"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(214 32% 91%)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"darkColors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(222 47% 11%)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"foreground"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(210 40% 98%)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"muted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(217 33% 17%)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"border"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hsl(217 33% 17%)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fonts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sans"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Inter, system-ui, sans-serif"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"mono"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JetBrains Mono, monospace"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cssPrefix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System Theme Detection&lt;/strong&gt;: Automatic &lt;code&gt;prefers-color-scheme&lt;/code&gt; detection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence&lt;/strong&gt;: localStorage + Cookie support (SSR-safe)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS Variables&lt;/strong&gt;: Auto-applies prefixed variables like &lt;code&gt;--app-primary&lt;/code&gt; to &lt;code&gt;:root&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dark Class&lt;/strong&gt;: Automatically manages &lt;code&gt;dark&lt;/code&gt; class on &lt;code&gt;document.documentElement&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feature 2: Islands Architecture
&lt;/h2&gt;

&lt;p&gt;We've implemented Islands Architecture for partial hydration. Choose from 6 hydration strategies to drastically reduce your initial JS bundle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"island"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"interactive-chart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"visible"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strategyOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"threshold"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootMargin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100px"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chartData"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"chartData"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"loadData"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"steps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fetch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/chart-data"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"onSuccess"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chartData"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"data"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hydration Strategies
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;load&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hydrate immediately on page load&lt;/td&gt;
&lt;td&gt;Critical interactive elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;idle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hydrate when browser is idle&lt;/td&gt;
&lt;td&gt;Non-urgent interactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;visible&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hydrate when element enters viewport&lt;/td&gt;
&lt;td&gt;Below-the-fold content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;interaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hydrate on first user interaction&lt;/td&gt;
&lt;td&gt;Lazy-loaded widgets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;media&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hydrate when media query matches&lt;/td&gt;
&lt;td&gt;Responsive components&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;never&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Never hydrate&lt;/td&gt;
&lt;td&gt;Static content only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Build Optimization
&lt;/h3&gt;

&lt;p&gt;Islands are automatically code-split:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dist/
  _islands/
    interactive-chart.js   # Island-specific bundle
    sidebar-menu.js
  client.js                # Main bundle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Feature 3: Realtime Features
&lt;/h2&gt;

&lt;p&gt;We've added SSE (Server-Sent Events) connections, optimistic updates, and state binding.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSE Connections
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sseConnect"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connection"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notifications"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/events"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventTypes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reconnect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"exponential"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maxRetries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"baseDelay"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"onMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"operation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"push"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"payload"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Optimistic Updates
&lt;/h3&gt;

&lt;p&gt;Update UI immediately with automatic rollback on server error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"likePost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"steps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"optimistic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"posts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"index"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"liked"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"updateId"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fetch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"concat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/posts/"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postId"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/like"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"onSuccess"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"confirm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"updateId"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"onError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reject"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"updateId"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Feature 4: Streaming SSR
&lt;/h2&gt;

&lt;p&gt;We've implemented ReadableStream-based streaming SSR with Edge Runtime support for significantly reduced TTFB. Just enable it in your config - no TypeScript required.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"streaming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"flushStrategy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"batched"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flush Strategies
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;immediate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flush each chunk immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;batched&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Batch flush at 1KB threshold (default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;manual&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Flush only at the end (small pages)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Suspense Boundaries
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;suspense&lt;/code&gt; nodes for async content with loading states:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"suspense"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-profile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fallback"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"div"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"className"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"skeleton"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"UserProfile"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server renders the fallback immediately, then streams the actual content when ready - all configured declaratively in JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feature 5: New UI Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  DatePicker / Calendar
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DatePicker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"selectedDate"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"onChange"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"change"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"updateDate"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yyyy-MM-dd"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"locale"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en-US"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tree / Accordion
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tree"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"treeData"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"onSelect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"select"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"handleSelect"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expandedKeys"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"expanded"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DataTable
&lt;/h3&gt;

&lt;p&gt;Full-featured table with sorting, filtering, and pagination:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DataTable"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"columns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sortable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"filterable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Status"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pageSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sortable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filterable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  VirtualScroll
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"VirtualScroll"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"largeList"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"itemHeight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"containerHeight"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"overscan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Chart (7 Types)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BarChart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chartData"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"valueKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"labelKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"label"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"showGrid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supported chart types: bar, line, pie, donut, area, radar, scatter&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This update brings significant enhancements for modern web application development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Islands Architecture reduces bundle size, Streaming SSR improves TTFB&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DX&lt;/strong&gt;: Theme system makes dark mode easy, realtime features enable declarative sync logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI&lt;/strong&gt;: Feature-rich UI components boost development efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try it out today!&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://constela.dev/" rel="noopener noreferrer"&gt;https://constela.dev/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/yuuichieguchi/constela" rel="noopener noreferrer"&gt;https://github.com/yuuichieguchi/constela&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Documentation: &lt;a href="https://constela.dev/docs" rel="noopener noreferrer"&gt;https://constela.dev/docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>constela</category>
      <category>json</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Introducing AI-Powered UI Generation for Constela</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Tue, 27 Jan 2026 13:29:51 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/introducing-ai-powered-ui-generation-for-constela-3npm</link>
      <guid>https://dev.to/yuu1ch13/introducing-ai-powered-ui-generation-for-constela-3npm</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Constela is a JSON-first declarative UI framework. With this release, we're adding AI-powered features to supercharge your development workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DSL Generator&lt;/strong&gt;: Generate Constela JSON from natural language prompts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Validation&lt;/strong&gt;: Automatically validate generated DSL for safety&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CLI suggest command&lt;/strong&gt;: Analyze existing DSL and get improvement suggestions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @constela/ai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Build-time Component Generation
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;type: "ai"&lt;/code&gt; in the &lt;code&gt;data&lt;/code&gt; section to generate components at build time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ai"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"anthropic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A card component with title, description, and action button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Welcome"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Get started"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI generates a component like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"div"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"className"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"card"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"h2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"p"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"onClick"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"handleAction"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Action"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Security Validation
&lt;/h3&gt;

&lt;p&gt;All generated DSL is automatically validated against security rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forbidden Tags&lt;/strong&gt; (blocked by default):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;script&lt;/code&gt;, &lt;code&gt;iframe&lt;/code&gt;, &lt;code&gt;object&lt;/code&gt;, &lt;code&gt;embed&lt;/code&gt;, &lt;code&gt;form&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Forbidden Actions&lt;/strong&gt; (cannot be whitelisted):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;import&lt;/code&gt;, &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;dom&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Restricted Actions&lt;/strong&gt; (require explicit whitelist):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fetch&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. CLI suggest Command
&lt;/h3&gt;

&lt;p&gt;Analyze existing DSL files and get AI-powered improvement suggestions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Analyze for accessibility issues&lt;/span&gt;
constela suggest app.json &lt;span class="nt"&gt;--aspect&lt;/span&gt; accessibility

&lt;span class="c"&gt;# Analyze for security (using OpenAI)&lt;/span&gt;
constela suggest app.json &lt;span class="nt"&gt;--aspect&lt;/span&gt; security &lt;span class="nt"&gt;--provider&lt;/span&gt; openai

&lt;span class="c"&gt;# JSON output for tooling&lt;/span&gt;
constela suggest app.json &lt;span class="nt"&gt;--aspect&lt;/span&gt; ux &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;=== Suggestions for app.json (accessibility) ===

[HIGH] Missing aria-label on button
   Recommendation: Add aria-label="Submit form" to the button element
   Location: view.children[0].props

[MED] Low color contrast in text
   Recommendation: Increase contrast ratio to meet WCAG AA standards
   Location: view.children[2].props.style

Total: 2 suggestion(s)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Runtime Dynamic Generation
&lt;/h3&gt;

&lt;p&gt;Use the &lt;code&gt;generate&lt;/code&gt; action step to create DSL in response to user input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"userPrompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"generatedCard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"generateCard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"steps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"generate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"anthropic"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"userPrompt"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"generatedCard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"onSuccess"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"card"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"generatedCard"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"onError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"do"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"set"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Generation failed"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"div"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"userPrompt"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"onInput"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"input"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"updatePrompt"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"onClick"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"event"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"click"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"generateCard"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Generate"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in your project root and add your API key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# For Anthropic (Claude)
ANTHROPIC_API_KEY=sk-ant-...

# For OpenAI
OPENAI_API_KEY=sk-...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure &lt;code&gt;.env&lt;/code&gt; is in your &lt;code&gt;.gitignore&lt;/code&gt; to avoid committing secrets.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Processing&lt;/strong&gt;: Your natural language description is sent to the AI provider&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DSL Generation&lt;/strong&gt;: The AI generates valid Constela JSON based on the language specification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Validation&lt;/strong&gt;: Generated DSL is validated against security rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: Any validation errors are reported with actionable fixes&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With @constela/ai, you can leverage AI to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate JSON DSL from natural language&lt;/li&gt;
&lt;li&gt;Automatically validate security&lt;/li&gt;
&lt;li&gt;Get improvement suggestions for existing code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the &lt;a href="https://constela.dev/reference/ai" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; for more details.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/yuuichieguchi/constela" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://constela.dev" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@constela/ai" rel="noopener noreferrer"&gt;npm Package&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>json</category>
      <category>constela</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Introducing @constela/ui - Copy-Paste UI Components for Constela JSON DSL</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Sun, 25 Jan 2026 07:03:52 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/introducing-constelaui-copy-paste-ui-components-for-constela-json-dsl-1i39</link>
      <guid>https://dev.to/yuu1ch13/introducing-constelaui-copy-paste-ui-components-for-constela-json-dsl-1i39</guid>
      <description>&lt;h1&gt;
  
  
  Introducing @constela/ui
&lt;/h1&gt;

&lt;p&gt;I'm excited to announce the release of &lt;code&gt;@constela/ui&lt;/code&gt; - a UI component library for Constela JSON DSL.&lt;/p&gt;

&lt;p&gt;This library provides &lt;strong&gt;copy-paste UI components&lt;/strong&gt; that you can drop directly into your Constela projects. All components are written in JSON format - no TypeScript or React knowledge required for customization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F542zqjir8hjzumyd4doi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F542zqjir8hjzumyd4doi.png" alt=" " width="800" height="909"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @constela/ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Available Components
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic (7)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Button&lt;/strong&gt; - 6 variants, 4 sizes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input&lt;/strong&gt; - Text input field&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Textarea&lt;/strong&gt; - Multi-line text input&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select&lt;/strong&gt; - Dropdown selection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checkbox&lt;/strong&gt; - Checkbox input&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Radio&lt;/strong&gt; - Radio button&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Switch&lt;/strong&gt; - Toggle switch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Feedback (5)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alert&lt;/strong&gt; - Alert messages (default, destructive)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Card&lt;/strong&gt; - Card container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Badge&lt;/strong&gt; - Inline badge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skeleton&lt;/strong&gt; - Loading placeholder&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avatar&lt;/strong&gt; - User avatar with fallback&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Overlay (4)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dialog&lt;/strong&gt; - Modal dialog with proper ARIA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tooltip&lt;/strong&gt; - Hover tooltip&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Popover&lt;/strong&gt; - Click-triggered popover&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toast&lt;/strong&gt; - Notifications (default, success, error, warning, info)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Navigation/Layout (6)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tabs&lt;/strong&gt; - Tab navigation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breadcrumb&lt;/strong&gt; - Breadcrumb navigation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pagination&lt;/strong&gt; - Page pagination&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Container&lt;/strong&gt; - Max-width container&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grid&lt;/strong&gt; - CSS Grid layout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack&lt;/strong&gt; - Flexbox stack&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to Use
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Copy Component Files
&lt;/h3&gt;

&lt;p&gt;Copy the components you need from &lt;code&gt;node_modules/@constela/ui/components/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;button/
├── button.constela.json   # Component definition
└── button.styles.json     # Style preset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Import Styles
&lt;/h3&gt;

&lt;p&gt;Add the style preset to your application's &lt;code&gt;styles&lt;/code&gt; section.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use the Component
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"component"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lg"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Click me"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Style System
&lt;/h2&gt;

&lt;p&gt;Each component uses a CVA-like style system with base classes, variants, and default variants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"buttonStyles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"variants"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bg-primary text-primary-foreground hover:bg-primary/90"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"destructive"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bg-destructive text-destructive-foreground hover:bg-destructive/90"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"outline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"border border-input bg-background hover:bg-accent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"secondary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bg-secondary text-secondary-foreground hover:bg-secondary/80"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ghost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hover:bg-accent hover:text-accent-foreground"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"link"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text-primary underline-offset-4 hover:underline"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"h-10 px-4 py-2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"h-9 rounded-md px-3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"lg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"h-11 rounded-md px-8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"h-10 w-10"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"defaultVariants"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dynamic Styling with StyleExpr
&lt;/h3&gt;

&lt;p&gt;Components use &lt;code&gt;StyleExpr&lt;/code&gt; to apply styles dynamically based on props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"className"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"buttonStyles"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"variant"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"size"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Accessibility First
&lt;/h2&gt;

&lt;p&gt;All components include proper ARIA attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;aria-label&lt;/code&gt; for screen reader labels&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-disabled&lt;/code&gt; for disabled state indication&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;role&lt;/code&gt; for semantic roles (dialog, alert, tablist, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aria-live&lt;/code&gt; for live region announcements&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example: Dialog Component
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"params"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"div"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dialog"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"aria-modal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"className"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"style"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"preset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dialogStyles"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"slot"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Customization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adding New Variants
&lt;/h3&gt;

&lt;p&gt;Edit the style file to add new variants:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"variants"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bg-green-500 text-white hover:bg-green-600"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extending Parameters
&lt;/h3&gt;

&lt;p&gt;Add new parameters to the component definition. All parameters are optional by default, so existing usage won't break.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modifying Base Styles
&lt;/h3&gt;

&lt;p&gt;Change the &lt;code&gt;base&lt;/code&gt; property to modify styles that apply to all variants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Coverage
&lt;/h2&gt;

&lt;p&gt;All components are verified with 563 tests covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structure validation&lt;/li&gt;
&lt;li&gt;Parameter validation&lt;/li&gt;
&lt;li&gt;Style preset validation&lt;/li&gt;
&lt;li&gt;Accessibility attributes
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;packages/ui &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pnpm &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="c"&gt;# Test Files  passed (22)&lt;/span&gt;
&lt;span class="c"&gt;#      Tests  563 passed (563)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Component catalog page on the official website&lt;/li&gt;
&lt;li&gt;More components based on community feedback&lt;/li&gt;
&lt;li&gt;Enhanced animation support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/yuuichieguchi/constela" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@constela/ui" rel="noopener noreferrer"&gt;npm Package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://constela.dev/reference/ui" rel="noopener noreferrer"&gt;Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@constela/ui&lt;/code&gt; provides a solid foundation for building UIs with Constela JSON DSL. The copy-paste approach makes it simple to get started, and the JSON-based format makes customization accessible to everyone.&lt;/p&gt;

&lt;p&gt;Give it a try and let me know what you think!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>json</category>
      <category>constela</category>
    </item>
    <item>
      <title>Constela: Call, Lambda, and Array Expressions for Type-Safe Data Operations</title>
      <dc:creator>Yuuichi Eguchi</dc:creator>
      <pubDate>Sat, 24 Jan 2026 16:18:15 +0000</pubDate>
      <link>https://dev.to/yuu1ch13/constela-call-lambda-and-array-expressions-for-type-safe-data-operations-3bjo</link>
      <guid>https://dev.to/yuu1ch13/constela-call-lambda-and-array-expressions-for-type-safe-data-operations-3bjo</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;We are excited to announce the latest update to Constela, bringing &lt;strong&gt;call expressions&lt;/strong&gt;, &lt;strong&gt;lambda expressions&lt;/strong&gt;, and the new &lt;strong&gt;array expressions&lt;/strong&gt; to our compiler-first UI language. This update enables powerful data transformations and dynamic array construction while maintaining the security and determinism that define Constela.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Constela?
&lt;/h3&gt;

&lt;p&gt;Constela is a compiler-first UI &lt;strong&gt;language&lt;/strong&gt; designed for AI-generated UI. Unlike React or Next.js where you write UI with JavaScript, Constela uses a constrained JSON DSL that is validated, analyzed, and compiled into minimal runtime code.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Constela&lt;/th&gt;
&lt;th&gt;React / Next.js&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UI authoring&lt;/td&gt;
&lt;td&gt;JSON DSL&lt;/td&gt;
&lt;td&gt;JavaScript / JSX&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution&lt;/td&gt;
&lt;td&gt;compiler-driven&lt;/td&gt;
&lt;td&gt;runtime-driven&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State updates&lt;/td&gt;
&lt;td&gt;declarative actions&lt;/td&gt;
&lt;td&gt;arbitrary JS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errors&lt;/td&gt;
&lt;td&gt;structured errors&lt;/td&gt;
&lt;td&gt;runtime exceptions&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Highlights
&lt;/h3&gt;

&lt;p&gt;This release introduces three major expression types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Call Expression&lt;/strong&gt; - Method calls on arrays, strings, Math, and Date&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Expression&lt;/strong&gt; - Anonymous functions for array higher-order methods&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Array Expression&lt;/strong&gt; - Dynamic array construction from expressions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Package Versions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@constela/core&lt;/code&gt;: 0.15.2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@constela/compiler&lt;/code&gt;: 0.14.2&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@constela/runtime&lt;/code&gt;: 0.18.3&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@constela/server&lt;/code&gt;: 11.0.1&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Call Expression
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;call&lt;/code&gt; expression enables method invocation on target values with a clean, declarative syntax.&lt;/p&gt;

&lt;h3&gt;
  
  
  Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;Expression&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;method_name&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;Expression&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Basic Examples
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Get array length:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"items"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"length"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Get element at index:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"items"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"at"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Check if array includes a value:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"includes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  String Operations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Convert to uppercase:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"toUpperCase"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Split string into array:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"react,typescript,node"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"split"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;","&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trim whitespace:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"userInput"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"trim"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Math Operations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Calculate minimum value:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Math"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"count"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Round a calculated value:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Math"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"round"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"price"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Date Operations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Get current timestamp:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Date"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"now"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Lambda Expression
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;lambda&lt;/code&gt; expression enables anonymous functions for array higher-order methods like &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, and more.&lt;/p&gt;

&lt;h3&gt;
  
  
  Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;parameter_name&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;index_name&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;optional&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;Expression&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Filter Example
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Filter items greater than a threshold:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"filter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;code&gt;[3, 4, 5]&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Map Example
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Double each element:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"map"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;code&gt;[2, 4, 6]&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Index Parameter
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Map with index:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"map"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"item"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"index"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"i"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;code&gt;[0, 1, 2]&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessing Object Properties
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Extract names from user objects:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"users"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"map"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Supported Methods
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Array Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;length&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get array length&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.length()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;at&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get element at index (supports negative)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.at(-1)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;includes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check if element exists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.includes(value)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;slice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract subarray&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.slice(1, 3)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;indexOf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find index of element&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.indexOf("foo")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;join&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Join elements into string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.join(", ")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filter by predicate (lambda)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.filter(x =&amp;gt; x &amp;gt; 0)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;map&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transform elements (lambda)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.map(x =&amp;gt; x * 2)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;find&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find first matching (lambda)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.find(x =&amp;gt; x.id === 1)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;findIndex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find index of first matching&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.findIndex(x =&amp;gt; x &amp;gt; 5)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;some&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check if any match&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.some(x =&amp;gt; x &amp;gt; 10)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;every&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check if all match&lt;/td&gt;
&lt;td&gt;&lt;code&gt;items.every(x =&amp;gt; x &amp;gt; 0)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  String Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;length&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get string length&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.length()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;charAt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get character at index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.charAt(0)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;substring&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract substring&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.substring(0, 5)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;slice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Extract with negative index support&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.slice(-3)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;split&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Split into array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.split(",")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;trim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Remove whitespace&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.trim()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;toUpperCase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert to uppercase&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.toUpperCase()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;toLowerCase&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Convert to lowercase&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.toLowerCase()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;replace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Replace first occurrence&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.replace("a", "b")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;includes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check substring exists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.includes("foo")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;startsWith&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check prefix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.startsWith("http")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;endsWith&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Check suffix&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.endsWith(".json")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;indexOf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Find substring index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;str.indexOf("@")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Math Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;min&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get minimum value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.min(a, b, c)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get maximum value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.max(0, count)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;round&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Round to nearest integer&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.round(3.7)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;floor&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Round down&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.floor(3.9)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ceil&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Round up&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.ceil(3.1)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;abs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Absolute value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.abs(-5)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sqrt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Square root&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.sqrt(16)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Power&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.pow(2, 3)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;random&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Random 0-1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.random()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;sin&lt;/code&gt; / &lt;code&gt;cos&lt;/code&gt; / &lt;code&gt;tan&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Trigonometric functions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Math.sin(0)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Date Methods
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;now&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current timestamp (ms)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Date.now()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;parse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parse date string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Date.parse("2024-01-01")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;toISOString&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ISO format string&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date.toISOString()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getTime&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get timestamp&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date.getTime()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getFullYear&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Get year&lt;/td&gt;
&lt;td&gt;&lt;code&gt;date.getFullYear()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Practical Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Filtering a TODO List
&lt;/h3&gt;

&lt;p&gt;Display only incomplete tasks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Learn Constela"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Build app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Deploy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"showCompleted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"boolean"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"view"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"each"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cond"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"if"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"showCompleted"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"then"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todos"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"else"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todos"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"filter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"not"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"operand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed"&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"as"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"element"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"div"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"children"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"todo"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Search Filter with Case-Insensitive Matching
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"products"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"filter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"base"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"product"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"toLowerCase"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"includes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"searchQuery"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"toLowerCase"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Chaining Methods
&lt;/h3&gt;

&lt;p&gt;Filter then map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"filter"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"map"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lambda"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"left"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"right"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;code&gt;[6, 8, 10]&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Dynamic Array Construction (Array Expression)
&lt;/h3&gt;

&lt;p&gt;The new &lt;code&gt;array&lt;/code&gt; expression enables dynamic array construction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"elements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"basicSetup"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"apply"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is equivalent to JavaScript's &lt;code&gt;[basicSetup, json()]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combining with state and literals:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"elements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"config"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customPlugin"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Nested arrays:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"elements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"elements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"array"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"elements"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Value Clamping
&lt;/h3&gt;

&lt;p&gt;Ensure a value stays within bounds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Math"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"min"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"var"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Math"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"max"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Security Measures
&lt;/h2&gt;

&lt;p&gt;Constela takes security seriously. All method calls are protected by multiple layers of defense:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Whitelist-Based Method Filtering
&lt;/h3&gt;

&lt;p&gt;Only explicitly allowed methods can be called. Attempting to call a non-whitelisted method returns &lt;code&gt;undefined&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"constructor"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;allowed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;returns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;undefined&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The whitelist is defined at compile time and cannot be bypassed at runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Prototype Pollution Prevention
&lt;/h3&gt;

&lt;p&gt;Constela prevents access to prototype chain methods that could be used for injection attacks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;__proto__&lt;/code&gt; access is blocked&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;constructor&lt;/code&gt; access is blocked&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prototype&lt;/code&gt; access is blocked&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Type-Aware Method Resolution
&lt;/h3&gt;

&lt;p&gt;Methods are resolved based on the target value type, preventing type confusion attacks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"toUpperCase"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Returns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;undefined&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;numbers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;don't&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;method&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Safe Null/Undefined Handling
&lt;/h3&gt;

&lt;p&gt;Calling methods on null or undefined returns undefined gracefully without throwing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nonexistent"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"length"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Returns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;undefined&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This update to Constela brings powerful data transformation and array construction capabilities while maintaining the security-first approach that defines the framework. Call, lambda, and array expressions enable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declarative data transformations&lt;/strong&gt; - Filter, map, and transform data without JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic array construction&lt;/strong&gt; - Build arrays from variables, function results, and expressions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-safe operations&lt;/strong&gt; - Compile-time validation ensures correct usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure by default&lt;/strong&gt; - Whitelist-based method filtering prevents injection attacks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composable expressions&lt;/strong&gt; - Chain operations for complex transformations&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new Constela project&lt;/span&gt;
npx create-constela my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/yuuichieguchi/constela" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://constela.dev/docs" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://constela.dev/examples" rel="noopener noreferrer"&gt;Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are excited to see what you build with Constela!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Constela is MIT licensed. Contributions welcome!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>constea</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
