@flyingrobots/bijou-tui 7.0.0 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (569) hide show
  1. package/README.md +27 -4
  2. package/dist/accordion.d.ts +3 -108
  3. package/dist/accordion.d.ts.map +1 -1
  4. package/dist/accordion.js +2 -161
  5. package/dist/accordion.js.map +1 -1
  6. package/dist/accordion.part01.d.ts +25 -0
  7. package/dist/accordion.part01.d.ts.map +1 -0
  8. package/dist/accordion.part01.js +61 -0
  9. package/dist/accordion.part01.js.map +1 -0
  10. package/dist/accordion.part02.d.ts +8 -0
  11. package/dist/accordion.part02.d.ts.map +1 -0
  12. package/dist/accordion.part02.js +15 -0
  13. package/dist/accordion.part02.js.map +1 -0
  14. package/dist/animate.d.ts.map +1 -1
  15. package/dist/animate.js +5 -6
  16. package/dist/animate.js.map +1 -1
  17. package/dist/app-frame-actions.d.ts +1 -18
  18. package/dist/app-frame-actions.d.ts.map +1 -1
  19. package/dist/app-frame-actions.js +47 -43
  20. package/dist/app-frame-actions.js.map +1 -1
  21. package/dist/app-frame-i18n.d.ts.map +1 -1
  22. package/dist/app-frame-i18n.js +20 -3
  23. package/dist/app-frame-i18n.js.map +1 -1
  24. package/dist/app-frame-overlays.d.ts +9 -1
  25. package/dist/app-frame-overlays.d.ts.map +1 -1
  26. package/dist/app-frame-overlays.js +117 -25
  27. package/dist/app-frame-overlays.js.map +1 -1
  28. package/dist/app-frame-palette.d.ts +1 -11
  29. package/dist/app-frame-palette.d.ts.map +1 -1
  30. package/dist/app-frame-palette.js +19 -18
  31. package/dist/app-frame-palette.js.map +1 -1
  32. package/dist/app-frame-render.d.ts +0 -7
  33. package/dist/app-frame-render.d.ts.map +1 -1
  34. package/dist/app-frame-render.js +17 -26
  35. package/dist/app-frame-render.js.map +1 -1
  36. package/dist/app-frame-types.d.ts +8 -4
  37. package/dist/app-frame-types.d.ts.map +1 -1
  38. package/dist/app-frame-types.js +5 -5
  39. package/dist/app-frame-types.js.map +1 -1
  40. package/dist/app-frame-utils.d.ts +2 -1
  41. package/dist/app-frame-utils.d.ts.map +1 -1
  42. package/dist/app-frame-utils.js +8 -1
  43. package/dist/app-frame-utils.js.map +1 -1
  44. package/dist/app-frame.d.ts +75 -4
  45. package/dist/app-frame.d.ts.map +1 -1
  46. package/dist/app-frame.js +346 -272
  47. package/dist/app-frame.js.map +1 -1
  48. package/dist/app-frame.test-support.part01.d.ts +149 -0
  49. package/dist/app-frame.test-support.part01.d.ts.map +1 -0
  50. package/dist/app-frame.test-support.part01.js +106 -0
  51. package/dist/app-frame.test-support.part01.js.map +1 -0
  52. package/dist/app-frame.test-support.part02.d.ts +15 -0
  53. package/dist/app-frame.test-support.part02.d.ts.map +1 -0
  54. package/dist/app-frame.test-support.part02.js +89 -0
  55. package/dist/app-frame.test-support.part02.js.map +1 -0
  56. package/dist/browsable-list.d.ts +5 -5
  57. package/dist/browsable-list.d.ts.map +1 -1
  58. package/dist/browsable-list.js.map +1 -1
  59. package/dist/canvas.d.ts +2 -34
  60. package/dist/canvas.d.ts.map +1 -1
  61. package/dist/canvas.js +43 -47
  62. package/dist/canvas.js.map +1 -1
  63. package/dist/cell-glyph-fit.js +1 -1
  64. package/dist/cell-glyph-fit.js.map +1 -1
  65. package/dist/collection-surface-types.d.ts +12 -0
  66. package/dist/collection-surface-types.d.ts.map +1 -0
  67. package/dist/collection-surface-types.js +2 -0
  68. package/dist/collection-surface-types.js.map +1 -0
  69. package/dist/collection-surface.d.ts +2 -7
  70. package/dist/collection-surface.d.ts.map +1 -1
  71. package/dist/collection-surface.js +8 -18
  72. package/dist/collection-surface.js.map +1 -1
  73. package/dist/command-palette.d.ts.map +1 -1
  74. package/dist/command-palette.js +2 -1
  75. package/dist/command-palette.js.map +1 -1
  76. package/dist/commands.d.ts.map +1 -1
  77. package/dist/commands.js +1 -1
  78. package/dist/commands.js.map +1 -1
  79. package/dist/css/install.js.map +1 -1
  80. package/dist/css/parser.js +9 -3
  81. package/dist/css/parser.js.map +1 -1
  82. package/dist/css/resolver-types.d.ts +13 -0
  83. package/dist/css/resolver-types.d.ts.map +1 -0
  84. package/dist/css/resolver-types.js +2 -0
  85. package/dist/css/resolver-types.js.map +1 -0
  86. package/dist/css/resolver.d.ts +2 -12
  87. package/dist/css/resolver.d.ts.map +1 -1
  88. package/dist/css/resolver.js +13 -20
  89. package/dist/css/resolver.js.map +1 -1
  90. package/dist/css/text-style.d.ts +4 -8
  91. package/dist/css/text-style.d.ts.map +1 -1
  92. package/dist/css/text-style.js +13 -18
  93. package/dist/css/text-style.js.map +1 -1
  94. package/dist/dag-pane.d.ts +4 -224
  95. package/dist/dag-pane.d.ts.map +1 -1
  96. package/dist/dag-pane.js +3 -516
  97. package/dist/dag-pane.js.map +1 -1
  98. package/dist/dag-pane.part01.d.ts +58 -0
  99. package/dist/dag-pane.part01.d.ts.map +1 -0
  100. package/dist/dag-pane.part01.js +2 -0
  101. package/dist/dag-pane.part01.js.map +1 -0
  102. package/dist/dag-pane.part02.d.ts +4 -0
  103. package/dist/dag-pane.part02.d.ts.map +1 -0
  104. package/dist/dag-pane.part02.js +56 -0
  105. package/dist/dag-pane.part02.js.map +1 -0
  106. package/dist/dag-pane.part03.d.ts +6 -0
  107. package/dist/dag-pane.part03.d.ts.map +1 -0
  108. package/dist/dag-pane.part03.js +76 -0
  109. package/dist/dag-pane.part03.js.map +1 -0
  110. package/dist/dag-pane.part04.d.ts +7 -0
  111. package/dist/dag-pane.part04.d.ts.map +1 -0
  112. package/dist/dag-pane.part04.js +66 -0
  113. package/dist/dag-pane.part04.js.map +1 -0
  114. package/dist/dag-pane.part05.d.ts +7 -0
  115. package/dist/dag-pane.part05.d.ts.map +1 -0
  116. package/dist/dag-pane.part05.js +78 -0
  117. package/dist/dag-pane.part05.js.map +1 -0
  118. package/dist/dag-pane.part06.d.ts +14 -0
  119. package/dist/dag-pane.part06.d.ts.map +1 -0
  120. package/dist/dag-pane.part06.js +65 -0
  121. package/dist/dag-pane.part06.js.map +1 -0
  122. package/dist/dag-pane.part07.d.ts +18 -0
  123. package/dist/dag-pane.part07.d.ts.map +1 -0
  124. package/dist/dag-pane.part07.js +22 -0
  125. package/dist/dag-pane.part07.js.map +1 -0
  126. package/dist/driver.d.ts +35 -21
  127. package/dist/driver.d.ts.map +1 -1
  128. package/dist/driver.js +69 -39
  129. package/dist/driver.js.map +1 -1
  130. package/dist/eventbus.d.ts.map +1 -1
  131. package/dist/eventbus.js +13 -23
  132. package/dist/eventbus.js.map +1 -1
  133. package/dist/file-picker.d.ts +4 -160
  134. package/dist/file-picker.d.ts.map +1 -1
  135. package/dist/file-picker.js +3 -284
  136. package/dist/file-picker.js.map +1 -1
  137. package/dist/file-picker.part01.d.ts +54 -0
  138. package/dist/file-picker.part01.d.ts.map +1 -0
  139. package/dist/file-picker.part01.js +47 -0
  140. package/dist/file-picker.part01.js.map +1 -0
  141. package/dist/file-picker.part02.d.ts +9 -0
  142. package/dist/file-picker.part02.d.ts.map +1 -0
  143. package/dist/file-picker.part02.js +77 -0
  144. package/dist/file-picker.part02.js.map +1 -0
  145. package/dist/file-picker.part03.d.ts +12 -0
  146. package/dist/file-picker.part03.d.ts.map +1 -0
  147. package/dist/file-picker.part03.js +35 -0
  148. package/dist/file-picker.part03.js.map +1 -0
  149. package/dist/flex.d.ts +0 -61
  150. package/dist/flex.d.ts.map +1 -1
  151. package/dist/flex.js +11 -161
  152. package/dist/flex.js.map +1 -1
  153. package/dist/focus-area.d.ts +6 -210
  154. package/dist/focus-area.d.ts.map +1 -1
  155. package/dist/focus-area.js +5 -425
  156. package/dist/focus-area.js.map +1 -1
  157. package/dist/focus-area.part01.d.ts +57 -0
  158. package/dist/focus-area.part01.d.ts.map +1 -0
  159. package/dist/focus-area.part01.js +23 -0
  160. package/dist/focus-area.part01.js.map +1 -0
  161. package/dist/focus-area.part02.d.ts +13 -0
  162. package/dist/focus-area.part02.d.ts.map +1 -0
  163. package/dist/focus-area.part02.js +65 -0
  164. package/dist/focus-area.part02.js.map +1 -0
  165. package/dist/focus-area.part03.d.ts +5 -0
  166. package/dist/focus-area.part03.d.ts.map +1 -0
  167. package/dist/focus-area.part03.js +42 -0
  168. package/dist/focus-area.part03.js.map +1 -0
  169. package/dist/focus-area.part04.d.ts +5 -0
  170. package/dist/focus-area.part04.d.ts.map +1 -0
  171. package/dist/focus-area.part04.js +49 -0
  172. package/dist/focus-area.part04.js.map +1 -0
  173. package/dist/focus-area.part05.d.ts +10 -0
  174. package/dist/focus-area.part05.d.ts.map +1 -0
  175. package/dist/focus-area.part05.js +66 -0
  176. package/dist/focus-area.part05.js.map +1 -0
  177. package/dist/focus-area.part06.d.ts +17 -0
  178. package/dist/focus-area.part06.d.ts.map +1 -0
  179. package/dist/focus-area.part06.js +48 -0
  180. package/dist/focus-area.part06.js.map +1 -0
  181. package/dist/focus-map.d.ts.map +1 -1
  182. package/dist/focus-map.js +8 -10
  183. package/dist/focus-map.js.map +1 -1
  184. package/dist/index.d.ts +8 -79
  185. package/dist/index.d.ts.map +1 -1
  186. package/dist/index.js +8 -105
  187. package/dist/index.js.map +1 -1
  188. package/dist/index.part01.d.ts +15 -0
  189. package/dist/index.part01.d.ts.map +1 -0
  190. package/dist/index.part01.js +11 -0
  191. package/dist/index.part01.js.map +1 -0
  192. package/dist/index.part02.d.ts +26 -0
  193. package/dist/index.part02.d.ts.map +1 -0
  194. package/dist/index.part02.js +24 -0
  195. package/dist/index.part02.js.map +1 -0
  196. package/dist/index.part03.d.ts +17 -0
  197. package/dist/index.part03.d.ts.map +1 -0
  198. package/dist/index.part03.js +11 -0
  199. package/dist/index.part03.js.map +1 -0
  200. package/dist/index.part04.d.ts +6 -0
  201. package/dist/index.part04.d.ts.map +1 -0
  202. package/dist/index.part04.js +11 -0
  203. package/dist/index.part04.js.map +1 -0
  204. package/dist/index.part05.d.ts +10 -0
  205. package/dist/index.part05.d.ts.map +1 -0
  206. package/dist/index.part05.js +26 -0
  207. package/dist/index.part05.js.map +1 -0
  208. package/dist/index.part06.d.ts +9 -0
  209. package/dist/index.part06.d.ts.map +1 -0
  210. package/dist/index.part06.js +16 -0
  211. package/dist/index.part06.js.map +1 -0
  212. package/dist/index.part07.d.ts +8 -0
  213. package/dist/index.part07.d.ts.map +1 -0
  214. package/dist/index.part07.js +15 -0
  215. package/dist/index.part07.js.map +1 -0
  216. package/dist/index.part08.d.ts +5 -0
  217. package/dist/index.part08.d.ts.map +1 -0
  218. package/dist/index.part08.js +9 -0
  219. package/dist/index.part08.js.map +1 -0
  220. package/dist/input-map.d.ts.map +1 -1
  221. package/dist/input-map.js +1 -2
  222. package/dist/input-map.js.map +1 -1
  223. package/dist/input-parser.d.ts +3 -0
  224. package/dist/input-parser.d.ts.map +1 -0
  225. package/dist/input-parser.js +95 -0
  226. package/dist/input-parser.js.map +1 -0
  227. package/dist/input-routing-inspector.js +3 -3
  228. package/dist/input-routing-inspector.js.map +1 -1
  229. package/dist/inputstack.d.ts.map +1 -1
  230. package/dist/inputstack.js +2 -0
  231. package/dist/inputstack.js.map +1 -1
  232. package/dist/keybindings.d.ts.map +1 -1
  233. package/dist/keybindings.js +5 -2
  234. package/dist/keybindings.js.map +1 -1
  235. package/dist/keys.d.ts +0 -36
  236. package/dist/keys.d.ts.map +1 -1
  237. package/dist/keys.js +11 -66
  238. package/dist/keys.js.map +1 -1
  239. package/dist/layout-inspector.d.ts +2 -30
  240. package/dist/layout-inspector.d.ts.map +1 -1
  241. package/dist/layout-inspector.js +1 -132
  242. package/dist/layout-inspector.js.map +1 -1
  243. package/dist/layout-inspector.part01.d.ts +52 -0
  244. package/dist/layout-inspector.part01.d.ts.map +1 -0
  245. package/dist/layout-inspector.part01.js +48 -0
  246. package/dist/layout-inspector.part01.js.map +1 -0
  247. package/dist/layout-inspector.part02.d.ts +11 -0
  248. package/dist/layout-inspector.part02.d.ts.map +1 -0
  249. package/dist/layout-inspector.part02.js +80 -0
  250. package/dist/layout-inspector.part02.js.map +1 -0
  251. package/dist/layout-inspector.part03.d.ts +5 -0
  252. package/dist/layout-inspector.part03.d.ts.map +1 -0
  253. package/dist/layout-inspector.part03.js +11 -0
  254. package/dist/layout-inspector.part03.js.map +1 -0
  255. package/dist/layout-preset.js +4 -4
  256. package/dist/layout-preset.js.map +1 -1
  257. package/dist/layout.d.ts.map +1 -1
  258. package/dist/layout.js +5 -3
  259. package/dist/layout.js.map +1 -1
  260. package/dist/motion/motion.js +8 -4
  261. package/dist/motion/motion.js.map +1 -1
  262. package/dist/motion/reconciler.d.ts.map +1 -1
  263. package/dist/motion/reconciler.js +9 -7
  264. package/dist/motion/reconciler.js.map +1 -1
  265. package/dist/mouse-mode.d.ts +5 -0
  266. package/dist/mouse-mode.d.ts.map +1 -0
  267. package/dist/mouse-mode.js +17 -0
  268. package/dist/mouse-mode.js.map +1 -0
  269. package/dist/navigable-table.d.ts +3 -136
  270. package/dist/navigable-table.d.ts.map +1 -1
  271. package/dist/navigable-table.js +2 -234
  272. package/dist/navigable-table.js.map +1 -1
  273. package/dist/navigable-table.part01.d.ts +42 -0
  274. package/dist/navigable-table.part01.d.ts.map +1 -0
  275. package/dist/navigable-table.part01.js +42 -0
  276. package/dist/navigable-table.part01.js.map +1 -0
  277. package/dist/navigable-table.part02.d.ts +16 -0
  278. package/dist/navigable-table.part02.d.ts.map +1 -0
  279. package/dist/navigable-table.part02.js +83 -0
  280. package/dist/navigable-table.part02.js.map +1 -0
  281. package/dist/notification-card.d.ts +11 -0
  282. package/dist/notification-card.d.ts.map +1 -0
  283. package/dist/notification-card.js +157 -0
  284. package/dist/notification-card.js.map +1 -0
  285. package/dist/notification-stack.d.ts +6 -0
  286. package/dist/notification-stack.d.ts.map +1 -0
  287. package/dist/notification-stack.js +257 -0
  288. package/dist/notification-stack.js.map +1 -0
  289. package/dist/notification.d.ts +14 -112
  290. package/dist/notification.d.ts.map +1 -1
  291. package/dist/notification.js +12 -1087
  292. package/dist/notification.js.map +1 -1
  293. package/dist/notification.part01.d.ts +77 -0
  294. package/dist/notification.part01.d.ts.map +1 -0
  295. package/dist/notification.part01.js +19 -0
  296. package/dist/notification.part01.js.map +1 -0
  297. package/dist/notification.part02.d.ts +42 -0
  298. package/dist/notification.part02.d.ts.map +1 -0
  299. package/dist/notification.part02.js +44 -0
  300. package/dist/notification.part02.js.map +1 -0
  301. package/dist/notification.part03.d.ts +6 -0
  302. package/dist/notification.part03.d.ts.map +1 -0
  303. package/dist/notification.part03.js +81 -0
  304. package/dist/notification.part03.js.map +1 -0
  305. package/dist/notification.part04.d.ts +4 -0
  306. package/dist/notification.part04.d.ts.map +1 -0
  307. package/dist/notification.part04.js +32 -0
  308. package/dist/notification.part04.js.map +1 -0
  309. package/dist/notification.part05.d.ts +6 -0
  310. package/dist/notification.part05.d.ts.map +1 -0
  311. package/dist/notification.part05.js +95 -0
  312. package/dist/notification.part05.js.map +1 -0
  313. package/dist/notification.part06.d.ts +9 -0
  314. package/dist/notification.part06.d.ts.map +1 -0
  315. package/dist/notification.part06.js +65 -0
  316. package/dist/notification.part06.js.map +1 -0
  317. package/dist/notification.part07.d.ts +10 -0
  318. package/dist/notification.part07.d.ts.map +1 -0
  319. package/dist/notification.part07.js +70 -0
  320. package/dist/notification.part07.js.map +1 -0
  321. package/dist/notification.part08.d.ts +5 -0
  322. package/dist/notification.part08.d.ts.map +1 -0
  323. package/dist/notification.part08.js +79 -0
  324. package/dist/notification.part08.js.map +1 -0
  325. package/dist/notification.part09.d.ts +14 -0
  326. package/dist/notification.part09.d.ts.map +1 -0
  327. package/dist/notification.part09.js +96 -0
  328. package/dist/notification.part09.js.map +1 -0
  329. package/dist/notification.part10.d.ts +13 -0
  330. package/dist/notification.part10.d.ts.map +1 -0
  331. package/dist/notification.part10.js +94 -0
  332. package/dist/notification.part10.js.map +1 -0
  333. package/dist/notification.part11.d.ts +6 -0
  334. package/dist/notification.part11.d.ts.map +1 -0
  335. package/dist/notification.part11.js +42 -0
  336. package/dist/notification.part11.js.map +1 -0
  337. package/dist/overlay.d.ts +8 -232
  338. package/dist/overlay.d.ts.map +1 -1
  339. package/dist/overlay.js +5 -629
  340. package/dist/overlay.js.map +1 -1
  341. package/dist/overlay.part01.d.ts +71 -0
  342. package/dist/overlay.part01.d.ts.map +1 -0
  343. package/dist/overlay.part01.js +34 -0
  344. package/dist/overlay.part01.js.map +1 -0
  345. package/dist/overlay.part02.d.ts +8 -0
  346. package/dist/overlay.part02.d.ts.map +1 -0
  347. package/dist/overlay.part02.js +81 -0
  348. package/dist/overlay.part02.js.map +1 -0
  349. package/dist/overlay.part03.d.ts +27 -0
  350. package/dist/overlay.part03.d.ts.map +1 -0
  351. package/dist/overlay.part03.js +83 -0
  352. package/dist/overlay.part03.js.map +1 -0
  353. package/dist/overlay.part04.d.ts +8 -0
  354. package/dist/overlay.part04.d.ts.map +1 -0
  355. package/dist/overlay.part04.js +103 -0
  356. package/dist/overlay.part04.js.map +1 -0
  357. package/dist/overlay.part05.d.ts +8 -0
  358. package/dist/overlay.part05.d.ts.map +1 -0
  359. package/dist/overlay.part05.js +66 -0
  360. package/dist/overlay.part05.js.map +1 -0
  361. package/dist/overlay.part06.d.ts +46 -0
  362. package/dist/overlay.part06.d.ts.map +1 -0
  363. package/dist/overlay.part06.js +35 -0
  364. package/dist/overlay.part06.js.map +1 -0
  365. package/dist/overlay.part07.d.ts +4 -0
  366. package/dist/overlay.part07.d.ts.map +1 -0
  367. package/dist/overlay.part07.js +64 -0
  368. package/dist/overlay.part07.js.map +1 -0
  369. package/dist/overlay.part08.d.ts +34 -0
  370. package/dist/overlay.part08.d.ts.map +1 -0
  371. package/dist/overlay.part08.js +54 -0
  372. package/dist/overlay.part08.js.map +1 -0
  373. package/dist/overlay.part09.d.ts +6 -0
  374. package/dist/overlay.part09.d.ts.map +1 -0
  375. package/dist/overlay.part09.js +42 -0
  376. package/dist/overlay.part09.js.map +1 -0
  377. package/dist/pager.d.ts.map +1 -1
  378. package/dist/pager.js +2 -4
  379. package/dist/pager.js.map +1 -1
  380. package/dist/panel-dock.d.ts.map +1 -1
  381. package/dist/panel-dock.js +9 -11
  382. package/dist/panel-dock.js.map +1 -1
  383. package/dist/panels.d.ts.map +1 -1
  384. package/dist/panels.js +2 -0
  385. package/dist/panels.js.map +1 -1
  386. package/dist/pipeline/middleware/css.d.ts +3 -5
  387. package/dist/pipeline/middleware/css.d.ts.map +1 -1
  388. package/dist/pipeline/middleware/css.js +12 -6
  389. package/dist/pipeline/middleware/css.js.map +1 -1
  390. package/dist/pipeline/middleware/grayscale.js +15 -10
  391. package/dist/pipeline/middleware/grayscale.js.map +1 -1
  392. package/dist/pipeline/middleware/motion.js +9 -1
  393. package/dist/pipeline/middleware/motion.js.map +1 -1
  394. package/dist/pipeline/middleware/paint.js +9 -1
  395. package/dist/pipeline/middleware/paint.js.map +1 -1
  396. package/dist/pipeline/middleware/surface-shaders.js +13 -11
  397. package/dist/pipeline/middleware/surface-shaders.js.map +1 -1
  398. package/dist/pipeline/pipeline.d.ts +7 -52
  399. package/dist/pipeline/pipeline.d.ts.map +1 -1
  400. package/dist/pipeline/pipeline.js +37 -27
  401. package/dist/pipeline/pipeline.js.map +1 -1
  402. package/dist/raster-glyph.d.ts +3 -0
  403. package/dist/raster-glyph.d.ts.map +1 -0
  404. package/dist/raster-glyph.js +2 -0
  405. package/dist/raster-glyph.js.map +1 -0
  406. package/dist/raster-glyph.part01.d.ts +75 -0
  407. package/dist/raster-glyph.part01.d.ts.map +1 -0
  408. package/dist/raster-glyph.part01.js +25 -0
  409. package/dist/raster-glyph.part01.js.map +1 -0
  410. package/dist/raster-glyph.part02.d.ts +12 -0
  411. package/dist/raster-glyph.part02.d.ts.map +1 -0
  412. package/dist/raster-glyph.part02.js +81 -0
  413. package/dist/raster-glyph.part02.js.map +1 -0
  414. package/dist/raster-glyph.part03.d.ts +6 -0
  415. package/dist/raster-glyph.part03.d.ts.map +1 -0
  416. package/dist/raster-glyph.part03.js +60 -0
  417. package/dist/raster-glyph.part03.js.map +1 -0
  418. package/dist/raster-glyph.part04.d.ts +7 -0
  419. package/dist/raster-glyph.part04.d.ts.map +1 -0
  420. package/dist/raster-glyph.part04.js +59 -0
  421. package/dist/raster-glyph.part04.js.map +1 -0
  422. package/dist/raster-glyph.part05.d.ts +6 -0
  423. package/dist/raster-glyph.part05.d.ts.map +1 -0
  424. package/dist/raster-glyph.part05.js +71 -0
  425. package/dist/raster-glyph.part05.js.map +1 -0
  426. package/dist/raster-glyph.part06.d.ts +18 -0
  427. package/dist/raster-glyph.part06.d.ts.map +1 -0
  428. package/dist/raster-glyph.part06.js +78 -0
  429. package/dist/raster-glyph.part06.js.map +1 -0
  430. package/dist/raster-glyph.part07.d.ts +11 -0
  431. package/dist/raster-glyph.part07.d.ts.map +1 -0
  432. package/dist/raster-glyph.part07.js +55 -0
  433. package/dist/raster-glyph.part07.js.map +1 -0
  434. package/dist/raytrace.d.ts +3 -58
  435. package/dist/raytrace.d.ts.map +1 -1
  436. package/dist/raytrace.js +2 -120
  437. package/dist/raytrace.js.map +1 -1
  438. package/dist/raytrace.part01.d.ts +47 -0
  439. package/dist/raytrace.part01.d.ts.map +1 -0
  440. package/dist/raytrace.part01.js +45 -0
  441. package/dist/raytrace.part01.js.map +1 -0
  442. package/dist/raytrace.part02.d.ts +12 -0
  443. package/dist/raytrace.part02.d.ts.map +1 -0
  444. package/dist/raytrace.part02.js +73 -0
  445. package/dist/raytrace.part02.js.map +1 -0
  446. package/dist/runtime-binding.d.ts +2 -3
  447. package/dist/runtime-binding.d.ts.map +1 -1
  448. package/dist/runtime-binding.js +49 -38
  449. package/dist/runtime-binding.js.map +1 -1
  450. package/dist/runtime-engine.d.ts +2 -2
  451. package/dist/runtime-engine.d.ts.map +1 -1
  452. package/dist/runtime-engine.js +15 -4
  453. package/dist/runtime-engine.js.map +1 -1
  454. package/dist/runtime.d.ts +2 -18
  455. package/dist/runtime.d.ts.map +1 -1
  456. package/dist/runtime.js +43 -62
  457. package/dist/runtime.js.map +1 -1
  458. package/dist/screen.js +1 -1
  459. package/dist/screen.js.map +1 -1
  460. package/dist/shell-quit.d.ts.map +1 -1
  461. package/dist/shell-quit.js +49 -6
  462. package/dist/shell-quit.js.map +1 -1
  463. package/dist/split-pane.d.ts +3 -104
  464. package/dist/split-pane.d.ts.map +1 -1
  465. package/dist/split-pane.js +2 -190
  466. package/dist/split-pane.js.map +1 -1
  467. package/dist/split-pane.part01.d.ts +60 -0
  468. package/dist/split-pane.part01.d.ts.map +1 -0
  469. package/dist/split-pane.part01.js +45 -0
  470. package/dist/split-pane.part01.js.map +1 -0
  471. package/dist/split-pane.part02.d.ts +8 -0
  472. package/dist/split-pane.part02.d.ts.map +1 -0
  473. package/dist/split-pane.part02.js +73 -0
  474. package/dist/split-pane.part02.js.map +1 -0
  475. package/dist/split-pane.part03.d.ts +4 -0
  476. package/dist/split-pane.part03.d.ts.map +1 -0
  477. package/dist/split-pane.part03.js +40 -0
  478. package/dist/split-pane.part03.js.map +1 -0
  479. package/dist/spring.d.ts +4 -187
  480. package/dist/spring.d.ts.map +1 -1
  481. package/dist/spring.js +2 -142
  482. package/dist/spring.js.map +1 -1
  483. package/dist/spring.part01.d.ts +90 -0
  484. package/dist/spring.part01.d.ts.map +1 -0
  485. package/dist/spring.part01.js +58 -0
  486. package/dist/spring.part01.js.map +1 -0
  487. package/dist/spring.part02.d.ts +25 -0
  488. package/dist/spring.part02.d.ts.map +1 -0
  489. package/dist/spring.part02.js +21 -0
  490. package/dist/spring.part02.js.map +1 -0
  491. package/dist/status-bar.js +1 -1
  492. package/dist/status-bar.js.map +1 -1
  493. package/dist/subapp/mount.d.ts +6 -6
  494. package/dist/subapp/mount.d.ts.map +1 -1
  495. package/dist/subapp/mount.js +19 -14
  496. package/dist/subapp/mount.js.map +1 -1
  497. package/dist/surface-budget-types.d.ts +26 -0
  498. package/dist/surface-budget-types.d.ts.map +1 -0
  499. package/dist/surface-budget-types.js +2 -0
  500. package/dist/surface-budget-types.js.map +1 -0
  501. package/dist/surface-budget.d.ts +2 -25
  502. package/dist/surface-budget.d.ts.map +1 -1
  503. package/dist/surface-budget.js.map +1 -1
  504. package/dist/surface-diff.js +26 -28
  505. package/dist/surface-diff.js.map +1 -1
  506. package/dist/surface-layout.d.ts.map +1 -1
  507. package/dist/surface-layout.js +1 -2
  508. package/dist/surface-layout.js.map +1 -1
  509. package/dist/timeline.d.ts +1 -1
  510. package/dist/timeline.d.ts.map +1 -1
  511. package/dist/timeline.js +17 -15
  512. package/dist/timeline.js.map +1 -1
  513. package/dist/transition-shaders.d.ts +5 -142
  514. package/dist/transition-shaders.d.ts.map +1 -1
  515. package/dist/transition-shaders.js +4 -391
  516. package/dist/transition-shaders.js.map +1 -1
  517. package/dist/transition-shaders.part01.d.ts +41 -0
  518. package/dist/transition-shaders.part01.d.ts.map +1 -0
  519. package/dist/transition-shaders.part01.js +38 -0
  520. package/dist/transition-shaders.part01.js.map +1 -0
  521. package/dist/transition-shaders.part02.d.ts +12 -0
  522. package/dist/transition-shaders.part02.d.ts.map +1 -0
  523. package/dist/transition-shaders.part02.js +88 -0
  524. package/dist/transition-shaders.part02.js.map +1 -0
  525. package/dist/transition-shaders.part03.d.ts +6 -0
  526. package/dist/transition-shaders.part03.d.ts.map +1 -0
  527. package/dist/transition-shaders.part03.js +82 -0
  528. package/dist/transition-shaders.part03.js.map +1 -0
  529. package/dist/transition-shaders.part04.d.ts +16 -0
  530. package/dist/transition-shaders.part04.d.ts.map +1 -0
  531. package/dist/transition-shaders.part04.js +98 -0
  532. package/dist/transition-shaders.part04.js.map +1 -0
  533. package/dist/types.d.ts +10 -42
  534. package/dist/types.d.ts.map +1 -1
  535. package/dist/types.js +9 -36
  536. package/dist/types.js.map +1 -1
  537. package/dist/view-output.d.ts +1 -1
  538. package/dist/view-output.d.ts.map +1 -1
  539. package/dist/view-output.js +3 -2
  540. package/dist/view-output.js.map +1 -1
  541. package/dist/viewport.d.ts +7 -213
  542. package/dist/viewport.d.ts.map +1 -1
  543. package/dist/viewport.js +6 -470
  544. package/dist/viewport.js.map +1 -1
  545. package/dist/viewport.part01.d.ts +51 -0
  546. package/dist/viewport.part01.d.ts.map +1 -0
  547. package/dist/viewport.part01.js +10 -0
  548. package/dist/viewport.part01.js.map +1 -0
  549. package/dist/viewport.part02.d.ts +2 -0
  550. package/dist/viewport.part02.d.ts.map +1 -0
  551. package/dist/viewport.part02.js +69 -0
  552. package/dist/viewport.part02.js.map +1 -0
  553. package/dist/viewport.part03.d.ts +10 -0
  554. package/dist/viewport.part03.d.ts.map +1 -0
  555. package/dist/viewport.part03.js +78 -0
  556. package/dist/viewport.part03.js.map +1 -0
  557. package/dist/viewport.part04.d.ts +4 -0
  558. package/dist/viewport.part04.d.ts.map +1 -0
  559. package/dist/viewport.part04.js +50 -0
  560. package/dist/viewport.part04.js.map +1 -0
  561. package/dist/viewport.part05.d.ts +5 -0
  562. package/dist/viewport.part05.d.ts.map +1 -0
  563. package/dist/viewport.part05.js +59 -0
  564. package/dist/viewport.part05.js.map +1 -0
  565. package/dist/viewport.part06.d.ts +14 -0
  566. package/dist/viewport.part06.d.ts.map +1 -0
  567. package/dist/viewport.part06.js +58 -0
  568. package/dist/viewport.part06.js.map +1 -0
  569. package/package.json +3 -3
