[{"data":1,"prerenderedAt":513},["ShallowReactive",2],{"navigation_docs":3,"-decisions-0002-jatatui-renders-the-cells":223,"-decisions-0002-jatatui-renders-the-cells-surround":508},[4,26,96,105,202],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":25},"Getting started","i-lucide-rocket","\u002Fgetting-started","1.getting-started",[10,15,20],{"title":11,"path":12,"stem":13,"icon":14},"Installation","\u002Fgetting-started\u002Finstallation","1.getting-started\u002F1.installation","i-lucide-download",{"title":16,"path":17,"stem":18,"icon":19},"The first app","\u002Fgetting-started\u002Ffirst-app","1.getting-started\u002F2.first-app","i-lucide-play",{"title":21,"path":22,"stem":23,"icon":24},"The first test","\u002Fgetting-started\u002Ffirst-test","1.getting-started\u002F3.first-test","i-lucide-flask-conical",false,{"title":27,"icon":28,"path":29,"stem":30,"children":31,"page":25},"Guide","i-lucide-book-open","\u002Fguide","2.guide",[32,37,42,47,52,57,62,67,72,77,81,86,91],{"title":33,"path":34,"stem":35,"icon":36},"Components","\u002Fguide\u002Fcomponents","2.guide\u002F01.components","i-lucide-component",{"title":38,"path":39,"stem":40,"icon":41},"Inputs and forms","\u002Fguide\u002Finputs-and-forms","2.guide\u002F02.inputs-and-forms","i-lucide-text-cursor-input",{"title":43,"path":44,"stem":45,"icon":46},"Scrolling and viewports","\u002Fguide\u002Fscrolling","2.guide\u002F03.scrolling","i-lucide-scroll-text",{"title":48,"path":49,"stem":50,"icon":51},"Overlays and layers","\u002Fguide\u002Foverlays","2.guide\u002F04.overlays","i-lucide-layers",{"title":53,"path":54,"stem":55,"icon":56},"Styles and theme","\u002Fguide\u002Fstyles","2.guide\u002F05.styles","i-lucide-palette",{"title":58,"path":59,"stem":60,"icon":61},"Composables","\u002Fguide\u002Fcomposables","2.guide\u002F06.composables","i-lucide-puzzle",{"title":63,"path":64,"stem":65,"icon":66},"Widgets","\u002Fguide\u002Fwidgets","2.guide\u002F07.widgets","i-lucide-blocks",{"title":68,"path":69,"stem":70,"icon":71},"The assistant","\u002Fguide\u002Fassistant","2.guide\u002F08.assistant","i-lucide-message-square",{"title":73,"path":74,"stem":75,"icon":76},"The same app on the web","\u002Fguide\u002Fweb","2.guide\u002F09.web","i-lucide-globe",{"title":78,"path":79,"stem":80,"icon":24},"Testing","\u002Fguide\u002Ftesting","2.guide\u002F10.testing",{"title":82,"path":83,"stem":84,"icon":85},"Running and packaging","\u002Fguide\u002Fpackaging","2.guide\u002F11.packaging","i-lucide-package",{"title":87,"path":88,"stem":89,"icon":90},"The terminal contract","\u002Fguide\u002Fterminal","2.guide\u002F12.terminal","i-lucide-square-terminal",{"title":92,"path":93,"stem":94,"icon":95},"Benchmarks","\u002Fguide\u002Fbenchmarks","2.guide\u002F13.benchmarks","i-lucide-gauge",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":25},"AI and agents","i-lucide-bot","\u002Fai","3.ai",[102],{"title":97,"path":103,"stem":104,"icon":98},"\u002Fai\u002Fagents","3.ai\u002F1.agents",{"title":106,"icon":107,"path":108,"stem":109,"children":110},"Decisions","i-lucide-scale","\u002Fdecisions","4.decisions",[111,114,118,122,126,130,134,138,142,146,150,154,158,162,166,170,174,178,182,186,190,194,198],{"title":112,"path":108,"stem":113,"icon":46},"All decisions","4.decisions\u002Findex",{"title":115,"path":116,"stem":117},"0001. kyo-ui is the DSL","\u002Fdecisions\u002F0001-kyo-ui-is-the-dsl","4.decisions\u002F0001-kyo-ui-is-the-dsl",{"title":119,"path":120,"stem":121},"0002. jatatui renders the cells","\u002Fdecisions\u002F0002-jatatui-renders-the-cells","4.decisions\u002F0002-jatatui-renders-the-cells",{"title":123,"path":124,"stem":125},"0003. Pin Kyo 1.0.0-RC7 and JDK 25","\u002Fdecisions\u002F0003-pin-kyo-rc7-and-jdk-25","4.decisions\u002F0003-pin-kyo-rc7-and-jdk-25",{"title":127,"path":128,"stem":129},"0004. Public Kyo APIs only","\u002Fdecisions\u002F0004-public-kyo-apis-only","4.decisions\u002F0004-public-kyo-apis-only",{"title":131,"path":132,"stem":133},"0005. A SOFT frame contract and an own watcher loop","\u002Fdecisions\u002F0005-soft-frame-contract-and-own-watcher-loop","4.decisions\u002F0005-soft-frame-contract-and-own-watcher-loop",{"title":135,"path":136,"stem":137},"0006. Purity: edge files, lint and a pure painter","\u002Fdecisions\u002F0006-purity-edge-files-and-lint","4.decisions\u002F0006-purity-edge-files-and-lint",{"title":139,"path":140,"stem":141},"0007. Compiler flags from sbt-tpolecat","\u002Fdecisions\u002F0007-compiler-flags-from-sbt-tpolecat","4.decisions\u002F0007-compiler-flags-from-sbt-tpolecat",{"title":143,"path":144,"stem":145},"0008. The artifact split: runner, app, testkit and core","\u002Fdecisions\u002F0008-artifact-split-runner-app-testkit-core","4.decisions\u002F0008-artifact-split-runner-app-testkit-core",{"title":147,"path":148,"stem":149},"0009. tui4s-use and tui4s-widgets, cross-built","\u002Fdecisions\u002F0009-use-and-widgets-cross-built","4.decisions\u002F0009-use-and-widgets-cross-built",{"title":151,"path":152,"stem":153},"0010. The flagship assistant is the blueprint of a real app","\u002Fdecisions\u002F0010-the-flagship-assistant-as-blueprint","4.decisions\u002F0010-the-flagship-assistant-as-blueprint",{"title":155,"path":156,"stem":157},"0011. The web target: a standalone Scala.js app and one theme","\u002Fdecisions\u002F0011-web-target-standalone-scalajs-and-one-theme","4.decisions\u002F0011-web-target-standalone-scalajs-and-one-theme",{"title":159,"path":160,"stem":161},"0012. The documentation is example-first","\u002Fdecisions\u002F0012-documentation-is-example-first","4.decisions\u002F0012-documentation-is-example-first",{"title":163,"path":164,"stem":165},"0013. Benchmarks in the repository, with their own build","\u002Fdecisions\u002F0013-benchmarks-in-repo-with-own-build","4.decisions\u002F0013-benchmarks-in-repo-with-own-build",{"title":167,"path":168,"stem":169},"0014. Release per module, by tag, as 0.1.0-RC1","\u002Fdecisions\u002F0014-release-per-module-tags-and-rc1","4.decisions\u002F0014-release-per-module-tags-and-rc1",{"title":171,"path":172,"stem":173},"0015. Packaging: the AOT cache in the start scripts, native-image as an option","\u002Fdecisions\u002F0015-packaging-aot-cache-and-native-image","4.decisions\u002F0015-packaging-aot-cache-and-native-image",{"title":175,"path":176,"stem":177},"0016. Process: lean mode and agent roles","\u002Fdecisions\u002F0016-process-lean-mode-and-agent-roles","4.decisions\u002F0016-process-lean-mode-and-agent-roles",{"title":179,"path":180,"stem":181},"0017. Deferred: templates, mouse, the kitty keyboard protocol and Windows","\u002Fdecisions\u002F0017-deferred-templates-mouse-and-kitty","4.decisions\u002F0017-deferred-templates-mouse-and-kitty",{"title":183,"path":184,"stem":185},"0018. pini4s is a vendored Git submodule","\u002Fdecisions\u002F0018-pini4s-as-a-vendored-submodule","4.decisions\u002F0018-pini4s-as-a-vendored-submodule",{"title":187,"path":188,"stem":189},"0019. Upstream issues are drafts that the user posts","\u002Fdecisions\u002F0019-upstream-kyo-issues-as-drafts","4.decisions\u002F0019-upstream-kyo-issues-as-drafts",{"title":191,"path":192,"stem":193},"0020. Release from the Actions form","\u002Fdecisions\u002F0020-release-from-the-actions-form","4.decisions\u002F0020-release-from-the-actions-form",{"title":195,"path":196,"stem":197},"0021. The rulebook is CONTRIBUTING.md","\u002Fdecisions\u002F0021-the-rulebook-is-contributing-md","4.decisions\u002F0021-the-rulebook-is-contributing-md",{"title":199,"path":200,"stem":201},"0022. The logo is text","\u002Fdecisions\u002F0022-the-logo-is-text","4.decisions\u002F0022-the-logo-is-text",{"title":203,"icon":204,"path":205,"stem":206,"children":207,"page":25},"Project","i-lucide-compass","\u002Fproject","5.project",[208,213,218],{"title":209,"path":210,"stem":211,"icon":212},"Roadmap","\u002Fproject\u002Froadmap","5.project\u002F1.roadmap","i-lucide-map",{"title":214,"path":215,"stem":216,"icon":217},"Contributing","\u002Fproject\u002Fcontributing","5.project\u002F2.contributing","i-lucide-git-pull-request",{"title":219,"path":220,"stem":221,"icon":222},"Security","\u002Fproject\u002Fsecurity","5.project\u002F3.security","i-lucide-shield",{"id":224,"title":119,"body":225,"description":501,"extension":502,"links":503,"meta":504,"navigation":505,"path":120,"seo":506,"stem":121,"__hash__":507},"docs\u002F4.decisions\u002F0002-jatatui-renders-the-cells.md",{"type":226,"value":227,"toc":490},"minimark",[228,233,237,241,244,248,299,303,383,387,421,425,475,479],[229,230,232],"h2",{"id":231},"status","Status",[234,235,236],"p",{},"Accepted",[229,238,240],{"id":239},"date","Date",[234,242,243],{},"2026-09-30 (the second round of the decision log).",[229,245,247],{"id":246},"context","Context",[249,250,251,260,271,278,289,296],"ul",{},[252,253,254,255,259],"li",{},"The user first asked for a wrapper of \"the Scala terminal library\", which was tui-scala (",[256,257,258],"code",{},"com.olvind.tui",").",[252,261,262,263,266,267,270],{},"Research on 2026-09-30 found that the tui-scala repository now redirects to ",[256,264,265],{},"oyvindberg\u002Fjatatui",". The release notes of jatatui 0.30.0 call it \"the successor to tui-scala\". jatatui ports ratatui 0.30; tui-scala 0.0.10 ports tui-rs of 2022 (no ",[256,268,269],{},"Line",", no Scrollbar, no Calendar).",[252,272,273,274,277],{},"jatatui publishes a ",[256,275,276],{},"TestBackend",", so a test can render without a terminal. tui-scala publishes none, and its unpublished one has a cell-aliasing bug.",[252,279,280,281,284,285,288],{},"tui-scala has hazards that a probe showed: a global unbounded ",[256,282,283],{},"Layout.LAYOUT_CACHE"," (one entry per frame), live cell references from ",[256,286,287],{},"Buffer.diff",", and 329 KiB per frame for one full-screen block.",[252,290,291,292,295],{},"Both libraries load a native crossterm library (Rust, JNI) from the jar. Both use the Java package ",[256,293,294],{},"tui.crossterm"," with the same class names, under different Maven coordinates, so eviction cannot remove one of them.",[252,297,298],{},"A typical 120x40 jatatui frame costs about 2 ms and 1.7 MiB, also when nothing changed. Buffers, cells and widget states are mutable.",[229,300,302],{"id":301},"decision","Decision",[249,304,305,328,333,344,358,368],{},[252,306,307,308,311,312,315,316,319,320,323,324,327],{},"tui4s uses ",[256,309,310],{},"com.olvind.jatatui"," 0.31.0: ",[256,313,314],{},"jatatui-core"," and ",[256,317,318],{},"jatatui-crossterm"," (which brings ",[256,321,322],{},"crossterm","). ",[256,325,326],{},"jatatui-widgets"," is not used (D-12 a).",[252,329,330,332],{},[256,331,258],{}," (tui-scala) never goes on the classpath.",[252,334,335,336,339,340,343],{},"tui4s does the terminal setup itself (raw mode, alternate screen, restore). It does not call ",[256,337,338],{},"Jatatui.init"," or ",[256,341,342],{},"runIo",", because they install a global exception handler and a shutdown hook.",[252,345,346,347,350,351,354,355,357],{},"All drawing happens on one fiber. Only the input thread reads the terminal, with ",[256,348,349],{},"poll(\u003Cshort timeout>)"," then ",[256,352,353],{},"read()",", because the native ",[256,356,353],{}," blocks and an interrupt cannot stop it.",[252,359,360,361,315,364,367],{},"No jatatui type is in the public API of tui4s: the test kit has its own ",[256,362,363],{},"CellStyle",[256,365,366],{},"CellColor"," (D-13 a).",[252,369,370,371,374,375,378,379,382],{},"A run in the terminal passes ",[256,372,373],{},"--enable-native-access=ALL-UNNAMED",". Without it JDK 25 prints a warning into the terminal; with ",[256,376,377],{},"--illegal-native-access=deny"," the run fails with ",[256,380,381],{},"TuiError.NativeAccessDenied",".",[229,384,386],{"id":385},"alternatives-considered","Alternatives considered",[249,388,389,399,405,411],{},[252,390,391,398],{},[392,393,394,395,397],"strong",{},"tui-scala (",[256,396,258],{}," 0.0.10)",". It lost: development moved to jatatui, its API is older, it has no published test backend, and its layout cache leaks.",[252,400,401,404],{},[392,402,403],{},"An own ANSI writer."," Not recorded. The decision log names no evaluation of it.",[252,406,407,410],{},[392,408,409],{},"Lanterna."," Not recorded.",[252,412,413,416,417,420],{},[392,414,415],{},"JLine."," Not recorded as a backend. The research names JLine only as the reason that ",[256,418,419],{},"sbt run"," fights raw mode.",[229,422,424],{"id":423},"consequences","Consequences",[249,426,427,436,455,469,472],{},[252,428,429,430,432,433,259],{},"Good: the cells, the diff and the backend come from a maintained port of ratatui. ",[256,431,276],{}," makes headless tests possible (",[256,434,435],{},"tui4s.testkit.Headless",[252,437,438,439,442,443,446,447,450,451,259],{},"Good: tui4s owns the painter (",[256,440,441],{},"internal.Paint"," gives a ",[256,444,445],{},"Plan","), and only the edge file ",[256,448,449],{},"internal\u002FCanvas.scala"," touches the jatatui buffers (",[452,453,454],"a",{"href":136},"ADR 0006",[252,456,457,458,461,462,465,466,259],{},"Bad: jatatui is 0.x with one maintainer. Its JNI copy leaves one 904 KB ",[256,459,460],{},"\u002Ftmp\u002Fjni-*"," folder per start; tui4s deletes it on Linux through ",[256,463,464],{},"\u002Fproc\u002Fself\u002Fmaps"," (",[452,467,468],{"href":188},"ADR 0019",[252,470,471],{},"Bad: the JNI cuts a typed character outside the Basic Multilingual Plane to 16 bits. A bracketed paste keeps it.",[252,473,474],{},"Bad: the native library ships for macOS (arm64, x86_64), Linux (x86_64, arm64, glibc 2.34 or later) and Windows x86_64 only. There is no musl or Windows arm64 build.",[229,476,478],{"id":477},"references","References",[249,480,481,484,487],{},[252,482,483],{},"Decision log: second round (2026-09-30).",[252,485,486],{},"Plan: D-12, D-13. Research reports on tui-scala and jatatui of 2026-09-30 (in the notes; the facts above are copied from them).",[252,488,489],{},"CONTRIBUTING.md: \"Rules for code\" (one fiber, the input thread, the JVM flag, no tui-scala). ",{"title":491,"searchDepth":492,"depth":492,"links":493},"",2,[494,495,496,497,498,499,500],{"id":231,"depth":492,"text":232},{"id":239,"depth":492,"text":240},{"id":246,"depth":492,"text":247},{"id":301,"depth":492,"text":302},{"id":385,"depth":492,"text":386},{"id":423,"depth":492,"text":424},{"id":477,"depth":492,"text":478},"jatatui 0.31.0 (a Java port of ratatui) with its crossterm JNI library is the terminal backend; tui-scala never goes on the classpath.","md",null,{},true,{"title":119,"description":501},"RJ84rPQ17vW8rNcijwdmdJAD6KUtiHEFbRIePt6AvAc",[509,511],{"title":115,"path":116,"stem":117,"description":510,"children":-1},"tui4s has no UI DSL of its own: kyo-ui describes the screen, and tui4s is its terminal runner.",{"title":123,"path":124,"stem":125,"description":512,"children":-1},"Kyo and kyo-ui stay at 1.0.0-RC7 for the first release, on JDK 25 and Scala 3.9.0, and a tasty-query surface guard pins the kyo-ui members that the runner reads.",1792528755766]