@pwfabric/runtime 0.0.0-stage → 1.0.0-rc.1

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 (447) hide show
  1. package/LICENSE +90 -0
  2. package/README.md +43 -2
  3. package/dist/a11y/announcer.d.ts +65 -0
  4. package/dist/a11y/announcer.js +140 -0
  5. package/dist/a11y/announcer.js.map +1 -0
  6. package/dist/a11y/contrast.d.ts +54 -0
  7. package/dist/a11y/contrast.js +93 -0
  8. package/dist/a11y/contrast.js.map +1 -0
  9. package/dist/a11y/focus.d.ts +123 -0
  10. package/dist/a11y/focus.js +282 -0
  11. package/dist/a11y/focus.js.map +1 -0
  12. package/dist/a11y/index.d.ts +21 -0
  13. package/dist/a11y/index.js +18 -0
  14. package/dist/a11y/index.js.map +1 -0
  15. package/dist/a11y/keyboard.d.ts +144 -0
  16. package/dist/a11y/keyboard.js +317 -0
  17. package/dist/a11y/keyboard.js.map +1 -0
  18. package/dist/adapters/default-image.d.ts +25 -0
  19. package/dist/adapters/default-image.js +37 -0
  20. package/dist/adapters/default-image.js.map +1 -0
  21. package/dist/adapters/index.d.ts +27 -0
  22. package/dist/adapters/index.js +28 -0
  23. package/dist/adapters/index.js.map +1 -0
  24. package/dist/adapters/next-image.d.ts +38 -0
  25. package/dist/adapters/next-image.js +124 -0
  26. package/dist/adapters/next-image.js.map +1 -0
  27. package/dist/adapters/types.d.ts +61 -0
  28. package/dist/adapters/types.js +11 -0
  29. package/dist/adapters/types.js.map +1 -0
  30. package/dist/analytics/index.d.ts +14 -0
  31. package/dist/analytics/index.js +15 -0
  32. package/dist/analytics/index.js.map +1 -0
  33. package/dist/analytics/tracker.d.ts +65 -0
  34. package/dist/analytics/tracker.js +251 -0
  35. package/dist/analytics/tracker.js.map +1 -0
  36. package/dist/analytics/types.d.ts +130 -0
  37. package/dist/analytics/types.js +11 -0
  38. package/dist/analytics/types.js.map +1 -0
  39. package/dist/auth/index.d.ts +15 -0
  40. package/dist/auth/index.js +14 -0
  41. package/dist/auth/index.js.map +1 -0
  42. package/dist/auth/provider.d.ts +132 -0
  43. package/dist/auth/provider.js +473 -0
  44. package/dist/auth/provider.js.map +1 -0
  45. package/dist/auth/types.d.ts +117 -0
  46. package/dist/auth/types.js +9 -0
  47. package/dist/auth/types.js.map +1 -0
  48. package/dist/binding/collection.d.ts +190 -0
  49. package/dist/binding/collection.js +358 -0
  50. package/dist/binding/collection.js.map +1 -0
  51. package/dist/binding/datasource.d.ts +319 -0
  52. package/dist/binding/datasource.js +556 -0
  53. package/dist/binding/datasource.js.map +1 -0
  54. package/dist/binding/engine.d.ts +65 -0
  55. package/dist/binding/engine.js +268 -0
  56. package/dist/binding/engine.js.map +1 -0
  57. package/dist/binding/expression.d.ts +150 -0
  58. package/dist/binding/expression.js +787 -0
  59. package/dist/binding/expression.js.map +1 -0
  60. package/dist/binding/index.d.ts +19 -0
  61. package/dist/binding/index.js +14 -0
  62. package/dist/binding/index.js.map +1 -0
  63. package/dist/binding/two-way.d.ts +149 -0
  64. package/dist/binding/two-way.js +462 -0
  65. package/dist/binding/two-way.js.map +1 -0
  66. package/dist/binding/types.d.ts +119 -0
  67. package/dist/binding/types.js +9 -0
  68. package/dist/binding/types.js.map +1 -0
  69. package/dist/blocks/component-types.d.ts +81 -0
  70. package/dist/blocks/component-types.js +11 -0
  71. package/dist/blocks/component-types.js.map +1 -0
  72. package/dist/blocks/composition-rules.d.ts +168 -0
  73. package/dist/blocks/composition-rules.js +391 -0
  74. package/dist/blocks/composition-rules.js.map +1 -0
  75. package/dist/blocks/hooks.d.ts +31 -0
  76. package/dist/blocks/hooks.js +42 -0
  77. package/dist/blocks/hooks.js.map +1 -0
  78. package/dist/blocks/index.d.ts +14 -0
  79. package/dist/blocks/index.js +23 -0
  80. package/dist/blocks/index.js.map +1 -0
  81. package/dist/blocks/registry.d.ts +84 -0
  82. package/dist/blocks/registry.js +136 -0
  83. package/dist/blocks/registry.js.map +1 -0
  84. package/dist/blocks/rule-engine.d.ts +131 -0
  85. package/dist/blocks/rule-engine.js +321 -0
  86. package/dist/blocks/rule-engine.js.map +1 -0
  87. package/dist/blocks/surface-event-context.d.ts +13 -0
  88. package/dist/blocks/surface-event-context.js +14 -0
  89. package/dist/blocks/surface-event-context.js.map +1 -0
  90. package/dist/blocks/surface-id-context.d.ts +13 -0
  91. package/dist/blocks/surface-id-context.js +15 -0
  92. package/dist/blocks/surface-id-context.js.map +1 -0
  93. package/dist/bus.d.ts +41 -0
  94. package/dist/bus.js +158 -0
  95. package/dist/bus.js.map +1 -0
  96. package/dist/cache/index.d.ts +9 -0
  97. package/dist/cache/index.js +10 -0
  98. package/dist/cache/index.js.map +1 -0
  99. package/dist/cache/memory.d.ts +32 -0
  100. package/dist/cache/memory.js +138 -0
  101. package/dist/cache/memory.js.map +1 -0
  102. package/dist/cache/types.d.ts +62 -0
  103. package/dist/cache/types.js +9 -0
  104. package/dist/cache/types.js.map +1 -0
  105. package/dist/capability/index.d.ts +72 -0
  106. package/dist/capability/index.js +87 -0
  107. package/dist/capability/index.js.map +1 -0
  108. package/dist/capability/kernel-capabilities.d.ts +31 -0
  109. package/dist/capability/kernel-capabilities.js +36 -0
  110. package/dist/capability/kernel-capabilities.js.map +1 -0
  111. package/dist/capability/platform-capabilities.d.ts +71 -0
  112. package/dist/capability/platform-capabilities.js +267 -0
  113. package/dist/capability/platform-capabilities.js.map +1 -0
  114. package/dist/capability/type-mapping.d.ts +70 -0
  115. package/dist/capability/type-mapping.js +107 -0
  116. package/dist/capability/type-mapping.js.map +1 -0
  117. package/dist/capability/validation.d.ts +46 -0
  118. package/dist/capability/validation.js +54 -0
  119. package/dist/capability/validation.js.map +1 -0
  120. package/dist/connectors/declared-connector.d.ts +48 -0
  121. package/dist/connectors/declared-connector.js +333 -0
  122. package/dist/connectors/declared-connector.js.map +1 -0
  123. package/dist/connectors/engine-registry.d.ts +50 -0
  124. package/dist/connectors/engine-registry.js +135 -0
  125. package/dist/connectors/engine-registry.js.map +1 -0
  126. package/dist/connectors/index.d.ts +14 -0
  127. package/dist/connectors/index.js +14 -0
  128. package/dist/connectors/index.js.map +1 -0
  129. package/dist/connectors/module-loader.d.ts +70 -0
  130. package/dist/connectors/module-loader.js +143 -0
  131. package/dist/connectors/module-loader.js.map +1 -0
  132. package/dist/connectors/types.d.ts +174 -0
  133. package/dist/connectors/types.js +23 -0
  134. package/dist/connectors/types.js.map +1 -0
  135. package/dist/context.d.ts +27 -0
  136. package/dist/context.js +38 -0
  137. package/dist/context.js.map +1 -0
  138. package/dist/data/DataCapabilityProvider.d.ts +39 -0
  139. package/dist/data/DataCapabilityProvider.js +58 -0
  140. package/dist/data/DataCapabilityProvider.js.map +1 -0
  141. package/dist/data/index.d.ts +17 -0
  142. package/dist/data/index.js +18 -0
  143. package/dist/data/index.js.map +1 -0
  144. package/dist/data/manager.d.ts +40 -0
  145. package/dist/data/manager.js +442 -0
  146. package/dist/data/manager.js.map +1 -0
  147. package/dist/data/types.d.ts +218 -0
  148. package/dist/data/types.js +2 -0
  149. package/dist/data/types.js.map +1 -0
  150. package/dist/entity/aggregate.d.ts +8 -0
  151. package/dist/entity/aggregate.js +24 -0
  152. package/dist/entity/aggregate.js.map +1 -0
  153. package/dist/entity/apply-query.d.ts +58 -0
  154. package/dist/entity/apply-query.js +264 -0
  155. package/dist/entity/apply-query.js.map +1 -0
  156. package/dist/entity/behavior.d.ts +94 -0
  157. package/dist/entity/behavior.js +78 -0
  158. package/dist/entity/behavior.js.map +1 -0
  159. package/dist/entity/connector.d.ts +141 -0
  160. package/dist/entity/connector.js +89 -0
  161. package/dist/entity/connector.js.map +1 -0
  162. package/dist/entity/connectors/index.d.ts +12 -0
  163. package/dist/entity/connectors/index.js +11 -0
  164. package/dist/entity/connectors/index.js.map +1 -0
  165. package/dist/entity/connectors/static.d.ts +51 -0
  166. package/dist/entity/connectors/static.js +245 -0
  167. package/dist/entity/connectors/static.js.map +1 -0
  168. package/dist/entity/entity-datasource.d.ts +50 -0
  169. package/dist/entity/entity-datasource.js +33 -0
  170. package/dist/entity/entity-datasource.js.map +1 -0
  171. package/dist/entity/facade.d.ts +70 -0
  172. package/dist/entity/facade.js +70 -0
  173. package/dist/entity/facade.js.map +1 -0
  174. package/dist/entity/http-connector.d.ts +123 -0
  175. package/dist/entity/http-connector.js +295 -0
  176. package/dist/entity/http-connector.js.map +1 -0
  177. package/dist/entity/index.d.ts +38 -0
  178. package/dist/entity/index.js +31 -0
  179. package/dist/entity/index.js.map +1 -0
  180. package/dist/entity/postgres-connector.d.ts +64 -0
  181. package/dist/entity/postgres-connector.js +239 -0
  182. package/dist/entity/postgres-connector.js.map +1 -0
  183. package/dist/entity/registry.d.ts +59 -0
  184. package/dist/entity/registry.js +90 -0
  185. package/dist/entity/registry.js.map +1 -0
  186. package/dist/entity/store.d.ts +66 -0
  187. package/dist/entity/store.js +349 -0
  188. package/dist/entity/store.js.map +1 -0
  189. package/dist/entity/types.d.ts +180 -0
  190. package/dist/entity/types.js +10 -0
  191. package/dist/entity/types.js.map +1 -0
  192. package/dist/execution-context.d.ts +88 -0
  193. package/dist/execution-context.js +146 -0
  194. package/dist/execution-context.js.map +1 -0
  195. package/dist/executor.d.ts +27 -0
  196. package/dist/executor.js +33 -0
  197. package/dist/executor.js.map +1 -0
  198. package/dist/forms/form-entity-bridge.d.ts +94 -0
  199. package/dist/forms/form-entity-bridge.js +96 -0
  200. package/dist/forms/form-entity-bridge.js.map +1 -0
  201. package/dist/forms/form-write-target.d.ts +34 -0
  202. package/dist/forms/form-write-target.js +81 -0
  203. package/dist/forms/form-write-target.js.map +1 -0
  204. package/dist/forms/index.d.ts +22 -0
  205. package/dist/forms/index.js +25 -0
  206. package/dist/forms/index.js.map +1 -0
  207. package/dist/forms/named-control-values.d.ts +27 -0
  208. package/dist/forms/named-control-values.js +42 -0
  209. package/dist/forms/named-control-values.js.map +1 -0
  210. package/dist/forms/processor.d.ts +55 -0
  211. package/dist/forms/processor.js +154 -0
  212. package/dist/forms/processor.js.map +1 -0
  213. package/dist/forms/types.d.ts +174 -0
  214. package/dist/forms/types.js +33 -0
  215. package/dist/forms/types.js.map +1 -0
  216. package/dist/forms/validator.d.ts +61 -0
  217. package/dist/forms/validator.js +218 -0
  218. package/dist/forms/validator.js.map +1 -0
  219. package/dist/graph/index.d.ts +10 -0
  220. package/dist/graph/index.js +9 -0
  221. package/dist/graph/index.js.map +1 -0
  222. package/dist/graph/resolver.d.ts +66 -0
  223. package/dist/graph/resolver.js +243 -0
  224. package/dist/graph/resolver.js.map +1 -0
  225. package/dist/graph/types.d.ts +125 -0
  226. package/dist/graph/types.js +9 -0
  227. package/dist/graph/types.js.map +1 -0
  228. package/dist/hooks/useInstalledAtoms.d.ts +96 -0
  229. package/dist/hooks/useInstalledAtoms.js +92 -0
  230. package/dist/hooks/useInstalledAtoms.js.map +1 -0
  231. package/dist/i18n/index.d.ts +15 -0
  232. package/dist/i18n/index.js +14 -0
  233. package/dist/i18n/index.js.map +1 -0
  234. package/dist/i18n/translator.d.ts +76 -0
  235. package/dist/i18n/translator.js +357 -0
  236. package/dist/i18n/translator.js.map +1 -0
  237. package/dist/i18n/types.d.ts +93 -0
  238. package/dist/i18n/types.js +9 -0
  239. package/dist/i18n/types.js.map +1 -0
  240. package/dist/index.d.ts +64 -0
  241. package/dist/index.js +95 -0
  242. package/dist/index.js.map +1 -0
  243. package/dist/kernel.d.ts +47 -0
  244. package/dist/kernel.js +258 -0
  245. package/dist/kernel.js.map +1 -0
  246. package/dist/perf/budget.d.ts +92 -0
  247. package/dist/perf/budget.js +122 -0
  248. package/dist/perf/budget.js.map +1 -0
  249. package/dist/perf/index.d.ts +16 -0
  250. package/dist/perf/index.js +13 -0
  251. package/dist/perf/index.js.map +1 -0
  252. package/dist/perf/render-timer.d.ts +90 -0
  253. package/dist/perf/render-timer.js +136 -0
  254. package/dist/perf/render-timer.js.map +1 -0
  255. package/dist/perf/reporter.d.ts +126 -0
  256. package/dist/perf/reporter.js +241 -0
  257. package/dist/perf/reporter.js.map +1 -0
  258. package/dist/perf/web-vitals.d.ts +111 -0
  259. package/dist/perf/web-vitals.js +268 -0
  260. package/dist/perf/web-vitals.js.map +1 -0
  261. package/dist/registry.d.ts +63 -0
  262. package/dist/registry.js +126 -0
  263. package/dist/registry.js.map +1 -0
  264. package/dist/render/BehaviorSurfaceProvider.d.ts +65 -0
  265. package/dist/render/BehaviorSurfaceProvider.js +185 -0
  266. package/dist/render/BehaviorSurfaceProvider.js.map +1 -0
  267. package/dist/render/BlockErrorBoundary.d.ts +26 -0
  268. package/dist/render/BlockErrorBoundary.js +41 -0
  269. package/dist/render/BlockErrorBoundary.js.map +1 -0
  270. package/dist/render/DataBoundSurfaceRenderer.d.ts +78 -0
  271. package/dist/render/DataBoundSurfaceRenderer.js +998 -0
  272. package/dist/render/DataBoundSurfaceRenderer.js.map +1 -0
  273. package/dist/render/DynamicBlockWrapper.d.ts +23 -0
  274. package/dist/render/DynamicBlockWrapper.js +117 -0
  275. package/dist/render/DynamicBlockWrapper.js.map +1 -0
  276. package/dist/render/appearance-resolver.d.ts +50 -0
  277. package/dist/render/appearance-resolver.js +78 -0
  278. package/dist/render/appearance-resolver.js.map +1 -0
  279. package/dist/render/capability-gate.d.ts +69 -0
  280. package/dist/render/capability-gate.js +94 -0
  281. package/dist/render/capability-gate.js.map +1 -0
  282. package/dist/render/conditional-states-css.d.ts +41 -0
  283. package/dist/render/conditional-states-css.js +82 -0
  284. package/dist/render/conditional-states-css.js.map +1 -0
  285. package/dist/render/connections-context.d.ts +31 -0
  286. package/dist/render/connections-context.js +34 -0
  287. package/dist/render/connections-context.js.map +1 -0
  288. package/dist/render/dark-theme-css.d.ts +36 -0
  289. package/dist/render/dark-theme-css.js +67 -0
  290. package/dist/render/dark-theme-css.js.map +1 -0
  291. package/dist/render/data-binding-types.d.ts +191 -0
  292. package/dist/render/data-binding-types.js +11 -0
  293. package/dist/render/data-binding-types.js.map +1 -0
  294. package/dist/render/data-cache.d.ts +30 -0
  295. package/dist/render/data-cache.js +37 -0
  296. package/dist/render/data-cache.js.map +1 -0
  297. package/dist/render/derive-dark-theme.d.ts +17 -0
  298. package/dist/render/derive-dark-theme.js +18 -0
  299. package/dist/render/derive-dark-theme.js.map +1 -0
  300. package/dist/render/dynamic-artifact-context.d.ts +77 -0
  301. package/dist/render/dynamic-artifact-context.js +89 -0
  302. package/dist/render/dynamic-artifact-context.js.map +1 -0
  303. package/dist/render/dynamic-block-loader.d.ts +80 -0
  304. package/dist/render/dynamic-block-loader.js +197 -0
  305. package/dist/render/dynamic-block-loader.js.map +1 -0
  306. package/dist/render/endpoint.d.ts +18 -0
  307. package/dist/render/endpoint.js +109 -0
  308. package/dist/render/endpoint.js.map +1 -0
  309. package/dist/render/entity-facade-context.d.ts +34 -0
  310. package/dist/render/entity-facade-context.js +37 -0
  311. package/dist/render/entity-facade-context.js.map +1 -0
  312. package/dist/render/group-states-css.d.ts +41 -0
  313. package/dist/render/group-states-css.js +73 -0
  314. package/dist/render/group-states-css.js.map +1 -0
  315. package/dist/render/hooks/useSurfaceEvents.d.ts +8 -0
  316. package/dist/render/hooks/useSurfaceEvents.js +9 -0
  317. package/dist/render/hooks/useSurfaceEvents.js.map +1 -0
  318. package/dist/render/hooks/useSurfaceId.d.ts +8 -0
  319. package/dist/render/hooks/useSurfaceId.js +9 -0
  320. package/dist/render/hooks/useSurfaceId.js.map +1 -0
  321. package/dist/render/index.d.ts +48 -0
  322. package/dist/render/index.js +61 -0
  323. package/dist/render/index.js.map +1 -0
  324. package/dist/render/interaction-rule-engine.d.ts +12 -0
  325. package/dist/render/interaction-rule-engine.js +47 -0
  326. package/dist/render/interaction-rule-engine.js.map +1 -0
  327. package/dist/render/keyframes-css.d.ts +31 -0
  328. package/dist/render/keyframes-css.js +44 -0
  329. package/dist/render/keyframes-css.js.map +1 -0
  330. package/dist/render/query-state.d.ts +3 -0
  331. package/dist/render/query-state.js +70 -0
  332. package/dist/render/query-state.js.map +1 -0
  333. package/dist/render/renderer.d.ts +44 -0
  334. package/dist/render/renderer.js +937 -0
  335. package/dist/render/renderer.js.map +1 -0
  336. package/dist/render/resolve-expressions.d.ts +37 -0
  337. package/dist/render/resolve-expressions.js +309 -0
  338. package/dist/render/resolve-expressions.js.map +1 -0
  339. package/dist/render/responsive-style-css.d.ts +42 -0
  340. package/dist/render/responsive-style-css.js +123 -0
  341. package/dist/render/responsive-style-css.js.map +1 -0
  342. package/dist/render/responsive.d.ts +49 -0
  343. package/dist/render/responsive.js +99 -0
  344. package/dist/render/responsive.js.map +1 -0
  345. package/dist/render/scoped-css.d.ts +71 -0
  346. package/dist/render/scoped-css.js +102 -0
  347. package/dist/render/scoped-css.js.map +1 -0
  348. package/dist/render/shell-selection.d.ts +69 -0
  349. package/dist/render/shell-selection.js +117 -0
  350. package/dist/render/shell-selection.js.map +1 -0
  351. package/dist/render/states-css.d.ts +32 -0
  352. package/dist/render/states-css.js +63 -0
  353. package/dist/render/states-css.js.map +1 -0
  354. package/dist/render/surface-data-context.d.ts +18 -0
  355. package/dist/render/surface-data-context.js +21 -0
  356. package/dist/render/surface-data-context.js.map +1 -0
  357. package/dist/render/surface-event-bus.d.ts +19 -0
  358. package/dist/render/surface-event-bus.js +68 -0
  359. package/dist/render/surface-event-bus.js.map +1 -0
  360. package/dist/render/surface-event-context.d.ts +11 -0
  361. package/dist/render/surface-event-context.js +12 -0
  362. package/dist/render/surface-event-context.js.map +1 -0
  363. package/dist/render/surface-id-context.d.ts +8 -0
  364. package/dist/render/surface-id-context.js +9 -0
  365. package/dist/render/surface-id-context.js.map +1 -0
  366. package/dist/render/theme-utils.d.ts +14 -0
  367. package/dist/render/theme-utils.js +15 -0
  368. package/dist/render/theme-utils.js.map +1 -0
  369. package/dist/render/types.d.ts +282 -0
  370. package/dist/render/types.js +9 -0
  371. package/dist/render/types.js.map +1 -0
  372. package/dist/render/visibility-css.d.ts +42 -0
  373. package/dist/render/visibility-css.js +119 -0
  374. package/dist/render/visibility-css.js.map +1 -0
  375. package/dist/render/width-scope.d.ts +34 -0
  376. package/dist/render/width-scope.js +44 -0
  377. package/dist/render/width-scope.js.map +1 -0
  378. package/dist/render/write-only-sources.d.ts +33 -0
  379. package/dist/render/write-only-sources.js +99 -0
  380. package/dist/render/write-only-sources.js.map +1 -0
  381. package/dist/routing/index.d.ts +14 -0
  382. package/dist/routing/index.js +15 -0
  383. package/dist/routing/index.js.map +1 -0
  384. package/dist/routing/router.d.ts +42 -0
  385. package/dist/routing/router.js +218 -0
  386. package/dist/routing/router.js.map +1 -0
  387. package/dist/routing/types.d.ts +124 -0
  388. package/dist/routing/types.js +9 -0
  389. package/dist/routing/types.js.map +1 -0
  390. package/dist/seo/generator.d.ts +39 -0
  391. package/dist/seo/generator.js +236 -0
  392. package/dist/seo/generator.js.map +1 -0
  393. package/dist/seo/index.d.ts +14 -0
  394. package/dist/seo/index.js +15 -0
  395. package/dist/seo/index.js.map +1 -0
  396. package/dist/seo/types.d.ts +181 -0
  397. package/dist/seo/types.js +9 -0
  398. package/dist/seo/types.js.map +1 -0
  399. package/dist/state/index.d.ts +15 -0
  400. package/dist/state/index.js +16 -0
  401. package/dist/state/index.js.map +1 -0
  402. package/dist/state/manager.d.ts +33 -0
  403. package/dist/state/manager.js +81 -0
  404. package/dist/state/manager.js.map +1 -0
  405. package/dist/state/store.d.ts +40 -0
  406. package/dist/state/store.js +145 -0
  407. package/dist/state/store.js.map +1 -0
  408. package/dist/state/types.d.ts +107 -0
  409. package/dist/state/types.js +9 -0
  410. package/dist/state/types.js.map +1 -0
  411. package/dist/surface/expression.d.ts +65 -0
  412. package/dist/surface/expression.js +179 -0
  413. package/dist/surface/expression.js.map +1 -0
  414. package/dist/surface/index.d.ts +32 -0
  415. package/dist/surface/index.js +25 -0
  416. package/dist/surface/index.js.map +1 -0
  417. package/dist/surface/interpreter.d.ts +96 -0
  418. package/dist/surface/interpreter.js +231 -0
  419. package/dist/surface/interpreter.js.map +1 -0
  420. package/dist/surface/layout.d.ts +54 -0
  421. package/dist/surface/layout.js +288 -0
  422. package/dist/surface/layout.js.map +1 -0
  423. package/dist/surface/output.d.ts +90 -0
  424. package/dist/surface/output.js +104 -0
  425. package/dist/surface/output.js.map +1 -0
  426. package/dist/surface/primitives.d.ts +180 -0
  427. package/dist/surface/primitives.js +339 -0
  428. package/dist/surface/primitives.js.map +1 -0
  429. package/dist/surface/renderer.d.ts +96 -0
  430. package/dist/surface/renderer.js +190 -0
  431. package/dist/surface/renderer.js.map +1 -0
  432. package/dist/surface/schemas.d.ts +181 -0
  433. package/dist/surface/schemas.js +142 -0
  434. package/dist/surface/schemas.js.map +1 -0
  435. package/dist/surface/transforms.d.ts +144 -0
  436. package/dist/surface/transforms.js +120 -0
  437. package/dist/surface/transforms.js.map +1 -0
  438. package/dist/surface/types.d.ts +160 -0
  439. package/dist/surface/types.js +51 -0
  440. package/dist/surface/types.js.map +1 -0
  441. package/dist/surface/validation.d.ts +123 -0
  442. package/dist/surface/validation.js +147 -0
  443. package/dist/surface/validation.js.map +1 -0
  444. package/dist/validator.d.ts +55 -0
  445. package/dist/validator.js +100 -0
  446. package/dist/validator.js.map +1 -0
  447. package/package.json +106 -3
