[{"data":1,"prerenderedAt":3482},["ShallowReactive",2],{"navigation_docs":3,"-guide-composables":223,"-guide-composables-surround":3477},[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":58,"body":225,"description":3470,"extension":3471,"links":3472,"meta":3473,"navigation":3474,"path":59,"seo":3475,"stem":60,"__hash__":3476},"docs\u002F2.guide\u002F06.composables.md",{"type":226,"value":227,"toc":3464},"minimark",[228,233,254,800,804,827,830,834,1029,1046,1050,1061,1382,1394,1693,1702,2044,2054,2438,2444,2642,2648,2857,2863,2969,2975,3182,3189,3460],[229,230,232],"h2",{"id":231},"you-may-not-need-a-composable","You may not need a composable",[234,235,236,237,241,242,245,246,249,250,253],"p",{},"Kyo does most of the work in one or two lines. A ",[238,239,240],"code",{},"SignalRef"," holds state, ",[238,243,244],{},"Signal.map"," derives a value, and ",[238,247,248],{},"Signal.observe"," runs a setup for each value in a new ",[238,251,252],{},"Scope",". These five need no library:",[255,256,262],"pre",{"className":257,"code":258,"filename":259,"language":260,"meta":261,"style":261},"language-scala shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","    \u002F** A toggle: `updateAndGet` on a `SignalRef`. *\u002F\n    def toggle(on: SignalRef[Boolean])(using Frame): Unit \u003C Sync =\n      on.updateAndGet(!_).unit\n\n    \u002F** A counter that stops at `max`. *\u002F\n    def increment(count: SignalRef[Int], max: Int)(using Frame): Unit \u003C Sync =\n      count.updateAndGet(n => (n + 1).min(max)).unit\n\n    \u002F** An interval: a fiber that the caller's `Scope` ends. *\u002F\n    def every(period: Duration)(action: => Any \u003C Async)(using\n        Frame,\n    ): Unit \u003C (Sync & Scope) =\n      Fiber.init(Loop.forever(Async.sleep(period).andThen(action))).unit\n\n    \u002F** A timer that pauses: `Signal.observe` starts the ticks while `paused` is\n      * false, and interrupts them when it changes.\n      *\u002F\n    def timer(paused: Signal[Boolean], ticks: SignalRef[Int], period: Duration)(\n        using Frame,\n    ): Unit \u003C (Sync & Scope) =\n      Fiber\n        .init(\n          paused.observe(p =>\n            if p then Kyo.unit\n            else every(period)(ticks.updateAndGet(_ + 1)),\n          ),\n        )\n        .unit\n\n    \u002F** A derived value: `Signal.map`. *\u002F\n    def label(count: Signal[Int])(using Frame): Signal[String] =\n      count.map(n => s\"$n items\")\n","Native.scala","scala","",[238,263,264,273,331,343,350,356,407,429,434,440,480,489,519,540,545,551,557,563,609,620,643,649,655,664,683,699,705,711,717,722,728,771],{"__ignoreMap":261},[265,266,269],"span",{"class":267,"line":268},"line",1,[265,270,272],{"class":271},"sHwdD","    \u002F** A toggle: `updateAndGet` on a `SignalRef`. *\u002F\n",[265,274,276,280,284,288,292,295,298,301,304,307,310,313,316,319,322,325,328],{"class":267,"line":275},2,[265,277,279],{"class":278},"sMK4o","    def",[265,281,283],{"class":282},"s2Zo4"," toggle",[265,285,287],{"class":286},"sTEyZ","(",[265,289,291],{"class":290},"sHdIc","on",[265,293,294],{"class":286},": ",[265,296,240],{"class":297},"sBMFI",[265,299,300],{"class":286},"[",[265,302,303],{"class":297},"Boolean",[265,305,306],{"class":286},"])(",[265,308,309],{"class":278},"using",[265,311,312],{"class":297}," Frame",[265,314,315],{"class":286},")",[265,317,318],{"class":278},":",[265,320,321],{"class":297}," Unit",[265,323,324],{"class":278}," \u003C",[265,326,327],{"class":297}," Sync",[265,329,330],{"class":278}," =\n",[265,332,334,337,340],{"class":267,"line":333},3,[265,335,336],{"class":286},"      on.updateAndGet(",[265,338,339],{"class":278},"!",[265,341,342],{"class":286},"_).unit\n",[265,344,346],{"class":267,"line":345},4,[265,347,349],{"emptyLinePlaceholder":348},true,"\n",[265,351,353],{"class":267,"line":352},5,[265,354,355],{"class":271},"    \u002F** A counter that stops at `max`. *\u002F\n",[265,357,359,361,364,366,369,371,373,375,378,381,384,386,388,391,393,395,397,399,401,403,405],{"class":267,"line":358},6,[265,360,279],{"class":278},[265,362,363],{"class":282}," increment",[265,365,287],{"class":286},[265,367,368],{"class":290},"count",[265,370,294],{"class":286},[265,372,240],{"class":297},[265,374,300],{"class":286},[265,376,377],{"class":297},"Int",[265,379,380],{"class":286},"], ",[265,382,383],{"class":290},"max",[265,385,294],{"class":286},[265,387,377],{"class":297},[265,389,390],{"class":286},")(",[265,392,309],{"class":278},[265,394,312],{"class":297},[265,396,315],{"class":286},[265,398,318],{"class":278},[265,400,321],{"class":297},[265,402,324],{"class":278},[265,404,327],{"class":297},[265,406,330],{"class":278},[265,408,410,413,416,419,422,426],{"class":267,"line":409},7,[265,411,412],{"class":286},"      count.updateAndGet(n ",[265,414,415],{"class":278},"=>",[265,417,418],{"class":286}," (n ",[265,420,421],{"class":278},"+",[265,423,425],{"class":424},"sbssI"," 1",[265,427,428],{"class":286},").min(max)).unit\n",[265,430,432],{"class":267,"line":431},8,[265,433,349],{"emptyLinePlaceholder":348},[265,435,437],{"class":267,"line":436},9,[265,438,439],{"class":271},"    \u002F** An interval: a fiber that the caller's `Scope` ends. *\u002F\n",[265,441,443,445,448,450,453,455,458,460,463,465,467,470,472,475,477],{"class":267,"line":442},10,[265,444,279],{"class":278},[265,446,447],{"class":282}," every",[265,449,287],{"class":286},[265,451,452],{"class":290},"period",[265,454,294],{"class":286},[265,456,457],{"class":297},"Duration",[265,459,390],{"class":286},[265,461,462],{"class":290},"action",[265,464,294],{"class":286},[265,466,415],{"class":278},[265,468,469],{"class":297}," Any",[265,471,324],{"class":278},[265,473,474],{"class":297}," Async",[265,476,390],{"class":286},[265,478,479],{"class":278},"using\n",[265,481,483,486],{"class":267,"line":482},11,[265,484,485],{"class":297},"        Frame",[265,487,488],{"class":286},",\n",[265,490,492,495,497,499,501,504,507,510,513,516],{"class":267,"line":491},12,[265,493,494],{"class":286},"    )",[265,496,318],{"class":278},[265,498,321],{"class":297},[265,500,324],{"class":278},[265,502,503],{"class":286}," (",[265,505,506],{"class":297},"Sync",[265,508,509],{"class":278}," &",[265,511,512],{"class":297}," Scope",[265,514,515],{"class":286},") ",[265,517,518],{"class":278},"=\n",[265,520,522,525,528,531,534,537],{"class":267,"line":521},13,[265,523,524],{"class":297},"      Fiber",[265,526,527],{"class":286},".init(",[265,529,530],{"class":297},"Loop",[265,532,533],{"class":286},".forever(",[265,535,536],{"class":297},"Async",[265,538,539],{"class":286},".sleep(period).andThen(action))).unit\n",[265,541,543],{"class":267,"line":542},14,[265,544,349],{"emptyLinePlaceholder":348},[265,546,548],{"class":267,"line":547},15,[265,549,550],{"class":271},"    \u002F** A timer that pauses: `Signal.observe` starts the ticks while `paused` is\n",[265,552,554],{"class":267,"line":553},16,[265,555,556],{"class":271},"      * false, and interrupts them when it changes.\n",[265,558,560],{"class":267,"line":559},17,[265,561,562],{"class":271},"      *\u002F\n",[265,564,566,568,571,573,576,578,581,583,585,587,590,592,594,596,598,600,602,604,606],{"class":267,"line":565},18,[265,567,279],{"class":278},[265,569,570],{"class":282}," timer",[265,572,287],{"class":286},[265,574,575],{"class":290},"paused",[265,577,294],{"class":286},[265,579,580],{"class":297},"Signal",[265,582,300],{"class":286},[265,584,303],{"class":297},[265,586,380],{"class":286},[265,588,589],{"class":290},"ticks",[265,591,294],{"class":286},[265,593,240],{"class":297},[265,595,300],{"class":286},[265,597,377],{"class":297},[265,599,380],{"class":286},[265,601,452],{"class":290},[265,603,294],{"class":286},[265,605,457],{"class":297},[265,607,608],{"class":286},")(\n",[265,610,612,615,618],{"class":267,"line":611},19,[265,613,614],{"class":286},"        using ",[265,616,617],{"class":297},"Frame",[265,619,488],{"class":286},[265,621,623,625,627,629,631,633,635,637,639,641],{"class":267,"line":622},20,[265,624,494],{"class":286},[265,626,318],{"class":278},[265,628,321],{"class":297},[265,630,324],{"class":278},[265,632,503],{"class":286},[265,634,506],{"class":297},[265,636,509],{"class":278},[265,638,512],{"class":297},[265,640,515],{"class":286},[265,642,518],{"class":278},[265,644,646],{"class":267,"line":645},21,[265,647,648],{"class":297},"      Fiber\n",[265,650,652],{"class":267,"line":651},22,[265,653,654],{"class":286},"        .init(\n",[265,656,658,661],{"class":267,"line":657},23,[265,659,660],{"class":286},"          paused.observe(p ",[265,662,663],{"class":278},"=>\n",[265,665,667,671,674,677,680],{"class":267,"line":666},24,[265,668,670],{"class":669},"s7zQu","            if",[265,672,673],{"class":286}," p ",[265,675,676],{"class":669},"then",[265,678,679],{"class":297}," Kyo",[265,681,682],{"class":286},".unit\n",[265,684,686,689,692,694,696],{"class":267,"line":685},25,[265,687,688],{"class":669},"            else",[265,690,691],{"class":286}," every(period)(ticks.updateAndGet(_ ",[265,693,421],{"class":278},[265,695,425],{"class":424},[265,697,698],{"class":286},")),\n",[265,700,702],{"class":267,"line":701},26,[265,703,704],{"class":286},"          ),\n",[265,706,708],{"class":267,"line":707},27,[265,709,710],{"class":286},"        )\n",[265,712,714],{"class":267,"line":713},28,[265,715,716],{"class":286},"        .unit\n",[265,718,720],{"class":267,"line":719},29,[265,721,349],{"emptyLinePlaceholder":348},[265,723,725],{"class":267,"line":724},30,[265,726,727],{"class":271},"    \u002F** A derived value: `Signal.map`. *\u002F\n",[265,729,731,733,736,738,740,742,744,746,748,750,752,754,756,758,761,763,766,769],{"class":267,"line":730},31,[265,732,279],{"class":278},[265,734,735],{"class":282}," label",[265,737,287],{"class":286},[265,739,368],{"class":290},[265,741,294],{"class":286},[265,743,580],{"class":297},[265,745,300],{"class":286},[265,747,377],{"class":297},[265,749,306],{"class":286},[265,751,309],{"class":278},[265,753,312],{"class":297},[265,755,315],{"class":286},[265,757,318],{"class":278},[265,759,760],{"class":297}," Signal",[265,762,300],{"class":286},[265,764,765],{"class":297},"String",[265,767,768],{"class":286},"] ",[265,770,518],{"class":278},[265,772,774,777,779,782,785,788,791,795,797],{"class":267,"line":773},32,[265,775,776],{"class":286},"      count.map(n ",[265,778,415],{"class":278},[265,780,781],{"class":278}," s",[265,783,784],{"class":278},"\"",[265,786,787],{"class":278},"$",[265,789,790],{"class":286},"n",[265,792,794],{"class":793},"sfazB"," items",[265,796,784],{"class":278},[265,798,799],{"class":286},")\n",[229,801,803],{"id":802},"add-tui4s-use","Add tui4s-use",[255,805,810],{"className":806,"code":807,"filename":808,"language":809,"meta":261,"style":261},"language-sbt shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","libraryDependencies += \"io.github.yuri4n\" %% \"tui4s-use\" % \"\u003Cversion>\"\n\u002F\u002F a Scala.js or a cross-built project:\nlibraryDependencies += \"io.github.yuri4n\" %%% \"tui4s-use\" % \"\u003Cversion>\"\n","build.sbt","sbt",[238,811,812,817,822],{"__ignoreMap":261},[265,813,814],{"class":267,"line":268},[265,815,816],{},"libraryDependencies += \"io.github.yuri4n\" %% \"tui4s-use\" % \"\u003Cversion>\"\n",[265,818,819],{"class":267,"line":275},[265,820,821],{},"\u002F\u002F a Scala.js or a cross-built project:\n",[265,823,824],{"class":267,"line":333},[265,825,826],{},"libraryDependencies += \"io.github.yuri4n\" %%% \"tui4s-use\" % \"\u003Cversion>\"\n",[234,828,829],{},"tui4s-use does not depend on tui4s-core. It builds for the JVM and for Scala.js, so a composable works in the terminal and on the web.",[229,831,833],{"id":832},"the-composables","The composables",[835,836,837,850],"table",{},[838,839,840],"thead",{},[841,842,843,847],"tr",{},[844,845,846],"th",{},"Composable",[844,848,849],{},"What it gives",[851,852,853,868,878,892,906,920,933,947,957,967,985,998,1019],"tbody",{},[841,854,855,861],{},[856,857,858],"td",{},[238,859,860],{},"useNow(every)",[856,862,863,864,867],{},"The time of Kyo's ",[238,865,866],{},"Clock"," as a signal.",[841,869,870,875],{},[856,871,872],{},[238,873,874],{},"timeAgo(at, now, format)",[856,876,877],{},"\"5 minutes ago\", with a formatter that you can replace.",[841,879,880,889],{},[856,881,882,885,886],{},[238,883,884],{},"debounced(s, quiet)",", ",[238,887,888],{},"throttled(s, every)",[856,890,891],{},"A signal after a quiet time, or at most once per window.",[841,893,894,899],{},[856,895,896],{},[238,897,898],{},"useDebouncedFn(quiet)(f)",[856,900,901,902,905],{},"A function that runs ",[238,903,904],{},"f"," after a quiet time.",[841,907,908,913],{},[856,909,910],{},[238,911,912],{},"useAsync(input)(f)",[856,914,915,916,919],{},"The ",[238,917,918],{},"AsyncState"," of the work for the current input. A new input interrupts the old work.",[841,921,922,930],{},[856,923,924,885,927],{},[238,925,926],{},"useStreamFold(s, zero)(f)",[238,928,929],{},"useStreamText(s)",[856,931,932],{},"A stream folded into a signal, with the status of the stream.",[841,934,935,940],{},[856,936,937],{},[238,938,939],{},"Keymap.of(bindings*)",[856,941,942,943,946],{},"A key map as data: a handler for ",[238,944,945],{},"onKeyDown"," and the key hints.",[841,948,949,954],{},[856,950,951],{},[238,952,953],{},"useSelection(items, key)",[856,955,956],{},"The selected item by its key, with the moves.",[841,958,959,964],{},[856,960,961],{},[238,962,963],{},"useScrollWindow(items, size, selected)",[856,965,966],{},"The visible slice of a long list.",[841,968,969,974],{},[856,970,971],{},[238,972,973],{},"useDialog[Q, A]",[856,975,976,977,980,981,984],{},"A modal flow: ",[238,978,979],{},"open"," waits for the answer, ",[238,982,983],{},"launch"," starts it from a key handler.",[841,986,987,992],{},[856,988,989],{},[238,990,991],{},"useHistory(ref, limit)",[856,993,994,995,997],{},"Undo and redo over a ",[238,996,240],{},".",[841,999,1000,1005],{},[856,1001,1002],{},[238,1003,1004],{},"usePersisted(key, initial, storage)",[856,1006,1007,1008,1010,1011,1014,1015,1018],{},"A ",[238,1009,240],{}," kept in a ",[238,1012,1013],{},"Storage",": files on the JVM, ",[238,1016,1017],{},"localStorage"," in a browser.",[841,1020,1021,1026],{},[856,1022,1023],{},[238,1024,1025],{},"useProcess(cmd)",[856,1027,1028],{},"A program that runs (JVM only): its output lines and its exit code.",[234,1030,1031,1032,1034,1035,885,1038,1041,1042,1045],{},"A composable that starts a fiber needs the ",[238,1033,252],{}," of its caller. Call a composable once, where its state lives: at the start of the app, or in the body of a component. Do not call it in a ",[238,1036,1037],{},"render",[238,1039,1040],{},"when"," or ",[238,1043,1044],{},"foreach"," closure: such a closure runs again on each change.",[229,1047,1049],{"id":1048},"recipes","Recipes",[234,1051,1052,1053,1056,1057,1060],{},"A search as the user types (",[238,1054,1055],{},"debounced"," and ",[238,1058,1059],{},"useAsync","):",[255,1062,1065],{"className":257,"code":1063,"filename":1064,"language":260,"meta":261,"style":261},"    \u002F** A search as the user types: `debounced` waits until the query is quiet\n      * for 200 ms, and `useAsync` runs `find` for it. A new query interrupts\n      * the search for the old one.\n      *\u002F\n    def search(find: String => Chunk[String] \u003C Async)(using\n        Frame,\n    ): UI \u003C (Sync & Scope) =\n      for\n          query   \u003C- Signal.initRef(\"\")\n          quiet   \u003C- debounced(query, 200.millis)\n          results \u003C- useAsync(quiet.map(q => Maybe.when(q.nonEmpty)(q)))(find)\n      yield div(\n        input.value(query).focusAuto(true),\n        results.map {\n          case AsyncState.Idle       => span(\"type to search\")\n          case AsyncState.Loading    => span(\"searching...\")\n          case AsyncState.Done(rows) => span(s\"found: ${rows.mkString(\", \")}\")\n          case AsyncState.Failed(e)  => span(s\"failed: $e\")\n        },\n      )\n","Recipes.scala",[238,1066,1067,1072,1077,1082,1086,1123,1129,1152,1157,1175,1191,1209,1217,1228,1236,1264,1289,1336,1370,1377],{"__ignoreMap":261},[265,1068,1069],{"class":267,"line":268},[265,1070,1071],{"class":271},"    \u002F** A search as the user types: `debounced` waits until the query is quiet\n",[265,1073,1074],{"class":267,"line":275},[265,1075,1076],{"class":271},"      * for 200 ms, and `useAsync` runs `find` for it. A new query interrupts\n",[265,1078,1079],{"class":267,"line":333},[265,1080,1081],{"class":271},"      * the search for the old one.\n",[265,1083,1084],{"class":267,"line":345},[265,1085,562],{"class":271},[265,1087,1088,1090,1093,1095,1098,1100,1102,1105,1108,1110,1112,1114,1117,1119,1121],{"class":267,"line":352},[265,1089,279],{"class":278},[265,1091,1092],{"class":282}," search",[265,1094,287],{"class":286},[265,1096,1097],{"class":290},"find",[265,1099,294],{"class":286},[265,1101,765],{"class":297},[265,1103,1104],{"class":278}," =>",[265,1106,1107],{"class":297}," Chunk",[265,1109,300],{"class":286},[265,1111,765],{"class":297},[265,1113,768],{"class":286},[265,1115,1116],{"class":278},"\u003C",[265,1118,474],{"class":297},[265,1120,390],{"class":286},[265,1122,479],{"class":278},[265,1124,1125,1127],{"class":267,"line":358},[265,1126,485],{"class":297},[265,1128,488],{"class":286},[265,1130,1131,1133,1135,1138,1140,1142,1144,1146,1148,1150],{"class":267,"line":409},[265,1132,494],{"class":286},[265,1134,318],{"class":278},[265,1136,1137],{"class":297}," UI",[265,1139,324],{"class":278},[265,1141,503],{"class":286},[265,1143,506],{"class":297},[265,1145,509],{"class":278},[265,1147,512],{"class":297},[265,1149,515],{"class":286},[265,1151,518],{"class":278},[265,1153,1154],{"class":267,"line":431},[265,1155,1156],{"class":669},"      for\n",[265,1158,1159,1162,1165,1167,1170,1173],{"class":267,"line":436},[265,1160,1161],{"class":286},"          query   ",[265,1163,1164],{"class":278},"\u003C-",[265,1166,760],{"class":297},[265,1168,1169],{"class":286},".initRef(",[265,1171,1172],{"class":278},"\"\"",[265,1174,799],{"class":286},[265,1176,1177,1180,1182,1185,1188],{"class":267,"line":442},[265,1178,1179],{"class":286},"          quiet   ",[265,1181,1164],{"class":278},[265,1183,1184],{"class":286}," debounced(query, ",[265,1186,1187],{"class":424},"200",[265,1189,1190],{"class":286},".millis)\n",[265,1192,1193,1196,1198,1201,1203,1206],{"class":267,"line":482},[265,1194,1195],{"class":286},"          results ",[265,1197,1164],{"class":278},[265,1199,1200],{"class":286}," useAsync(quiet.map(q ",[265,1202,415],{"class":278},[265,1204,1205],{"class":297}," Maybe",[265,1207,1208],{"class":286},".when(q.nonEmpty)(q)))(find)\n",[265,1210,1211,1214],{"class":267,"line":491},[265,1212,1213],{"class":669},"      yield",[265,1215,1216],{"class":286}," div(\n",[265,1218,1219,1222,1225],{"class":267,"line":521},[265,1220,1221],{"class":286},"        input.value(query).focusAuto(",[265,1223,1224],{"class":278},"true",[265,1226,1227],{"class":286},"),\n",[265,1229,1230,1233],{"class":267,"line":542},[265,1231,1232],{"class":286},"        results.map ",[265,1234,1235],{"class":278},"{\n",[265,1237,1238,1241,1244,1246,1249,1252,1255,1257,1260,1262],{"class":267,"line":547},[265,1239,1240],{"class":669},"          case",[265,1242,1243],{"class":297}," AsyncState",[265,1245,997],{"class":286},[265,1247,1248],{"class":297},"Idle",[265,1250,1251],{"class":278},"       =>",[265,1253,1254],{"class":286}," span(",[265,1256,784],{"class":278},[265,1258,1259],{"class":793},"type to search",[265,1261,784],{"class":278},[265,1263,799],{"class":286},[265,1265,1266,1268,1270,1272,1275,1278,1280,1282,1285,1287],{"class":267,"line":553},[265,1267,1240],{"class":669},[265,1269,1243],{"class":297},[265,1271,997],{"class":286},[265,1273,1274],{"class":297},"Loading",[265,1276,1277],{"class":278},"    =>",[265,1279,1254],{"class":286},[265,1281,784],{"class":278},[265,1283,1284],{"class":793},"searching...",[265,1286,784],{"class":278},[265,1288,799],{"class":286},[265,1290,1291,1293,1295,1297,1300,1303,1305,1307,1310,1312,1315,1318,1321,1323,1325,1327,1329,1332,1334],{"class":267,"line":559},[265,1292,1240],{"class":669},[265,1294,1243],{"class":297},[265,1296,997],{"class":286},[265,1298,1299],{"class":297},"Done",[265,1301,1302],{"class":286},"(rows) ",[265,1304,415],{"class":278},[265,1306,1254],{"class":286},[265,1308,1309],{"class":278},"s",[265,1311,784],{"class":278},[265,1313,1314],{"class":793},"found: ",[265,1316,1317],{"class":278},"${",[265,1319,1320],{"class":286},"rows.mkString(",[265,1322,784],{"class":278},[265,1324,885],{"class":793},[265,1326,784],{"class":278},[265,1328,315],{"class":286},[265,1330,1331],{"class":278},"}",[265,1333,784],{"class":278},[265,1335,799],{"class":286},[265,1337,1338,1340,1342,1344,1347,1350,1352,1354,1356,1358,1361,1363,1366,1368],{"class":267,"line":565},[265,1339,1240],{"class":669},[265,1341,1243],{"class":297},[265,1343,997],{"class":286},[265,1345,1346],{"class":297},"Failed",[265,1348,1349],{"class":286},"(e)  ",[265,1351,415],{"class":278},[265,1353,1254],{"class":286},[265,1355,1309],{"class":278},[265,1357,784],{"class":278},[265,1359,1360],{"class":793},"failed: ",[265,1362,787],{"class":278},[265,1364,1365],{"class":286},"e",[265,1367,784],{"class":278},[265,1369,799],{"class":286},[265,1371,1372,1375],{"class":267,"line":611},[265,1373,1374],{"class":278},"        }",[265,1376,488],{"class":286},[265,1378,1379],{"class":267,"line":622},[265,1380,1381],{"class":286},"      )\n",[234,1383,1384,1385,885,1388,885,1391,1060],{},"The keys of an app as data, and how long ago the last sample came (",[238,1386,1387],{},"Keymap.of",[238,1389,1390],{},"useNow",[238,1392,1393],{},"timeAgo",[255,1395,1398],{"className":257,"code":1396,"filename":1397,"language":260,"meta":261,"style":261},"    \u002F** The keys of the Dashboard as data: the root handler runs them, and the\n      * help box lists them.\n      *\u002F\n    def keys(s: State)(using Frame): Keymap =\n      Keymap.of(\n        Binding.of(\"pause or resume the sampler\", Chord.char('p'))(pause(s)),\n        Binding.of(\"this help (Esc closes it)\", Chord.char('?'))(\n          s.help.set(true),\n        ),\n        Binding.of(\"quit\", Chord.char('q'))(Tui.quit),\n      )\n\n    \u002F** How long ago the last sample came (\"updated 5 seconds ago\"), from a\n      * clock that ticks each second; the clock stops with the `Scope` of the\n      * app.\n      *\u002F\n    def stamp(s: State)(using Frame): UI \u003C (Sync & Scope) =\n      for now \u003C- useNow(1.second)\n      yield span(timeAgo(s.updated, now).map(t => s\"updated $t\"))\n        .style(_.color(Style.Color.gray))\n","Dashboard.scala",[238,1399,1400,1405,1410,1414,1445,1453,1486,1515,1524,1529,1564,1568,1572,1577,1582,1587,1591,1632,1651,1677],{"__ignoreMap":261},[265,1401,1402],{"class":267,"line":268},[265,1403,1404],{"class":271},"    \u002F** The keys of the Dashboard as data: the root handler runs them, and the\n",[265,1406,1407],{"class":267,"line":275},[265,1408,1409],{"class":271},"      * help box lists them.\n",[265,1411,1412],{"class":267,"line":333},[265,1413,562],{"class":271},[265,1415,1416,1418,1421,1423,1425,1427,1430,1432,1434,1436,1438,1440,1443],{"class":267,"line":345},[265,1417,279],{"class":278},[265,1419,1420],{"class":282}," keys",[265,1422,287],{"class":286},[265,1424,1309],{"class":290},[265,1426,294],{"class":286},[265,1428,1429],{"class":297},"State",[265,1431,390],{"class":286},[265,1433,309],{"class":278},[265,1435,312],{"class":297},[265,1437,315],{"class":286},[265,1439,318],{"class":278},[265,1441,1442],{"class":297}," Keymap",[265,1444,330],{"class":278},[265,1446,1447,1450],{"class":267,"line":352},[265,1448,1449],{"class":297},"      Keymap",[265,1451,1452],{"class":286},".of(\n",[265,1454,1455,1458,1461,1463,1466,1468,1470,1473,1476,1479,1481,1483],{"class":267,"line":358},[265,1456,1457],{"class":297},"        Binding",[265,1459,1460],{"class":286},".of(",[265,1462,784],{"class":278},[265,1464,1465],{"class":793},"pause or resume the sampler",[265,1467,784],{"class":278},[265,1469,885],{"class":286},[265,1471,1472],{"class":297},"Chord",[265,1474,1475],{"class":286},".char(",[265,1477,1478],{"class":278},"'",[265,1480,234],{"class":793},[265,1482,1478],{"class":278},[265,1484,1485],{"class":286},"))(pause(s)),\n",[265,1487,1488,1490,1492,1494,1497,1499,1501,1503,1505,1507,1510,1512],{"class":267,"line":409},[265,1489,1457],{"class":297},[265,1491,1460],{"class":286},[265,1493,784],{"class":278},[265,1495,1496],{"class":793},"this help (Esc closes it)",[265,1498,784],{"class":278},[265,1500,885],{"class":286},[265,1502,1472],{"class":297},[265,1504,1475],{"class":286},[265,1506,1478],{"class":278},[265,1508,1509],{"class":793},"?",[265,1511,1478],{"class":278},[265,1513,1514],{"class":286},"))(\n",[265,1516,1517,1520,1522],{"class":267,"line":431},[265,1518,1519],{"class":286},"          s.help.set(",[265,1521,1224],{"class":278},[265,1523,1227],{"class":286},[265,1525,1526],{"class":267,"line":436},[265,1527,1528],{"class":286},"        ),\n",[265,1530,1531,1533,1535,1537,1540,1542,1544,1546,1548,1550,1553,1555,1558,1561],{"class":267,"line":442},[265,1532,1457],{"class":297},[265,1534,1460],{"class":286},[265,1536,784],{"class":278},[265,1538,1539],{"class":793},"quit",[265,1541,784],{"class":278},[265,1543,885],{"class":286},[265,1545,1472],{"class":297},[265,1547,1475],{"class":286},[265,1549,1478],{"class":278},[265,1551,1552],{"class":793},"q",[265,1554,1478],{"class":278},[265,1556,1557],{"class":286},"))(",[265,1559,1560],{"class":297},"Tui",[265,1562,1563],{"class":286},".quit),\n",[265,1565,1566],{"class":267,"line":482},[265,1567,1381],{"class":286},[265,1569,1570],{"class":267,"line":491},[265,1571,349],{"emptyLinePlaceholder":348},[265,1573,1574],{"class":267,"line":521},[265,1575,1576],{"class":271},"    \u002F** How long ago the last sample came (\"updated 5 seconds ago\"), from a\n",[265,1578,1579],{"class":267,"line":542},[265,1580,1581],{"class":271},"      * clock that ticks each second; the clock stops with the `Scope` of the\n",[265,1583,1584],{"class":267,"line":547},[265,1585,1586],{"class":271},"      * app.\n",[265,1588,1589],{"class":267,"line":553},[265,1590,562],{"class":271},[265,1592,1593,1595,1598,1600,1602,1604,1606,1608,1610,1612,1614,1616,1618,1620,1622,1624,1626,1628,1630],{"class":267,"line":559},[265,1594,279],{"class":278},[265,1596,1597],{"class":282}," stamp",[265,1599,287],{"class":286},[265,1601,1309],{"class":290},[265,1603,294],{"class":286},[265,1605,1429],{"class":297},[265,1607,390],{"class":286},[265,1609,309],{"class":278},[265,1611,312],{"class":297},[265,1613,315],{"class":286},[265,1615,318],{"class":278},[265,1617,1137],{"class":297},[265,1619,324],{"class":278},[265,1621,503],{"class":286},[265,1623,506],{"class":297},[265,1625,509],{"class":278},[265,1627,512],{"class":297},[265,1629,515],{"class":286},[265,1631,518],{"class":278},[265,1633,1634,1637,1640,1642,1645,1648],{"class":267,"line":565},[265,1635,1636],{"class":669},"      for",[265,1638,1639],{"class":286}," now ",[265,1641,1164],{"class":278},[265,1643,1644],{"class":286}," useNow(",[265,1646,1647],{"class":424},"1",[265,1649,1650],{"class":286},".second)\n",[265,1652,1653,1655,1658,1660,1662,1664,1667,1669,1672,1674],{"class":267,"line":611},[265,1654,1213],{"class":669},[265,1656,1657],{"class":286}," span(timeAgo(s.updated, now).map(t ",[265,1659,415],{"class":278},[265,1661,781],{"class":278},[265,1663,784],{"class":278},[265,1665,1666],{"class":793},"updated ",[265,1668,787],{"class":278},[265,1670,1671],{"class":286},"t",[265,1673,784],{"class":278},[265,1675,1676],{"class":286},"))\n",[265,1678,1679,1682,1685,1687,1690],{"class":267,"line":622},[265,1680,1681],{"class":286},"        .style(_.color(",[265,1683,1684],{"class":297},"Style",[265,1686,997],{"class":286},[265,1688,1689],{"class":297},"Color",[265,1691,1692],{"class":286},".gray))\n",[234,1694,1695,1696,1056,1699,1060],{},"A long list with a selection and a visible window (",[238,1697,1698],{},"useSelection",[238,1700,1701],{},"useScrollWindow",[255,1703,1705],{"className":257,"code":1704,"filename":1064,"language":260,"meta":261,"style":261},"    \u002F** A long list that shows 5 rows: `useSelection` keeps the selected row by\n      * its key, and `useScrollWindow` gives the rows to show. The arrows move\n      * the selection.\n      *\u002F\n    def list(items: Signal[Chunk[String]])(using Frame): UI \u003C (Sync & Scope) =\n      for\n          selection \u003C- useSelection(items, identity[String])\n          window    \u003C- useScrollWindow(items, Signal.initConst(5), selection.index)\n          keys       = Keymap.of(\n                         Binding.of(\"up\", Chord(Keyboard.ArrowUp))(selection.moveUp),\n                         Binding.of(\"down\", Chord(Keyboard.ArrowDown))(\n                           selection.moveDown,\n                         ),\n                       )\n      yield div\n        .tabIndex(0)\n        .focusAuto(true)\n        .onKeyDown(keys.handler)(\n          window\n            .combineLatest(selection.key)\n            .map((w, key) =>\n              div(\n                UI.fragment(\n                  w.slice.map(row =>\n                    span(if key.contains(row) then s\"> $row\" else s\"  $row\"),\n                  )*,\n                ),\n              ),\n            ),\n        )\n",[238,1706,1707,1712,1717,1722,1726,1778,1782,1797,1818,1830,1861,1889,1894,1899,1904,1911,1921,1930,1935,1940,1945,1952,1957,1965,1972,2015,2025,2030,2035,2040],{"__ignoreMap":261},[265,1708,1709],{"class":267,"line":268},[265,1710,1711],{"class":271},"    \u002F** A long list that shows 5 rows: `useSelection` keeps the selected row by\n",[265,1713,1714],{"class":267,"line":275},[265,1715,1716],{"class":271},"      * its key, and `useScrollWindow` gives the rows to show. The arrows move\n",[265,1718,1719],{"class":267,"line":333},[265,1720,1721],{"class":271},"      * the selection.\n",[265,1723,1724],{"class":267,"line":345},[265,1725,562],{"class":271},[265,1727,1728,1730,1733,1735,1738,1740,1742,1744,1747,1749,1751,1754,1756,1758,1760,1762,1764,1766,1768,1770,1772,1774,1776],{"class":267,"line":352},[265,1729,279],{"class":278},[265,1731,1732],{"class":282}," list",[265,1734,287],{"class":286},[265,1736,1737],{"class":290},"items",[265,1739,294],{"class":286},[265,1741,580],{"class":297},[265,1743,300],{"class":286},[265,1745,1746],{"class":297},"Chunk",[265,1748,300],{"class":286},[265,1750,765],{"class":297},[265,1752,1753],{"class":286},"]])(",[265,1755,309],{"class":278},[265,1757,312],{"class":297},[265,1759,315],{"class":286},[265,1761,318],{"class":278},[265,1763,1137],{"class":297},[265,1765,324],{"class":278},[265,1767,503],{"class":286},[265,1769,506],{"class":297},[265,1771,509],{"class":278},[265,1773,512],{"class":297},[265,1775,515],{"class":286},[265,1777,518],{"class":278},[265,1779,1780],{"class":267,"line":358},[265,1781,1156],{"class":669},[265,1783,1784,1787,1789,1792,1794],{"class":267,"line":409},[265,1785,1786],{"class":286},"          selection ",[265,1788,1164],{"class":278},[265,1790,1791],{"class":286}," useSelection(items, identity[",[265,1793,765],{"class":297},[265,1795,1796],{"class":286},"])\n",[265,1798,1799,1802,1804,1807,1809,1812,1815],{"class":267,"line":431},[265,1800,1801],{"class":286},"          window    ",[265,1803,1164],{"class":278},[265,1805,1806],{"class":286}," useScrollWindow(items, ",[265,1808,580],{"class":297},[265,1810,1811],{"class":286},".initConst(",[265,1813,1814],{"class":424},"5",[265,1816,1817],{"class":286},"), selection.index)\n",[265,1819,1820,1823,1826,1828],{"class":267,"line":436},[265,1821,1822],{"class":286},"          keys       ",[265,1824,1825],{"class":278},"=",[265,1827,1442],{"class":297},[265,1829,1452],{"class":286},[265,1831,1832,1835,1837,1839,1842,1844,1846,1848,1850,1853,1855,1858],{"class":267,"line":442},[265,1833,1834],{"class":297},"                         Binding",[265,1836,1460],{"class":286},[265,1838,784],{"class":278},[265,1840,1841],{"class":793},"up",[265,1843,784],{"class":278},[265,1845,885],{"class":286},[265,1847,1472],{"class":297},[265,1849,287],{"class":286},[265,1851,1852],{"class":297},"Keyboard",[265,1854,997],{"class":286},[265,1856,1857],{"class":297},"ArrowUp",[265,1859,1860],{"class":286},"))(selection.moveUp),\n",[265,1862,1863,1865,1867,1869,1872,1874,1876,1878,1880,1882,1884,1887],{"class":267,"line":482},[265,1864,1834],{"class":297},[265,1866,1460],{"class":286},[265,1868,784],{"class":278},[265,1870,1871],{"class":793},"down",[265,1873,784],{"class":278},[265,1875,885],{"class":286},[265,1877,1472],{"class":297},[265,1879,287],{"class":286},[265,1881,1852],{"class":297},[265,1883,997],{"class":286},[265,1885,1886],{"class":297},"ArrowDown",[265,1888,1514],{"class":286},[265,1890,1891],{"class":267,"line":491},[265,1892,1893],{"class":286},"                           selection.moveDown,\n",[265,1895,1896],{"class":267,"line":521},[265,1897,1898],{"class":286},"                         ),\n",[265,1900,1901],{"class":267,"line":542},[265,1902,1903],{"class":286},"                       )\n",[265,1905,1906,1908],{"class":267,"line":547},[265,1907,1213],{"class":669},[265,1909,1910],{"class":286}," div\n",[265,1912,1913,1916,1919],{"class":267,"line":553},[265,1914,1915],{"class":286},"        .tabIndex(",[265,1917,1918],{"class":424},"0",[265,1920,799],{"class":286},[265,1922,1923,1926,1928],{"class":267,"line":559},[265,1924,1925],{"class":286},"        .focusAuto(",[265,1927,1224],{"class":278},[265,1929,799],{"class":286},[265,1931,1932],{"class":267,"line":565},[265,1933,1934],{"class":286},"        .onKeyDown(keys.handler)(\n",[265,1936,1937],{"class":267,"line":611},[265,1938,1939],{"class":286},"          window\n",[265,1941,1942],{"class":267,"line":622},[265,1943,1944],{"class":286},"            .combineLatest(selection.key)\n",[265,1946,1947,1950],{"class":267,"line":645},[265,1948,1949],{"class":286},"            .map((w, key) ",[265,1951,663],{"class":278},[265,1953,1954],{"class":267,"line":651},[265,1955,1956],{"class":286},"              div(\n",[265,1958,1959,1962],{"class":267,"line":657},[265,1960,1961],{"class":297},"                UI",[265,1963,1964],{"class":286},".fragment(\n",[265,1966,1967,1970],{"class":267,"line":666},[265,1968,1969],{"class":286},"                  w.slice.map(row ",[265,1971,663],{"class":278},[265,1973,1974,1977,1980,1983,1985,1987,1989,1992,1994,1997,1999,2002,2004,2006,2009,2011,2013],{"class":267,"line":685},[265,1975,1976],{"class":286},"                    span(",[265,1978,1979],{"class":669},"if",[265,1981,1982],{"class":286}," key.contains(row) ",[265,1984,676],{"class":669},[265,1986,781],{"class":278},[265,1988,784],{"class":278},[265,1990,1991],{"class":793},"> ",[265,1993,787],{"class":278},[265,1995,1996],{"class":286},"row",[265,1998,784],{"class":278},[265,2000,2001],{"class":669}," else",[265,2003,781],{"class":278},[265,2005,784],{"class":278},[265,2007,2008],{"class":278},"  $",[265,2010,1996],{"class":286},[265,2012,784],{"class":278},[265,2014,1227],{"class":286},[265,2016,2017,2020,2023],{"class":267,"line":701},[265,2018,2019],{"class":286},"                  )",[265,2021,2022],{"class":278},"*",[265,2024,488],{"class":286},[265,2026,2027],{"class":267,"line":707},[265,2028,2029],{"class":286},"                ),\n",[265,2031,2032],{"class":267,"line":713},[265,2033,2034],{"class":286},"              ),\n",[265,2036,2037],{"class":267,"line":719},[265,2038,2039],{"class":286},"            ),\n",[265,2041,2042],{"class":267,"line":724},[265,2043,710],{"class":286},[234,2045,2046,2047,2050,2051,2053],{},"A delete that asks first (",[238,2048,2049],{},"useDialog","). A key handler runs on the runner, one at a time, so it starts the flow with ",[238,2052,983],{},", which never waits:",[255,2055,2057],{"className":257,"code":2056,"filename":1064,"language":260,"meta":261,"style":261},"    \u002F** A delete that asks first: `d` starts a flow (`launch`) that opens the\n      * dialog and waits for its answer; `y` or `n` answers.\n      *\u002F\n    def confirm(items: SignalRef[Chunk[String]])(using\n        Frame,\n    ): UI \u003C (Sync & Scope) =\n      for\n          ask   \u003C- useDialog[String, Boolean]\n          delete = items.get.map(xs =>\n                     if xs.isEmpty then Kyo.unit\n                     else\n                         ask\n                           .open(s\"Delete ${xs.head}?\")\n                           .map(yes =>\n                             if yes then items.updateAndGet(_.drop(1)).unit\n                             else Kyo.unit,\n                           ),\n                   )\n          keys   = Keymap.of(\n                     Binding.of(\"delete\", Chord.char('d'))(ask.launch(delete)),\n                     Binding.of(\"yes\", Chord.char('y'))(ask.close(true)),\n                     Binding.of(\"no\", Chord.char('n'))(ask.close(false)),\n                   )\n      yield div\n        .tabIndex(0)\n        .focusAuto(true)\n        .onKeyDown(keys.handler)(\n          span(items.map(_.mkString(\", \"))),\n          span(ask.request.map(_.fold(\"\")(q => s\"$q (y\u002Fn)\"))),\n        )\n",[238,2058,2059,2064,2069,2073,2100,2106,2128,2132,2151,2163,2177,2182,2187,2212,2219,2237,2247,2252,2257,2268,2298,2331,2363,2367,2373,2381,2389,2393,2407,2434],{"__ignoreMap":261},[265,2060,2061],{"class":267,"line":268},[265,2062,2063],{"class":271},"    \u002F** A delete that asks first: `d` starts a flow (`launch`) that opens the\n",[265,2065,2066],{"class":267,"line":275},[265,2067,2068],{"class":271},"      * dialog and waits for its answer; `y` or `n` answers.\n",[265,2070,2071],{"class":267,"line":333},[265,2072,562],{"class":271},[265,2074,2075,2077,2080,2082,2084,2086,2088,2090,2092,2094,2096,2098],{"class":267,"line":345},[265,2076,279],{"class":278},[265,2078,2079],{"class":282}," confirm",[265,2081,287],{"class":286},[265,2083,1737],{"class":290},[265,2085,294],{"class":286},[265,2087,240],{"class":297},[265,2089,300],{"class":286},[265,2091,1746],{"class":297},[265,2093,300],{"class":286},[265,2095,765],{"class":297},[265,2097,1753],{"class":286},[265,2099,479],{"class":278},[265,2101,2102,2104],{"class":267,"line":352},[265,2103,485],{"class":297},[265,2105,488],{"class":286},[265,2107,2108,2110,2112,2114,2116,2118,2120,2122,2124,2126],{"class":267,"line":358},[265,2109,494],{"class":286},[265,2111,318],{"class":278},[265,2113,1137],{"class":297},[265,2115,324],{"class":278},[265,2117,503],{"class":286},[265,2119,506],{"class":297},[265,2121,509],{"class":278},[265,2123,512],{"class":297},[265,2125,515],{"class":286},[265,2127,518],{"class":278},[265,2129,2130],{"class":267,"line":409},[265,2131,1156],{"class":669},[265,2133,2134,2137,2139,2142,2144,2146,2148],{"class":267,"line":431},[265,2135,2136],{"class":286},"          ask   ",[265,2138,1164],{"class":278},[265,2140,2141],{"class":286}," useDialog[",[265,2143,765],{"class":297},[265,2145,885],{"class":286},[265,2147,303],{"class":297},[265,2149,2150],{"class":286},"]\n",[265,2152,2153,2156,2158,2161],{"class":267,"line":436},[265,2154,2155],{"class":286},"          delete ",[265,2157,1825],{"class":278},[265,2159,2160],{"class":286}," items.get.map(xs ",[265,2162,663],{"class":278},[265,2164,2165,2168,2171,2173,2175],{"class":267,"line":442},[265,2166,2167],{"class":669},"                     if",[265,2169,2170],{"class":286}," xs.isEmpty ",[265,2172,676],{"class":669},[265,2174,679],{"class":297},[265,2176,682],{"class":286},[265,2178,2179],{"class":267,"line":482},[265,2180,2181],{"class":669},"                     else\n",[265,2183,2184],{"class":267,"line":491},[265,2185,2186],{"class":286},"                         ask\n",[265,2188,2189,2192,2194,2196,2199,2201,2204,2206,2208,2210],{"class":267,"line":521},[265,2190,2191],{"class":286},"                           .open(",[265,2193,1309],{"class":278},[265,2195,784],{"class":278},[265,2197,2198],{"class":793},"Delete ",[265,2200,1317],{"class":278},[265,2202,2203],{"class":286},"xs.head",[265,2205,1331],{"class":278},[265,2207,1509],{"class":793},[265,2209,784],{"class":278},[265,2211,799],{"class":286},[265,2213,2214,2217],{"class":267,"line":542},[265,2215,2216],{"class":286},"                           .map(yes ",[265,2218,663],{"class":278},[265,2220,2221,2224,2227,2229,2232,2234],{"class":267,"line":547},[265,2222,2223],{"class":669},"                             if",[265,2225,2226],{"class":286}," yes ",[265,2228,676],{"class":669},[265,2230,2231],{"class":286}," items.updateAndGet(_.drop(",[265,2233,1647],{"class":424},[265,2235,2236],{"class":286},")).unit\n",[265,2238,2239,2242,2244],{"class":267,"line":553},[265,2240,2241],{"class":669},"                             else",[265,2243,679],{"class":297},[265,2245,2246],{"class":286},".unit,\n",[265,2248,2249],{"class":267,"line":559},[265,2250,2251],{"class":286},"                           ),\n",[265,2253,2254],{"class":267,"line":565},[265,2255,2256],{"class":286},"                   )\n",[265,2258,2259,2262,2264,2266],{"class":267,"line":611},[265,2260,2261],{"class":286},"          keys   ",[265,2263,1825],{"class":278},[265,2265,1442],{"class":297},[265,2267,1452],{"class":286},[265,2269,2270,2273,2275,2277,2280,2282,2284,2286,2288,2290,2293,2295],{"class":267,"line":622},[265,2271,2272],{"class":297},"                     Binding",[265,2274,1460],{"class":286},[265,2276,784],{"class":278},[265,2278,2279],{"class":793},"delete",[265,2281,784],{"class":278},[265,2283,885],{"class":286},[265,2285,1472],{"class":297},[265,2287,1475],{"class":286},[265,2289,1478],{"class":278},[265,2291,2292],{"class":793},"d",[265,2294,1478],{"class":278},[265,2296,2297],{"class":286},"))(ask.launch(delete)),\n",[265,2299,2300,2302,2304,2306,2309,2311,2313,2315,2317,2319,2322,2324,2327,2329],{"class":267,"line":645},[265,2301,2272],{"class":297},[265,2303,1460],{"class":286},[265,2305,784],{"class":278},[265,2307,2308],{"class":793},"yes",[265,2310,784],{"class":278},[265,2312,885],{"class":286},[265,2314,1472],{"class":297},[265,2316,1475],{"class":286},[265,2318,1478],{"class":278},[265,2320,2321],{"class":793},"y",[265,2323,1478],{"class":278},[265,2325,2326],{"class":286},"))(ask.close(",[265,2328,1224],{"class":278},[265,2330,698],{"class":286},[265,2332,2333,2335,2337,2339,2342,2344,2346,2348,2350,2352,2354,2356,2358,2361],{"class":267,"line":651},[265,2334,2272],{"class":297},[265,2336,1460],{"class":286},[265,2338,784],{"class":278},[265,2340,2341],{"class":793},"no",[265,2343,784],{"class":278},[265,2345,885],{"class":286},[265,2347,1472],{"class":297},[265,2349,1475],{"class":286},[265,2351,1478],{"class":278},[265,2353,790],{"class":793},[265,2355,1478],{"class":278},[265,2357,2326],{"class":286},[265,2359,2360],{"class":278},"false",[265,2362,698],{"class":286},[265,2364,2365],{"class":267,"line":657},[265,2366,2256],{"class":286},[265,2368,2369,2371],{"class":267,"line":666},[265,2370,1213],{"class":669},[265,2372,1910],{"class":286},[265,2374,2375,2377,2379],{"class":267,"line":685},[265,2376,1915],{"class":286},[265,2378,1918],{"class":424},[265,2380,799],{"class":286},[265,2382,2383,2385,2387],{"class":267,"line":701},[265,2384,1925],{"class":286},[265,2386,1224],{"class":278},[265,2388,799],{"class":286},[265,2390,2391],{"class":267,"line":707},[265,2392,1934],{"class":286},[265,2394,2395,2398,2400,2402,2404],{"class":267,"line":713},[265,2396,2397],{"class":286},"          span(items.map(_.mkString(",[265,2399,784],{"class":278},[265,2401,885],{"class":793},[265,2403,784],{"class":278},[265,2405,2406],{"class":286},"))),\n",[265,2408,2409,2412,2414,2417,2419,2421,2423,2425,2427,2430,2432],{"class":267,"line":719},[265,2410,2411],{"class":286},"          span(ask.request.map(_.fold(",[265,2413,1172],{"class":278},[265,2415,2416],{"class":286},")(q ",[265,2418,415],{"class":278},[265,2420,781],{"class":278},[265,2422,784],{"class":278},[265,2424,787],{"class":278},[265,2426,1552],{"class":286},[265,2428,2429],{"class":793}," (y\u002Fn)",[265,2431,784],{"class":278},[265,2433,2406],{"class":286},[265,2435,2436],{"class":267,"line":724},[265,2437,710],{"class":286},[234,2439,2440,2441,1060],{},"An answer that comes in pieces (",[238,2442,2443],{},"useStreamText",[255,2445,2447],{"className":257,"code":2446,"filename":1064,"language":260,"meta":261,"style":261},"    \u002F** An answer that comes in pieces (the tokens of a chat model):\n      * `useStreamText` joins them, and its status says when the answer is\n      * complete.\n      *\u002F\n    def answer(tokens: Stream[String, Async])(using\n        Frame,\n    ): UI \u003C (Sync & Scope) =\n      for reply \u003C- useStreamText(tokens)\n      yield div(\n        span(reply.value),\n        span(reply.status.map {\n          case StreamStatus.Running   => \"...\"\n          case StreamStatus.Done      => \"(complete)\"\n          case StreamStatus.Failed(e) => s\"(failed: ${e.getMessage})\"\n        }),\n      )\n",[238,2448,2449,2454,2459,2464,2468,2497,2503,2525,2537,2543,2548,2555,2579,2599,2632,2638],{"__ignoreMap":261},[265,2450,2451],{"class":267,"line":268},[265,2452,2453],{"class":271},"    \u002F** An answer that comes in pieces (the tokens of a chat model):\n",[265,2455,2456],{"class":267,"line":275},[265,2457,2458],{"class":271},"      * `useStreamText` joins them, and its status says when the answer is\n",[265,2460,2461],{"class":267,"line":333},[265,2462,2463],{"class":271},"      * complete.\n",[265,2465,2466],{"class":267,"line":345},[265,2467,562],{"class":271},[265,2469,2470,2472,2475,2477,2480,2482,2485,2487,2489,2491,2493,2495],{"class":267,"line":352},[265,2471,279],{"class":278},[265,2473,2474],{"class":282}," answer",[265,2476,287],{"class":286},[265,2478,2479],{"class":290},"tokens",[265,2481,294],{"class":286},[265,2483,2484],{"class":297},"Stream",[265,2486,300],{"class":286},[265,2488,765],{"class":297},[265,2490,885],{"class":286},[265,2492,536],{"class":297},[265,2494,306],{"class":286},[265,2496,479],{"class":278},[265,2498,2499,2501],{"class":267,"line":358},[265,2500,485],{"class":297},[265,2502,488],{"class":286},[265,2504,2505,2507,2509,2511,2513,2515,2517,2519,2521,2523],{"class":267,"line":409},[265,2506,494],{"class":286},[265,2508,318],{"class":278},[265,2510,1137],{"class":297},[265,2512,324],{"class":278},[265,2514,503],{"class":286},[265,2516,506],{"class":297},[265,2518,509],{"class":278},[265,2520,512],{"class":297},[265,2522,515],{"class":286},[265,2524,518],{"class":278},[265,2526,2527,2529,2532,2534],{"class":267,"line":431},[265,2528,1636],{"class":669},[265,2530,2531],{"class":286}," reply ",[265,2533,1164],{"class":278},[265,2535,2536],{"class":286}," useStreamText(tokens)\n",[265,2538,2539,2541],{"class":267,"line":436},[265,2540,1213],{"class":669},[265,2542,1216],{"class":286},[265,2544,2545],{"class":267,"line":442},[265,2546,2547],{"class":286},"        span(reply.value),\n",[265,2549,2550,2553],{"class":267,"line":482},[265,2551,2552],{"class":286},"        span(reply.status.map ",[265,2554,1235],{"class":278},[265,2556,2557,2559,2562,2564,2567,2570,2573,2576],{"class":267,"line":491},[265,2558,1240],{"class":669},[265,2560,2561],{"class":297}," StreamStatus",[265,2563,997],{"class":286},[265,2565,2566],{"class":297},"Running",[265,2568,2569],{"class":278},"   =>",[265,2571,2572],{"class":278}," \"",[265,2574,2575],{"class":793},"...",[265,2577,2578],{"class":278},"\"\n",[265,2580,2581,2583,2585,2587,2589,2592,2594,2597],{"class":267,"line":521},[265,2582,1240],{"class":669},[265,2584,2561],{"class":297},[265,2586,997],{"class":286},[265,2588,1299],{"class":297},[265,2590,2591],{"class":278},"      =>",[265,2593,2572],{"class":278},[265,2595,2596],{"class":793},"(complete)",[265,2598,2578],{"class":278},[265,2600,2601,2603,2605,2607,2609,2612,2614,2616,2618,2621,2623,2626,2628,2630],{"class":267,"line":542},[265,2602,1240],{"class":669},[265,2604,2561],{"class":297},[265,2606,997],{"class":286},[265,2608,1346],{"class":297},[265,2610,2611],{"class":286},"(e) ",[265,2613,415],{"class":278},[265,2615,781],{"class":278},[265,2617,784],{"class":278},[265,2619,2620],{"class":793},"(failed: ",[265,2622,1317],{"class":278},[265,2624,2625],{"class":286},"e.getMessage",[265,2627,1331],{"class":278},[265,2629,315],{"class":793},[265,2631,2578],{"class":278},[265,2633,2634,2636],{"class":267,"line":547},[265,2635,1374],{"class":278},[265,2637,1227],{"class":286},[265,2639,2640],{"class":267,"line":553},[265,2641,1381],{"class":286},[234,2643,2644,2645,1060],{},"A note that saves itself after the edits stop (",[238,2646,2647],{},"useDebouncedFn",[255,2649,2651],{"className":257,"code":2650,"filename":1064,"language":260,"meta":261,"style":261},"    \u002F** A note that saves itself: each edit starts the wait again, and `save`\n      * runs 300 ms after the last edit, with the last text.\n      *\u002F\n    def autosave(save: String => Unit \u003C Async)(using\n        Frame,\n    ): UI \u003C (Sync & Scope) =\n      for\n          note   \u003C- Signal.initRef(\"\")\n          status \u003C- Signal.initRef(\"saved\")\n          later  \u003C- useDebouncedFn[String](300.millis)(text =>\n                      save(text).andThen(status.set(\"saved\")),\n                    )\n      yield div(\n        input\n          .value(note)\n          .focusAuto(true)\n          .onInput(text => status.set(\"editing\").andThen(later(text))),\n        span(status),\n      )\n",[238,2652,2653,2658,2663,2667,2695,2701,2723,2727,2742,2762,2785,2798,2803,2809,2814,2819,2828,2848,2853],{"__ignoreMap":261},[265,2654,2655],{"class":267,"line":268},[265,2656,2657],{"class":271},"    \u002F** A note that saves itself: each edit starts the wait again, and `save`\n",[265,2659,2660],{"class":267,"line":275},[265,2661,2662],{"class":271},"      * runs 300 ms after the last edit, with the last text.\n",[265,2664,2665],{"class":267,"line":333},[265,2666,562],{"class":271},[265,2668,2669,2671,2674,2676,2679,2681,2683,2685,2687,2689,2691,2693],{"class":267,"line":345},[265,2670,279],{"class":278},[265,2672,2673],{"class":282}," autosave",[265,2675,287],{"class":286},[265,2677,2678],{"class":290},"save",[265,2680,294],{"class":286},[265,2682,765],{"class":297},[265,2684,1104],{"class":278},[265,2686,321],{"class":297},[265,2688,324],{"class":278},[265,2690,474],{"class":297},[265,2692,390],{"class":286},[265,2694,479],{"class":278},[265,2696,2697,2699],{"class":267,"line":352},[265,2698,485],{"class":297},[265,2700,488],{"class":286},[265,2702,2703,2705,2707,2709,2711,2713,2715,2717,2719,2721],{"class":267,"line":358},[265,2704,494],{"class":286},[265,2706,318],{"class":278},[265,2708,1137],{"class":297},[265,2710,324],{"class":278},[265,2712,503],{"class":286},[265,2714,506],{"class":297},[265,2716,509],{"class":278},[265,2718,512],{"class":297},[265,2720,515],{"class":286},[265,2722,518],{"class":278},[265,2724,2725],{"class":267,"line":409},[265,2726,1156],{"class":669},[265,2728,2729,2732,2734,2736,2738,2740],{"class":267,"line":431},[265,2730,2731],{"class":286},"          note   ",[265,2733,1164],{"class":278},[265,2735,760],{"class":297},[265,2737,1169],{"class":286},[265,2739,1172],{"class":278},[265,2741,799],{"class":286},[265,2743,2744,2747,2749,2751,2753,2755,2758,2760],{"class":267,"line":436},[265,2745,2746],{"class":286},"          status ",[265,2748,1164],{"class":278},[265,2750,760],{"class":297},[265,2752,1169],{"class":286},[265,2754,784],{"class":278},[265,2756,2757],{"class":793},"saved",[265,2759,784],{"class":278},[265,2761,799],{"class":286},[265,2763,2764,2767,2769,2772,2774,2777,2780,2783],{"class":267,"line":442},[265,2765,2766],{"class":286},"          later  ",[265,2768,1164],{"class":278},[265,2770,2771],{"class":286}," useDebouncedFn[",[265,2773,765],{"class":297},[265,2775,2776],{"class":286},"](",[265,2778,2779],{"class":424},"300",[265,2781,2782],{"class":286},".millis)(text ",[265,2784,663],{"class":278},[265,2786,2787,2790,2792,2794,2796],{"class":267,"line":482},[265,2788,2789],{"class":286},"                      save(text).andThen(status.set(",[265,2791,784],{"class":278},[265,2793,2757],{"class":793},[265,2795,784],{"class":278},[265,2797,698],{"class":286},[265,2799,2800],{"class":267,"line":491},[265,2801,2802],{"class":286},"                    )\n",[265,2804,2805,2807],{"class":267,"line":521},[265,2806,1213],{"class":669},[265,2808,1216],{"class":286},[265,2810,2811],{"class":267,"line":542},[265,2812,2813],{"class":286},"        input\n",[265,2815,2816],{"class":267,"line":547},[265,2817,2818],{"class":286},"          .value(note)\n",[265,2820,2821,2824,2826],{"class":267,"line":553},[265,2822,2823],{"class":286},"          .focusAuto(",[265,2825,1224],{"class":278},[265,2827,799],{"class":286},[265,2829,2830,2833,2835,2838,2840,2843,2845],{"class":267,"line":559},[265,2831,2832],{"class":286},"          .onInput(text ",[265,2834,415],{"class":278},[265,2836,2837],{"class":286}," status.set(",[265,2839,784],{"class":278},[265,2841,2842],{"class":793},"editing",[265,2844,784],{"class":278},[265,2846,2847],{"class":286},").andThen(later(text))),\n",[265,2849,2850],{"class":267,"line":565},[265,2851,2852],{"class":286},"        span(status),\n",[265,2854,2855],{"class":267,"line":611},[265,2856,1381],{"class":286},[234,2858,2859,2860,1060],{},"A progress that changes faster than the screen needs (",[238,2861,2862],{},"throttled",[255,2864,2866],{"className":257,"code":2865,"filename":1064,"language":260,"meta":261,"style":261},"    \u002F** A progress that changes faster than the screen needs: `throttled` shows\n      * it at most once per 100 ms, and the last value always shows.\n      *\u002F\n    def progress(done: Signal[Int])(using Frame): UI \u003C (Sync & Scope) =\n      for shown \u003C- throttled(done, 100.millis)\n      yield span(shown.map(n => s\"$n %\"))\n",[238,2867,2868,2873,2878,2882,2928,2945],{"__ignoreMap":261},[265,2869,2870],{"class":267,"line":268},[265,2871,2872],{"class":271},"    \u002F** A progress that changes faster than the screen needs: `throttled` shows\n",[265,2874,2875],{"class":267,"line":275},[265,2876,2877],{"class":271},"      * it at most once per 100 ms, and the last value always shows.\n",[265,2879,2880],{"class":267,"line":333},[265,2881,562],{"class":271},[265,2883,2884,2886,2889,2891,2894,2896,2898,2900,2902,2904,2906,2908,2910,2912,2914,2916,2918,2920,2922,2924,2926],{"class":267,"line":345},[265,2885,279],{"class":278},[265,2887,2888],{"class":282}," progress",[265,2890,287],{"class":286},[265,2892,2893],{"class":290},"done",[265,2895,294],{"class":286},[265,2897,580],{"class":297},[265,2899,300],{"class":286},[265,2901,377],{"class":297},[265,2903,306],{"class":286},[265,2905,309],{"class":278},[265,2907,312],{"class":297},[265,2909,315],{"class":286},[265,2911,318],{"class":278},[265,2913,1137],{"class":297},[265,2915,324],{"class":278},[265,2917,503],{"class":286},[265,2919,506],{"class":297},[265,2921,509],{"class":278},[265,2923,512],{"class":297},[265,2925,515],{"class":286},[265,2927,518],{"class":278},[265,2929,2930,2932,2935,2937,2940,2943],{"class":267,"line":352},[265,2931,1636],{"class":669},[265,2933,2934],{"class":286}," shown ",[265,2936,1164],{"class":278},[265,2938,2939],{"class":286}," throttled(done, ",[265,2941,2942],{"class":424},"100",[265,2944,1190],{"class":286},[265,2946,2947,2949,2952,2954,2956,2958,2960,2962,2965,2967],{"class":267,"line":358},[265,2948,1213],{"class":669},[265,2950,2951],{"class":286}," span(shown.map(n ",[265,2953,415],{"class":278},[265,2955,781],{"class":278},[265,2957,784],{"class":278},[265,2959,787],{"class":278},[265,2961,790],{"class":286},[265,2963,2964],{"class":793}," %",[265,2966,784],{"class":278},[265,2968,1676],{"class":286},[234,2970,2971,2972,1060],{},"A setting that the next run keeps (",[238,2973,2974],{},"usePersisted",[255,2976,2978],{"className":257,"code":2977,"filename":1064,"language":260,"meta":261,"style":261},"    \u002F** A setting that the next run keeps: `usePersisted` reads it from\n      * `storage` and writes each change. `t` changes it. An app gives\n      * `Storage.forApp(\"\u003Capp>\")`; a test gives `Storage.inMemory`.\n      *\u002F\n    def setting(storage: Storage)(using Frame): UI \u003C (Sync & Scope) =\n      for\n          dark \u003C- usePersisted(\"dark\", false, storage)\n          keys  = Keymap.of(\n                    Binding.of(\"theme\", Chord.char('t'))(dark.updateAndGet(!_)),\n                  )\n      yield div\n        .tabIndex(0)\n        .focusAuto(true)\n        .onKeyDown(keys.handler)(\n          span(dark.map(d => if d then \"theme: dark\" else \"theme: light\")),\n        )\n",[238,2979,2980,2985,2990,2995,2999,3041,3045,3069,3080,3114,3119,3125,3133,3141,3145,3178],{"__ignoreMap":261},[265,2981,2982],{"class":267,"line":268},[265,2983,2984],{"class":271},"    \u002F** A setting that the next run keeps: `usePersisted` reads it from\n",[265,2986,2987],{"class":267,"line":275},[265,2988,2989],{"class":271},"      * `storage` and writes each change. `t` changes it. An app gives\n",[265,2991,2992],{"class":267,"line":333},[265,2993,2994],{"class":271},"      * `Storage.forApp(\"\u003Capp>\")`; a test gives `Storage.inMemory`.\n",[265,2996,2997],{"class":267,"line":345},[265,2998,562],{"class":271},[265,3000,3001,3003,3006,3008,3011,3013,3015,3017,3019,3021,3023,3025,3027,3029,3031,3033,3035,3037,3039],{"class":267,"line":352},[265,3002,279],{"class":278},[265,3004,3005],{"class":282}," setting",[265,3007,287],{"class":286},[265,3009,3010],{"class":290},"storage",[265,3012,294],{"class":286},[265,3014,1013],{"class":297},[265,3016,390],{"class":286},[265,3018,309],{"class":278},[265,3020,312],{"class":297},[265,3022,315],{"class":286},[265,3024,318],{"class":278},[265,3026,1137],{"class":297},[265,3028,324],{"class":278},[265,3030,503],{"class":286},[265,3032,506],{"class":297},[265,3034,509],{"class":278},[265,3036,512],{"class":297},[265,3038,515],{"class":286},[265,3040,518],{"class":278},[265,3042,3043],{"class":267,"line":358},[265,3044,1156],{"class":669},[265,3046,3047,3050,3052,3055,3057,3060,3062,3064,3066],{"class":267,"line":409},[265,3048,3049],{"class":286},"          dark ",[265,3051,1164],{"class":278},[265,3053,3054],{"class":286}," usePersisted(",[265,3056,784],{"class":278},[265,3058,3059],{"class":793},"dark",[265,3061,784],{"class":278},[265,3063,885],{"class":286},[265,3065,2360],{"class":278},[265,3067,3068],{"class":286},", storage)\n",[265,3070,3071,3074,3076,3078],{"class":267,"line":431},[265,3072,3073],{"class":286},"          keys  ",[265,3075,1825],{"class":278},[265,3077,1442],{"class":297},[265,3079,1452],{"class":286},[265,3081,3082,3085,3087,3089,3092,3094,3096,3098,3100,3102,3104,3106,3109,3111],{"class":267,"line":436},[265,3083,3084],{"class":297},"                    Binding",[265,3086,1460],{"class":286},[265,3088,784],{"class":278},[265,3090,3091],{"class":793},"theme",[265,3093,784],{"class":278},[265,3095,885],{"class":286},[265,3097,1472],{"class":297},[265,3099,1475],{"class":286},[265,3101,1478],{"class":278},[265,3103,1671],{"class":793},[265,3105,1478],{"class":278},[265,3107,3108],{"class":286},"))(dark.updateAndGet(",[265,3110,339],{"class":278},[265,3112,3113],{"class":286},"_)),\n",[265,3115,3116],{"class":267,"line":442},[265,3117,3118],{"class":286},"                  )\n",[265,3120,3121,3123],{"class":267,"line":482},[265,3122,1213],{"class":669},[265,3124,1910],{"class":286},[265,3126,3127,3129,3131],{"class":267,"line":491},[265,3128,1915],{"class":286},[265,3130,1918],{"class":424},[265,3132,799],{"class":286},[265,3134,3135,3137,3139],{"class":267,"line":521},[265,3136,1925],{"class":286},[265,3138,1224],{"class":278},[265,3140,799],{"class":286},[265,3142,3143],{"class":267,"line":542},[265,3144,1934],{"class":286},[265,3146,3147,3150,3152,3155,3158,3160,3162,3165,3167,3169,3171,3174,3176],{"class":267,"line":547},[265,3148,3149],{"class":286},"          span(dark.map(d ",[265,3151,415],{"class":278},[265,3153,3154],{"class":669}," if",[265,3156,3157],{"class":286}," d ",[265,3159,676],{"class":669},[265,3161,2572],{"class":278},[265,3163,3164],{"class":793},"theme: dark",[265,3166,784],{"class":278},[265,3168,2001],{"class":669},[265,3170,2572],{"class":278},[265,3172,3173],{"class":793},"theme: light",[265,3175,784],{"class":278},[265,3177,698],{"class":286},[265,3179,3180],{"class":267,"line":553},[265,3181,710],{"class":286},[234,3183,3184,3185,3188],{},"The output and the exit code of a program (",[238,3186,3187],{},"useProcess",", on the JVM):",[255,3190,3192],{"className":257,"code":3191,"filename":1064,"language":260,"meta":261,"style":261},"    \u002F** The output of a program, a line at a time, and its exit code:\n      * `useProcess` starts it, and the end of the `Scope` kills it.\n      *\u002F\n    def process(cmd: Chunk[String])(using Frame): UI \u003C (Sync & Scope) =\n      Abort.run[ProcessError](useProcess(cmd)).map {\n        case Result.Success(run) =>\n          useStreamFold(run.lines, Chunk.empty[String])((ls, l) =>\n            (ls :+ l).takeRight(5),\n          ).map(out =>\n            div(\n              span(out.value.map(_.mkString(\" | \"))),\n              span(run.exit.map(_.fold(\"running\")(c => s\"exit ${c.toInt}\"))),\n            ),\n          )\n        case Result.Failure(e)   => span(s\"cannot start: $e\")\n        case Result.Panic(t)     => span(s\"cannot start: $t\")\n      }\n",[238,3193,3194,3199,3204,3208,3254,3270,3288,3305,3320,3327,3332,3346,3381,3385,3390,3423,3455],{"__ignoreMap":261},[265,3195,3196],{"class":267,"line":268},[265,3197,3198],{"class":271},"    \u002F** The output of a program, a line at a time, and its exit code:\n",[265,3200,3201],{"class":267,"line":275},[265,3202,3203],{"class":271},"      * `useProcess` starts it, and the end of the `Scope` kills it.\n",[265,3205,3206],{"class":267,"line":333},[265,3207,562],{"class":271},[265,3209,3210,3212,3215,3217,3220,3222,3224,3226,3228,3230,3232,3234,3236,3238,3240,3242,3244,3246,3248,3250,3252],{"class":267,"line":345},[265,3211,279],{"class":278},[265,3213,3214],{"class":282}," process",[265,3216,287],{"class":286},[265,3218,3219],{"class":290},"cmd",[265,3221,294],{"class":286},[265,3223,1746],{"class":297},[265,3225,300],{"class":286},[265,3227,765],{"class":297},[265,3229,306],{"class":286},[265,3231,309],{"class":278},[265,3233,312],{"class":297},[265,3235,315],{"class":286},[265,3237,318],{"class":278},[265,3239,1137],{"class":297},[265,3241,324],{"class":278},[265,3243,503],{"class":286},[265,3245,506],{"class":297},[265,3247,509],{"class":278},[265,3249,512],{"class":297},[265,3251,515],{"class":286},[265,3253,518],{"class":278},[265,3255,3256,3259,3262,3265,3268],{"class":267,"line":352},[265,3257,3258],{"class":297},"      Abort",[265,3260,3261],{"class":286},".run[",[265,3263,3264],{"class":297},"ProcessError",[265,3266,3267],{"class":286},"](useProcess(cmd)).map ",[265,3269,1235],{"class":278},[265,3271,3272,3275,3278,3280,3283,3286],{"class":267,"line":358},[265,3273,3274],{"class":669},"        case",[265,3276,3277],{"class":297}," Result",[265,3279,997],{"class":286},[265,3281,3282],{"class":297},"Success",[265,3284,3285],{"class":286},"(run) ",[265,3287,663],{"class":278},[265,3289,3290,3293,3295,3298,3300,3303],{"class":267,"line":409},[265,3291,3292],{"class":286},"          useStreamFold(run.lines, ",[265,3294,1746],{"class":297},[265,3296,3297],{"class":286},".empty[",[265,3299,765],{"class":297},[265,3301,3302],{"class":286},"])((ls, l) ",[265,3304,663],{"class":278},[265,3306,3307,3310,3313,3316,3318],{"class":267,"line":431},[265,3308,3309],{"class":286},"            (ls ",[265,3311,3312],{"class":278},":+",[265,3314,3315],{"class":286}," l).takeRight(",[265,3317,1814],{"class":424},[265,3319,1227],{"class":286},[265,3321,3322,3325],{"class":267,"line":436},[265,3323,3324],{"class":286},"          ).map(out ",[265,3326,663],{"class":278},[265,3328,3329],{"class":267,"line":442},[265,3330,3331],{"class":286},"            div(\n",[265,3333,3334,3337,3339,3342,3344],{"class":267,"line":482},[265,3335,3336],{"class":286},"              span(out.value.map(_.mkString(",[265,3338,784],{"class":278},[265,3340,3341],{"class":793}," | ",[265,3343,784],{"class":278},[265,3345,2406],{"class":286},[265,3347,3348,3351,3353,3356,3358,3361,3363,3365,3367,3370,3372,3375,3377,3379],{"class":267,"line":491},[265,3349,3350],{"class":286},"              span(run.exit.map(_.fold(",[265,3352,784],{"class":278},[265,3354,3355],{"class":793},"running",[265,3357,784],{"class":278},[265,3359,3360],{"class":286},")(c ",[265,3362,415],{"class":278},[265,3364,781],{"class":278},[265,3366,784],{"class":278},[265,3368,3369],{"class":793},"exit ",[265,3371,1317],{"class":278},[265,3373,3374],{"class":286},"c.toInt",[265,3376,1331],{"class":278},[265,3378,784],{"class":278},[265,3380,2406],{"class":286},[265,3382,3383],{"class":267,"line":521},[265,3384,2039],{"class":286},[265,3386,3387],{"class":267,"line":542},[265,3388,3389],{"class":286},"          )\n",[265,3391,3392,3394,3396,3398,3401,3404,3406,3408,3410,3412,3415,3417,3419,3421],{"class":267,"line":547},[265,3393,3274],{"class":669},[265,3395,3277],{"class":297},[265,3397,997],{"class":286},[265,3399,3400],{"class":297},"Failure",[265,3402,3403],{"class":286},"(e)   ",[265,3405,415],{"class":278},[265,3407,1254],{"class":286},[265,3409,1309],{"class":278},[265,3411,784],{"class":278},[265,3413,3414],{"class":793},"cannot start: ",[265,3416,787],{"class":278},[265,3418,1365],{"class":286},[265,3420,784],{"class":278},[265,3422,799],{"class":286},[265,3424,3425,3427,3429,3431,3434,3437,3439,3441,3443,3445,3447,3449,3451,3453],{"class":267,"line":553},[265,3426,3274],{"class":669},[265,3428,3277],{"class":297},[265,3430,997],{"class":286},[265,3432,3433],{"class":297},"Panic",[265,3435,3436],{"class":286},"(t)     ",[265,3438,415],{"class":278},[265,3440,1254],{"class":286},[265,3442,1309],{"class":278},[265,3444,784],{"class":278},[265,3446,3414],{"class":793},[265,3448,787],{"class":278},[265,3450,1671],{"class":286},[265,3452,784],{"class":278},[265,3454,799],{"class":286},[265,3456,3457],{"class":267,"line":559},[265,3458,3459],{"class":278},"      }\n",[3461,3462,3463],"style",{},"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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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":261,"searchDepth":275,"depth":275,"links":3465},[3466,3467,3468,3469],{"id":231,"depth":275,"text":232},{"id":802,"depth":275,"text":803},{"id":832,"depth":275,"text":833},{"id":1048,"depth":275,"text":1049},"tui4s-use, plain functions over Kyo signals for the JVM and Scala.js; first, what needs no composable.","md",null,{},{"icon":61},{"title":58,"description":3470},"YIHkAHaisXFqrWGCdPkJWykUg0hLXTlka4as7HwlBcU",[3478,3480],{"title":53,"path":54,"stem":55,"description":3479,"icon":56,"children":-1},"kyo-ui styles in the terminal, custom colour properties and the two built-in looks.",{"title":63,"path":64,"stem":65,"description":3481,"icon":66,"children":-1},"tui4s-widgets, by family, as the gallery example shows them.",1792528755063]