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