package/dist/app-frame.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Provides tabs, pane focus/scroll isolation, shell key handling, help,
5
5
  * panel-scoped overlay context, and optional frame-level command palette.
6
6
  */
7
- import { cloneContextWithResolvedTheme, createResolved, createSurface, perfOverlaySurface, setDefaultContext, resolveClock, resolveSafeCtx, } from '@flyingrobots/bijou';
7
+ import { cloneContextWithResolvedTheme, createSurface, perfOverlaySurface, setDefaultContext, resolveClock, resolveSafeCtx, } from '@flyingrobots/bijou';
8
8
  import { createKeyMap, formatKeyCombo } from './keybindings.js';
9
9
  import { isKeyMsg, isMouseMsg, isResizeMsg } from './types.js';
10
10
  import { quit } from './commands.js';
@@ -16,15 +16,19 @@ import { restoreLayoutState } from './layout-preset.js';
16
16
  import { createNotificationState, dismissNotification, hitTestNotificationStack, notificationsNeedTick, pushNotification, renderNotificationStack, tickNotifications, trimNotificationsToViewport, } from './notification.js';
17
17
  import { isFrameScopedMsg, isPageScopedMsg, wrapCmdForPage, emitMsg, emitMsgForPage, wrapFrameMsg, } from './app-frame-types.js';
