@rezi-ui/core 0.1.0-alpha.22 → 0.1.0-alpha.24

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 (182) hide show
  1. package/dist/app/createApp.d.ts +3 -0
  2. package/dist/app/createApp.d.ts.map +1 -1
  3. package/dist/app/createApp.js +346 -3
  4. package/dist/app/createApp.js.map +1 -1
  5. package/dist/app/inspectorOverlayHelper.d.ts.map +1 -1
  6. package/dist/app/inspectorOverlayHelper.js +15 -0
  7. package/dist/app/inspectorOverlayHelper.js.map +1 -1
  8. package/dist/app/runtimeBreadcrumbs.d.ts +1 -0
  9. package/dist/app/runtimeBreadcrumbs.d.ts.map +1 -1
  10. package/dist/app/runtimeBreadcrumbs.js +1 -0
  11. package/dist/app/runtimeBreadcrumbs.js.map +1 -1
  12. package/dist/app/widgetRenderer.d.ts +40 -2
  13. package/dist/app/widgetRenderer.d.ts.map +1 -1
  14. package/dist/app/widgetRenderer.js +492 -33
  15. package/dist/app/widgetRenderer.js.map +1 -1
  16. package/dist/backend.d.ts +10 -0
  17. package/dist/backend.d.ts.map +1 -1
  18. package/dist/backend.js +8 -0
  19. package/dist/backend.js.map +1 -1
  20. package/dist/forms/index.d.ts +1 -1
  21. package/dist/forms/index.d.ts.map +1 -1
  22. package/dist/forms/index.js.map +1 -1
  23. package/dist/forms/types.d.ts +39 -0
  24. package/dist/forms/types.d.ts.map +1 -1
  25. package/dist/forms/useForm.d.ts.map +1 -1
  26. package/dist/forms/useForm.js +53 -0
  27. package/dist/forms/useForm.js.map +1 -1
  28. package/dist/index.d.ts +51 -11
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +10 -4
  31. package/dist/index.js.map +1 -1
  32. package/dist/keybindings/index.d.ts +2 -2
  33. package/dist/keybindings/index.d.ts.map +1 -1
  34. package/dist/keybindings/index.js +1 -1
  35. package/dist/keybindings/index.js.map +1 -1
  36. package/dist/keybindings/manager.d.ts +21 -0
  37. package/dist/keybindings/manager.d.ts.map +1 -1
  38. package/dist/keybindings/manager.js +62 -4
  39. package/dist/keybindings/manager.js.map +1 -1
  40. package/dist/keybindings/types.d.ts +2 -0
  41. package/dist/keybindings/types.d.ts.map +1 -1
  42. package/dist/layout/constraints.d.ts +2 -2
  43. package/dist/layout/constraints.d.ts.map +1 -1
  44. package/dist/layout/constraints.js +4 -0
  45. package/dist/layout/constraints.js.map +1 -1
  46. package/dist/layout/engine/layoutEngine.d.ts.map +1 -1
  47. package/dist/layout/engine/layoutEngine.js +2 -0
  48. package/dist/layout/engine/layoutEngine.js.map +1 -1
  49. package/dist/layout/kinds/box.js +2 -2
  50. package/dist/layout/kinds/box.js.map +1 -1
  51. package/dist/layout/kinds/leaf.d.ts.map +1 -1
  52. package/dist/layout/kinds/leaf.js +7 -0
  53. package/dist/layout/kinds/leaf.js.map +1 -1
  54. package/dist/layout/kinds/navigation.js +2 -2
  55. package/dist/layout/kinds/navigation.js.map +1 -1
  56. package/dist/layout/kinds/overlays.js +2 -2
  57. package/dist/layout/kinds/overlays.js.map +1 -1
  58. package/dist/layout/responsive.d.ts +21 -0
  59. package/dist/layout/responsive.d.ts.map +1 -0
  60. package/dist/layout/responsive.js +86 -0
  61. package/dist/layout/responsive.js.map +1 -0
  62. package/dist/layout/spacing-scale.d.ts +4 -1
  63. package/dist/layout/spacing-scale.d.ts.map +1 -1
  64. package/dist/layout/spacing-scale.js +15 -8
  65. package/dist/layout/spacing-scale.js.map +1 -1
  66. package/dist/layout/types.d.ts +5 -2
  67. package/dist/layout/types.d.ts.map +1 -1
  68. package/dist/layout/types.js +0 -8
  69. package/dist/layout/types.js.map +1 -1
  70. package/dist/layout/validateProps.d.ts +3 -0
  71. package/dist/layout/validateProps.d.ts.map +1 -1
  72. package/dist/layout/validateProps.js +70 -30
  73. package/dist/layout/validateProps.js.map +1 -1
  74. package/dist/renderer/renderToDrawlist/renderTree.d.ts +1 -1
  75. package/dist/renderer/renderToDrawlist/renderTree.d.ts.map +1 -1
  76. package/dist/renderer/renderToDrawlist/renderTree.js +14 -2
  77. package/dist/renderer/renderToDrawlist/renderTree.js.map +1 -1
  78. package/dist/renderer/renderToDrawlist/spacing.d.ts.map +1 -1
  79. package/dist/renderer/renderToDrawlist/spacing.js +12 -8
  80. package/dist/renderer/renderToDrawlist/spacing.js.map +1 -1
  81. package/dist/renderer/renderToDrawlist/types.d.ts +2 -0
  82. package/dist/renderer/renderToDrawlist/types.d.ts.map +1 -1
  83. package/dist/renderer/renderToDrawlist/widgets/basic.d.ts +1 -1
  84. package/dist/renderer/renderToDrawlist/widgets/basic.d.ts.map +1 -1
  85. package/dist/renderer/renderToDrawlist/widgets/basic.js +218 -43
  86. package/dist/renderer/renderToDrawlist/widgets/basic.js.map +1 -1
  87. package/dist/renderer/renderToDrawlist/widgets/containers.d.ts.map +1 -1
  88. package/dist/renderer/renderToDrawlist/widgets/containers.js +6 -2
  89. package/dist/renderer/renderToDrawlist/widgets/containers.js.map +1 -1
  90. package/dist/renderer/renderToDrawlist.d.ts.map +1 -1
  91. package/dist/renderer/renderToDrawlist.js +1 -1
  92. package/dist/renderer/renderToDrawlist.js.map +1 -1
  93. package/dist/router/helpers.d.ts.map +1 -1
  94. package/dist/router/helpers.js +32 -4
  95. package/dist/router/helpers.js.map +1 -1
  96. package/dist/router/index.d.ts +1 -1
  97. package/dist/router/index.d.ts.map +1 -1
  98. package/dist/router/integration.d.ts +1 -0
  99. package/dist/router/integration.d.ts.map +1 -1
  100. package/dist/router/integration.js +109 -19
  101. package/dist/router/integration.js.map +1 -1
  102. package/dist/router/keybindings.d.ts.map +1 -1
  103. package/dist/router/keybindings.js +19 -12
  104. package/dist/router/keybindings.js.map +1 -1
  105. package/dist/router/router.d.ts +13 -0
  106. package/dist/router/router.d.ts.map +1 -1
  107. package/dist/router/router.js +37 -11
  108. package/dist/router/router.js.map +1 -1
  109. package/dist/router/types.d.ts +35 -0
  110. package/dist/router/types.d.ts.map +1 -1
  111. package/dist/runtime/commit.d.ts +21 -0
  112. package/dist/runtime/commit.d.ts.map +1 -1
  113. package/dist/runtime/commit.js +508 -326
  114. package/dist/runtime/commit.js.map +1 -1
  115. package/dist/runtime/focus.d.ts +2 -2
  116. package/dist/runtime/focus.js +2 -2
  117. package/dist/runtime/inputEditor.d.ts +22 -0
  118. package/dist/runtime/inputEditor.d.ts.map +1 -1
  119. package/dist/runtime/inputEditor.js +204 -10
  120. package/dist/runtime/inputEditor.js.map +1 -1
  121. package/dist/runtime/instances.d.ts +22 -0
  122. package/dist/runtime/instances.d.ts.map +1 -1
  123. package/dist/runtime/instances.js +64 -0
  124. package/dist/runtime/instances.js.map +1 -1
  125. package/dist/runtime/widgetMeta.d.ts +16 -0
  126. package/dist/runtime/widgetMeta.d.ts.map +1 -1
  127. package/dist/runtime/widgetMeta.js +231 -7
  128. package/dist/runtime/widgetMeta.js.map +1 -1
  129. package/dist/testing/events.d.ts +168 -0
  130. package/dist/testing/events.d.ts.map +1 -0
  131. package/dist/testing/events.js +445 -0
  132. package/dist/testing/events.js.map +1 -0
  133. package/dist/testing/index.d.ts +5 -0
  134. package/dist/testing/index.d.ts.map +1 -0
  135. package/dist/testing/index.js +3 -0
  136. package/dist/testing/index.js.map +1 -0
  137. package/dist/testing/renderer.d.ts +59 -0
  138. package/dist/testing/renderer.d.ts.map +1 -0
  139. package/dist/testing/renderer.js +271 -0
  140. package/dist/testing/renderer.js.map +1 -0
  141. package/dist/ui.d.ts +1 -1
  142. package/dist/ui.d.ts.map +1 -1
  143. package/dist/widgets/canvas.d.ts.map +1 -1
  144. package/dist/widgets/canvas.js +136 -0
  145. package/dist/widgets/canvas.js.map +1 -1
  146. package/dist/widgets/collections.js +1 -1
  147. package/dist/widgets/collections.js.map +1 -1
  148. package/dist/widgets/composition.d.ts +58 -1
  149. package/dist/widgets/composition.d.ts.map +1 -1
  150. package/dist/widgets/composition.js +82 -1
  151. package/dist/widgets/composition.js.map +1 -1
  152. package/dist/widgets/dialogs/confirm.d.ts.map +1 -1
  153. package/dist/widgets/dialogs/confirm.js +8 -7
  154. package/dist/widgets/dialogs/confirm.js.map +1 -1
  155. package/dist/widgets/dialogs/dialog.d.ts +7 -0
  156. package/dist/widgets/dialogs/dialog.d.ts.map +1 -0
  157. package/dist/widgets/dialogs/dialog.js +8 -0
  158. package/dist/widgets/dialogs/dialog.js.map +1 -0
  159. package/dist/widgets/dialogs/index.d.ts +2 -1
  160. package/dist/widgets/dialogs/index.d.ts.map +1 -1
  161. package/dist/widgets/dialogs/index.js +1 -0
  162. package/dist/widgets/dialogs/index.js.map +1 -1
  163. package/dist/widgets/dialogs/types.d.ts +1 -0
  164. package/dist/widgets/dialogs/types.d.ts.map +1 -1
  165. package/dist/widgets/inspectorOverlay.d.ts.map +1 -1
  166. package/dist/widgets/inspectorOverlay.js +2 -0
  167. package/dist/widgets/inspectorOverlay.js.map +1 -1
  168. package/dist/widgets/types.d.ts +176 -0
  169. package/dist/widgets/types.d.ts.map +1 -1
  170. package/dist/widgets/ui.d.ts +104 -5
  171. package/dist/widgets/ui.d.ts.map +1 -1
  172. package/dist/widgets/ui.js +227 -9
  173. package/dist/widgets/ui.js.map +1 -1
  174. package/dist/widgets/useModalStack.d.ts +21 -0
  175. package/dist/widgets/useModalStack.d.ts.map +1 -0
  176. package/dist/widgets/useModalStack.js +107 -0
  177. package/dist/widgets/useModalStack.js.map +1 -0
  178. package/dist/widgets/useTable.d.ts +30 -0
  179. package/dist/widgets/useTable.d.ts.map +1 -0
  180. package/dist/widgets/useTable.js +184 -0
  181. package/dist/widgets/useTable.js.map +1 -0
  182. package/package.json +6 -2
