[{"data":1,"prerenderedAt":1025},["ShallowReactive",2],{"navigation_docs":3,"landing":223},[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,"page":-1},"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":225,"body":226,"description":225,"extension":1016,"meta":1017,"navigation":354,"path":1018,"seo":1019,"stem":1023,"__hash__":1024},"landing\u002Findex.md","",{"type":227,"value":228,"toc":1014},"minimark",[229,268,936,1010],[230,231,232,236,253],"u-page-hero",{},[233,234,235],"template",{"v-slot:title":225},"kyo-ui in the terminal.",[233,237,238,239,246,247,252],{"v-slot:description":225},"tui4s runs a ",[240,241,245],"a",{"href":242,"rel":243},"https:\u002F\u002Fgithub.com\u002Fgetkyo\u002Fkyo",[244],"nofollow","kyo-ui"," value in the terminal, in Scala 3. kyo-ui describes the screen. tui4s lays it out in cells, follows its signals, sends the keys to it and paints it with ",[240,248,251],{"href":249,"rel":250},"https:\u002F\u002Fgithub.com\u002Foyvindberg\u002Fjatatui",[244],"jatatui",".",[233,254,255,262],{"v-slot:links":225},[256,257,261],"u-button",{"color":258,"size":259,"to":12,"trailing-icon":260},"neutral","xl","i-lucide-arrow-right","Get started",[256,263,267],{"color":258,"size":259,"to":264,"icon":265,"variant":266},"https:\u002F\u002Fgithub.com\u002Fyuri4n\u002Ftui4s","i-simple-icons-github","outline","Source on GitHub",[269,270,273,931],"u-page-section",{"description":271,"title":272},"This is the complete Counter example. The UI is plain kyo-ui, and `TuiApp` is its main class.","The smallest app",[274,275,280],"pre",{"className":276,"code":277,"filename":278,"language":279,"meta":225,"style":225},"language-scala shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import kyo.*\nimport kyo.UI.*\nimport tui4s.{Tui, TuiApp, initSig}\n\n\u002F** The Counter: a kyo-ui UI with one piece of state (a `SignalRef`), key\n  * handlers and two buttons. `+` and `-` change the count, Tab moves between\n  * the buttons, Enter and Space press them, `q` quits (and so does Ctrl-C).\n  *\u002F\nobject Counter:\n\n    \u002F** The Counter UI; `quit` ends the run. *\u002F\n    def ui(quit: Unit \u003C Sync): UI \u003C Sync =\n      initSig(0).map(count => view(count, quit))\n\n    \u002F** The Counter view over a count that the caller owns. *\u002F\n    def view(count: SignalRef[Int], quit: Unit \u003C Sync): UI =\n      div.onKeyDown {\n        case KeyboardEvent(Keyboard.Char('+'), _, _) =>\n          count.updateAndGet(_ + 1)\n        case KeyboardEvent(Keyboard.Char('-'), _, _) =>\n          count.updateAndGet(_ - 1)\n        case KeyboardEvent(Keyboard.Char('q'), _, _) => quit\n        case _                                       => ()\n      }(\n        h1(\"Counter\"),\n        span(\"Count: \", count.map(_.toString)).id(\"count\"),\n        div.style(_.row.gap(1.px))(\n          button(\"+\").onClick(count.updateAndGet(_ + 1)),\n          button(\"-\").onClick(count.updateAndGet(_ - 1)),\n        ),\n        p(\"+ \u002F - change it, Tab moves, Enter presses, q quits\").style(\n          _.color(Style.Color.gray),\n        ),\n      )\nend Counter\n\n\u002F** Runs the Counter in the real terminal. Start it with its start script, not\n  * with `sbt run` (sbt also reads the keyboard):\n  * {{{\n  * sbt examples\u002Flaunchers\n  * examples\u002Ftarget\u002Fbin\u002Fcounter\n  * }}}\n  *\u002F\nobject CounterApp extends TuiApp:\n    def ui: UI \u003C (Async & Scope) = Counter.ui(Tui.quit)\n","Counter.scala","scala",[281,282,283,302,318,349,356,363,369,375,381,393,398,404,449,467,472,478,523,532,569,583,611,622,653,666,675,692,716,728,750,769,775,791,808,813,819,828,833,839,845,851,857,863,869,874,890],"code",{"__ignoreMap":225},[284,285,288,292,296,299],"span",{"class":286,"line":287},"line",1,[284,289,291],{"class":290},"sbssI","import",[284,293,295],{"class":294},"sBMFI"," kyo",[284,297,252],{"class":298},"sMK4o",[284,300,301],{"class":294},"*\n",[284,303,305,307,309,311,314,316],{"class":286,"line":304},2,[284,306,291],{"class":290},[284,308,295],{"class":294},[284,310,252],{"class":298},[284,312,313],{"class":294},"UI",[284,315,252],{"class":298},[284,317,301],{"class":294},[284,319,321,323,326,328,332,335,338,341,343,346],{"class":286,"line":320},3,[284,322,291],{"class":290},[284,324,325],{"class":294}," tui4s",[284,327,252],{"class":298},[284,329,331],{"class":330},"sTEyZ","{",[284,333,334],{"class":294},"Tui",[284,336,337],{"class":330},", ",[284,339,340],{"class":294},"TuiApp",[284,342,337],{"class":330},[284,344,345],{"class":294},"initSig",[284,347,348],{"class":330},"}\n",[284,350,352],{"class":286,"line":351},4,[284,353,355],{"emptyLinePlaceholder":354},true,"\n",[284,357,359],{"class":286,"line":358},5,[284,360,362],{"class":361},"sHwdD","\u002F** The Counter: a kyo-ui UI with one piece of state (a `SignalRef`), key\n",[284,364,366],{"class":286,"line":365},6,[284,367,368],{"class":361},"  * handlers and two buttons. `+` and `-` change the count, Tab moves between\n",[284,370,372],{"class":286,"line":371},7,[284,373,374],{"class":361},"  * the buttons, Enter and Space press them, `q` quits (and so does Ctrl-C).\n",[284,376,378],{"class":286,"line":377},8,[284,379,380],{"class":361},"  *\u002F\n",[284,382,384,387,390],{"class":286,"line":383},9,[284,385,386],{"class":298},"object",[284,388,389],{"class":294}," Counter",[284,391,392],{"class":298},":\n",[284,394,396],{"class":286,"line":395},10,[284,397,355],{"emptyLinePlaceholder":354},[284,399,401],{"class":286,"line":400},11,[284,402,403],{"class":361},"    \u002F** The Counter UI; `quit` ends the run. *\u002F\n",[284,405,407,410,414,417,421,424,427,430,433,436,439,442,444,446],{"class":286,"line":406},12,[284,408,409],{"class":298},"    def",[284,411,413],{"class":412},"s2Zo4"," ui",[284,415,416],{"class":330},"(",[284,418,420],{"class":419},"sHdIc","quit",[284,422,423],{"class":330},": ",[284,425,426],{"class":294},"Unit",[284,428,429],{"class":298}," \u003C",[284,431,432],{"class":294}," Sync",[284,434,435],{"class":330},")",[284,437,438],{"class":298},":",[284,440,441],{"class":294}," UI",[284,443,429],{"class":298},[284,445,432],{"class":294},[284,447,448],{"class":298}," =\n",[284,450,452,455,458,461,464],{"class":286,"line":451},13,[284,453,454],{"class":330},"      initSig(",[284,456,457],{"class":290},"0",[284,459,460],{"class":330},").map(count ",[284,462,463],{"class":298},"=>",[284,465,466],{"class":330}," view(count, quit))\n",[284,468,470],{"class":286,"line":469},14,[284,471,355],{"emptyLinePlaceholder":354},[284,473,475],{"class":286,"line":474},15,[284,476,477],{"class":361},"    \u002F** The Counter view over a count that the caller owns. *\u002F\n",[284,479,481,483,486,488,491,493,496,499,502,505,507,509,511,513,515,517,519,521],{"class":286,"line":480},16,[284,482,409],{"class":298},[284,484,485],{"class":412}," view",[284,487,416],{"class":330},[284,489,490],{"class":419},"count",[284,492,423],{"class":330},[284,494,495],{"class":294},"SignalRef",[284,497,498],{"class":330},"[",[284,500,501],{"class":294},"Int",[284,503,504],{"class":330},"], ",[284,506,420],{"class":419},[284,508,423],{"class":330},[284,510,426],{"class":294},[284,512,429],{"class":298},[284,514,432],{"class":294},[284,516,435],{"class":330},[284,518,438],{"class":298},[284,520,441],{"class":294},[284,522,448],{"class":298},[284,524,526,529],{"class":286,"line":525},17,[284,527,528],{"class":330},"      div.onKeyDown ",[284,530,531],{"class":298},"{\n",[284,533,535,539,542,544,547,549,552,554,557,561,563,566],{"class":286,"line":534},18,[284,536,538],{"class":537},"s7zQu","        case",[284,540,541],{"class":294}," KeyboardEvent",[284,543,416],{"class":330},[284,545,546],{"class":294},"Keyboard",[284,548,252],{"class":330},[284,550,551],{"class":294},"Char",[284,553,416],{"class":330},[284,555,556],{"class":298},"'",[284,558,560],{"class":559},"sfazB","+",[284,562,556],{"class":298},[284,564,565],{"class":330},"), _, _) ",[284,567,568],{"class":298},"=>\n",[284,570,572,575,577,580],{"class":286,"line":571},19,[284,573,574],{"class":330},"          count.updateAndGet(_ ",[284,576,560],{"class":298},[284,578,579],{"class":290}," 1",[284,581,582],{"class":330},")\n",[284,584,586,588,590,592,594,596,598,600,602,605,607,609],{"class":286,"line":585},20,[284,587,538],{"class":537},[284,589,541],{"class":294},[284,591,416],{"class":330},[284,593,546],{"class":294},[284,595,252],{"class":330},[284,597,551],{"class":294},[284,599,416],{"class":330},[284,601,556],{"class":298},[284,603,604],{"class":559},"-",[284,606,556],{"class":298},[284,608,565],{"class":330},[284,610,568],{"class":298},[284,612,614,616,618,620],{"class":286,"line":613},21,[284,615,574],{"class":330},[284,617,604],{"class":298},[284,619,579],{"class":290},[284,621,582],{"class":330},[284,623,625,627,629,631,633,635,637,639,641,644,646,648,650],{"class":286,"line":624},22,[284,626,538],{"class":537},[284,628,541],{"class":294},[284,630,416],{"class":330},[284,632,546],{"class":294},[284,634,252],{"class":330},[284,636,551],{"class":294},[284,638,416],{"class":330},[284,640,556],{"class":298},[284,642,643],{"class":559},"q",[284,645,556],{"class":298},[284,647,565],{"class":330},[284,649,463],{"class":298},[284,651,652],{"class":330}," quit\n",[284,654,656,658,661,663],{"class":286,"line":655},23,[284,657,538],{"class":537},[284,659,660],{"class":330}," _                                       ",[284,662,463],{"class":298},[284,664,665],{"class":330}," ()\n",[284,667,669,672],{"class":286,"line":668},24,[284,670,671],{"class":298},"      }",[284,673,674],{"class":330},"(\n",[284,676,678,681,684,687,689],{"class":286,"line":677},25,[284,679,680],{"class":330},"        h1(",[284,682,683],{"class":298},"\"",[284,685,686],{"class":559},"Counter",[284,688,683],{"class":298},[284,690,691],{"class":330},"),\n",[284,693,695,698,700,703,705,708,710,712,714],{"class":286,"line":694},26,[284,696,697],{"class":330},"        span(",[284,699,683],{"class":298},[284,701,702],{"class":559},"Count: ",[284,704,683],{"class":298},[284,706,707],{"class":330},", count.map(_.toString)).id(",[284,709,683],{"class":298},[284,711,490],{"class":559},[284,713,683],{"class":298},[284,715,691],{"class":330},[284,717,719,722,725],{"class":286,"line":718},27,[284,720,721],{"class":330},"        div.style(_.row.gap(",[284,723,724],{"class":290},"1",[284,726,727],{"class":330},".px))(\n",[284,729,731,734,736,738,740,743,745,747],{"class":286,"line":730},28,[284,732,733],{"class":330},"          button(",[284,735,683],{"class":298},[284,737,560],{"class":559},[284,739,683],{"class":298},[284,741,742],{"class":330},").onClick(count.updateAndGet(_ ",[284,744,560],{"class":298},[284,746,579],{"class":290},[284,748,749],{"class":330},")),\n",[284,751,753,755,757,759,761,763,765,767],{"class":286,"line":752},29,[284,754,733],{"class":330},[284,756,683],{"class":298},[284,758,604],{"class":559},[284,760,683],{"class":298},[284,762,742],{"class":330},[284,764,604],{"class":298},[284,766,579],{"class":290},[284,768,749],{"class":330},[284,770,772],{"class":286,"line":771},30,[284,773,774],{"class":330},"        ),\n",[284,776,778,781,783,786,788],{"class":286,"line":777},31,[284,779,780],{"class":330},"        p(",[284,782,683],{"class":298},[284,784,785],{"class":559},"+ \u002F - change it, Tab moves, Enter presses, q quits",[284,787,683],{"class":298},[284,789,790],{"class":330},").style(\n",[284,792,794,797,800,802,805],{"class":286,"line":793},32,[284,795,796],{"class":330},"          _.color(",[284,798,799],{"class":294},"Style",[284,801,252],{"class":330},[284,803,804],{"class":294},"Color",[284,806,807],{"class":330},".gray),\n",[284,809,811],{"class":286,"line":810},33,[284,812,774],{"class":330},[284,814,816],{"class":286,"line":815},34,[284,817,818],{"class":330},"      )\n",[284,820,822,825],{"class":286,"line":821},35,[284,823,824],{"class":298},"end",[284,826,827],{"class":294}," Counter\n",[284,829,831],{"class":286,"line":830},36,[284,832,355],{"emptyLinePlaceholder":354},[284,834,836],{"class":286,"line":835},37,[284,837,838],{"class":361},"\u002F** Runs the Counter in the real terminal. Start it with its start script, not\n",[284,840,842],{"class":286,"line":841},38,[284,843,844],{"class":361},"  * with `sbt run` (sbt also reads the keyboard):\n",[284,846,848],{"class":286,"line":847},39,[284,849,850],{"class":361},"  * {{{\n",[284,852,854],{"class":286,"line":853},40,[284,855,856],{"class":361},"  * sbt examples\u002Flaunchers\n",[284,858,860],{"class":286,"line":859},41,[284,861,862],{"class":361},"  * examples\u002Ftarget\u002Fbin\u002Fcounter\n",[284,864,866],{"class":286,"line":865},42,[284,867,868],{"class":361},"  * }}}\n",[284,870,872],{"class":286,"line":871},43,[284,873,380],{"class":361},[284,875,877,879,882,885,888],{"class":286,"line":876},44,[284,878,386],{"class":298},[284,880,881],{"class":294}," CounterApp",[284,883,884],{"class":298}," extends",[284,886,887],{"class":294}," TuiApp",[284,889,392],{"class":298},[284,891,893,895,897,899,901,903,906,909,912,915,918,921,923,926,928],{"class":286,"line":892},45,[284,894,409],{"class":298},[284,896,413],{"class":412},[284,898,438],{"class":298},[284,900,441],{"class":294},[284,902,429],{"class":298},[284,904,905],{"class":330}," (",[284,907,908],{"class":294},"Async",[284,910,911],{"class":298}," &",[284,913,914],{"class":294}," Scope",[284,916,917],{"class":330},") ",[284,919,920],{"class":298},"=",[284,922,389],{"class":294},[284,924,925],{"class":330},".ui(",[284,927,334],{"class":294},[284,929,930],{"class":330},".quit)\n",[932,933],"cast",{"max-width":934,"src":935},"36rem","\u002Fcasts\u002Fcounter.cast",[269,937,938,941],{},[233,939,940],{"v-slot:title":225},"What tui4s gives",[233,942,943,953,964,979,986,998],{"v-slot:features":225},[944,945,947,950],"u-page-feature",{"icon":946,"to":34},"i-lucide-layout-template",[233,948,949],{"v-slot:title":225},"kyo-ui is the DSL",[233,951,952],{"v-slot:description":225},"tui4s has no UI DSL of its own. A kyo-ui value describes the screen, and its signals drive the frames.",[944,954,955,958],{"icon":24,"to":79},[233,956,957],{"v-slot:title":225},"Tests with no terminal",[233,959,960,963],{"v-slot:description":225},[281,961,962],{},"Headless"," runs the app, sends keys and reads the screen in a plain unit test.",[944,965,966,969],{"icon":66,"to":64},[233,967,968],{"v-slot:title":225},"Composables and widgets",[233,970,971,974,975,978],{"v-slot:description":225},[281,972,973],{},"tui4s-use"," and ",[281,976,977],{},"tui4s-widgets"," are plain kyo-ui code for the JVM and Scala.js.",[944,980,981,983],{"icon":76,"to":74},[233,982,73],{"v-slot:title":225},[233,984,985],{"v-slot:description":225},"The assistant example runs in the terminal and in a browser, from one shared code base.",[944,987,989,992],{"icon":988,"to":88},"i-lucide-shield-check",[233,990,991],{"v-slot:title":225},"The terminal comes back",[233,993,994,995,252],{"v-slot:description":225},"Each way that a run ends restores the terminal: a quit, a failure, a signal or ",[281,996,997],{},"System.exit",[944,999,1000,1003],{"icon":98,"to":103},[233,1001,1002],{"v-slot:title":225},"Ready for AI agents",[233,1004,1005,1006,1009],{"v-slot:description":225},"Each page is also Markdown, with ",[281,1007,1008],{},"llms.txt"," and an MCP server. A test checks each Scala block.",[1011,1012,1013],"style",{},"html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":225,"searchDepth":304,"depth":304,"links":1015},[],"md",{},"\u002F",{"title":1020,"description":1021,"ogImage":1022},"tui4s - a terminal runner for kyo-ui","Run a kyo-ui value in the terminal, in Scala 3, and test it with no terminal. A ZIO and a Cats Effect integration are not in this version (issues","\u002Fog-image.png","index","Zrunj3tvmtVquzhodyeXNBkShVklungRmFO0EvVKkgs",1792528751553]