18
18
  import { createFrameKeyMap, frameBodyRect, mergeBindingSources, resolveFramePageText, } from './app-frame-utils.js';
19
- import { renderHelpOverlay, isHelpScrollAction, resolveCurrentShellTheme, resolveNextShellTheme, resolveShellThemeForContext, resolveFrameSettings, resolveFrameNotificationCenter, resolveSettingsLayout, resolveNotificationCenterLayout, resolveInputAreas, findInputAreaByPaneId, moveSettingsFocus, scrollSettingsBy, clampSettingsScroll, scrollNotificationCenterBy, cycleNotificationCenterFilter, clampSettingsFocus, renderSettingsDrawer, renderNotificationCenterDrawer, resolveNotificationFooterCue, } from './app-frame-overlays.js';
19
+ import { renderHelpOverlay, isHelpScrollAction, resolveFrameShellThemeChoices, resolveCurrentShellTheme, resolveNextShellTheme, resolveShellThemeModeToggle, resolveShellThemeForContext, resolveFrameSettings, resolveFrameNotificationCenter, resolveSettingsLayout, resolveNotificationCenterLayout, resolveInputAreas, findInputAreaByPaneId, moveSettingsFocus, scrollSettingsBy, clampSettingsScroll, scrollNotificationCenterBy, cycleNotificationCenterFilter, clampSettingsFocus, renderSettingsDrawer, renderNotificationCenterDrawer, resolveNotificationFooterCue, } from './app-frame-overlays.js';
20
20
  import { activeFrameLayer, describeFrameRuntimeViewStack, projectFrameControls, } from './app-frame-layers.js';