@@ -0,0 +1,937 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Surface Renderer
4
+ *
5
+ * Phase 2A.1: Main Surface-to-React rendering component
6
+ *
7
+ * @module @pwfabric/runtime/render/renderer
8
+ */
9
+ import * as React from 'react';
10
+ import { FLEX_HOIST_KEYS, LAYOUT_HOIST_KEYS } from './scoped-css.js';
11
+ import { resolveBlockLayout, resolveMeasureStyle, resolveSurfaceRootStyle, useViewport, getColumnsAtBreakpoint, } from './responsive.js';
12
+ import { chunkByWidthScope, resolveWidthScope } from './width-scope.js';
13
+ import { resolveAppearanceVars, resolveAppearanceDarkVars, resolveAppearanceKeyframes, } from './appearance-resolver.js';
14
+ import { collectVisibilityCss } from './visibility-css.js';
15
+ import { collectStatesCss } from './states-css.js';
16
+ import { collectConditionalStatesCss } from './conditional-states-css.js';
17
+ import { collectResponsiveStyleCss } from './responsive-style-css.js';
18
+ import { collectGroupStatesCss } from './group-states-css.js';
19
+ import { buildDarkThemeCss } from './dark-theme-css.js';
20
+ import { resolveAppearanceMode } from '@pwfabric/contracts';
21
+ import { buildKeyframesCss } from './keyframes-css.js';
22
+ import { DEFAULT_BLOCK_LAYOUT } from '@pwfabric/core';
23
+ // `defaultBlockComponents` retired. Production render
24
+ // path goes exclusively through the install pipeline — `customComponents`
25
+ // (typically wired from `useDynamicArtifacts`) supplies the runtime
26
+ // component map; blocks without a descriptor render as
27
+ // "Unknown block" placeholders.
28
+ import { useEntitlements, GatedBlockPlaceholder } from './capability-gate.js';
29
+ import { useDynamicBlocks } from './dynamic-artifact-context.js';
30
+ import { DynamicBlockWrapper } from './DynamicBlockWrapper.js';
31
+ import { BlockErrorBoundary } from './BlockErrorBoundary.js';
32
+ import { BehaviorSurfaceProvider } from './BehaviorSurfaceProvider.js';
33
+ import { isCapability } from '../capability/type-mapping.js';
34
+ import { EntityFacadeContext } from './entity-facade-context.js';
35
+ import { SurfaceConnectionsContext } from './connections-context.js';
36
+ import { ShellSelectionProvider } from './shell-selection.js';
37
+ import { isRepeatExpanded } from './resolve-expressions.js';
38
+ // ============================================================
39
+ // HELPERS
40
+ // ============================================================
41
+ /**
42
+ * Merge component registries, filtering out undefined values
43
+ */
44
+ function mergeComponentRegistries(base = {}, overrides) {
45
+ const merged = {};
46
+ for (const [key, value] of Object.entries(base)) {
47
+ if (value !== undefined)
48
+ merged[key] = value;
49
+ }
50
+ if (overrides) {
51
+ for (const [key, value] of Object.entries(overrides)) {
52
+ if (value !== undefined)
53
+ merged[key] = value;
54
+ }
55
+ }
56
+ return merged;
57
+ }
58
+ /**
59
+ * Project a canonical `SurfaceEventDescriptor.type` (contracts vocabulary, e.g.
60
+ * `'block:click'`, `'form:submit'`, `'input:change'`) onto the renderer's legacy
61
+ * `SurfaceEvent['type']` enum. Anything without a direct legacy equivalent
62
+ * collapses to `'custom'`. Only used to satisfy the `onEvent` handler's shape —
63
+ * the dispatcher reads `blockId`/`payload`, not the projected `type`.
64
+ */
65
+ function mapCanonicalEventType(type) {
66
+ switch (type) {
67
+ case 'block:click':
68
+ return 'click';
69
+ case 'form:submit':
70
+ return 'submit';
71
+ case 'form:change':
72
+ case 'input:change':
73
+ return 'change';
74
+ default:
75
+ return 'custom';
76
+ }
77
+ }
78
+ /**
79
+ * Create an ImageBlock component that uses the provided adapter
80
+ */
81
+ function createImageBlockWithAdapter(adapter) {
82
+ return ({ props, block }) => {
83
+ // Build adapter props, only including defined values
84
+ const adapterProps = {
85
+ src: props.src || '/placeholder.png',
86
+ alt: props.alt ?? '',
87
+ blockId: block.id,
88
+ };
89
+ // Add optional props only if they have values
90
+ if (props.width !== undefined) {
91
+ ;
92
+ adapterProps.width = props.width;
93
+ }
94
+ if (props.height !== undefined) {
95
+ ;
96
+ adapterProps.height = props.height;
97
+ }
98
+ if (props.loading !== undefined) {
99
+ ;
100
+ adapterProps.loading = props.loading;
101
+ adapterProps.priority = props.loading === 'eager';
102
+ }
103
+ if (props.objectFit !== undefined) {
104
+ ;
105
+ adapterProps.objectFit =
106
+ props.objectFit;
107
+ }
108
+ if (props.aspectRatio !== undefined) {
109
+ ;
110
+ adapterProps.aspectRatio = props.aspectRatio;
111
+ }
112
+ if (props.borderRadius !== undefined) {
113
+ ;
114
+ adapterProps.borderRadius = props.borderRadius;
115
+ }
116
+ if (props.placeholder !== undefined) {
117
+ ;
118
+ adapterProps.placeholder = props.placeholder;
119
+ }
120
+ if (props.blurDataURL !== undefined) {
121
+ ;
122
+ adapterProps.blurDataURL = props.blurDataURL;
123
+ }
124
+ return React.createElement(adapter, adapterProps);
125
+ };
126
+ }
127
+ /**
128
+ * Emit a block's outermost wrapper. When `renderBlockWrapper` is provided
129
+ * (editor seam) the decorator owns the wrapper element and receives the
130
+ * runtime-computed `wrapperProps` (layout style + selectors). Otherwise the
131
+ * default `<div {...wrapperProps}>` is rendered — byte-identical to the
132
+ * pre-seam runtime (modulo the unified `data-block-id`).
133
+ */
134
+ /**
135
+ * The attributes EVERY wrapper must carry — in one place.
136
+ *
137
+ * `states-css.ts` and `conditional-states-css.ts` EXCLUDE the wrapper from
138
+ * their state rules with `:not([data-block-wrapper])`. Without the attribute
139
+ * there is no exclusion: a rule matches both the wrapper and the component
140
+ * root, and the defect described in states-css comes back ("the square
141
+ * wrapper takes the hover box-shadow, corners show under the rounded card,
142
+ * transforms stack") — on every block, including blocks installed from a pack.
143
+ *
144
+ * All four call sites derive from this base, so they cannot drift apart.
145
+ */
146
+ function baseWrapperProps(block, layoutStyle, scopeAttr) {
147
+ return {
148
+ // `exactOptionalPropertyTypes`: an optional field cannot be given an
149
+ // explicit `undefined`; when absent it is left out.
150
+ ...(layoutStyle ? { style: layoutStyle } : {}),
151
+ id: block.id,
152
+ 'data-block-id': block.id,
153
+ ...scopeAttr,
154
+ // `data-block-wrapper`: an ALIAS carrying the same value as `data-block-id`
155
+ // but with a separate job — the one marker that tells the wrapper apart
156
+ // from the component root.
157
+ 'data-block-wrapper': block.id,
158
+ };
159
+ }
160
+ function emitBlockWrapper(block, wrapperProps, inner, parentBlock, renderBlockWrapper) {
161
+ if (renderBlockWrapper) {
162
+ return (_jsx(React.Fragment, { children: renderBlockWrapper({
163
+ block,
164
+ children: inner,
165
+ wrapperProps,
166
+ ...(parentBlock !== undefined ? { parentBlock } : {}),
167
+ }) }, block.id));
168
+ }
169
+ return (_jsx("div", { ...wrapperProps, children: inner }, block.id));
170
+ }
171
+ // ============================================================
172
+ // RESPONSIVE STYLE FLATTENING
173
+ // ============================================================
174
+ /**
175
+ * Strip a block's responsive (`{ xs?, sm?, … }`) `style` prop values for INLINE
176
+ * application: those are emitted ENTIRELY as scoped stylesheet rules by
177
+ * `collectResponsiveStyleCss` (base as a non-media rule + larger breakpoints as
178
+ * `@media`). They must NOT be applied inline — inline declarations beat
179
+ * stylesheet/@media rules, so an inline base would pin the value and the
180
+ * responsive overrides would never take effect. Flat string values pass through
181
+ * untouched. Returns the SAME block object when no responsive value is present
182
+ * (zero allocation for existing surfaces).
183
+ */
184
+ function flattenResponsiveStyle(block) {
185
+ const style = block.style;
186
+ if (!style)
187
+ return block;
188
+ let hasResponsive = false;
189
+ const flat = {};
190
+ for (const [prop, value] of Object.entries(style)) {
191
+ if (typeof value === 'string') {
192
+ flat[prop] = value;
193
+ continue;
194
+ }
195
+ if (value === null || value === undefined)
196
+ continue;
197
+ // Responsive value map → handled ENTIRELY by responsive-style-css (base as a
198
+ // non-media scoped rule + larger breakpoints as @media). It must NOT be
199
+ // applied inline: inline declarations beat stylesheet/@media rules, so an
200
+ // inline base would pin the value and the responsive overrides would never
201
+ // take effect. Strip it from the inline style here.
202
+ hasResponsive = true;
203
+ }
204
+ if (!hasResponsive)
205
+ return block;
206
+ return { ...block, style: flat };
207
+ }
208
+ // ============================================================
209
+ // BLOCK RENDERER
210
+ // ============================================================
211
+ /**
212
+ * Compute a block's grid/flex layout style. Applied to EVERY render path
213
+ * (static, dynamic, unknown, gated) so installed dynamic blocks lay out
214
+ * identically to static ones — otherwise full-span dynamic sections auto-place
215
+ * side-by-side instead of stacking.
216
+ */
217
+ /**
218
+ * CHROME IS DECLARED, NOT HARD-CODED.
219
+ *
220
+ * The engine keeps no list of chrome block names (navbar, rail, bottom bar…):
221
+ * such a list would be one pack's vocabulary, and a second pack could not
222
+ * express its own chrome. Instead a block declares its own
223
+ * `layout.widthScope` and the engine applies it. That a piece of chrome
224
+ * inside a row is not stretched to an equal column also follows from that
225
+ * declaration (see `resolveWidthScope` below).
226
+ *
227
+ * The one name kept below, `split-view`, is not pack vocabulary: it is a
228
+ * LAYOUT block that places its own children and keeps the selection in
229
+ * surface-owned state (see `shell-selection`).
230
+ */
231
+ const CHILD_LAYOUT_OWNER = 'split-view';
232
+ // Style keys hoisted from block.style to the WRAPPER div (the flex/grid item).
233
+ // Module-scoped because renderBlock must strip the SAME keys from the style the
234
+ // component receives — otherwise a margin is applied twice (wrapper + inner):
235
+ // marginTop:'-40%' would move content by -335px AND -256px.
236
+ // FLEX_HOIST_KEYS / LAYOUT_HOIST_KEYS live in scoped-css.ts — the responsive
237
+ // CSS generator must read the SAME list (one hoisting rule, one place).
238
+ /** block.style minus the hoisted keys — what the component itself may apply. */
239
+ function stripHoistedStyle(block) {
240
+ const style = block.style;
241
+ if (!style)
242
+ return block;
243
+ let has = false;
244
+ for (const key of LAYOUT_HOIST_KEYS)
245
+ if (style[key] !== undefined) {
246
+ has = true;
247
+ break;
248
+ }
249
+ if (!has)
250
+ return block;
251
+ const rest = { ...style };
252
+ for (const key of LAYOUT_HOIST_KEYS)
253
+ delete rest[key];
254
+ return { ...block, style: rest };
255
+ }
256
+ /**
257
+ * ROW STACKING LAW.
258
+ *
259
+ * A `container` with `direction: 'row'` is a flex row with `nowrap`; without
260
+ * this rule nothing turns it into a column on a phone, so on xs a heading
261
+ * sits squeezed beside a segmented control that overflows, navbar pills run
262
+ * off the edge, and a two-column body becomes two 190px columns.
263
+ *
264
+ * The law: at the `xs` breakpoint a row container whose children include at
265
+ * least TWO non-intrinsic blocks renders as a column. Rows made of intrinsic
266
+ * blocks (avatar + name, icon + label, a button pair) are content rows and
267
+ * keep their direction. Authors opt out with `keepRow: true`; a row that
268
+ * already declares `wrap: true` handles itself. The stacked container carries
269
+ * `stackedFromRow: true` so child layout does not apply column-mode
270
+ * partial-span widths to what used to be row cells.
271
+ */
272
+ const ROW_DIRECTIONS = new Set(['row', 'row-reverse']);
273
+ function applyRowStacking(block, viewport, components) {
274
+ if (viewport !== 'xs' || block.type !== 'container')
275
+ return block;
276
+ const props = (block.props ?? {});
277
+ if (!ROW_DIRECTIONS.has(String(props.direction)))
278
+ return block;
279
+ if (props.keepRow === true || props.wrap === true)
280
+ return block;
281
+ const children = block.children ?? [];
282
+ const fluid = children.filter((c) => components[c.type]?.sizing !== 'intrinsic').length;
283
+ if (fluid < 2)
284
+ return block;
285
+ return { ...block, props: { ...props, direction: 'column', stackedFromRow: true } };
286
+ }
287
+ const FIXED_PX_WIDTH = /^\s*\d+(?:\.\d+)?\s*px\s*$/i;
288
+ /**
289
+ * A width that is a fixed length — a finite number (px, the props unit) or a
290
+ * `px` string. Percentages, `auto`, `fit-content`, theme vars and
291
+ * responsive maps are NOT fixed: they say nothing about the block keeping
292
+ * its own size in a row.
293
+ */
294
+ function isFixedWidth(value) {
295
+ if (typeof value === 'number')
296
+ return Number.isFinite(value) && value > 0;
297
+ return typeof value === 'string' && FIXED_PX_WIDTH.test(value) && parseFloat(value) > 0;
298
+ }
299
+ /** The instance states a fixed width in `props.width` or `style.width`. */
300
+ function hasFixedWidth(block) {
301
+ const props = block.props;
302
+ const style = block.style;
303
+ return isFixedWidth(props?.width) || isFixedWidth(style?.width);
304
+ }
305
+ function computeBlockLayoutStyle(block, parentBlock, viewport, sizing) {
306
+ // Skip grid-column when parent manages its own layout (grid, container, form, etc.)
307
+ const parentType = parentBlock?.type;
308
+ // A child that EXPLICITLY declares layout.span keeps it inside a grid parent.
309
+ // Otherwise asymmetric 12-col layouts are impossible: the declaration would
310
+ // be silently swallowed. Grid stays the owner only for span-less children
311
+ // (equal auto columns, the default).
312
+ const childDeclaresSpan = block.layout?.span !== undefined;
313
+ // A parent that expanded a `repeat` (the repeater family, or any block that
314
+ // repeats) lays its clones out with its own grid/columns — the clones must
315
+ // not carry the surface's 12-column span into it. A grid
316
+ // parent keeps its own rule above.
317
+ const parentRepeats = parentType !== 'grid' && isRepeatExpanded(parentBlock);
318
+ const parentOwnsLayout = parentRepeats ||
319
+ (parentType === 'grid' && !childDeclaresSpan) ||
320
+ parentType === 'container' ||
321
+ parentType === 'section' ||
322
+ parentType === 'form' ||
323
+ parentType === 'tabs' ||
324
+ parentType === 'tooltip' ||
325
+ parentType === CHILD_LAYOUT_OWNER;
326
+ const layout = block.layout ?? DEFAULT_BLOCK_LAYOUT;
327
+ const resolved = resolveBlockLayout(layout, viewport);
328
+ // Column container children: apply explicit width when span < surface columns
329
+ const parentDirection = parentBlock?.props?.direction;
330
+ // A row stacked by the ROW STACKING LAW is not an authored column: its
331
+ // children were row cells, so their partial spans must not become widths.
332
+ const parentStackedFromRow = parentBlock?.props?.stackedFromRow === true;
333
+ const isColumnContainer = parentType === 'container' &&
334
+ (parentDirection === 'column' || parentDirection === undefined) &&
335
+ !parentStackedFromRow;
336
+ let columnChildWidth;
337
+ if (isColumnContainer && parentBlock) {
338
+ const surfaceCols = getColumnsAtBreakpoint(viewport);
339
+ const parentCols = resolveBlockLayout(parentBlock.layout ?? DEFAULT_BLOCK_LAYOUT, viewport).span;
340
+ if (resolved.span < surfaceCols) {
341
+ columnChildWidth = `${(resolved.span / parentCols) * 100}%`;
342
+ }
343
+ }
344
+ // Row container children: apply flexBasis so spans translate to proportional widths.
345
+ //
346
+ // Weighted columns: a child's OWN `block.style` flex hints win over the span-derived
347
+ // default. The wrapper is the flex item — so `flex`/`flexGrow`/`flexBasis`/`flexShrink`
348
+ // declared on a child must land on the wrapper, not the inner block. When present they
349
+ // fully replace the default span/equal distribution, letting an author express an
350
+ // arbitrary ratio (e.g. `flex:"1.62 1 0"` + `flex:"1 1 0"` → exact 1.62:1 columns)
351
+ // that the integer-span model cannot. `layout.span` remains the default when no flex
352
+ // hint is given.
353
+ const childStyle = block.style;
354
+ const childHasFlexHint = childStyle !== undefined && FLEX_HOIST_KEYS.some((k) => childStyle[k] !== undefined);
355
+ const isRowContainer = parentType === 'container' && parentDirection === 'row';
356
+ let rowChildStyle;
357
+ // A rail beside a row is NOT one of two equal columns. The default below
358
+ // gives every row child `flex: 1 1 0` — right for two content columns,
359
+ // wrong for chrome: it would stretch a 76px rail to half the row and squeeze
360
+ // the body it exists to sit beside. The block DECLARES its own scope.
361
+ if (isRowContainer && resolveWidthScope(block, viewport) === 'viewport' && !childHasFlexHint) {
362
+ rowChildStyle = { flexGrow: 0, flexShrink: 0, flexBasis: 'auto', alignSelf: 'stretch' };
363
+ }
364
+ else if (isRowContainer &&
365
+ (sizing === 'intrinsic' || hasFixedWidth(block)) &&
366
+ !childHasFlexHint) {
367
+ // The block declared its width is its content (avatar, badge, button…):
368
+ // it keeps that width; whatever sits beside it takes the rest. Without
369
+ // this every such block would take half the row.
370
+ //
371
+ // The same holds for an INSTANCE that states a fixed width (a 56px
372
+ // thumbnail beside a product title). `sizing` is static per block type,
373
+ // so a fluid-by-default block (image) cannot declare it; the instance's
374
+ // own numeric/px width is the declaration. Without it a 56px image took
375
+ // the equal share and squeezed the text beside it into two lines.
376
+ rowChildStyle = { flexGrow: 0, flexShrink: 0, flexBasis: 'auto' };
377
+ }
378
+ else if (isRowContainer && parentBlock && !childHasFlexHint) {
379
+ // No author flex hint → derive proportional width from the integer span.
380
+ const surfaceCols = getColumnsAtBreakpoint(viewport);
381
+ const parentCols = resolveBlockLayout(parentBlock.layout ?? DEFAULT_BLOCK_LAYOUT, viewport).span;
382
+ if (resolved.span < surfaceCols) {
383
+ // Explicit partial span → proportional width
384
+ rowChildStyle = {
385
+ flexBasis: `${(resolved.span / parentCols) * 100}%`,
386
+ flexGrow: 0,
387
+ flexShrink: 0,
388
+ };
389
+ }
390
+ else {
391
+ // Default/full-width span → equal distribution
392
+ rowChildStyle = { flexGrow: 1, flexShrink: 1, flexBasis: '0' };
393
+ }
394
+ }
395
+ // Hoist layout-positioning props (margin/maxWidth/minWidth) to the wrapper div so
396
+ // grid-item centering works (the inner block element is usually width:100%). Flex
397
+ // hints hoist here too so a row-container child's authored ratio reaches the flex
398
+ // item (the wrapper) rather than the inner block.
399
+ const hoistedStyle = {};
400
+ if (block.style) {
401
+ for (const key of LAYOUT_HOIST_KEYS) {
402
+ const val = block.style[key];
403
+ if (val !== undefined) {
404
+ ;
405
+ hoistedStyle[key] = val;
406
+ }
407
+ }
408
+ }
409
+ // Auto-center when maxWidth is set without an explicit margin.
410
+ if (hoistedStyle.maxWidth !== undefined &&
411
+ hoistedStyle.margin === undefined &&
412
+ hoistedStyle.marginLeft === undefined &&
413
+ hoistedStyle.marginRight === undefined) {
414
+ hoistedStyle.margin = '0 auto';
415
+ // An auto-centred max-width block FILLS up to its max width, as a block
416
+ // box with `max-width` + `margin: 0 auto` does in normal flow. Inside a
417
+ // flex column or a grid cell, auto inline margins cancel `stretch`, so
418
+ // without an explicit width the wrapper shrank to its content and the
419
+ // inner `width:100%` resolved against that shrunken box (
420
+ // a two-pane doc shell collapsed to 307 px and the panes stacked unless
421
+ // the selected text was long, and content columns narrowed).
422
+ // Not applied where the block keeps its own width: an intrinsic block
423
+ // (button, badge), a row cell (its flex basis is the width), a column
424
+ // child with a span-derived width, or an authored non-100% width.
425
+ const authoredWidth = block.style?.width;
426
+ if (sizing !== 'intrinsic' &&
427
+ !isRowContainer &&
428
+ columnChildWidth === undefined &&
429
+ (authoredWidth === undefined || authoredWidth === '100%')) {
430
+ hoistedStyle.width = '100%';
431
+ }
432
+ }
433
+ // Interaction states (block.states) are rendered as scoped `:hover`/`:active`/
434
+ // `:focus-visible` CSS rules elsewhere; here we add a default `transition` to
435
+ // the base wrapper style so the state change animates. Authors can still
436
+ // override via `block.style.transition`, and `prefers-reduced-motion`
437
+ // (SURFACE_BASE_CSS) neutralises it for users who opt out.
438
+ const hasStates = block.states !== undefined && block.states !== null;
439
+ const transitionStyle = hasStates && block.style?.transition === undefined
440
+ ? { transition: 'all var(--pw-duration-fast, 150ms) var(--pw-easing, ease)' }
441
+ : {};
442
+ // GRID items default to min-width:auto, which lets nowrap/max-content content
443
+ // leak its intrinsic width into track sizing even under overflow:hidden
444
+ // (measured: a marquee pushed the whole page 63px). Zero it for grid items
445
+ // only — on FLEX children min-width:auto is load-bearing (equal-distribution
446
+ // rows keep children at least min-content wide; zeroing it made a highlighted
447
+ // word stretch to half the row). An author's own minWidth (hoisted) wins.
448
+ const isGridItem = !parentOwnsLayout || parentType === 'grid';
449
+ return {
450
+ ...(isGridItem ? { minWidth: 0 } : {}),
451
+ ...(!parentOwnsLayout ? { gridColumn: `span ${resolved.span} / span ${resolved.span}` } : {}),
452
+ ...(resolved.offset !== undefined && !parentOwnsLayout
453
+ ? { gridColumnStart: resolved.offset + 1 }
454
+ : {}),
455
+ ...(columnChildWidth !== undefined ? { width: columnChildWidth } : {}),
456
+ ...(rowChildStyle ?? {}),
457
+ ...(resolved.order !== undefined ? { order: resolved.order } : {}),
458
+ ...(resolved.hidden ? { display: 'none' } : {}),
459
+ ...transitionStyle,
460
+ ...hoistedStyle,
461
+ };
462
+ }
463
+ /**
464
+ * Render a single block recursively.
465
+ * parentBlock is used to determine the CSS strategy for nested blocks.
466
+ * entitlementCheck gates unentitled blocks.
467
+ * dynamicBlocks provides dynamic block descriptors for fallback rendering.
468
+ */
469
+ function renderBlock({ block: rawBlock, context, components }, parentBlock, entitlementCheck, dynamicBlocks, renderBlockWrapper) {
470
+ // Per-prop responsive styles (block.style values that are `{ xs?, … }` maps)
471
+ // can't be expressed inline as media queries — flatten them to their base
472
+ // value here so the inline path (this block + computeBlockLayoutStyle hoists)
473
+ // sees a flat `Record<string,string>`; the larger-breakpoint values are
474
+ // emitted as scoped media-query CSS by collectResponsiveStyleCss.
475
+ const block = applyRowStacking(flattenResponsiveStyle(rawBlock), (context.viewport ?? 'lg'), components);
476
+ // Layout applies to EVERY path below (static/dynamic/unknown/gated) so installed
477
+ // dynamic blocks get the same grid span/width/centering as static ones. Previously
478
+ // only the static path applied it → full-span dynamic sections collapsed side-by-side.
479
+ const layoutStyle = computeBlockLayoutStyle(block, parentBlock, (context.viewport ?? 'lg'), components[block.type]?.sizing);
480
+ // Scope stamp. A viewport audit needs to see the declaration in the DOM to
481
+ // say "viewport was declared but the block does not reach the edge"; the
482
+ // style alone carries the result, not the declaration.
483
+ const scopeAttr = resolveWidthScope(block, (context.viewport ?? 'lg')) === 'viewport'
484
+ ? { 'data-pw-width-scope': 'viewport' }
485
+ : {};
486
+ // Gate check — show placeholder for unentitled blocks
487
+ // Dynamic blocks with descriptors are entitled (installed + built)
488
+ if (entitlementCheck && !entitlementCheck(block.type) && !dynamicBlocks?.has(block.type)) {
489
+ return emitBlockWrapper(block, baseWrapperProps(block, layoutStyle, scopeAttr), _jsx(GatedBlockPlaceholder, { blockId: block.id, blockType: block.type }), parentBlock, renderBlockWrapper);
490
+ }
491
+ // Install-only render — when the world has the atom
492
+ // installed (CDN bundle URL on the descriptor) the dynamic wrapper
493
+ // takes over. `customComponents` may still supply test/preview-time
494
+ // component overrides, but the legacy `defaultBlockComponents` map is
495
+ // gone; unmatched block types render as "Unknown block" placeholders.
496
+ const dynamicDescriptor = dynamicBlocks?.get(block.type);
497
+ if (dynamicDescriptor) {
498
+ let dynamicChildren = null;
499
+ if (block.children && block.children.length > 0) {
500
+ dynamicChildren = block.children.map((child) => renderBlock({ block: child, context, components }, block, entitlementCheck, dynamicBlocks, renderBlockWrapper));
501
+ }
502
+ return emitBlockWrapper(block, { ...baseWrapperProps(block, layoutStyle, scopeAttr), 'data-dynamic-block': block.type }, _jsx(BlockErrorBoundary, { blockId: block.id, blockType: block.type, children: _jsx(DynamicBlockWrapper, { descriptor: dynamicDescriptor, block: stripHoistedStyle(block), context: context, children: dynamicChildren }) }), parentBlock, renderBlockWrapper);
503
+ }
504
+ const Component = components[block.type];
505
+ if (!Component) {
506
+ console.warn(`Unknown block type: ${block.type}`);
507
+ // Render children so a partial subtree still shows when a block type isn't
508
+ // installed (e.g. an uninstalled "Container" wrapper), but DON'T surface a
509
+ // visible "Unknown block: <type>" placeholder to end users on published /
510
+ // preview surfaces — that read as a broken page. The console.warn above and
511
+ // the `data-unknown-type` attribute keep it diagnosable, and the authoring
512
+ // editor uses its own EditableBlock placeholder, so nothing is lost there.
513
+ let unknownChildren = null;
514
+ if (block.children && block.children.length > 0) {
515
+ unknownChildren = block.children.map((child) => renderBlock({ block: child, context, components }, block, entitlementCheck, dynamicBlocks, renderBlockWrapper));
516
+ }
517
+ return emitBlockWrapper(block, { ...baseWrapperProps(block, layoutStyle, scopeAttr), 'data-unknown-type': block.type }, _jsx(_Fragment, { children: unknownChildren }), parentBlock, renderBlockWrapper);
518
+ }
519
+ // Recursively render children — current block becomes the parent
520
+ let children = null;
521
+ if (block.children && block.children.length > 0) {
522
+ children = block.children.map((child) => renderBlock({ block: child, context, components }, block, entitlementCheck, dynamicBlocks, renderBlockWrapper));
523
+ }
524
+ // TODO: When FormBlock contract includes `bindToEntity` prop,
525
+ // call attachFormEntityBridge(bus, entityStore, { formId: block.id, ...block.props.bindToEntity })
526
+ // Requires: platform/entities capability active (platform/input retired).
527
+ // See packages/runtime/src/forms/form-entity-bridge.ts for the bridge implementation.
528
+ return emitBlockWrapper(block, baseWrapperProps(block, layoutStyle, scopeAttr), _jsx(BlockErrorBoundary, { blockId: block.id, blockType: block.type, children: _jsx(Component, { block: stripHoistedStyle(block), props: block.props ?? {}, context: context, children: children }) }), parentBlock, renderBlockWrapper);
529
+ }
530
+ // ============================================================
531
+ // BASE CSS STANDARD (box-sizing reset)
532
+ // ============================================================
533
+ /**
534
+ * Scoped box-sizing reset injected at the surface render root.
535
+ *
536
+ * `box-sizing` is NOT an inherited CSS property, so setting it only on the
537
+ * `[data-pw-surface]` root would leave every descendant at the browser
538
+ * default `content-box`. Under `content-box`, a block declared `width:100%`
539
+ * with padding (e.g. `layout/section` padding:80) computes its border-box
540
+ * width as `100% + padding` and overflows its parent → horizontal scroll.
541
+ * A descendant selector (`[data-pw-surface] *`) is therefore required so
542
+ * every block inside the surface respects its declared width.
543
+ *
544
+ * The reset is scoped to the `[data-pw-surface]` subtree (root + descendants
545
+ * + their generated `::before`/`::after` boxes) rather than emitted as a
546
+ * global `*{}` rule so it cannot leak into and reset a host page that embeds
547
+ * the surface. This makes the standard ship from the runtime itself — a host
548
+ * app, the embed bundle, and a preview tool all inherit it for free.
549
+ */
550
+ const SURFACE_BASE_CSS = '[data-pw-surface],' +
551
+ '[data-pw-surface] *,' +
552
+ '[data-pw-surface] *::before,' +
553
+ '[data-pw-surface] *::after{box-sizing:border-box}' +
554
+ // Horizontal-overflow guard. Reference pages protect themselves with
555
+ // body{overflow-x:hidden}; a surface must not rely on its host for that.
556
+ // `clip` (not `hidden`) so no scroll container is created and position:sticky
557
+ // descendants keep working.
558
+ '[data-pw-surface]{overflow-x:clip}' +
559
+ // Surface root paints the THEME ground (light/dark). Without this the
560
+ // root stays transparent and content OUTSIDE a card (a bare heading/section)
561
+ // shows the host page's own background — fine in light mode, but in dark mode
562
+ // (derived dark vars) it left light text on a white page → unreadable. Painting
563
+ // background+color from the tokens makes the whole surface follow the mode
564
+ // (the dark <style> block re-resolves --pw-color-* under [data-pw-theme]/@media).
565
+ // Scoped to the root only (not descendants) so blocks still set their own.
566
+ '[data-pw-surface]{background:var(--pw-color-background);color:var(--pw-color-text);' +
567
+ // The BODY FONT, bound on the root like the ground. Only the published
568
+ // theme.css bound it (on html,body), so a serif-bodied theme (rose) read
569
+ // serif on the published page and in the host font on the canvas and in an
570
+ // embed. Blocks inherit it; a block that sets its own font still wins.
571
+ // `inherit` keeps the host font when a theme declares none.
572
+ 'font-family:var(--pw-font-family,inherit);font-size:var(--pw-font-size-base,inherit);' +
573
+ 'line-height:var(--pw-line-height-body,inherit)}' +
574
+ // Controls take the surface font. Browsers give button, input,
575
+ // select and textarea their OWN system font instead of inheriting, so a
576
+ // themed page showed its body face everywhere except in its controls (a
577
+ // serif theme rendered sans-serif buttons). Family only: sizes and colours stay the
578
+ // block's. :where() keeps specificity at zero so a block's own font wins.
579
+ '[data-pw-surface] :where(button,input,select,textarea,optgroup){font-family:inherit}' +
580
+ // Centralised keyboard focus ring (WCAG 2.4.7/2.4.11) — every focusable element
581
+ // inside the surface gets the theme's --pw-focus-ring-* (with on-brand AA fallbacks)
582
+ // instead of each block hard-coding (or dropping) a ring. :where() = zero
583
+ // specificity so a block can still override. A tenant re-skin changes the ring in
584
+ // one place; it can never silently lose focus visibility.
585
+ '[data-pw-surface] :where(a,button,input,select,textarea,[tabindex]:not([tabindex="-1"])):focus-visible{' +
586
+ 'outline:var(--pw-focus-ring-width,2px) solid var(--pw-focus-ring-color,#4f46e5);' +
587
+ 'outline-offset:var(--pw-focus-ring-offset,2px)}' +
588
+ // Named animation keyframes shipped from the runtime so a block can drive
589
+ // motion via `block.style.animation: 'pw-pulse 1.5s ease-in-out infinite'` (or
590
+ // pw-marquee / pw-fade-in) WITHOUT each surface defining @keyframes (block.style
591
+ // can't). Names are pw-* to avoid host-page collision. Covers the soundwave-pulse
592
+ // / logo-marquee patterns that previously had no offline-capable expression.
593
+ '@keyframes pw-pulse{0%,100%{opacity:1}50%{opacity:.45}}' +
594
+ '@keyframes pw-fade-in{from{opacity:0}to{opacity:1}}' +
595
+ '@keyframes pw-marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}' +
596
+ // Batch-1 animation library — entrance/loop motion blocks can drive via
597
+ // block.style.animation without defining @keyframes.
598
+ '@keyframes pw-slide-up{from{transform:translateY(8px);opacity:0}to{transform:translateY(0);opacity:1}}' +
599
+ '@keyframes pw-slide-down{from{transform:translateY(-8px);opacity:0}to{transform:translateY(0);opacity:1}}' +
600
+ '@keyframes pw-scale-in{from{transform:scale(.96);opacity:0}to{transform:scale(1);opacity:1}}' +
601
+ '@keyframes pw-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}' +
602
+ '@keyframes pw-bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(-25%)}}' +
603
+ // Spring ENTRANCE (Dalga 1, core (d)). pw-bounce is a vertical loop and the
604
+ // theme's --pw-easing-bounce token had no entrance keyframe to ride on, so
605
+ // delivered apps reached for pw-pulse. The overshoot is the keyframe's own
606
+ // timing function: `animation: pw-spring-in .4s` springs with no easing
607
+ // named, a theme re-tunes it through the token, and the fallback is the
608
+ // token's canonical default (surface-theme.ts). Reduced-motion gated below.
609
+ '@keyframes pw-spring-in{from{transform:scale(.9);opacity:0;' +
610
+ 'animation-timing-function:var(--pw-easing-bounce,cubic-bezier(0.34, 1.56, 0.64, 1))}' +
611
+ 'to{transform:scale(1);opacity:1}}' +
612
+ // Interaction-state transitions (theme motion tokens): every interactive
613
+ // element eases its colour/elevation changes instead of snapping — the
614
+ // micro-polish half of hover/focus/pressed feedback that inline block
615
+ // styles cannot express (pseudo-states need a stylesheet). :where() keeps
616
+ // specificity at zero so any block's own transition wins; the property
617
+ // list is colour/elevation-only (no `all`) so layout-affecting properties
618
+ // never animate by accident. Durations/easing come from the theme's motion
619
+ // tokens with the old instant behaviour recoverable per-theme by setting
620
+ // them to 0ms. The reduced-motion rule below already neutralises these.
621
+ '[data-pw-surface] :where(a,button,input,select,textarea,[role="button"]):not(:disabled){' +
622
+ 'transition:background-color var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
623
+ 'color var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
624
+ 'border-color var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
625
+ 'box-shadow var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
626
+ 'opacity var(--pw-duration-fast,150ms) var(--pw-easing,ease),' +
627
+ 'filter var(--pw-duration-fast,150ms) var(--pw-easing,ease)}' +
628
+ // Honour reduced-motion: neutralise pw-* animations + transitions for users who
629
+ // opt out (a11y). Covers pseudo-elements too.
630
+ '@media (prefers-reduced-motion:reduce){[data-pw-surface] *,[data-pw-surface] *::before,[data-pw-surface] *::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}' +
631
+ // iOS safe-area insets. Host apps already set `viewport-fit=cover`, so
632
+ // env(safe-area-inset-*) is live but nothing consumed it → on notch /
633
+ // Dynamic-Island / home-indicator devices sticky navbars tuck under the notch
634
+ // and trailing footers/bottom bars sit under the home indicator. These rules
635
+ // make EVERY published surface (embed, preview, host app) inset-aware at a
636
+ // single point. The surface root takes the horizontal insets; navbar/footer
637
+ // block roots (which emit data-block-type) take the vertical ones. max(N,env)
638
+ // keeps each ≥ its normal value when no inset exists (env resolves to 0px),
639
+ // so a static mid-page navbar only ever gains pad on an actual inset device.
640
+ '[data-pw-surface]{' +
641
+ 'padding-left:max(0px,env(safe-area-inset-left));' +
642
+ 'padding-right:max(0px,env(safe-area-inset-right))}' +
643
+ '[data-pw-surface] [data-block-type="navbar"]{' +
644
+ 'padding-top:max(12px,env(safe-area-inset-top))}' +
645
+ '[data-pw-surface] [data-block-type="footer"]{' +
646
+ 'padding-bottom:max(48px,env(safe-area-inset-bottom))}';
647
+ // ============================================================
648
+ // SURFACE RENDERER COMPONENT
649
+ // ============================================================
650
+ /**
651
+ * SurfaceRenderer component
652
+ *
653
+ * Renders a surface definition to React components. surface
654
+ * definitions come from the pack-derived registry (the host's installed
655
+ * blocks and receipts) rather than hardcoded starter exports.
656
+ */
657
+ export function SurfaceRenderer({ surface, capabilities = [], theme = 'light', projectTheme, projectDarkTheme, basePath = '', onEvent, components: customComponents, className, viewport: viewportProp, gap, padding, imageAdapter, entityFacade, connections, renderBlockWrapper, interactionMode = 'live', }) {
658
+ // Use explicit viewport or auto-detect
659
+ const autoViewport = useViewport();
660
+ const viewport = viewportProp ?? autoViewport;
661
+ // `onEvent` is a pure notification — it ALWAYS flows, even in inert mode, so
662
+ // the host (editor/preview) can observe interactions and give feedback. The
663
+ // `interactionMode` flag (threaded into BlockContext below) is what gates real
664
+ // side-effects: blocks must skip navigation / data writes when `inert`.
665
+ const effectiveOnEvent = onEvent;
666
+ // Canonical bus → renderer `onEvent` bridge. The `BehaviorSurfaceProvider`
667
+ // subscribes this to the bus' global `*` wildcard, so every block-emitted
668
+ // `SurfaceEventDescriptor` (contracts vocabulary: namespaced `type` string +
669
+ // `surfaceId`/`payload`/`timestamp`) reaches the renderer's `onEvent` handler
670
+ // — i.e. `DataBoundSurfaceRenderer.handleEvent`, which dispatches a form's
671
+ // `payload.action` setState and a block's `action` prop. The handler consumes
672
+ // the legacy `SurfaceEvent` shape (`{type, blockId, payload}`), so we project
673
+ // the descriptor onto it: the canonical namespaced type maps to its legacy
674
+ // equivalent, with anything else collapsing to `'custom'` (blockId/payload pass
675
+ // through unchanged — that is all the dispatcher reads).
676
+ const busOnEvent = React.useMemo(() => {
677
+ if (!effectiveOnEvent)
678
+ return undefined;
679
+ return (event) => {
680
+ // Surface lifecycle events (`surface:load`/`surface:unload`) are bus-internal
681
+ // and were never part of the legacy `onEvent` host/dispatcher contract — only
682
+ // block interactions flow to the renderer's handler. Skip them so the host
683
+ // callback (and the DataBound dispatcher) see exactly the block-level events
684
+ // they saw under the retired `context.onEvent` path.
685
+ if (event.type.startsWith('surface:'))
686
+ return;
687
+ effectiveOnEvent({
688
+ type: mapCanonicalEventType(event.type),
689
+ blockId: event.blockId ?? '',
690
+ ...(event.payload !== undefined ? { payload: event.payload } : {}),
691
+ canonicalType: event.type,
692
+ });
693
+ };
694
+ }, [effectiveOnEvent]);
695
+ // Build capability map
696
+ const capabilityMap = React.useMemo(() => {
697
+ const map = new Map();
698
+ for (const cap of capabilities) {
699
+ map.set(cap.type, cap);
700
+ }
701
+ return map;
702
+ }, [capabilities]);
703
+ // Build block context (theme removed from BlockContext)
704
+ const context = React.useMemo(() => ({
705
+ capabilities: capabilityMap,
706
+ basePath,
707
+ onEvent: effectiveOnEvent,
708
+ imageAdapter,
709
+ viewport,
710
+ interactionMode,
711
+ }), [capabilityMap, basePath, effectiveOnEvent, imageAdapter, viewport, interactionMode]);
712
+ // Create image component override if adapter is provided
713
+ const imageComponentOverride = React.useMemo(() => {
714
+ if (!imageAdapter)
715
+ return undefined;
716
+ return { image: createImageBlockWithAdapter(imageAdapter) };
717
+ }, [imageAdapter]);
718
+ // Merge component registries (image adapter takes precedence over custom components)
719
+ // `defaultBlockComponents` removed; `customComponents`
720
+ // is now the only static seed for the registry alongside the image override.
721
+ const componentRegistry = React.useMemo(() => mergeComponentRegistries(customComponents, imageComponentOverride), [customComponents, imageComponentOverride]);
722
+ // Read entitlements from context
723
+ const { isBlockEntitled, loaded: entitlementsLoaded } = useEntitlements();
724
+ const entitlementCheck = entitlementsLoaded ? isBlockEntitled : undefined;
725
+ // Read dynamic block descriptors
726
+ const { blocks: dynamicBlockMap } = useDynamicBlocks();
727
+ // The root FLOWS; the measure lives in an INNER container.
728
+ //
729
+ // If the root carried grid + `maxWidth` + `margin:0 auto`, persistent
730
+ // navigation chrome would sit inside the centred box too (at 1536px a rail
731
+ // would start 218px in from the screen edge). The rule: chrome belongs to
732
+ // the viewport, the body to the measure.
733
+ //
734
+ // Gap/padding come from CSS variables so the `surface.theme.layout` tokens
735
+ // take effect; the variables are set on this element through `themeVars`.
736
+ const rootLayoutStyle = resolveSurfaceRootStyle(gap ?? 'var(--pw-layout-surface-gap)', 0);
737
+ const measureStyle = resolveMeasureStyle(viewport, gap ?? 'var(--pw-layout-surface-gap)', padding ?? 'var(--pw-layout-surface-padding)');
738
+ // Resolve appearance atom (theme + layout + styleVariables unified).
739
+ // Theme tokens + custom CSS vars both live inside surface.appearance.config.
740
+ const appearanceVars = React.useMemo(() => resolveAppearanceVars(surface.appearance, projectTheme), [surface.appearance, projectTheme]);
741
+ const rootStyle = React.useMemo(() => ({
742
+ ...rootLayoutStyle,
743
+ ...appearanceVars,
744
+ }), [rootLayoutStyle, appearanceVars]);
745
+ // Surface-scope key for every emitted <style> rule. The embed autoloader
746
+ // hydrates EVERY [data-pw-surface] element in a document, so a host page can
747
+ // hold several surfaces at once; scoping by the ATTRIBUTE VALUE keeps each
748
+ // surface's rules off its siblings. Undefined only when a surface has no id
749
+ // (isolated test renders), where the historical value-less selector is fine.
750
+ const scopeId = surface.id || undefined;
751
+ // Per-breakpoint visibility (hideOn/showOn). Media queries can't be
752
+ // expressed inline, so the runtime emits scoped CSS rules — keyed by each
753
+ // block's `data-block-id` — into the surface stylesheet (alongside the
754
+ // box-sizing reset). Empty string when no block uses hideOn/showOn → existing
755
+ // surfaces emit zero extra bytes.
756
+ const visibilityCss = React.useMemo(() => collectVisibilityCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
757
+ // Interaction-state styling (block.states). Pseudo-classes (:hover/:active/
758
+ // :focus-visible) can't be expressed inline, so the runtime emits scoped CSS
759
+ // rules — keyed by each block's `data-block-id` — alongside the visibility CSS.
760
+ // Empty string when no block uses `states` → existing surfaces emit zero bytes.
761
+ const statesCss = React.useMemo(() => collectStatesCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
762
+ // Conditional-state styling (block.conditionalStates) — aria/data/disabled/
763
+ // checked/:has variants. Attribute selectors and these pseudo-classes can't be
764
+ // expressed inline, so the runtime emits scoped `[data-block-id]<selector>`
765
+ // rules (with `!important` to beat component inline defaults). Empty string
766
+ // when no block uses `conditionalStates` → existing surfaces emit zero bytes.
767
+ const conditionalStatesCss = React.useMemo(() => collectConditionalStatesCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
768
+ // Per-prop responsive styles (block.style values that vary by breakpoint).
769
+ // The base (xs) value is applied inline by the renderer (flattenResponsiveStyle);
770
+ // the larger breakpoints are emitted here as scoped min-width media-query rules
771
+ // keyed by each block's `data-block-id`. Empty string when no block uses a
772
+ // responsive style value → existing surfaces emit zero bytes.
773
+ const responsiveStyleCss = React.useMemo(() => collectResponsiveStyleCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
774
+ // Group-hover styling (block.groupRoot + descendant block.groupStates.hover) —
775
+ // a parent marked groupRoot, on :hover, restyles descendants declaring
776
+ // groupStates. The ancestor `:hover` + descendant relationship can't be
777
+ // expressed inline, so the runtime emits scoped
778
+ // `[data-block-id="<root>"]:hover [data-block-id="<desc>"]{…}` rules keyed by
779
+ // both block ids. Empty string when no block uses groupStates under a
780
+ // groupRoot → existing surfaces emit zero bytes.
781
+ const groupStatesCss = React.useMemo(() => collectGroupStatesCss(surface.blocks, scopeId), [surface.blocks, scopeId]);
782
+ // Dark theme variant (appearance.config.darkTheme). Light vars stay inline on
783
+ // the root; the dark vars override under BOTH `[data-pw-theme="dark"]` (manual
784
+ // force) AND `@media (prefers-color-scheme:dark)` (auto). Empty string when no
785
+ // darkTheme is declared.
786
+ const darkThemeCss = React.useMemo(() => {
787
+ // Same `projectTheme` middle layer the light vars above resolve with —
788
+ // otherwise a project-themed page is on-brand in light and platform-default
789
+ // in dark.
790
+ const darkVars = resolveAppearanceDarkVars(surface.appearance, projectTheme, projectDarkTheme);
791
+ // `appearance.config.mode.policy` decides whether the dark rule
792
+ // exists at all (light), always applies (dark) or follows the device.
793
+ return buildDarkThemeCss(darkVars, scopeId, resolveAppearanceMode(surface.appearance?.config).policy);
794
+ }, [surface.appearance, projectTheme, projectDarkTheme, scopeId]);
795
+ // Surface keyframe registry (appearance.config.keyframes) — arbitrary named
796
+ // @keyframes a block can reference via `block.style.animation: 'pw-kf-<name> …'`
797
+ // (each declared name is prefixed `pw-kf-`). Empty string when no registry is
798
+ // declared. The prefers-reduced-motion gate in SURFACE_BASE_CSS neutralises
799
+ // these for users who opt out (it matches by animation, not keyframe name).
800
+ const keyframesCss = React.useMemo(() => buildKeyframesCss(resolveAppearanceKeyframes(surface.appearance)), [surface.appearance]);
801
+ // extract form→entity bridge configs from surface capabilities
802
+ const entityFormBindings = React.useMemo(() => {
803
+ // NO LONGER GATED ON THE ENTITY FACADE. A form now writes to a SOURCE, and
804
+ // a rest-backed source has no facade behind it — requiring one here meant
805
+ // a form pointed at anyone else's endpoint silently never attached.
806
+ if (!surface.capabilities?.length)
807
+ return undefined;
808
+ const entityCap = surface.capabilities.find((c) => isCapability(c, 'platform/entities'));
809
+ const formBindings = entityCap?.config?.formBindings;
810
+ if (!Array.isArray(formBindings) || formBindings.length === 0)
811
+ return undefined;
812
+ return formBindings;
813
+ }, [surface.capabilities]);
814
+ /**
815
+ * The read sources, so a form binding's `sourceId` can be resolved.
816
+ *
817
+ * The same declaration the persistence capability already carries — a form
818
+ * writes to something the page also knows how to read, which is what makes
819
+ * "refresh after write" meaningful.
820
+ */
821
+ const formWriteSources = React.useMemo(() => {
822
+ const cap = surface.capabilities?.find((c) => isCapability(c, 'platform/persistence'));
823
+ const sources = cap?.config?.dataSources;
824
+ return Array.isArray(sources) && sources.length > 0
825
+ ? sources
826
+ : undefined;
827
+ }, [surface.capabilities]);
828
+ // Render blocks — in scope groups, keeping source order.
829
+ //
830
+ // `container` groups are wrapped in the measured container; `viewport`
831
+ // groups become DIRECT children of the root. The escape is PLACEMENT, not a
832
+ // CSS trick: `width:100vw` counts the scrollbar too and would produce ~15px
833
+ // of horizontal overflow on every page with a vertical scrollbar.
834
+ const renderOne = (block) => renderBlock({
835
+ block,
836
+ context,
837
+ components: componentRegistry,
838
+ }, undefined, entitlementCheck, dynamicBlockMap, renderBlockWrapper);
839
+ const renderedBlocks = chunkByWidthScope(surface.blocks, viewport).map((chunk, i) => chunk.scope === 'viewport' ? (_jsx(React.Fragment, { children: chunk.blocks.map(renderOne) }, `pw-vp-${i}`)) : (_jsx("div", { "data-pw-measure": "", style: measureStyle, children: chunk.blocks.map(renderOne) }, `pw-ct-${i}`)));
840
+ return (
841
+ // SurfaceConnectionsContext carries the connections
842
+ // context (world connections + engine registry) to blocks on the
843
+ // engine-bridge path. null (host didn't opt in) = today's behavior.
844
+ _jsx(SurfaceConnectionsContext.Provider, { value: connections ?? null, children: _jsx(EntityFacadeContext.Provider, { value: entityFacade ?? null, children: _jsx(BehaviorSurfaceProvider, { surfaceId: surface.id ?? '', ...(entityFormBindings ? { entityFormBindings } : {}), ...(formWriteSources ? { dataSources: formWriteSources } : {}), ...(busOnEvent ? { onEvent: busOnEvent } : {}), blocks: surface.blocks, children: _jsx(ShellSelectionProvider, { syncUrl: interactionMode !== 'inert', children: _jsxs("div", { className: className, style: rootStyle, "data-pw-surface": scopeId ?? '', "data-surface-renderer": true, "data-theme": theme, "data-viewport": viewport, children: [_jsx("style", { "data-pw-base-css": true, children: SURFACE_BASE_CSS }), visibilityCss ? (_jsx("style", { "data-pw-visibility-css": true, children: visibilityCss })) : null, statesCss ? _jsx("style", { "data-pw-states-css": true, children: statesCss }) : null, conditionalStatesCss ? (_jsx("style", { "data-pw-conditional-states-css": true, children: conditionalStatesCss })) : null, responsiveStyleCss ? (_jsx("style", { "data-pw-responsive-style-css": true, children: responsiveStyleCss })) : null, groupStatesCss ? (_jsx("style", { "data-pw-group-states-css": true, children: groupStatesCss })) : null, darkThemeCss ? _jsx("style", { "data-pw-dark-theme-css": true, children: darkThemeCss }) : null, keyframesCss ? _jsx("style", { "data-pw-keyframes-css": true, children: keyframesCss }) : null, renderedBlocks] }) }) }) }) }));
845
+ }
846
+ // ============================================================
847
+ // BLOCK RENDERER (STANDALONE)
848
+ // ============================================================
849
+ /**
850
+ * Standalone block renderer
851
+ *
852
+ * Renders a single block without a surface wrapper.
853
+ */
854
+ export function BlockRenderer({ block, capabilities = [], theme: _theme = 'light', basePath = '', onEvent, components: customComponents, imageAdapter, }) {
855
+ // Build capability map
856
+ const capabilityMap = React.useMemo(() => {
857
+ const map = new Map();
858
+ for (const cap of capabilities) {
859
+ map.set(cap.type, cap);
860
+ }
861
+ return map;
862
+ }, [capabilities]);
863
+ // Build block context (theme removed from BlockContext)
864
+ const context = React.useMemo(() => ({
865
+ capabilities: capabilityMap,
866
+ basePath,
867
+ onEvent,
868
+ imageAdapter,
869
+ }), [capabilityMap, basePath, onEvent, imageAdapter]);
870
+ // Create image component override if adapter is provided
871
+ const imageComponentOverride = React.useMemo(() => {
872
+ if (!imageAdapter)
873
+ return undefined;
874
+ return { image: createImageBlockWithAdapter(imageAdapter) };
875
+ }, [imageAdapter]);
876
+ // Merge component registries
877
+ // `defaultBlockComponents` retired; `customComponents`
878
+ // is the only static seed (defaulted to {}) alongside the image override.
879
+ const componentRegistry = React.useMemo(() => mergeComponentRegistries(customComponents, imageComponentOverride), [customComponents, imageComponentOverride]);
880
+ return renderBlock({ block, context, components: componentRegistry });
881
+ }
882
+ // ============================================================
883
+ // UTILITY FUNCTIONS
884
+ // ============================================================
885
+ /**
886
+ * Get all block IDs from a surface
887
+ */
888
+ export function getBlockIds(blocks) {
889
+ const ids = [];
890
+ function collectIds(block) {
891
+ ids.push(block.id);
892
+ if (block.children) {
893
+ for (const child of block.children) {
894
+ collectIds(child);
895
+ }
896
+ }
897
+ }
898
+ for (const block of blocks) {
899
+ collectIds(block);
900
+ }
901
+ return ids;
902
+ }
903
+ /**
904
+ * Find a block by ID in a surface
905
+ */
906
+ export function findBlockById(blocks, id) {
907
+ for (const block of blocks) {
908
+ if (block.id === id) {
909
+ return block;
910
+ }
911
+ if (block.children) {
912
+ const found = findBlockById(block.children, id);
913
+ if (found) {
914
+ return found;
915
+ }
916
+ }
917
+ }
918
+ return undefined;
919
+ }
920
+ /**
921
+ * Update a block by ID in a surface
922
+ */
923
+ export function updateBlockById(blocks, id, updates) {
924
+ return blocks.map((block) => {
925
+ if (block.id === id) {
926
+ return { ...block, ...updates };
927
+ }
928
+ if (block.children) {
929
+ return {
930
+ ...block,
931
+ children: updateBlockById(block.children, id, updates),
932
+ };
933
+ }
934
+ return block;
935
+ });
936
+ }
937
+ //# sourceMappingURL=renderer.js.map