@@ -23,6 +23,7 @@
23
23
  */
24
24
  import { type RuntimeBackend } from "../backend.js";
25
25
  import type { App, AppConfig, AppLayoutSnapshot, AppRenderMetrics } from "../index.js";
26
+ import { type ResponsiveBreakpointThresholds } from "../layout/responsive.js";
26
27
  import type { RouteDefinition } from "../router/types.js";
27
28
  import type { Theme } from "../theme/theme.js";
28
29
  import type { ThemeDefinition } from "../theme/tokens.js";
@@ -31,6 +32,8 @@ type ResolvedAppConfig = Readonly<{
31
32
  fpsCap: number;
32
33
  maxEventBytes: number;
33
34
  maxDrawlistBytes: number;
35
+ rootPadding: number;
36
+ breakpointThresholds: ResponsiveBreakpointThresholds;
34
37
  useV2Cursor: boolean;
35
38
  drawlistValidateParams: boolean;
36
39
  drawlistReuseOutputBuffer: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"createApp.d.ts","sourceRoot":"","sources":["../../src/app/createApp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EACV,GAAG,EACH,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAIjB,MAAM,aAAa,CAAC;AAoBrB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAQ1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAoB1D,oDAAoD;AACpD,KAAK,iBAAiB,GAAG,QAAQ,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,sBAAsB,EAAE,OAAO,CAAC;IAChC,yBAAyB,EAAE,OAAO,CAAC;IACnC,6BAA6B,EAAE,MAAM,CAAC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACtE,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACzE,CAAC,CAAC;AAwBH,eAAO,MAAM,uCAAuC,4BAA4B,CAAC;AACjF,eAAO,MAAM,gDAAgD,oCAAoC,CAAC;AAyGlG,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,iBAAiB,CAmDjF;AAoDD,KAAK,oBAAoB,GAAG,QAAQ,CAAC;IACnC,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,eAAe,CAAC;CACjC,CAAC,CAAC;AAEH,KAAK,qBAAqB,CAAC,CAAC,IAAI,oBAAoB,GAClD,QAAQ,CAAC;IACP,YAAY,EAAE,CAAC,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEL,KAAK,0BAA0B,GAAG,oBAAoB,GACpD,QAAQ,CAAC;IACP,MAAM,EAAE,SAAS,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACrC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEL;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,0BAA0B,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AACxF,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"createApp.d.ts","sourceRoot":"","sources":["../../src/app/createApp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EACV,GAAG,EACH,SAAS,EACT,iBAAiB,EACjB,gBAAgB,EAKjB,MAAM,aAAa,CAAC;AAmBrB,OAAO,EACL,KAAK,8BAA8B,EAEpC,MAAM,yBAAyB,CAAC;AAMjC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAQ1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAsB1D,oDAAoD;AACpD,KAAK,iBAAiB,GAAG,QAAQ,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,8BAA8B,CAAC;IACrD,WAAW,EAAE,OAAO,CAAC;IACrB,sBAAsB,EAAE,OAAO,CAAC;IAChC,yBAAyB,EAAE,OAAO,CAAC;IACnC,6BAA6B,EAAE,MAAM,CAAC;IACtC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACtE,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACzE,CAAC,CAAC;AA0BH,eAAO,MAAM,uCAAuC,4BAA4B,CAAC;AACjF,eAAO,MAAM,gDAAgD,oCAAoC,CAAC;AAgIlG,qEAAqE;AACrE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,iBAAiB,CA0DjF;AAuKD,KAAK,oBAAoB,GAAG,QAAQ,CAAC;IACnC,OAAO,EAAE,cAAc,CAAC;IACxB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,eAAe,CAAC;CACjC,CAAC,CAAC;AAEH,KAAK,qBAAqB,CAAC,CAAC,IAAI,oBAAoB,GAClD,QAAQ,CAAC;IACP,YAAY,EAAE,CAAC,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEL,KAAK,0BAA0B,GAAG,oBAAoB,GACpD,QAAQ,CAAC;IACP,MAAM,EAAE,SAAS,eAAe,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACrC,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC,CAAC;AAEL;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,0BAA0B,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AACxF,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC"}
@@ -23,13 +23,16 @@
23
23
  */
24
24
  import { ZrUiError } from "../abi.js";
25
25
  import { BACKEND_DRAWLIST_V2_MARKER, BACKEND_DRAWLIST_VERSION_MARKER, BACKEND_FPS_CAP_MARKER, BACKEND_MAX_EVENT_BYTES_MARKER, FRAME_ACCEPTED_ACK_MARKER, } from "../backend.js";
26
- import { createManagerState, getMode, registerBindings, registerModes, resetChordState, routeKeyEvent, setMode, } from "../keybindings/index.js";
26
+ import { createManagerState, getBindings, getMode, getPendingChord, registerBindings, registerModes, resetChordState, routeKeyEvent, setMode, } from "../keybindings/index.js";
27
+ import { ZR_MOD_ALT, ZR_MOD_CTRL, ZR_MOD_META } from "../keybindings/keyCodes.js";
28
+ import { normalizeBreakpointThresholds, } from "../layout/responsive.js";
27
29
  import { PERF_ENABLED, perfMarkEnd, perfMarkStart, perfNow, perfRecord } from "../perf/perf.js";
28
30
  import { parseEventBatchV1 } from "../protocol/zrev_v1.js";
29
31
  import { createRouterIntegration } from "../router/integration.js";
30
32
  import { DEFAULT_TERMINAL_PROFILE, terminalProfileFromCaps, } from "../terminalProfile.js";
31
33
  import { defaultTheme } from "../theme/defaultTheme.js";
32
34
  import { coerceToLegacyTheme } from "../theme/interop.js";
35
+ import { ui } from "../widgets/ui.js";
33
36
  import { RawRenderer } from "./rawRenderer.js";
34
37
  import { isRuntimeBreadcrumbEventKind, mergeRuntimeBreadcrumbSnapshot, toRuntimeBreadcrumbAction, } from "./runtimeBreadcrumbs.js";
35
38
  import { AppStateMachine } from "./stateMachine.js";
@@ -41,6 +44,8 @@ const DEFAULT_CONFIG = Object.freeze({
41
44
  fpsCap: 60,
42
45
  maxEventBytes: 1 << 20 /* 1 MiB */,
43
46
  maxDrawlistBytes: 2 << 20 /* 2 MiB */,
47
+ rootPadding: 0,
48
+ breakpointThresholds: normalizeBreakpointThresholds(undefined),
44
49
  useV2Cursor: false,
45
50
  drawlistValidateParams: true,
46
51
  drawlistReuseOutputBuffer: true,
@@ -134,6 +139,27 @@ async function loadTerminalProfile(backend) {
134
139
  return DEFAULT_TERMINAL_PROFILE;
135
140
  }
136
141
  }
142
+ function buildLayoutDebugOverlay(rectById) {
143
+ if (rectById.size === 0)
144
+ return null;
145
+ const rows = [...rectById.entries()]
146
+ .sort((a, b) => a[0].localeCompare(b[0]))
147
+ .slice(0, 18)
148
+ .map(([id, rect]) => ui.text(`${id} ${String(rect.x)},${String(rect.y)} ${String(rect.w)}x${String(rect.h)}`));
149
+ const panel = ui.box({ border: "single", title: `Layout (${String(rectById.size)})`, p: 1 }, [
150
+ ui.column({ gap: 0 }, rows),
151
+ ]);
152
+ return ui.layer({
153
+ id: "rezi.layout.debug.overlay",
154
+ zIndex: 2_000_000_000,
155
+ modal: false,
156
+ backdrop: "none",
157
+ closeOnEscape: false,
158
+ content: ui.column({ width: "100%", height: "100%", justify: "end", p: 1 }, [
159
+ ui.row({ width: "100%", justify: "start" }, [panel]),
160
+ ]),
161
+ });
162
+ }
137
163
  /** Apply defaults to user-provided config, validating all values. */
138
164
  export function resolveAppConfig(config) {
139
165
  if (!config)
@@ -147,6 +173,10 @@ export function resolveAppConfig(config) {
147
173
  const maxDrawlistBytes = config.maxDrawlistBytes === undefined
148
174
  ? DEFAULT_CONFIG.maxDrawlistBytes
149
175
  : requirePositiveInt("maxDrawlistBytes", config.maxDrawlistBytes);
176
+ const rootPadding = config.rootPadding === undefined
177
+ ? DEFAULT_CONFIG.rootPadding
178
+ : requireNonNegativeInt("rootPadding", config.rootPadding);
179
+ const breakpointThresholds = normalizeBreakpointThresholds(config.breakpoints);
150
180
  const useV2Cursor = config.useV2Cursor === true;
151
181
  const drawlistValidateParams = config.drawlistValidateParams === undefined
152
182
  ? DEFAULT_CONFIG.drawlistValidateParams
@@ -166,6 +196,8 @@ export function resolveAppConfig(config) {
166
196
  fpsCap,
167
197
  maxEventBytes,
168
198
  maxDrawlistBytes,
199
+ rootPadding,
200
+ breakpointThresholds,
169
201
  useV2Cursor,
170
202
  drawlistValidateParams,
171
203
  drawlistReuseOutputBuffer,
@@ -180,6 +212,83 @@ function describeThrown(v) {
180
212
  return `${v.name}: ${v.message}`;
181
213
  return String(v);
182
214
  }
215
+ const KEY_Q = 81;
216
+ const KEY_R = 82;
217
+ function captureTopLevelViewError(value) {
218
+ if (value instanceof Error) {
219
+ return Object.freeze({
220
+ code: "ZRUI_USER_CODE_THROW",
221
+ detail: `${value.name}: ${value.message}`,
222
+ message: value.message,
223
+ ...(typeof value.stack === "string" && value.stack.length > 0 ? { stack: value.stack } : {}),
224
+ });
225
+ }
226
+ const detail = String(value);
227
+ return Object.freeze({
228
+ code: "ZRUI_USER_CODE_THROW",
229
+ detail,
230
+ message: detail,
231
+ });
232
+ }
233
+ function buildTopLevelViewErrorScreen(error) {
234
+ const lines = [`Code: ${error.code}`, `Message: ${error.message}`];
235
+ if (error.stack === undefined || error.stack.length === 0) {
236
+ lines.push(`Detail: ${error.detail}`);
237
+ }
238
+ return ui.column({ width: "100%", height: "100%", justify: "center", align: "center", p: 1 }, [
239
+ ui.box({
240
+ width: "100%",
241
+ height: "100%",
242
+ border: "single",
243
+ title: "Runtime Error",
244
+ p: 1,
245
+ }, [
246
+ ui.errorDisplay(lines.join("\n"), {
247
+ title: "Top-level view() threw",
248
+ ...(error.stack === undefined || error.stack.length === 0
249
+ ? {}
250
+ : { stack: error.stack, showStack: true }),
251
+ }),
252
+ ui.callout("Press R to retry, Q to quit", { variant: "warning" }),
253
+ ]),
254
+ ]);
255
+ }
256
+ function isUnmodifiedLetterKey(mods) {
257
+ return (mods & (ZR_MOD_CTRL | ZR_MOD_ALT | ZR_MOD_META)) === 0;
258
+ }
259
+ function isTopLevelRetryEvent(ev) {
260
+ if (ev.kind === "key") {
261
+ return ev.action === "down" && isUnmodifiedLetterKey(ev.mods) && ev.key === KEY_R;
262
+ }
263
+ if (ev.kind === "text") {
264
+ return ev.codepoint === KEY_R || ev.codepoint === 114;
265
+ }
266
+ return false;
267
+ }
268
+ function isTopLevelQuitEvent(ev) {
269
+ if (ev.kind === "key") {
270
+ return ev.action === "down" && isUnmodifiedLetterKey(ev.mods) && ev.key === KEY_Q;
271
+ }
272
+ if (ev.kind === "text") {
273
+ return ev.codepoint === KEY_Q || ev.codepoint === 113;
274
+ }
275
+ return false;
276
+ }
277
+ function readProcessLike() {
278
+ const processRef = globalThis.process;
279
+ if (!processRef || typeof processRef !== "object")
280
+ return null;
281
+ return processRef;
282
+ }
283
+ function removeSignalHandler(proc, signal, handler) {
284
+ if (typeof proc.off === "function") {
285
+ proc.off(signal, handler);
286
+ return;
287
+ }
288
+ if (typeof proc.removeListener === "function") {
289
+ proc.removeListener(signal, handler);
290
+ }
291
+ }
183
292
  /**
184
293
  * Convert a text codepoint to a key code for keybinding matching.
185
294
  * Letters are normalized to uppercase (A-Z = 65-90).
@@ -240,10 +349,14 @@ export function createApp(opts) {
240
349
  let mode = null;
241
350
  let drawFn = null;
242
351
  let viewFn = null;
352
+ let topLevelViewError = null;
353
+ let debugLayoutEnabled = false;
243
354
  const hasInitialState = "initialState" in opts;
244
355
  let committedState = hasInitialState ? opts.initialState : Object.freeze({});
245
356
  const updates = new UpdateQueue();
246
357
  const handlers = [];
358
+ const focusHandlers = [];
359
+ let lastEmittedFocusId = null;
247
360
  const DIRTY_RENDER = 1 << 0;
248
361
  const DIRTY_LAYOUT = 1 << 1;
249
362
  const DIRTY_VIEW = 1 << 2;
@@ -264,6 +377,7 @@ export function createApp(opts) {
264
377
  let inEventHandlerDepth = 0;
265
378
  let lifecycleBusy = null;
266
379
  let pollToken = 0;
380
+ let settleActiveRun = null;
267
381
  let userCommitScheduled = false;
268
382
  // Perf tracking: submit time for backend_ack calculation
269
383
  let submitFrameStartMs = null;
@@ -337,6 +451,8 @@ export function createApp(opts) {
337
451
  backend,
338
452
  drawlistVersion,
339
453
  maxDrawlistBytes: config.maxDrawlistBytes,
454
+ rootPadding: config.rootPadding,
455
+ breakpointThresholds: config.breakpointThresholds,
340
456
  terminalProfile,
341
457
  useV2Cursor: config.useV2Cursor,
342
458
  ...(opts.config?.drawlistValidateParams === undefined
@@ -348,6 +464,7 @@ export function createApp(opts) {
348
464
  requestView: requestViewFromRenderer,
349
465
  collectRuntimeBreadcrumbs: runtimeBreadcrumbsEnabled,
350
466
  });
467
+ lastEmittedFocusId = widgetRenderer.getFocusedId();
351
468
  let routeStateUpdater = null;
352
469
  let routerIntegration = null;
353
470
  if (routes !== undefined) {
@@ -390,9 +507,13 @@ export function createApp(opts) {
390
507
  return false;
391
508
  }
392
509
  function applyRoutedKeybindingState(routeInputState, routeNextState) {
510
+ const previousChordState = keybindingState.chordState;
393
511
  // If handlers did not mutate keybinding state, take the routed state directly.
394
512
  if (keybindingState === routeInputState) {
395
513
  keybindingState = routeNextState;
514
+ if (keybindingState.chordState !== previousChordState) {
515
+ markDirty(DIRTY_VIEW);
516
+ }
396
517
  return;
397
518
  }
398
519
  // Preserve handler-triggered mode changes (for example app.setMode() in a binding).
@@ -405,6 +526,9 @@ export function createApp(opts) {
405
526
  ...keybindingState,
406
527
  chordState: routeNextState.chordState,
407
528
  });
529
+ if (keybindingState.chordState !== previousChordState) {
530
+ markDirty(DIRTY_VIEW);
531
+ }
408
532
  }
409
533
  function noteBreadcrumbEvent(kind) {
410
534
  breadcrumbEventTracked = false;
@@ -430,6 +554,33 @@ export function createApp(opts) {
430
554
  return;
431
555
  breadcrumbLastAction = toRuntimeBreadcrumbAction(action);
432
556
  }
557
+ function retryTopLevelViewError() {
558
+ topLevelViewError = null;
559
+ markDirty(DIRTY_VIEW | DIRTY_LAYOUT);
560
+ }
561
+ function quitFromTopLevelViewError() {
562
+ let stopPromise;
563
+ try {
564
+ stopPromise = app.stop();
565
+ }
566
+ catch {
567
+ try {
568
+ app.dispose();
569
+ }
570
+ catch {
571
+ // ignore
572
+ }
573
+ return;
574
+ }
575
+ void stopPromise.finally(() => {
576
+ try {
577
+ app.dispose();
578
+ }
579
+ catch {
580
+ // ignore
581
+ }
582
+ });
583
+ }
433
584
  function buildRuntimeBreadcrumbSnapshot(renderTimeMs) {
434
585
  if (!runtimeBreadcrumbsEnabled)
435
586
  return null;
@@ -476,6 +627,7 @@ export function createApp(opts) {
476
627
  ...(opts.routeHistoryMaxDepth === undefined
477
628
  ? {}
478
629
  : { maxHistoryDepth: opts.routeHistoryMaxDepth }),
630
+ getState: () => committedState,
479
631
  // Route transitions can swap the entire screen tree; force both commit
480
632
  // and layout so id->rect indexes and focus metadata stay in sync.
481
633
  requestRouteRender: () => markDirty(DIRTY_VIEW | DIRTY_LAYOUT),
@@ -507,6 +659,28 @@ export function createApp(opts) {
507
659
  inEventHandlerDepth--;
508
660
  }
509
661
  }
662
+ function emitFocusChangeIfNeeded() {
663
+ const focusedId = widgetRenderer.getFocusedId();
664
+ if (focusedId === lastEmittedFocusId)
665
+ return true;
666
+ lastEmittedFocusId = focusedId;
667
+ const info = widgetRenderer.getCurrentFocusInfo();
668
+ const snapshot = [];
669
+ for (const slot of focusHandlers) {
670
+ if (slot.active.value)
671
+ snapshot.push(slot.fn);
672
+ }
673
+ for (const fn of snapshot) {
674
+ try {
675
+ fn(info);
676
+ }
677
+ catch (e) {
678
+ enqueueFatal("ZRUI_USER_CODE_THROW", `onFocusChange handler threw: ${describeThrown(e)}`);
679
+ return false;
680
+ }
681
+ }
682
+ return true;
683
+ }
510
684
  function doFatal(code, detail) {
511
685
  if (sm.state !== "Running")
512
686
  return;
@@ -546,6 +720,7 @@ export function createApp(opts) {
546
720
  catch {
547
721
  // ignore
548
722
  }
723
+ settleActiveRun?.();
549
724
  });
550
725
  }
551
726
  catch {
@@ -555,6 +730,7 @@ export function createApp(opts) {
555
730
  catch {
556
731
  // ignore
557
732
  }
733
+ settleActiveRun?.();
558
734
  }
559
735
  }
560
736
  function releaseOnce(batch) {
@@ -609,7 +785,13 @@ export function createApp(opts) {
609
785
  const prev = viewport;
610
786
  if (prev === null || prev.cols !== ev.cols || prev.rows !== ev.rows) {
611
787
  viewport = Object.freeze({ cols: ev.cols, rows: ev.rows });
612
- markDirty(DIRTY_LAYOUT);
788
+ if (widgetRenderer.hasViewportAwareComposites()) {
789
+ widgetRenderer.invalidateCompositeWidgets();
790
+ markDirty(DIRTY_LAYOUT | DIRTY_VIEW);
791
+ }
792
+ else {
793
+ markDirty(DIRTY_LAYOUT);
794
+ }
613
795
  }
614
796
  }
615
797
  if (ev.kind === "tick" && mode === "widget") {
@@ -635,6 +817,25 @@ export function createApp(opts) {
635
817
  }
636
818
  }
637
819
  }
820
+ if (mode === "widget" && topLevelViewError !== null) {
821
+ if (isTopLevelRetryEvent(ev)) {
822
+ noteBreadcrumbConsumptionPath("widgetRouting");
823
+ retryTopLevelViewError();
824
+ continue;
825
+ }
826
+ if (isTopLevelQuitEvent(ev)) {
827
+ noteBreadcrumbConsumptionPath("widgetRouting");
828
+ quitFromTopLevelViewError();
829
+ continue;
830
+ }
831
+ if (ev.kind === "key" ||
832
+ ev.kind === "text" ||
833
+ ev.kind === "paste" ||
834
+ ev.kind === "mouse") {
835
+ noteBreadcrumbConsumptionPath("widgetRouting");
836
+ continue;
837
+ }
838
+ }
638
839
  const isWidgetRoutableEvent = ev.kind === "key" || ev.kind === "text" || ev.kind === "paste" || ev.kind === "mouse";
639
840
  if (mode === "widget" && isWidgetRoutableEvent) {
640
841
  if (keybindingsEnabled) {
@@ -708,6 +909,8 @@ export function createApp(opts) {
708
909
  enqueueFatal("ZRUI_USER_CODE_THROW", `widget routing threw: ${describeThrown(e)}`);
709
910
  return;
710
911
  }
912
+ if (!emitFocusChangeIfNeeded())
913
+ return;
711
914
  if (routed.needsRender)
712
915
  markDirty(DIRTY_RENDER);
713
916
  if (routed.action) {
@@ -914,14 +1117,37 @@ export function createApp(opts) {
914
1117
  // newly-committed tree against stale layout nodes until the next resize.
915
1118
  checkLayoutStability: (pendingDirtyFlags & DIRTY_LAYOUT) === 0 && (pendingDirtyFlags & DIRTY_VIEW) !== 0,
916
1119
  };
1120
+ const resilientView = (state) => {
1121
+ if (topLevelViewError !== null) {
1122
+ return buildTopLevelViewErrorScreen(topLevelViewError);
1123
+ }
1124
+ try {
1125
+ return vf(state);
1126
+ }
1127
+ catch (e) {
1128
+ topLevelViewError = captureTopLevelViewError(e);
1129
+ return buildTopLevelViewErrorScreen(topLevelViewError);
1130
+ }
1131
+ };
917
1132
  const renderStart = perfNow();
918
1133
  const submitToken = perfMarkStart("submit_frame");
919
- const res = widgetRenderer.submitFrame(vf, snapshot, viewport, theme, hooks, plan);
1134
+ const frameView = debugLayoutEnabled
1135
+ ? (state) => {
1136
+ const root = resilientView(state);
1137
+ const overlay = buildLayoutDebugOverlay(widgetRenderer.getRectByIdIndex());
1138
+ if (!overlay)
1139
+ return root;
1140
+ return ui.layers([root, overlay]);
1141
+ }
1142
+ : resilientView;
1143
+ const res = widgetRenderer.submitFrame(frameView, snapshot, viewport, theme, hooks, plan);
920
1144
  perfMarkEnd("submit_frame", submitToken);
921
1145
  if (!res.ok) {
922
1146
  fatalNowOrEnqueue(res.code, res.detail);
923
1147
  return;
924
1148
  }
1149
+ if (!emitFocusChangeIfNeeded())
1150
+ return;
925
1151
  const renderTime = perfNow() - renderStart;
926
1152
  const runtimeBreadcrumbs = buildRuntimeBreadcrumbSnapshot(Math.max(0, renderTime));
927
1153
  if (!emitInternalRenderMetrics(renderTime, runtimeBreadcrumbs))
@@ -1079,6 +1305,17 @@ export function createApp(opts) {
1079
1305
  active.value = false;
1080
1306
  };
1081
1307
  },
1308
+ onFocusChange(handler) {
1309
+ assertOperational("onFocusChange");
1310
+ if (inCommit || inRender) {
1311
+ throwCode("ZRUI_REENTRANT_CALL", "onFocusChange: re-entrant call");
1312
+ }
1313
+ const active = { value: true };
1314
+ focusHandlers.push({ fn: handler, active });
1315
+ return () => {
1316
+ active.value = false;
1317
+ };
1318
+ },
1082
1319
  update(updater) {
1083
1320
  assertOperational("update");
1084
1321
  if (inCommit)
@@ -1103,6 +1340,18 @@ export function createApp(opts) {
1103
1340
  theme = nextTheme;
1104
1341
  requestRenderFromRenderer();
1105
1342
  },
1343
+ debugLayout(enabled) {
1344
+ assertOperational("debugLayout");
1345
+ if (mode === "raw") {
1346
+ throwCode("ZRUI_MODE_CONFLICT", "debugLayout: not available in draw mode");
1347
+ }
1348
+ const next = enabled === undefined ? !debugLayoutEnabled : enabled === true;
1349
+ if (next === debugLayoutEnabled)
1350
+ return debugLayoutEnabled;
1351
+ debugLayoutEnabled = next;
1352
+ requestViewFromRenderer();
1353
+ return debugLayoutEnabled;
1354
+ },
1106
1355
  start() {
1107
1356
  assertOperational("start");
1108
1357
  assertNotReentrant("start");
@@ -1122,6 +1371,7 @@ export function createApp(opts) {
1122
1371
  }
1123
1372
  return p.then(async () => {
1124
1373
  lifecycleBusy = null;
1374
+ topLevelViewError = null;
1125
1375
  terminalProfile = await loadTerminalProfile(backend);
1126
1376
  widgetRenderer.setTerminalProfile(terminalProfile);
1127
1377
  sm.toRunning();
@@ -1134,6 +1384,91 @@ export function createApp(opts) {
1134
1384
  throw new ZrUiError("ZRUI_BACKEND_ERROR", `backend.start rejected: ${describeThrown(e)}`);
1135
1385
  });
1136
1386
  },
1387
+ run() {
1388
+ assertOperational("run");
1389
+ assertNotReentrant("run");
1390
+ if (lifecycleBusy)
1391
+ throwCode("ZRUI_INVALID_STATE", "run: lifecycle operation already in flight");
1392
+ sm.assertOneOf(["Created", "Stopped"], "run: must be Created or Stopped");
1393
+ if (mode === null)
1394
+ throwCode("ZRUI_NO_RENDER_MODE", "run: no render mode selected");
1395
+ const proc = readProcessLike();
1396
+ const addSignalHandler = proc !== null && typeof proc.on === "function" ? proc.on.bind(proc) : null;
1397
+ const canRegisterSignals = addSignalHandler !== null;
1398
+ const signals = ["SIGINT", "SIGTERM", "SIGHUP"];
1399
+ const handlers = [];
1400
+ let runSettled = false;
1401
+ let resolveRun;
1402
+ const runPromise = new Promise((resolve) => {
1403
+ resolveRun = resolve;
1404
+ });
1405
+ const cleanupSignalHandlers = () => {
1406
+ if (!proc)
1407
+ return;
1408
+ for (const entry of handlers) {
1409
+ removeSignalHandler(proc, entry.signal, entry.handler);
1410
+ }
1411
+ handlers.length = 0;
1412
+ };
1413
+ const settleRun = () => {
1414
+ if (runSettled)
1415
+ return;
1416
+ runSettled = true;
1417
+ cleanupSignalHandlers();
1418
+ if (settleActiveRun === settleRun)
1419
+ settleActiveRun = null;
1420
+ resolveRun();
1421
+ };
1422
+ const onSignal = () => {
1423
+ if (runSettled)
1424
+ return;
1425
+ runSettled = true;
1426
+ cleanupSignalHandlers();
1427
+ if (settleActiveRun === settleRun)
1428
+ settleActiveRun = null;
1429
+ void (async () => {
1430
+ try {
1431
+ if (sm.state === "Running")
1432
+ await app.stop();
1433
+ }
1434
+ catch {
1435
+ // ignore
1436
+ }
1437
+ try {
1438
+ app.dispose();
1439
+ }
1440
+ catch {
1441
+ // ignore
1442
+ }
1443
+ try {
1444
+ proc?.exit?.(0);
1445
+ }
1446
+ catch {
1447
+ // ignore
1448
+ }
1449
+ resolveRun();
1450
+ })();
1451
+ };
1452
+ if (canRegisterSignals) {
1453
+ for (const signal of signals) {
1454
+ const handler = () => onSignal();
1455
+ handlers.push(Object.freeze({ signal, handler }));
1456
+ addSignalHandler(signal, handler);
1457
+ }
1458
+ }
1459
+ settleActiveRun = settleRun;
1460
+ return app.start().then(() => {
1461
+ if (!canRegisterSignals) {
1462
+ settleRun();
1463
+ }
1464
+ return runPromise;
1465
+ }, (e) => {
1466
+ cleanupSignalHandlers();
1467
+ if (settleActiveRun === settleRun)
1468
+ settleActiveRun = null;
1469
+ throw e;
1470
+ });
1471
+ },
1137
1472
  stop() {
1138
1473
  assertOperational("stop");
1139
1474
  assertNotReentrant("stop");
@@ -1158,6 +1493,7 @@ export function createApp(opts) {
1158
1493
  return p.then(() => {
1159
1494
  lifecycleBusy = null;
1160
1495
  sm.toStopped();
1496
+ settleActiveRun?.();
1161
1497
  }, (e) => {
1162
1498
  lifecycleBusy = null;
1163
1499
  throw new ZrUiError("ZRUI_BACKEND_ERROR", `backend.stop rejected: ${describeThrown(e)}`);
@@ -1191,6 +1527,7 @@ export function createApp(opts) {
1191
1527
  catch {
1192
1528
  // ignore
1193
1529
  }
1530
+ settleActiveRun?.();
1194
1531
  },
1195
1532
  /* --- Keybinding API --- */
1196
1533
  keys(bindings) {
@@ -1210,6 +1547,12 @@ export function createApp(opts) {
1210
1547
  getMode() {
1211
1548
  return getMode(keybindingState);
1212
1549
  },
1550
+ getBindings(mode) {
1551
+ return getBindings(keybindingState, mode);
1552
+ },
1553
+ get pendingChord() {
1554
+ return getPendingChord(keybindingState);
1555
+ },
1213
1556
  getTerminalProfile() {
1214
1557
  return terminalProfile;
1215
1558
  },