21
21
  import { applyRuntimeCommandBuffer, bufferRuntimeRouteResult, createRuntimeBuffers, createRuntimeRetainedLayouts, retainRuntimeLayout, routeRuntimeInput, } from './runtime-engine.js';
22
22
  import { frameMessage, frameNotificationFilterLabel, } from './app-frame-i18n.js';
23
23
  import { resolveHeaderLine, renderHelpLine, renderPageContent, renderPageContentInto, renderMaximizedPane, renderMaximizedPaneInto, renderTransition, createFramePaneScratchPool, } from './app-frame-render.js';
24
24
  import { applyFrameAction, scrollFocusedPane, switchTab, syncPageFrameState, } from './app-frame-actions.js';
25
25
  import { handlePaletteKey, openCommandPalette, openSearchPalette, } from './app-frame-palette.js';
26
- import { createInputActionMap, createInputGestureRecognizer, } from './input-map.js';
27
- export { emitFrameAction, notify, } from './app-frame-types.js';
26
+ /** Stable values for shell theme mode siblings. */
27
+ export const FRAME_SHELL_THEME_MODE_SUPPORT = Object.freeze({
28
+ single: 'single',
29
+ paired: 'paired',
30
+ });
31
+ export { FRAME_MSG_TOKEN, PAGE_MSG_TOKEN, emitMsg, emitFrameAction, emitMsgForPage, isFrameScopedMsg, isPageScopedMsg, notify, wrapCmdForPage, wrapFrameMsg, wrapPageMsg, } from './app-frame-types.js';
28
32
  export { activeFrameLayer, describeFrameLayerStack, describeFrameRuntimeViewStack, projectFrameControls, underlyingFrameLayer, } from './app-frame-layers.js';
29
33
  // ---------------------------------------------------------------------------
30
34
  // Frame Notification Helpers
@@ -32,7 +36,6 @@ export { activeFrameLayer, describeFrameLayerStack, describeFrameRuntimeViewStac
32
36
  const FRAME_NOTIFICATION_TICK_MS = 40;
33
37
  const DEFAULT_FRAME_NOTIFICATION_DURATION_MS = 6_000;
34
38
  const SETTINGS_FEEDBACK_TOAST_WIDTH = 40;
35
- const FRAME_FOOTER_DOUBLE_TAB_MS = 300;
36
39
  const EMPTY_RUNTIME_LAYOUTS = createRuntimeRetainedLayouts();
37
40
  function createQuitHelpKeys(i18n) {
38
41
  const t = (id, fallback) => frameMessage(i18n, id, fallback);
@@ -148,7 +151,7 @@ function cloneShellThemeContext(ctx, resolvedTheme) {
148
151
  function readStageDuration(timings, stage) {
149
152
  return timings.find((timing) => timing.stage === stage)?.durationMs ?? 0;
150
153
  }
151
- function summarizeFrameTimings(timings, frameBudgetMs) {
154
+ export function summarizeFrameTimings(timings, frameBudgetMs) {
152
155
  const frameTimeMs = timings.reduce((total, timing) => total + timing.durationMs, 0);
153
156
  return {
154
157
  frameTimeMs,
@@ -158,6 +161,36 @@ function summarizeFrameTimings(timings, frameBudgetMs) {
158
161
  frameOverBudget: frameBudgetMs != null && frameTimeMs > frameBudgetMs,
159
162
  };
160
163
  }
164
+ export function renderFramePerfHudOverlay(telemetry, options = {}) {
165
+ const refreshRate = telemetry.refreshRate ?? options.ctx?.runtime.refreshRate ?? 60;
166
+ const fps = telemetry.frameTimeMs > 0
167
+ ? Math.min(refreshRate, 1000 / telemetry.frameTimeMs)
168
+ : refreshRate;
169
+ const width = Math.max(12, Math.min(40, telemetry.columns - 2));
170
+ const surface = perfOverlaySurface({
171
+ fps,
172
+ frameTimeMs: telemetry.frameTimeMs,
173
+ width: telemetry.columns,
174
+ height: telemetry.rows,
175
+ extras: [
176
+ { label: 'view', value: `${telemetry.viewTimeMs.toFixed(2)} ms` },
177
+ { label: 'diff', value: `${telemetry.diffTimeMs.toFixed(2)} ms` },
178
+ ],
179
+ }, {
180
+ title: frameMessage(options.i18n, 'perfHud.title', 'Perf HUD'),
181
+ width,
182
+ showChart: false,
183
+ borderToken: options.ctx?.border('primary'),
184
+ bgToken: options.ctx?.surface('elevated'),
185
+ ctx: options.ctx,
186
+ });
187
+ return {
188
+ content: '',
189
+ surface,
190
+ row: 1,
191
+ col: Math.max(0, telemetry.columns - surface.width - 1),
192
+ };
193
+ }
161
194
  function applyFrameTimingSnapshot(model, snapshot) {
162
195
  if (model.frameTimeMs === snapshot.frameTimeMs
163
196
  && model.viewTimeMs === snapshot.viewTimeMs
@@ -201,14 +234,15 @@ export function createFramedApp(options) {
201
234
  pagesById.set(page.id, page);
202
235
  }
203
236
  const pageOrder = options.pages.map((p) => p.id);
204
- const defaultPageId = options.defaultPageId ?? pageOrder[0];
205
- if (!pagesById.has(defaultPageId)) {
206
- throw new Error(`createFramedApp: defaultPageId "${defaultPageId}" not found in pages`);
237
+ const defaultPageId = options.defaultPageId ?? pageOrder.at(0);
238
+ if (defaultPageId === undefined || !pagesById.has(defaultPageId)) {
239
+ throw new Error(`createFramedApp: defaultPageId "${String(defaultPageId)}" not found in pages`);
207
240
  }
208
241
  const shellThemeSpecs = options.shellThemes ?? [];
209
242
  let defaultFrameCtx = options.ctx ?? resolveSafeCtx();
210
243
  let resolvedShellThemes = [];
211
- const enableShellThemeSettings = shellThemeSpecs.length > 1;
244
+ const enableShellThemeSettings = shellThemeSpecs.length > 1
245
+ || shellThemeSpecs.some((theme) => (theme.modes?.length ?? 0) > 1);
212
246
  const usesAmbientDefaultContext = options.ctx == null && defaultFrameCtx != null;
213
247
  let frameCtx = options.ctx;
214
248
  let frameCtxShellThemeId;
@@ -221,13 +255,7 @@ export function createFramedApp(options) {
221
255
  throw new Error('createFramedApp: shellThemes requires options.ctx, app.run({ ctx }), or a default Bijou context');
222
256
  }
223
257
  defaultFrameCtx ??= baseCtx;
224
- resolvedShellThemes = shellThemeSpecs.map((theme) => ({
225
- id: theme.id,
226
- label: theme.label,
227
- description: theme.description,
228
- shellTheme: theme,
229
- resolvedTheme: createResolved(theme.theme, defaultFrameCtx.theme.noColor, defaultFrameCtx.theme.colorScheme),
230
- }));
258
+ resolvedShellThemes = resolveFrameShellThemeChoices(shellThemeSpecs, defaultFrameCtx);
231
259
  }
232
260
  if (frameCtx != null) {
233
261
  ensureResolvedShellThemes(frameCtx);
@@ -263,22 +291,21 @@ export function createFramedApp(options) {
263
291
  }
264
292
  options.onShellThemeChange?.({
265
293
  shellTheme: nextTheme.shellTheme,
294
+ shellThemeSpec: nextTheme.shellThemeSpec,
295
+ shellThemeId: nextTheme.shellThemeId,
296
+ shellThemeLabel: nextTheme.shellThemeLabel,
297
+ modeId: nextTheme.modeId,
298
+ modeLabel: nextTheme.modeLabel,
266
299
  ctx: frameCtx,
267
300
  });
268
301
  return frameCtx;
269
302
  }
270
303
  const frameKeys = createFrameKeyMap({
271
304
  enableSettings: options.settings != null || enableShellThemeSettings,
305
+ enableShellThemeModeToggle: shellThemeSpecs.some((theme) => (theme.modes?.length ?? 0) > 1),
272
306
  enableNotifications: options.notificationCenter != null || options.runtimeNotifications !== false,
273
307
  i18n: options.i18n,
274
308
  });
275
- const frameInputRecognizer = createInputGestureRecognizer({
276
- doubleTapMs: FRAME_FOOTER_DOUBLE_TAB_MS,
277
- });
278
- const frameInputActions = createInputActionMap()
279
- .bind('frame.footer.doubleTab', 'Toggle footer', [
280
- { deviceId: 'keyboard', featureId: 'key.tab', type: 'double-tap' },
281
- ], { type: 'toggle-footer' });
282
309
  const quitHelpKeys = createQuitHelpKeys(options.i18n);
283
310
  const helpLayerHelpKeys = createHelpLayerHelpKeys(options.i18n);
284
311
  const settingsHelpKeys = createSettingsHelpKeys(options.i18n);
@@ -300,10 +327,10 @@ export function createFramedApp(options) {
300
327
  });
301
328
  function bindingComboKey(binding) {
302
329
  const combo = binding.combo;
303
- return `${combo.key}|${combo.ctrl ? 1 : 0}|${combo.alt ? 1 : 0}|${combo.shift ? 1 : 0}`;
330
+ return [combo.key, combo.ctrl ? '1' : '0', combo.alt ? '1' : '0', combo.shift ? '1' : '0'].join('|');
304
331
  }
305
332
  function findBindingForMessage(bindings, msg) {
306
- const comboKey = `${msg.key}|${msg.ctrl ? 1 : 0}|${msg.alt ? 1 : 0}|${msg.shift ? 1 : 0}`;
333
+ const comboKey = [msg.key, msg.ctrl ? '1' : '0', msg.alt ? '1' : '0', msg.shift ? '1' : '0'].join('|');
307
334
  return bindings.find((binding) => binding.enabled && bindingComboKey(binding) === comboKey);
308
335
  }
309
336
  function queueFrameKeyCollisionWarning(model, msg, teaCmds) {
@@ -320,7 +347,7 @@ export function createFramedApp(options) {
320
347
  const frameBinding = findBindingForMessage(frameKeys.bindings(), msg);
321
348
  if (pageBinding == null || frameBinding == null)
322
349
  return model;
323
- const warningCmd = async () => wrapFrameMsg({
350
+ const warningCmd = () => wrapFrameMsg({
324
351
  type: 'runtime-issue',
325
352
  issue: {
326
353
  level: 'warning',
@@ -342,43 +369,12 @@ export function createFramedApp(options) {
342
369
  };
343
370
  }
344
371
  function getComposedFrameScratch(width, height) {
345
- if (composedFrameScratch == null
346
- || composedFrameScratch.width !== width
372
+ if (composedFrameScratch?.width !== width
347
373
  || composedFrameScratch.height !== height) {
348
374
  composedFrameScratch = createSurface(width, height);
349
375
  }
350
376
  return composedFrameScratch;
351
377
  }
352
- function renderFramePerfHudOverlay(model, ctx) {
353
- const refreshRate = ctx?.runtime.refreshRate ?? 60;
354
- const fps = model.frameTimeMs > 0
355
- ? Math.min(refreshRate, 1000 / model.frameTimeMs)
356
- : refreshRate;
357
- const width = Math.max(12, Math.min(40, model.columns - 2));
358
- const surface = perfOverlaySurface({
359
- fps,
360
- frameTimeMs: model.frameTimeMs,
361
- width: model.columns,
362
- height: model.rows,
363
- extras: [
364
- { label: 'view', value: `${model.viewTimeMs.toFixed(2)} ms` },
365
- { label: 'diff', value: `${model.diffTimeMs.toFixed(2)} ms` },
366
- ],
367
- }, {
368
- title: frameMessage(options.i18n, 'perfHud.title', 'Perf HUD'),
369
- width,
370
- showChart: false,
371
- borderToken: ctx?.border('primary'),
372
- bgToken: ctx?.surface('elevated'),
373
- ctx,
374
- });
375
- return {
376
- content: '',
377
- surface,
378
- row: 1,
379
- col: Math.max(0, model.columns - surface.width - 1),
380
- };
381
- }
382
378
  function closeCommandPalette(model) {
383
379
  return {
384
380
  ...model,
@@ -388,189 +384,171 @@ export function createFramedApp(options) {
388
384
  commandPaletteKind: undefined,
389
385
  };
390
386
  }
391
- const shellCommandHandlers = {
392
- // --- overlay lifecycle ---
393
- 'close-help': (model) => ({ ...model, helpOpen: false, helpScrollY: 0 }),
394
- 'close-settings': (model) => ({ ...model, settingsOpen: false }),
395
- 'close-notification-center': (model) => ({ ...model, notificationCenterOpen: false, notificationCenterScrollY: 0 }),
396
- 'close-palette': (model) => closeCommandPalette(model),
397
- 'close-quit-confirm': (model) => ({ ...model, quitConfirmOpen: false }),
398
- 'open-help': (model) => ({ ...model, helpOpen: true }),
399
- 'open-quit-confirm': (model) => {
400
- if (!shouldUseShellQuitConfirm())
401
- return model;
402
- if (model.quitConfirmOpen)
403
- return model;
404
- return {
405
- ...model,
406
- quitConfirmOpen: true,
407
- helpOpen: false,
408
- helpScrollY: 0,
409
- settingsOpen: false,
410
- notificationCenterOpen: false,
411
- commandPalette: undefined,
412
- commandPaletteEntries: undefined,
413
- commandPaletteTitle: undefined,
414
- commandPaletteKind: undefined,
415
- };
416
- },
417
- 'open-search-palette': (model) => openSearchPalette(model, frameKeys, options, pagesById),
418
- 'open-command-palette': (model) => openCommandPalette(model, frameKeys, options, pagesById),
419
- // --- settings ---
420
- 'settings-focus-move': (model, cmd) => {
421
- const c = cmd;
422
- const layout = resolveSettingsLayout(model, options, pagesById, resolvedShellThemes);
423
- return layout != null ? moveSettingsFocus(model, layout, c.delta) : model;
424
- },
425
- 'settings-scroll': (model, cmd) => {
426
- const c = cmd;
427
- const layout = resolveSettingsLayout(model, options, pagesById, resolvedShellThemes);
428
- return layout != null ? scrollSettingsBy(model, layout, c.delta) : model;
429
- },
430
- 'settings-scroll-to': (model, cmd) => {
431
- const c = cmd;
432
- const layout = resolveSettingsLayout(model, options, pagesById, resolvedShellThemes);
433
- if (layout == null)
434
- return model;
435
- return { ...model, settingsScrollY: c.position === 'top' ? 0 : layout.maxScrollY };
436
- },
437
- 'activate-settings-row': (model, cmd, teaCmds) => {
438
- const c = cmd;
439
- const layout = resolveSettingsLayout(model, options, pagesById, resolvedShellThemes);
440
- if (layout == null)
441
- return model;
442
- const hitRow = layout.rows.find((r) => r.index === c.rowIndex);
443
- if (hitRow == null)
444
- return model;
445
- const focusedModel = { ...model, settingsFocusIndex: hitRow.index };
446
- if (hitRow.behavior === 'cycle-shell-theme') {
447
- const [nextModel, cmds] = cycleShellThemeSetting(focusedModel, hitRow.row);
387
+ function applyShellCommand(model, cmd, teaCmds) {
388
+ switch (cmd.type) {
389
+ case 'close-help':
390
+ return { ...model, helpOpen: false, helpScrollY: 0 };
391
+ case 'close-settings':
392
+ return { ...model, settingsOpen: false };
393
+ case 'close-notification-center':
394
+ return { ...model, notificationCenterOpen: false, notificationCenterScrollY: 0 };
395
+ case 'close-palette':
396
+ return closeCommandPalette(model);
397
+ case 'close-quit-confirm':
398
+ return { ...model, quitConfirmOpen: false };
399
+ case 'open-help':
400
+ return { ...model, helpOpen: true };
401
+ case 'open-quit-confirm': {
402
+ if (!shouldUseShellQuitConfirm() || model.quitConfirmOpen)
403
+ return model;
404
+ return {
405
+ ...model,
406
+ quitConfirmOpen: true,
407
+ helpOpen: false,
408
+ helpScrollY: 0,
409
+ settingsOpen: false,
410
+ notificationCenterOpen: false,
411
+ commandPalette: undefined,
412
+ commandPaletteEntries: undefined,
413
+ commandPaletteTitle: undefined,
414
+ commandPaletteKind: undefined,
415
+ };
416
+ }
417
+ case 'open-search-palette':
418
+ return openSearchPalette(model, frameKeys, options, pagesById);
419
+ case 'open-command-palette':
420
+ return openCommandPalette(model, frameKeys, options, pagesById);
421
+ case 'settings-focus-move': {
422
+ const layout = resolveSettingsLayout(model, options, pagesById, resolvedShellThemes);
423
+ return layout != null ? moveSettingsFocus(model, layout, cmd.delta) : model;
424
+ }
425
+ case 'settings-scroll': {
426
+ const layout = resolveSettingsLayout(model, options, pagesById, resolvedShellThemes);
427
+ return layout != null ? scrollSettingsBy(model, layout, cmd.delta) : model;
428
+ }
429
+ case 'settings-scroll-to': {
430
+ const layout = resolveSettingsLayout(model, options, pagesById, resolvedShellThemes);
431
+ if (layout == null)
432
+ return model;
433
+ return { ...model, settingsScrollY: cmd.position === 'top' ? 0 : layout.maxScrollY };
434
+ }
435
+ case 'activate-settings-row': {
436
+ const layout = resolveSettingsLayout(model, options, pagesById, resolvedShellThemes);
437
+ if (layout == null)
438
+ return model;
439
+ const hitRow = layout.rows.find((row) => row.index === cmd.rowIndex);
440
+ if (hitRow == null)
441
+ return model;
442
+ const focusedModel = { ...model, settingsFocusIndex: hitRow.index };
443
+ if (hitRow.behavior === 'cycle-shell-theme') {
444
+ const [nextModel, cmds] = cycleShellThemeSetting(focusedModel, hitRow.row);
445
+ teaCmds.push(...cmds);
446
+ return nextModel;
447
+ }
448
+ if (hitRow.row.action === undefined || hitRow.row.enabled === false || hitRow.row.kind === 'info') {
449
+ return focusedModel;
450
+ }
451
+ const [nextModel, cmds] = activateSettingsRow(focusedModel, hitRow.row);
448
452
  teaCmds.push(...cmds);
449
453
  return nextModel;
450
454
  }
451
- if (hitRow.row.action === undefined || hitRow.row.enabled === false || hitRow.row.kind === 'info') {
452
- return focusedModel;
455
+ case 'toggle-shell-theme-mode': {
456
+ const [nextModel, cmds] = toggleShellThemeMode(model);
457
+ teaCmds.push(...cmds);
458
+ return nextModel;
453
459
  }
454
- const [nextModel, cmds] = activateSettingsRow(focusedModel, hitRow.row);
455
- teaCmds.push(...cmds);
456
- return nextModel;
457
- },
458
- // --- notification center ---
459
- 'notification-center-scroll': (model, cmd) => {
460
- const c = cmd;
461
- const layout = resolveNotificationCenterLayout(model, options, pagesById, resolveFrameThemeCtx(model.activeShellThemeId));
462
- return layout != null ? scrollNotificationCenterBy(model, layout, c.delta) : model;
463
- },
464
- 'notification-center-scroll-to': (model, cmd) => {
465
- const c = cmd;
466
- const layout = resolveNotificationCenterLayout(model, options, pagesById, resolveFrameThemeCtx(model.activeShellThemeId));
467
- if (layout == null)
460
+ case 'notification-center-scroll': {
461
+ const layout = resolveNotificationCenterLayout(model, options, pagesById, resolveFrameThemeCtx(model.activeShellThemeId));
462
+ return layout != null ? scrollNotificationCenterBy(model, layout, cmd.delta) : model;
463
+ }
464
+ case 'notification-center-scroll-to': {
465
+ const layout = resolveNotificationCenterLayout(model, options, pagesById, resolveFrameThemeCtx(model.activeShellThemeId));
466
+ if (layout == null)
467
+ return model;
468
+ return { ...model, notificationCenterScrollY: cmd.position === 'top' ? 0 : layout.maxScrollY };
469
+ }
470
+ case 'cycle-notification-filter': {
471
+ const layout = resolveNotificationCenterLayout(model, options, pagesById, resolveFrameThemeCtx(model.activeShellThemeId));
472
+ if (layout == null)
473
+ return model;
474
+ const [nextModel, cmds] = cycleNotificationCenterFilter(model, layout);
475
+ teaCmds.push(...cmds);
476
+ return nextModel;
477
+ }
478
+ case 'warn-frame-key-collision':
479
+ return queueFrameKeyCollisionWarning(model, cmd.msg, teaCmds);
480
+ case 'help-scroll': {
481
+ const helpSource = resolvePresentedLayerContext(model).controlProjection.helpSource;
482
+ if (helpSource == null)
483
+ return model;
484
+ const overlay = renderHelpOverlay(model, helpSource, options.i18n);
485
+ const viewportHeight = Math.max(1, overlay.body.height - 1);
486
+ const delta = cmd.action === 'down' ? 3
487
+ : cmd.action === 'up' ? -3
488
+ : cmd.action === 'page-down' ? viewportHeight
489
+ : cmd.action === 'page-up' ? -viewportHeight
490
+ : cmd.action === 'bottom' ? Infinity
491
+ : -Infinity;
492
+ return { ...model, helpScrollY: Math.max(0, Math.min(overlay.maxScrollY, overlay.scrollY + delta)) };
493
+ }
494
+ case 'focus-pane':
495
+ return focusPane(model, cmd.paneId);
496
+ case 'scroll-focused-pane':
497
+ return scrollFocusedPane(model, { type: cmd.direction === 'down' ? 'scroll-down' : 'scroll-up' }, pagesById, options);
498
+ case 'switch-tab': {
499
+ const [nextModel, cmds] = switchTab(model, cmd.delta, pagesById, options);
500
+ teaCmds.push(...cmds);
501
+ return nextModel;
502
+ }
503
+ case 'apply-frame-action': {
504
+ const [nextModel, cmds] = applyFrameAction(cmd.action, model, options, pagesById);
505
+ teaCmds.push(...cmds);
506
+ return nextModel;
507
+ }
508
+ case 'palette-key': {
509
+ const [nextModel, cmds] = handlePaletteKey(cmd.msg, model, paletteKeys, options, pagesById, (action, closedModel) => action.type === 'toggle-shell-theme-mode'
510
+ ? toggleShellThemeMode(closedModel)
511
+ : undefined);
512
+ teaCmds.push(...cmds);
513
+ return nextModel;
514
+ }
515
+ case 'emit-page-msg':
516
+ teaCmds.push(emitMsgForPage(cmd.pageId, cmd.msg));
468
517
  return model;
469
- return { ...model, notificationCenterScrollY: c.position === 'top' ? 0 : layout.maxScrollY };
470
- },
471
- 'cycle-notification-filter': (model, _cmd, teaCmds) => {
472
- const layout = resolveNotificationCenterLayout(model, options, pagesById, resolveFrameThemeCtx(model.activeShellThemeId));
473
- if (layout == null)
518
+ case 'emit-global-msg':
519
+ teaCmds.push(emitMsg(cmd.msg));
474
520
  return model;
475
- const [nextModel, cmds] = cycleNotificationCenterFilter(model, layout);
476
- teaCmds.push(...cmds);
477
- return nextModel;
478
- },
479
- 'warn-frame-key-collision': (model, cmd, teaCmds) => {
480
- const c = cmd;
481
- return queueFrameKeyCollisionWarning(model, c.msg, teaCmds);
482
- },
483
- // --- help ---
484
- 'help-scroll': (model, cmd) => {
485
- const c = cmd;
486
- const helpSource = resolvePresentedLayerContext(model).controlProjection.helpSource;
487
- if (helpSource == null) {
521
+ case 'quit':
522
+ teaCmds.push(quit());
488
523
  return model;
524
+ case 'dismiss-notification': {
525
+ if (!frameNotificationOptions.enabled)
526
+ return model;
527
+ const nowMs = resolveClock(resolveFrameCtx()).now();
528
+ const [nextModel, cmds] = applyFrameNotificationState(model, dismissNotification(model.runtimeNotifications, cmd.notificationId, nowMs), nowMs);
529
+ teaCmds.push(...cmds);
530
+ return nextModel;
489
531
  }
490
- const overlay = renderHelpOverlay(model, helpSource, options.i18n);
491
- const viewportHeight = Math.max(1, overlay.body.height - 1);
492
- const delta = c.action === 'down' ? 3
493
- : c.action === 'up' ? -3
494
- : c.action === 'page-down' ? viewportHeight
495
- : c.action === 'page-up' ? -viewportHeight
496
- : c.action === 'bottom' ? Infinity
497
- : /* top */ -Infinity;
498
- return {
499
- ...model,
500
- helpScrollY: Math.max(0, Math.min(overlay.maxScrollY, overlay.scrollY + delta)),
501
- };
502
- },
503
- // --- workspace ---
504
- 'focus-pane': (model, cmd) => {
505
- const c = cmd;
506
- return focusPane(model, c.paneId);
507
- },
508
- 'scroll-focused-pane': (model, cmd) => {
509
- const c = cmd;
510
- return scrollFocusedPane(model, { type: c.direction === 'down' ? 'scroll-down' : 'scroll-up' }, pagesById, options);
511
- },
512
- 'switch-tab': (model, cmd, teaCmds) => {
513
- const c = cmd;
514
- const [nextModel, cmds] = switchTab(model, c.delta, pagesById, options);
515
- teaCmds.push(...cmds);
516
- return nextModel;
517
- },
518
- // --- delegation ---
519
- 'apply-frame-action': (model, cmd, teaCmds) => {
520
- const c = cmd;
521
- const [nextModel, cmds] = applyFrameAction(c.action, model, options, pagesById);
522
- teaCmds.push(...cmds);
523
- return nextModel;
524
- },
525
- 'palette-key': (model, cmd, teaCmds) => {
526
- const c = cmd;
527
- const [nextModel, cmds] = handlePaletteKey(c.msg, model, paletteKeys, options, pagesById);
528
- teaCmds.push(...cmds);
529
- return nextModel;
530
- },
531
- // --- TEA command emissions ---
532
- 'emit-page-msg': (model, cmd, teaCmds) => {
533
- const c = cmd;
534
- teaCmds.push(emitMsgForPage(c.pageId, c.msg));
535
- return model;
536
- },
537
- 'emit-global-msg': (model, cmd, teaCmds) => {
538
- const c = cmd;
539
- teaCmds.push(emitMsg(c.msg));
540
- return model;
541
- },
542
- 'quit': (_model, _cmd, teaCmds) => {
543
- teaCmds.push(quit());
544
- return _model;
545
- },
546
- 'dismiss-notification': (model, cmd, teaCmds) => {
547
- const c = cmd;
548
- if (!frameNotificationOptions.enabled)
532
+ case 'observed-key': {
533
+ const observed = options.observeKey?.(cmd.msg, cmd.route);
534
+ if (observed !== undefined)
535
+ teaCmds.push(emitMsgForPage(model.activePageId, observed));
549
536
  return model;
550
- const nowMs = resolveClock(resolveFrameCtx()).now();
551
- const [nextModel, cmds] = applyFrameNotificationState(model, dismissNotification(model.runtimeNotifications, c.notificationId, nowMs), nowMs);
552
- teaCmds.push(...cmds);
553
- return nextModel;
554
- },
555
- // --- observation ---
556
- 'observed-key': (model, cmd, teaCmds) => {
557
- const c = cmd;
558
- const observed = options.observeKey?.(c.msg, c.route);
559
- if (observed !== undefined) {
560
- teaCmds.push(emitMsgForPage(model.activePageId, observed));
561
537
  }
562
- return model;
563
- },
564
- };
538
+ }
539
+ }
565
540
  function drainShellCommandBuffer(model, routeResult) {
566
541
  const buffers = bufferRuntimeRouteResult(createRuntimeBuffers(), routeResult);
567
542
  const teaCmds = [];
568
- const { state } = applyRuntimeCommandBuffer(model, buffers.commands, (s, cmd) => shellCommandHandlers[cmd.type](s, cmd, teaCmds));
543
+ const { state } = applyRuntimeCommandBuffer(model, buffers.commands, (s, cmd) => applyShellCommand(s, cmd, teaCmds));
569
544
  return [state, teaCmds];
570
545
  }
571
546
  function resolveLayerContext(model) {
572
547
  const activePage = pagesById.get(model.activePageId);
573
548
  const activePageModel = model.pageModels[model.activePageId];
549
+ if (activePage == null || activePageModel === undefined) {
550
+ throw new Error(`createFramedApp: active page "${model.activePageId}" is missing`);
551
+ }
574
552
  const inputAreas = resolveInputAreas(activePage, activePageModel);
575
553
  const activeInputArea = findInputAreaByPaneId(inputAreas, model.focusedPaneByPage[model.activePageId]);
576
554
  const modalKeyMap = activePage.modalKeyMap?.(activePageModel);
@@ -592,23 +570,6 @@ export function createFramedApp(options) {
592
570
  }
593
571
  return [{ type: 'observed-key', msg, route }, { type: 'open-quit-confirm' }];
594
572
  }
595
- function resolveFrameInputActionCommands(msg, route) {
596
- const inputEvent = frameInputRecognizer.observeKey(msg, resolveClock(resolveFrameCtx()).now());
597
- const inputAction = frameInputActions.handle(inputEvent);
598
- if (inputAction == null || msg.ctrl || msg.alt || msg.shift) {
599
- return undefined;
600
- }
601
- return [{ type: 'observed-key', msg, route }, { type: 'apply-frame-action', action: inputAction }];
602
- }
603
- function routeForFrameInputAction(layerKind) {
604
- if (layerKind === 'search' || layerKind === 'command-palette')
605
- return 'palette';
606
- if (layerKind === 'help')
607
- return 'help';
608
- if (layerKind === 'page-modal')
609
- return 'page';
610
- return 'frame';
611
- }
612
573
  function resolveFrameActionCommands(msg, action, route) {
613
574
  if (action.type === 'open-search' && options.enableCommandPalette) {
614
575
  return [{ type: 'observed-key', msg, route }, { type: 'open-search-palette' }];
@@ -616,6 +577,9 @@ export function createFramedApp(options) {
616
577
  if (action.type === 'open-palette' && options.enableCommandPalette) {
617
578
  return [{ type: 'observed-key', msg, route }, { type: 'open-command-palette' }];
618
579
  }
580
+ if (action.type === 'toggle-shell-theme-mode') {
581
+ return [{ type: 'observed-key', msg, route }, { type: 'toggle-shell-theme-mode' }];
582
+ }
619
583
  return [{ type: 'observed-key', msg, route }, { type: 'apply-frame-action', action }];
620
584
  }
621
585
  function handlePaletteLayerKeyCommands(msg, routedLayerKind) {
@@ -637,6 +601,9 @@ export function createFramedApp(options) {
637
601
  ? [obs, { type: 'close-palette' }]
638
602
  : [obs, { type: 'open-command-palette' }];
639
603
  }
604
+ if (frameAction?.type === 'toggle-shell-theme-mode') {
605
+ return [obs, { type: 'toggle-shell-theme-mode' }];
606
+ }
640
607
  if (frameAction?.type === 'toggle-notifications') {
641
608
  return [obs, { type: 'close-palette' }, { type: 'apply-frame-action', action: frameAction }];
642
609
  }
@@ -654,6 +621,9 @@ export function createFramedApp(options) {
654
621
  return quitRequestCommands(msg, 'help');
655
622
  }
656
623
  const helpAction = frameKeys.handle(msg);
624
+ if (helpAction?.type === 'toggle-shell-theme-mode') {
625
+ return [obs, { type: 'toggle-shell-theme-mode' }];
626
+ }
657
627
  if (helpAction?.type === 'toggle-perf-hud') {
658
628
  return [obs, { type: 'apply-frame-action', action: helpAction }];
659
629
  }
@@ -692,6 +662,9 @@ export function createFramedApp(options) {
692
662
  return [obs, { type: 'open-command-palette' }];
693
663
  }
694
664
  const settingsFrameAction = frameKeys.handle(msg);
665
+ if (settingsFrameAction?.type === 'toggle-shell-theme-mode') {
666
+ return [obs, { type: 'toggle-shell-theme-mode' }];
667
+ }
695
668
  if (settingsFrameAction?.type === 'toggle-notifications') {
696
669
  return [obs, { type: 'apply-frame-action', action: settingsFrameAction }];
697
670
  }
@@ -747,6 +720,9 @@ export function createFramedApp(options) {
747
720
  return quitRequestCommands(msg, 'frame');
748
721
  }
749
722
  const centerFrameAction = frameKeys.handle(msg);
723
+ if (centerFrameAction?.type === 'toggle-shell-theme-mode') {
724
+ return [obs, { type: 'toggle-shell-theme-mode' }];
725
+ }
750
726
  if (centerFrameAction?.type === 'toggle-notifications') {
751
727
  return [obs, { type: 'apply-frame-action', action: centerFrameAction }];
752
728
  }
@@ -840,10 +816,6 @@ export function createFramedApp(options) {
840
816
  const frameLayer = layer.model;
841
817
  if (frameLayer == null)
842
818
  return undefined;
843
- const inputActionCommands = resolveFrameInputActionCommands(msg, routeForFrameInputAction(frameLayer.kind));
844
- if (inputActionCommands != null) {
845
- return { handled: true, commands: inputActionCommands };
846
- }
847
819
  if (frameLayer.kind === 'search' || frameLayer.kind === 'command-palette') {
848
820
  return { handled: true, commands: handlePaletteLayerKeyCommands(msg, frameLayer.kind) };
849
821
  }
@@ -923,9 +895,13 @@ export function createFramedApp(options) {
923
895
  }
924
896
  function rememberWorkspaceLayout(model, paneRects, tabTargets) {
925
897
  const activePageId = model.activePageId;
898
+ const activePageModel = model.pageModels[activePageId];
899
+ if (activePageModel === undefined) {
900
+ throw new Error(`createFramedApp: active page model "${activePageId}" is missing`);
901
+ }
926
902
  const next = {
927
903
  activePageId,
928
- activePageModel: model.pageModels[activePageId],
904
+ activePageModel,
929
905
  columns: model.columns,
930
906
  rows: model.rows,
931
907
  visibilityState: model.minimizedByPage[activePageId],
@@ -953,7 +929,9 @@ export function createFramedApp(options) {
953
929
  }
954
930
  function resolveWorkspaceLayout(model) {
955
931
  if (matchesWorkspaceLayoutCache(model)) {
956
- return workspaceLayoutCache;
932
+ const cached = workspaceLayoutCache;
933
+ if (cached !== undefined)
934
+ return cached;
957
935
  }
958
936
  const bodyRect = resolveBodyRect(model, options);
959
937
  const maxState = model.maximizedPaneByPage[model.activePageId];
@@ -981,7 +959,7 @@ export function createFramedApp(options) {
981
959
  const clippedBottom = Math.min(viewportBottom, screenRow + flatRow.height);
982
960
  if (clippedTop >= clippedBottom)
983
961
  continue;
984
- children.push(createShellRetainedLayoutNode(`settings-row:${flatRow.index}`, {
962
+ children.push(createShellRetainedLayoutNode(`settings-row:${String(flatRow.index)}`, {
985
963
  row: clippedTop,
986
964
  col: layout.startCol,
987
965
  width: layout.drawerWidth,
@@ -990,6 +968,10 @@ export function createFramedApp(options) {
990
968
  }
991
969
  return children;
992
970
  }
971
+ function nodeIdSuffix(node, prefix) {
972
+ const id = node.id;
973
+ return id?.startsWith(prefix) === true ? id.slice(prefix.length) : undefined;
974
+ }
993
975
  function resolveFrameMouseRuntimeLayouts(model) {
994
976
  let layouts = EMPTY_RUNTIME_LAYOUTS;
995
977
  const settingsLayout = model.settingsOpen
@@ -1063,8 +1045,9 @@ export function createFramedApp(options) {
1063
1045
  if (msg.action === 'press' && msg.button === 'left') {
1064
1046
  const rowNode = hit.path.find((n) => n.id?.startsWith('settings-row:'));
1065
1047
  if (rowNode != null) {
1066
- const rowIndex = parseInt(rowNode.id.slice('settings-row:'.length), 10);
1067
- cmds.push({ type: 'activate-settings-row', rowIndex });
1048
+ const rowId = nodeIdSuffix(rowNode, 'settings-row:');
1049
+ if (rowId !== undefined)
1050
+ cmds.push({ type: 'activate-settings-row', rowIndex: Number.parseInt(rowId, 10) });
1068
1051
  }
1069
1052
  return { handled: true, commands: cmds };
1070
1053
  }
@@ -1100,8 +1083,8 @@ export function createFramedApp(options) {
1100
1083
  }
1101
1084
  // tab click
1102
1085
  const tabNode = hit?.path.find((n) => n.id?.startsWith('tab:'));
1103
- if (tabNode != null) {
1104
- const pageId = tabNode.id.slice('tab:'.length);
1086
+ const pageId = tabNode == null ? undefined : nodeIdSuffix(tabNode, 'tab:');
1087
+ if (pageId !== undefined) {
1105
1088
  const currentIndex = model.pageOrder.indexOf(model.activePageId);
1106
1089
  const nextIndex = model.pageOrder.indexOf(pageId);
1107
1090
  if (currentIndex >= 0 && nextIndex >= 0 && nextIndex !== currentIndex) {
@@ -1114,18 +1097,21 @@ export function createFramedApp(options) {
1114
1097
  }
1115
1098
  // pane click
1116
1099
  const clickedPaneNode = hit?.path.find((n) => n.id?.startsWith('pane:'));
1117
- if (clickedPaneNode != null) {
1118
- const paneId = clickedPaneNode.id.slice('pane:'.length);
1100
+ const clickedPaneId = clickedPaneNode == null ? undefined : nodeIdSuffix(clickedPaneNode, 'pane:');
1101
+ if (clickedPaneId !== undefined) {
1119
1102
  const paneRects = resolveWorkspacePaneRects(model);
1120
- const paneRect = paneRects.get(paneId);
1103
+ const paneRect = paneRects.get(clickedPaneId);
1121
1104
  if (paneRect != null) {
1122
- cmds.push({ type: 'focus-pane', paneId });
1123
- const inputArea = findInputAreaByPaneId(inputAreas, paneId);
1105
+ cmds.push({ type: 'focus-pane', paneId: clickedPaneId });
1106
+ const inputArea = findInputAreaByPaneId(inputAreas, clickedPaneId);
1124
1107
  const areaMsg = inputArea?.mouse?.({ msg, model: activePageModel, rect: paneRect });
1108
+ let routedMsg = msg;
1109
+ if (areaMsg !== undefined)
1110
+ routedMsg = areaMsg;
1125
1111
  cmds.push({
1126
1112
  type: 'emit-page-msg',
1127
1113
  pageId: model.activePageId,
1128
- msg: areaMsg !== undefined ? areaMsg : msg,
1114
+ msg: routedMsg,
1129
1115
  });
1130
1116
  return { handled: true, commands: cmds };
1131
1117
  }
@@ -1133,13 +1119,13 @@ export function createFramedApp(options) {
1133
1119
  }
1134
1120
  if (msg.action === 'scroll-up' || msg.action === 'scroll-down') {
1135
1121
  const scrollPaneNode = hit?.path.find((n) => n.id?.startsWith('pane:'));
1136
- if (scrollPaneNode != null) {
1137
- const paneId = scrollPaneNode.id.slice('pane:'.length);
1122
+ const scrollPaneId = scrollPaneNode == null ? undefined : nodeIdSuffix(scrollPaneNode, 'pane:');
1123
+ if (scrollPaneId !== undefined) {
1138
1124
  const paneRects = resolveWorkspacePaneRects(model);
1139
- const paneRect = paneRects.get(paneId);
1125
+ const paneRect = paneRects.get(scrollPaneId);
1140
1126
  if (paneRect != null) {
1141
- cmds.push({ type: 'focus-pane', paneId });
1142
- const inputArea = findInputAreaByPaneId(inputAreas, paneId);
1127
+ cmds.push({ type: 'focus-pane', paneId: scrollPaneId });
1128
+ const inputArea = findInputAreaByPaneId(inputAreas, scrollPaneId);
1143
1129
  const areaMsg = inputArea?.mouse?.({ msg, model: activePageModel, rect: paneRect });
1144
1130
  if (areaMsg !== undefined) {
1145
1131
  cmds.push({ type: 'emit-page-msg', pageId: model.activePageId, msg: areaMsg });
@@ -1151,7 +1137,7 @@ export function createFramedApp(options) {
1151
1137
  }
1152
1138
  }
1153
1139
  }
1154
- return { handled: true };
1140
+ return { handled: false };
1155
1141
  });
1156
1142
  }
1157
1143
  function resolveWorkspaceHelpSource(activePage, activeInputArea) {
@@ -1178,7 +1164,7 @@ export function createFramedApp(options) {
1178
1164
  const helpHint = frameMessage(options.i18n, 'help.hint', 'j/k scroll • d/u page • g/G top/bottom • mouse wheel • ?/Esc close');
1179
1165
  const settingsHint = frameMessage(options.i18n, 'settings.footer', 'F2/Esc close • ↑/↓ rows • Enter toggle • / search • q quit');
1180
1166
  const notificationsHint = frameMessage(options.i18n, 'notifications.footer', 'Shift+N close • f filter • j/k scroll • q quit');
1181
- const quitHint = frameMessage(options.i18n, 'quit.footer', 'Y quit • N stay');
1167
+ const quitHint = frameMessage(options.i18n, 'quit.footer', 'Y Quit • N Stay');
1182
1168
  const paletteTitle = model.commandPaletteTitle
1183
1169
  ?? frameMessage(options.i18n, 'palette.title', 'Command Palette');
1184
1170
  const activePageTitle = resolveFramePageText(activePage.title, activePageModel) ?? '';
@@ -1262,8 +1248,10 @@ export function createFramedApp(options) {
1262
1248
  if (targetPage == null)
1263
1249
  return [model, []];
1264
1250
  const pageModel = model.pageModels[targetPageId];
1251
+ if (pageModel === undefined)
1252
+ return [model, []];
1265
1253
  const updateResult = targetPage.update(targetMsg, pageModel);
1266
- const [nextPageModel, cmds = []] = updateResult;
1254
+ const [nextPageModel, cmds] = updateResult;
1267
1255
  const nextModels = { ...model.pageModels, [targetPageId]: nextPageModel };
1268
1256
  const synced = syncPageFrameState({ ...model, pageModels: nextModels }, targetPageId, pagesById);
1269
1257
  const wrappedCmds = cmds.map((cmd) => wrapCmdForPage(targetPageId, cmd));
@@ -1328,6 +1316,53 @@ export function createFramedApp(options) {
1328
1316
  }, row);
1329
1317
  return [nextModel, notificationCmds];
1330
1318
  }
1319
+ function pushShellThemeModeFeedback(model, nextTheme) {
1320
+ if (!frameNotificationOptions.enabled) {
1321
+ return [model, []];
1322
+ }
1323
+ const nowMs = resolveClock(resolveFrameCtx()).now();
1324
+ const notifications = pushNotification(model.runtimeNotifications, {
1325
+ title: frameMessage(options.i18n, 'settings.title', 'Settings'),
1326
+ message: frameMessage(options.i18n, 'settings.shellTheme.feedback', 'Shell theme set to {theme}.', { theme: nextTheme.label }),
1327
+ variant: 'TOAST',
1328
+ tone: 'INFO',
1329
+ width: SETTINGS_FEEDBACK_TOAST_WIDTH,
1330
+ placement: frameNotificationOptions.placement,
1331
+ durationMs: 2_500,
1332
+ overflow: frameNotificationOptions.overflow,
1333
+ }, nowMs);
1334
+ return applyFrameNotificationState(model, notifications, nowMs);
1335
+ }
1336
+ function pushShellThemeModeUnsupportedFeedback(model, currentTheme) {
1337
+ if (!frameNotificationOptions.enabled || currentTheme == null) {
1338
+ return [model, []];
1339
+ }
1340
+ const nowMs = resolveClock(resolveFrameCtx()).now();
1341
+ const notifications = pushNotification(model.runtimeNotifications, {
1342
+ title: frameMessage(options.i18n, 'settings.title', 'Settings'),
1343
+ message: frameMessage(options.i18n, 'settings.shellTheme.modeUnsupported', 'Shell theme {theme} has no alternate mode.', { theme: currentTheme.label }),
1344
+ variant: 'TOAST',
1345
+ tone: 'INFO',
1346
+ width: SETTINGS_FEEDBACK_TOAST_WIDTH,
1347
+ placement: frameNotificationOptions.placement,
1348
+ durationMs: 2_500,
1349
+ overflow: frameNotificationOptions.overflow,
1350
+ }, nowMs);
1351
+ return applyFrameNotificationState(model, notifications, nowMs);
1352
+ }
1353
+ function toggleShellThemeMode(model) {
1354
+ ensureResolvedShellThemes(resolveFrameCtx());
1355
+ const currentTheme = resolveCurrentShellTheme(resolvedShellThemes, model.activeShellThemeId);
1356
+ const nextTheme = resolveShellThemeModeToggle(resolvedShellThemes, model.activeShellThemeId);
1357
+ if (nextTheme == null) {
1358
+ return pushShellThemeModeUnsupportedFeedback(model, currentTheme);
1359
+ }
1360
+ publishShellThemeContext(nextTheme);
1361
+ return pushShellThemeModeFeedback({
1362
+ ...model,
1363
+ activeShellThemeId: nextTheme.id,
1364
+ }, nextTheme);
1365
+ }
1331
1366
  const app = {
1332
1367
  init() {
1333
1368
  const pageModels = {};
@@ -1439,6 +1474,9 @@ export function createFramedApp(options) {
1439
1474
  const notifications = tickNotifications(model.runtimeNotifications, action.atMs);
1440
1475
  return applyFrameNotificationState(model, notifications, action.atMs, true);
1441
1476
  }
1477
+ if (action.type === 'toggle-shell-theme-mode') {
1478
+ return toggleShellThemeMode(model);
1479
+ }
1442
1480
  if (action.type === 'transition') {
1443
1481
  // Ignore stale transition ticks from a previous generation
1444
1482
  if (action.generation !== model.transitionGeneration)
@@ -1561,9 +1599,13 @@ export function createFramedApp(options) {
1561
1599
  rememberWorkspaceLayout(model, activeResult.paneRects, headerResult.tabTargets);
1562
1600
  const overlays = [];
1563
1601
  if (options.overlayFactory != null) {
1602
+ const activePageModel = model.pageModels[model.activePageId];
1603
+ if (activePageModel === undefined) {
1604
+ throw new Error(`createFramedApp: active page model "${model.activePageId}" is missing`);
1605
+ }
1564
1606
  overlays.push(...options.overlayFactory({
1565
1607
  activePageId: model.activePageId,
1566
- pageModel: model.pageModels[model.activePageId],
1608
+ pageModel: activePageModel,
1567
1609
  paneRects: activeResult.paneRects,
1568
1610
  screenRect: { row: 0, col: 0, width: model.columns, height: model.rows },
1569
1611
  }));
@@ -1643,7 +1685,17 @@ export function createFramedApp(options) {
1643
1685
  }
1644
1686
  const dimBackground = overlays.length > 0;
1645
1687
  if (model.perfHudOpen) {
1646
- overlays.push(renderFramePerfHudOverlay(model, themedFrameCtx));
1688
+ overlays.push(renderFramePerfHudOverlay({
1689
+ columns: model.columns,
1690
+ rows: model.rows,
1691
+ frameTimeMs: model.frameTimeMs,
1692
+ viewTimeMs: model.viewTimeMs,
1693
+ diffTimeMs: model.diffTimeMs,
1694
+ refreshRate: themedFrameCtx?.runtime.refreshRate,
1695
+ }, {
1696
+ i18n: options.i18n,
1697
+ ctx: themedFrameCtx,
1698
+ }));
1647
1699
  }
1648
1700
  if (bodySurface != null && bodyRect.width > 0 && bodyRect.height > 0) {
1649
1701
  frameSurface.blit(bodySurface, bodyRect.col, bodyRect.row);
@@ -1656,9 +1708,31 @@ export function createFramedApp(options) {
1656
1708
  return wrapFrameMsg({ type: 'runtime-issue', issue });
1657
1709
  },
1658
1710
  };
1659
- const framedApp = app;
1711
+ function isInternalFrameModel(model) {
1712
+ return 'helpScrollY' in model && 'warnedFrameKeyCollisionPages' in model;
1713
+ }
1714
+ function requireInternalFrameModel(model) {
1715
+ if (isInternalFrameModel(model))
1716
+ return model;
1717
+ throw new Error('createFramedApp: framed runtime received a non-frame model');
1718
+ }
1660
1719
  let hostedRunActive = false;
1661
- framedApp.run = async (runOptions) => {
1720
+ const framedApp = {
1721
+ init() {
1722
+ return app.init();
1723
+ },
1724
+ update(msg, model) {
1725
+ return app.update(msg, requireInternalFrameModel(model));
1726
+ },
1727
+ view(model) {
1728
+ return app.view(requireInternalFrameModel(model));
1729
+ },
1730
+ routeRuntimeIssue(issue) {
1731
+ return app.routeRuntimeIssue?.(issue);
1732
+ },
1733
+ run: runHostedFramedApp,
1734
+ };
1735
+ async function runHostedFramedApp(runOptions) {
1662
1736
  if (hostedRunActive) {
1663
1737
  throw new Error('createFramedApp: concurrent app.run() calls on the same framed app are not supported');
1664
1738
  }
@@ -1687,7 +1761,7 @@ export function createFramedApp(options) {
1687
1761
  beforeRender(model) {
1688
1762
  if (pendingTimingSnapshot == null)
1689
1763
  return model;
1690
- return applyFrameTimingSnapshot(model, pendingTimingSnapshot);
1764
+ return applyFrameTimingSnapshot(requireInternalFrameModel(model), pendingTimingSnapshot);
1691
1765
  },
1692
1766
  afterRender({ timings }) {
1693
1767
  pendingTimingSnapshot = summarizeFrameTimings(timings, frameBudgetMs);
@@ -1706,7 +1780,7 @@ export function createFramedApp(options) {
1706
1780
  defaultFrameCtx = previousDefaultFrameCtx;
1707
1781
  resolvedShellThemes = previousResolvedShellThemes;
1708
1782
  }
1709
- };
1783
+ }
1710
1784
  return framedApp;
1711
1785
  }
1712
1786
  /**