@number10/jsx-core 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/LICENSE +667 -0
  2. package/README.md +38 -0
  3. package/dist/animation/animated-signal.cjs +19 -0
  4. package/dist/animation/animated-signal.cjs.map +1 -0
  5. package/dist/animation/animated-signal.d.ts +35 -0
  6. package/dist/animation/animated-signal.d.ts.map +1 -0
  7. package/dist/animation/animated-signal.js +19 -0
  8. package/dist/animation/animated-signal.js.map +1 -0
  9. package/dist/animation/create-spring.cjs +80 -0
  10. package/dist/animation/create-spring.cjs.map +1 -0
  11. package/dist/animation/create-spring.d.ts +56 -0
  12. package/dist/animation/create-spring.d.ts.map +1 -0
  13. package/dist/animation/create-spring.js +80 -0
  14. package/dist/animation/create-spring.js.map +1 -0
  15. package/dist/animation/easing.cjs +76 -0
  16. package/dist/animation/easing.cjs.map +1 -0
  17. package/dist/animation/easing.d.ts +45 -0
  18. package/dist/animation/easing.d.ts.map +1 -0
  19. package/dist/animation/easing.js +76 -0
  20. package/dist/animation/easing.js.map +1 -0
  21. package/dist/animation/effect-definitions.cjs +441 -0
  22. package/dist/animation/effect-definitions.cjs.map +1 -0
  23. package/dist/animation/effect-definitions.d.ts +60 -0
  24. package/dist/animation/effect-definitions.d.ts.map +1 -0
  25. package/dist/animation/effect-definitions.js +441 -0
  26. package/dist/animation/effect-definitions.js.map +1 -0
  27. package/dist/animation/effect-engine.cjs +189 -0
  28. package/dist/animation/effect-engine.cjs.map +1 -0
  29. package/dist/animation/effect-engine.d.ts +70 -0
  30. package/dist/animation/effect-engine.d.ts.map +1 -0
  31. package/dist/animation/effect-engine.js +189 -0
  32. package/dist/animation/effect-engine.js.map +1 -0
  33. package/dist/animation/index.d.ts +24 -0
  34. package/dist/animation/index.d.ts.map +1 -0
  35. package/dist/animation/spring-physics.cjs +94 -0
  36. package/dist/animation/spring-physics.cjs.map +1 -0
  37. package/dist/animation/spring-physics.d.ts +145 -0
  38. package/dist/animation/spring-physics.d.ts.map +1 -0
  39. package/dist/animation/spring-physics.js +94 -0
  40. package/dist/animation/spring-physics.js.map +1 -0
  41. package/dist/animation/tween.cjs +67 -0
  42. package/dist/animation/tween.cjs.map +1 -0
  43. package/dist/animation/tween.d.ts +60 -0
  44. package/dist/animation/tween.d.ts.map +1 -0
  45. package/dist/animation/tween.js +67 -0
  46. package/dist/animation/tween.js.map +1 -0
  47. package/dist/animation/types.d.ts +31 -0
  48. package/dist/animation/types.d.ts.map +1 -0
  49. package/dist/animation/useSpring.cjs +152 -0
  50. package/dist/animation/useSpring.cjs.map +1 -0
  51. package/dist/animation/useSpring.d.ts +55 -0
  52. package/dist/animation/useSpring.d.ts.map +1 -0
  53. package/dist/animation/useSpring.js +149 -0
  54. package/dist/animation/useSpring.js.map +1 -0
  55. package/dist/animation/useTween.cjs +208 -0
  56. package/dist/animation/useTween.cjs.map +1 -0
  57. package/dist/animation/useTween.d.ts +116 -0
  58. package/dist/animation/useTween.d.ts.map +1 -0
  59. package/dist/animation/useTween.js +208 -0
  60. package/dist/animation/useTween.js.map +1 -0
  61. package/dist/border-geometry.cjs +447 -0
  62. package/dist/border-geometry.cjs.map +1 -0
  63. package/dist/border-geometry.d.ts +114 -0
  64. package/dist/border-geometry.d.ts.map +1 -0
  65. package/dist/border-geometry.js +447 -0
  66. package/dist/border-geometry.js.map +1 -0
  67. package/dist/colors/color-presets.cjs +236 -0
  68. package/dist/colors/color-presets.cjs.map +1 -0
  69. package/dist/colors/color-presets.d.ts +89 -0
  70. package/dist/colors/color-presets.d.ts.map +1 -0
  71. package/dist/colors/color-presets.js +236 -0
  72. package/dist/colors/color-presets.js.map +1 -0
  73. package/dist/colors/color-theme-helpers.cjs +32 -0
  74. package/dist/colors/color-theme-helpers.cjs.map +1 -0
  75. package/dist/colors/color-theme-helpers.d.ts +65 -0
  76. package/dist/colors/color-theme-helpers.d.ts.map +1 -0
  77. package/dist/colors/color-theme-helpers.js +32 -0
  78. package/dist/colors/color-theme-helpers.js.map +1 -0
  79. package/dist/colors/color-types.cjs +38 -0
  80. package/dist/colors/color-types.cjs.map +1 -0
  81. package/dist/colors/color-types.d.ts +112 -0
  82. package/dist/colors/color-types.d.ts.map +1 -0
  83. package/dist/colors/color-types.js +38 -0
  84. package/dist/colors/color-types.js.map +1 -0
  85. package/dist/colors/color-utils.cjs +197 -0
  86. package/dist/colors/color-utils.cjs.map +1 -0
  87. package/dist/colors/color-utils.d.ts +216 -0
  88. package/dist/colors/color-utils.d.ts.map +1 -0
  89. package/dist/colors/color-utils.js +197 -0
  90. package/dist/colors/color-utils.js.map +1 -0
  91. package/dist/colors/contrast-contracts.cjs +265 -0
  92. package/dist/colors/contrast-contracts.cjs.map +1 -0
  93. package/dist/colors/contrast-contracts.d.ts +289 -0
  94. package/dist/colors/contrast-contracts.d.ts.map +1 -0
  95. package/dist/colors/contrast-contracts.js +265 -0
  96. package/dist/colors/contrast-contracts.js.map +1 -0
  97. package/dist/colors/index.cjs +55 -0
  98. package/dist/colors/index.cjs.map +1 -0
  99. package/dist/colors/index.d.ts +16 -0
  100. package/dist/colors/index.d.ts.map +1 -0
  101. package/dist/colors/index.js +55 -0
  102. package/dist/colors/index.js.map +1 -0
  103. package/dist/core-props.cjs +66 -0
  104. package/dist/core-props.cjs.map +1 -0
  105. package/dist/core-props.d.ts +395 -0
  106. package/dist/core-props.d.ts.map +1 -0
  107. package/dist/core-props.js +66 -0
  108. package/dist/core-props.js.map +1 -0
  109. package/dist/design-tokens/design-token-presets.cjs +91 -0
  110. package/dist/design-tokens/design-token-presets.cjs.map +1 -0
  111. package/dist/design-tokens/design-token-presets.d.ts +34 -0
  112. package/dist/design-tokens/design-token-presets.d.ts.map +1 -0
  113. package/dist/design-tokens/design-token-presets.js +91 -0
  114. package/dist/design-tokens/design-token-presets.js.map +1 -0
  115. package/dist/design-tokens/design-token-types.d.ts +97 -0
  116. package/dist/design-tokens/design-token-types.d.ts.map +1 -0
  117. package/dist/design-tokens/index.cjs +10 -0
  118. package/dist/design-tokens/index.cjs.map +1 -0
  119. package/dist/design-tokens/index.d.ts +9 -0
  120. package/dist/design-tokens/index.d.ts.map +1 -0
  121. package/dist/design-tokens/index.js +10 -0
  122. package/dist/design-tokens/index.js.map +1 -0
  123. package/dist/dev-config.cjs +296 -0
  124. package/dist/dev-config.cjs.map +1 -0
  125. package/dist/dev-config.d.ts +238 -0
  126. package/dist/dev-config.d.ts.map +1 -0
  127. package/dist/dev-config.js +296 -0
  128. package/dist/dev-config.js.map +1 -0
  129. package/dist/effects.cjs +69 -0
  130. package/dist/effects.cjs.map +1 -0
  131. package/dist/effects.d.ts +152 -0
  132. package/dist/effects.d.ts.map +1 -0
  133. package/dist/effects.js +69 -0
  134. package/dist/effects.js.map +1 -0
  135. package/dist/global-registry.cjs +25 -0
  136. package/dist/global-registry.cjs.map +1 -0
  137. package/dist/global-registry.d.ts +19 -0
  138. package/dist/global-registry.d.ts.map +1 -0
  139. package/dist/global-registry.js +25 -0
  140. package/dist/global-registry.js.map +1 -0
  141. package/dist/hooks.cjs +378 -0
  142. package/dist/hooks.cjs.map +1 -0
  143. package/dist/hooks.d.ts +322 -0
  144. package/dist/hooks.d.ts.map +1 -0
  145. package/dist/hooks.js +378 -0
  146. package/dist/hooks.js.map +1 -0
  147. package/dist/host-adapter.d.ts +414 -0
  148. package/dist/host-adapter.d.ts.map +1 -0
  149. package/dist/index.cjs +229 -0
  150. package/dist/index.cjs.map +1 -0
  151. package/dist/index.d.ts +32 -0
  152. package/dist/index.d.ts.map +1 -0
  153. package/dist/index.js +226 -0
  154. package/dist/index.js.map +1 -0
  155. package/dist/jsx-dev-runtime.cjs +10 -0
  156. package/dist/jsx-dev-runtime.cjs.map +1 -0
  157. package/dist/jsx-dev-runtime.d.ts +5 -0
  158. package/dist/jsx-dev-runtime.d.ts.map +1 -0
  159. package/dist/jsx-dev-runtime.js +11 -0
  160. package/dist/jsx-dev-runtime.js.map +1 -0
  161. package/dist/jsx-runtime.cjs +44 -0
  162. package/dist/jsx-runtime.cjs.map +1 -0
  163. package/dist/jsx-runtime.d.ts +24 -0
  164. package/dist/jsx-runtime.d.ts.map +1 -0
  165. package/dist/jsx-runtime.js +44 -0
  166. package/dist/jsx-runtime.js.map +1 -0
  167. package/dist/jsx-types.d.ts +25 -0
  168. package/dist/jsx-types.d.ts.map +1 -0
  169. package/dist/layout/appliers/container-applier.cjs +20 -0
  170. package/dist/layout/appliers/container-applier.cjs.map +1 -0
  171. package/dist/layout/appliers/container-applier.d.ts +9 -0
  172. package/dist/layout/appliers/container-applier.d.ts.map +1 -0
  173. package/dist/layout/appliers/container-applier.js +20 -0
  174. package/dist/layout/appliers/container-applier.js.map +1 -0
  175. package/dist/layout/appliers/position-applier.cjs +31 -0
  176. package/dist/layout/appliers/position-applier.cjs.map +1 -0
  177. package/dist/layout/appliers/position-applier.d.ts +8 -0
  178. package/dist/layout/appliers/position-applier.d.ts.map +1 -0
  179. package/dist/layout/appliers/position-applier.js +31 -0
  180. package/dist/layout/appliers/position-applier.js.map +1 -0
  181. package/dist/layout/layout-engine-test-utils.d.ts +26 -0
  182. package/dist/layout/layout-engine-test-utils.d.ts.map +1 -0
  183. package/dist/layout/layout-engine.cjs +628 -0
  184. package/dist/layout/layout-engine.cjs.map +1 -0
  185. package/dist/layout/layout-engine.d.ts +140 -0
  186. package/dist/layout/layout-engine.d.ts.map +1 -0
  187. package/dist/layout/layout-engine.js +628 -0
  188. package/dist/layout/layout-engine.js.map +1 -0
  189. package/dist/layout/strategies/base-strategy.cjs +31 -0
  190. package/dist/layout/strategies/base-strategy.cjs.map +1 -0
  191. package/dist/layout/strategies/base-strategy.d.ts +59 -0
  192. package/dist/layout/strategies/base-strategy.d.ts.map +1 -0
  193. package/dist/layout/strategies/base-strategy.js +31 -0
  194. package/dist/layout/strategies/base-strategy.js.map +1 -0
  195. package/dist/layout/strategies/column-layout.cjs +145 -0
  196. package/dist/layout/strategies/column-layout.cjs.map +1 -0
  197. package/dist/layout/strategies/column-layout.d.ts +38 -0
  198. package/dist/layout/strategies/column-layout.d.ts.map +1 -0
  199. package/dist/layout/strategies/column-layout.js +145 -0
  200. package/dist/layout/strategies/column-layout.js.map +1 -0
  201. package/dist/layout/strategies/row-layout.cjs +145 -0
  202. package/dist/layout/strategies/row-layout.cjs.map +1 -0
  203. package/dist/layout/strategies/row-layout.d.ts +38 -0
  204. package/dist/layout/strategies/row-layout.d.ts.map +1 -0
  205. package/dist/layout/strategies/row-layout.js +145 -0
  206. package/dist/layout/strategies/row-layout.js.map +1 -0
  207. package/dist/layout/strategies/stack-layout.cjs +94 -0
  208. package/dist/layout/strategies/stack-layout.cjs.map +1 -0
  209. package/dist/layout/strategies/stack-layout.d.ts +30 -0
  210. package/dist/layout/strategies/stack-layout.d.ts.map +1 -0
  211. package/dist/layout/strategies/stack-layout.js +94 -0
  212. package/dist/layout/strategies/stack-layout.js.map +1 -0
  213. package/dist/layout/types.d.ts +134 -0
  214. package/dist/layout/types.d.ts.map +1 -0
  215. package/dist/layout/utils/child-utils.cjs +129 -0
  216. package/dist/layout/utils/child-utils.cjs.map +1 -0
  217. package/dist/layout/utils/child-utils.d.ts +54 -0
  218. package/dist/layout/utils/child-utils.d.ts.map +1 -0
  219. package/dist/layout/utils/child-utils.js +129 -0
  220. package/dist/layout/utils/child-utils.js.map +1 -0
  221. package/dist/layout/utils/dimension-calculator.cjs +109 -0
  222. package/dist/layout/utils/dimension-calculator.cjs.map +1 -0
  223. package/dist/layout/utils/dimension-calculator.d.ts +59 -0
  224. package/dist/layout/utils/dimension-calculator.d.ts.map +1 -0
  225. package/dist/layout/utils/dimension-calculator.js +109 -0
  226. package/dist/layout/utils/dimension-calculator.js.map +1 -0
  227. package/dist/layout/utils/flex-distributor.cjs +173 -0
  228. package/dist/layout/utils/flex-distributor.cjs.map +1 -0
  229. package/dist/layout/utils/flex-distributor.d.ts +18 -0
  230. package/dist/layout/utils/flex-distributor.d.ts.map +1 -0
  231. package/dist/layout/utils/flex-distributor.js +173 -0
  232. package/dist/layout/utils/flex-distributor.js.map +1 -0
  233. package/dist/layout/utils/size-resolver.cjs +280 -0
  234. package/dist/layout/utils/size-resolver.cjs.map +1 -0
  235. package/dist/layout/utils/size-resolver.d.ts +84 -0
  236. package/dist/layout/utils/size-resolver.d.ts.map +1 -0
  237. package/dist/layout/utils/size-resolver.js +280 -0
  238. package/dist/layout/utils/size-resolver.js.map +1 -0
  239. package/dist/layout/utils/spacing-calculator.cjs +50 -0
  240. package/dist/layout/utils/spacing-calculator.cjs.map +1 -0
  241. package/dist/layout/utils/spacing-calculator.d.ts +27 -0
  242. package/dist/layout/utils/spacing-calculator.d.ts.map +1 -0
  243. package/dist/layout/utils/spacing-calculator.js +50 -0
  244. package/dist/layout/utils/spacing-calculator.js.map +1 -0
  245. package/dist/memo.cjs +18 -0
  246. package/dist/memo.cjs.map +1 -0
  247. package/dist/memo.d.ts +29 -0
  248. package/dist/memo.d.ts.map +1 -0
  249. package/dist/memo.js +18 -0
  250. package/dist/memo.js.map +1 -0
  251. package/dist/nineslice-contract.cjs +60 -0
  252. package/dist/nineslice-contract.cjs.map +1 -0
  253. package/dist/nineslice-contract.d.ts +107 -0
  254. package/dist/nineslice-contract.d.ts.map +1 -0
  255. package/dist/nineslice-contract.js +60 -0
  256. package/dist/nineslice-contract.js.map +1 -0
  257. package/dist/placeholder.cjs +12 -0
  258. package/dist/placeholder.cjs.map +1 -0
  259. package/dist/placeholder.d.ts +44 -0
  260. package/dist/placeholder.d.ts.map +1 -0
  261. package/dist/placeholder.js +12 -0
  262. package/dist/placeholder.js.map +1 -0
  263. package/dist/portal.cjs +89 -0
  264. package/dist/portal.cjs.map +1 -0
  265. package/dist/portal.d.ts +62 -0
  266. package/dist/portal.d.ts.map +1 -0
  267. package/dist/portal.js +89 -0
  268. package/dist/portal.js.map +1 -0
  269. package/dist/primitive-contracts.cjs +12 -0
  270. package/dist/primitive-contracts.cjs.map +1 -0
  271. package/dist/primitive-contracts.d.ts +186 -0
  272. package/dist/primitive-contracts.d.ts.map +1 -0
  273. package/dist/primitive-contracts.js +12 -0
  274. package/dist/primitive-contracts.js.map +1 -0
  275. package/dist/theme.cjs +332 -0
  276. package/dist/theme.cjs.map +1 -0
  277. package/dist/theme.d.ts +204 -0
  278. package/dist/theme.d.ts.map +1 -0
  279. package/dist/theme.js +332 -0
  280. package/dist/theme.js.map +1 -0
  281. package/dist/types.d.ts +86 -0
  282. package/dist/types.d.ts.map +1 -0
  283. package/dist/vdom/create-runtime.cjs +79 -0
  284. package/dist/vdom/create-runtime.cjs.map +1 -0
  285. package/dist/vdom/create-runtime.d.ts +7 -0
  286. package/dist/vdom/create-runtime.d.ts.map +1 -0
  287. package/dist/vdom/create-runtime.js +79 -0
  288. package/dist/vdom/create-runtime.js.map +1 -0
  289. package/dist/vdom/index.cjs +8 -0
  290. package/dist/vdom/index.cjs.map +1 -0
  291. package/dist/vdom/index.d.ts +4 -0
  292. package/dist/vdom/index.d.ts.map +1 -0
  293. package/dist/vdom/index.js +8 -0
  294. package/dist/vdom/index.js.map +1 -0
  295. package/dist/vdom/mount.cjs +161 -0
  296. package/dist/vdom/mount.cjs.map +1 -0
  297. package/dist/vdom/mount.d.ts +6 -0
  298. package/dist/vdom/mount.d.ts.map +1 -0
  299. package/dist/vdom/mount.js +161 -0
  300. package/dist/vdom/mount.js.map +1 -0
  301. package/dist/vdom/patch.cjs +163 -0
  302. package/dist/vdom/patch.cjs.map +1 -0
  303. package/dist/vdom/patch.d.ts +3 -0
  304. package/dist/vdom/patch.d.ts.map +1 -0
  305. package/dist/vdom/patch.js +163 -0
  306. package/dist/vdom/patch.js.map +1 -0
  307. package/dist/vdom/registry.cjs +85 -0
  308. package/dist/vdom/registry.cjs.map +1 -0
  309. package/dist/vdom/registry.d.ts +28 -0
  310. package/dist/vdom/registry.d.ts.map +1 -0
  311. package/dist/vdom/registry.js +85 -0
  312. package/dist/vdom/registry.js.map +1 -0
  313. package/dist/vdom/runtime-helpers.cjs +23 -0
  314. package/dist/vdom/runtime-helpers.cjs.map +1 -0
  315. package/dist/vdom/runtime-helpers.d.ts +8 -0
  316. package/dist/vdom/runtime-helpers.d.ts.map +1 -0
  317. package/dist/vdom/runtime-helpers.js +23 -0
  318. package/dist/vdom/runtime-helpers.js.map +1 -0
  319. package/dist/vdom/surface.cjs +107 -0
  320. package/dist/vdom/surface.cjs.map +1 -0
  321. package/dist/vdom/surface.d.ts +9 -0
  322. package/dist/vdom/surface.d.ts.map +1 -0
  323. package/dist/vdom/surface.js +107 -0
  324. package/dist/vdom/surface.js.map +1 -0
  325. package/dist/vdom/types.d.ts +104 -0
  326. package/dist/vdom/types.d.ts.map +1 -0
  327. package/dist/vdom/utils.cjs +146 -0
  328. package/dist/vdom/utils.cjs.map +1 -0
  329. package/dist/vdom/utils.d.ts +24 -0
  330. package/dist/vdom/utils.d.ts.map +1 -0
  331. package/dist/vdom/utils.js +146 -0
  332. package/dist/vdom/utils.js.map +1 -0
  333. package/dist/vdom.d.ts +4 -0
  334. package/dist/vdom.d.ts.map +1 -0
  335. package/dist/vector-draw-context.d.ts +63 -0
  336. package/dist/vector-draw-context.d.ts.map +1 -0
  337. package/package.json +161 -0
  338. package/vdom/index.cjs +1 -0
  339. package/vdom/index.d.ts +1 -0
  340. package/vdom/index.js +1 -0
@@ -0,0 +1,107 @@
1
+ import { SizeValue, TransformProps } from './core-props';
2
+ import { PropsDefaultExtension } from './types';
3
+ /** A rectangle in the source texture (texture-pixel coordinates) */
4
+ export interface SliceRect {
5
+ readonly x: number;
6
+ readonly y: number;
7
+ readonly w: number;
8
+ readonly h: number;
9
+ }
10
+ /**
11
+ * Portable props for the native NineSlice primitive.
12
+ *
13
+ * The portable NineSlice component passes texture identity and slice
14
+ * dimensions. The renderer resolves texture frame geometry, computes
15
+ * the 9 source regions, and derives target-space edge arrays from the
16
+ * resolved layout size — snapping once per axis to avoid sub-pixel seams.
17
+ */
18
+ export interface NineSlicePrimitiveProps extends TransformProps {
19
+ /** Texture key */
20
+ texture: string;
21
+ /** Optional atlas frame name */
22
+ frame?: string | undefined;
23
+ /** Optional tint colour */
24
+ tint?: number | undefined;
25
+ /** Target display width (resolved by the layout system) */
26
+ width: SizeValue;
27
+ /** Target display height (resolved by the layout system) */
28
+ height: SizeValue;
29
+ /** Width of the left slice in source texture pixels */
30
+ leftWidth: number;
31
+ /** Width of the right slice in source texture pixels */
32
+ rightWidth: number;
33
+ /** Height of the top slice in source texture pixels (0 = 3-slice mode) */
34
+ topHeight: number;
35
+ /** Height of the bottom slice in source texture pixels (0 = 3-slice mode) */
36
+ bottomHeight: number;
37
+ /**
38
+ * Whether image smoothing is enabled when scaling segments.
39
+ * Default: false (prefer crisp rendering for UI textures).
40
+ */
41
+ imageSmoothingEnabled?: boolean | undefined;
42
+ /**
43
+ * When true, the element is rendered but excluded from layout calculations.
44
+ * Used for decorative background layers inside stack containers.
45
+ */
46
+ headless?: boolean | undefined;
47
+ }
48
+ /**
49
+ * Complete NineSlice props including JSX extension props (ref, key).
50
+ */
51
+ export interface NineSlicePrimitivePropsComplete<TNode = unknown> extends NineSlicePrimitiveProps, PropsDefaultExtension<TNode> {
52
+ }
53
+ /**
54
+ * Compute the 9 source slice rectangles from a texture frame and slice widths.
55
+ *
56
+ * @param frameW - Source texture / frame width in pixels
57
+ * @param frameH - Source texture / frame height in pixels
58
+ * @param left - Left slice width
59
+ * @param right - Right slice width
60
+ * @param top - Top slice height (0 for 3-slice mode)
61
+ * @param bottom - Bottom slice height (0 for 3-slice mode)
62
+ * @returns 9-element tuple of source slice rects
63
+ */
64
+ export declare function computeSourceSlices(frameW: number, frameH: number, left: number, right: number, top: number, bottom: number): readonly [
65
+ SliceRect,
66
+ SliceRect,
67
+ SliceRect,
68
+ SliceRect,
69
+ SliceRect,
70
+ SliceRect,
71
+ SliceRect,
72
+ SliceRect,
73
+ SliceRect
74
+ ];
75
+ /**
76
+ * Compute 4 shared X edge positions for the target NineSlice.
77
+ *
78
+ * @param x - Left edge of the target area
79
+ * @param width - Total target width
80
+ * @param leftWidth - Left slice width (in target pixels)
81
+ * @param rightWidth - Right slice width (in target pixels)
82
+ * @returns [left, left+leftWidth, right-rightWidth, right]
83
+ */
84
+ export declare function computeTargetXs(x: number, width: number, leftWidth: number, rightWidth: number): readonly [number, number, number, number];
85
+ /**
86
+ * Compute 4 shared Y edge positions for the target NineSlice.
87
+ *
88
+ * @param y - Top edge of the target area
89
+ * @param height - Total target height
90
+ * @param topHeight - Top slice height (in target pixels)
91
+ * @param bottomHeight - Bottom slice height (in target pixels)
92
+ * @returns [top, top+topHeight, bottom-bottomHeight, bottom]
93
+ */
94
+ export declare function computeTargetYs(y: number, height: number, topHeight: number, bottomHeight: number): readonly [number, number, number, number];
95
+ /**
96
+ * Derive all 9 target rectangles from shared edge arrays.
97
+ *
98
+ * Since adjacent segments share edges, computing target rects this way
99
+ * guarantees zero overlap and zero gaps — provided the edge arrays have
100
+ * been snapped to the physical pixel grid by the renderer backend.
101
+ *
102
+ * @param xs - 4 shared X edges (must be monotonically non-decreasing)
103
+ * @param ys - 4 shared Y edges (must be monotonically non-decreasing)
104
+ * @returns 9 target rectangles with {x, y, w, h}
105
+ */
106
+ export declare function deriveTargetRects(xs: readonly [number, number, number, number], ys: readonly [number, number, number, number]): readonly SliceRect[];
107
+ //# sourceMappingURL=nineslice-contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nineslice-contract.d.ts","sourceRoot":"","sources":["../src/nineslice-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAA;AAEpD,oEAAoE;AACpE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAwB,SAAQ,cAAc;IAC7D,kBAAkB;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACzB,2DAA2D;IAC3D,KAAK,EAAE,SAAS,CAAA;IAChB,4DAA4D;IAC5D,MAAM,EAAE,SAAS,CAAA;IACjB,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAA;IACjB,wDAAwD;IACxD,UAAU,EAAE,MAAM,CAAA;IAClB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;IACjB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,qBAAqB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC3C;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B,CAAC,KAAK,GAAG,OAAO,CAC9D,SAAQ,uBAAuB,EAAE,qBAAqB,CAAC,KAAK,CAAC;CAAG;AAMlE;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,SAAS;IACV,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;IACT,SAAS;CACV,CAeA;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAE3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,CAAC,EAAE,MAAM,EACT,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,GACnB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAE3C;AAkBD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAC7C,EAAE,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAC5C,SAAS,SAAS,EAAE,CAWtB"}
@@ -0,0 +1,60 @@
1
+ function computeSourceSlices(frameW, frameH, left, right, top, bottom) {
2
+ const centerW = Math.max(0, frameW - left - right);
3
+ const centerH = Math.max(0, frameH - top - bottom);
4
+ return [
5
+ { x: 0, y: 0, w: left, h: top },
6
+ { x: left, y: 0, w: centerW, h: top },
7
+ { x: frameW - right, y: 0, w: right, h: top },
8
+ { x: 0, y: top, w: left, h: centerH },
9
+ { x: left, y: top, w: centerW, h: centerH },
10
+ { x: frameW - right, y: top, w: right, h: centerH },
11
+ { x: 0, y: frameH - bottom, w: left, h: bottom },
12
+ { x: left, y: frameH - bottom, w: centerW, h: bottom },
13
+ { x: frameW - right, y: frameH - bottom, w: right, h: bottom }
14
+ ];
15
+ }
16
+ function computeTargetXs(x, width, leftWidth, rightWidth) {
17
+ return [x, x + leftWidth, x + width - rightWidth, x + width];
18
+ }
19
+ function computeTargetYs(y, height, topHeight, bottomHeight) {
20
+ return [y, y + topHeight, y + height - bottomHeight, y + height];
21
+ }
22
+ const SEGMENT_EDGE_MAP = [
23
+ [0, 1, 0, 1],
24
+ // TL
25
+ [1, 2, 0, 1],
26
+ // Top
27
+ [2, 3, 0, 1],
28
+ // TR
29
+ [0, 1, 1, 2],
30
+ // Left
31
+ [1, 2, 1, 2],
32
+ // Center
33
+ [2, 3, 1, 2],
34
+ // Right
35
+ [0, 1, 2, 3],
36
+ // BL
37
+ [1, 2, 2, 3],
38
+ // Bottom
39
+ [2, 3, 2, 3]
40
+ // BR
41
+ ];
42
+ function deriveTargetRects(xs, ys) {
43
+ const rects = [];
44
+ for (const [x0, x1, y0, y1] of SEGMENT_EDGE_MAP) {
45
+ rects.push({
46
+ x: xs[x0] ?? 0,
47
+ y: ys[y0] ?? 0,
48
+ w: Math.max(0, (xs[x1] ?? 0) - (xs[x0] ?? 0)),
49
+ h: Math.max(0, (ys[y1] ?? 0) - (ys[y0] ?? 0))
50
+ });
51
+ }
52
+ return rects;
53
+ }
54
+ export {
55
+ computeSourceSlices,
56
+ computeTargetXs,
57
+ computeTargetYs,
58
+ deriveTargetRects
59
+ };
60
+ //# sourceMappingURL=nineslice-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nineslice-contract.js","sources":["../src/nineslice-contract.ts"],"sourcesContent":["/**\n * Portable NineSlice primitive contract — renderer-neutral.\n *\n * Defines the props interface for the native `nineslice` host primitive.\n * When a renderer declares the `nineslice` capability, the portable\n * NineSlice component renders a single `<nineslice>` element instead\n * of multiple `<image>` fragments.\n *\n * The portable layer passes texture info and slice dimensions\n * (leftWidth, rightWidth, topHeight, bottomHeight). The renderer:\n *\n * - Computes the 9 source slice regions from the texture frame geometry\n * - Receives the resolved target width/height from the layout system\n * - Applies the effective device/content scale\n * - Snaps shared edges to the physical pixel grid (once per axis)\n * - Derives all 9 target rectangles from snapped edges\n * - Draws all segments as a single draw operation\n */\n\nimport type { SizeValue, TransformProps } from './core-props'\nimport type { PropsDefaultExtension } from './types'\n\n/** A rectangle in the source texture (texture-pixel coordinates) */\nexport interface SliceRect {\n readonly x: number\n readonly y: number\n readonly w: number\n readonly h: number\n}\n\n/**\n * Portable props for the native NineSlice primitive.\n *\n * The portable NineSlice component passes texture identity and slice\n * dimensions. The renderer resolves texture frame geometry, computes\n * the 9 source regions, and derives target-space edge arrays from the\n * resolved layout size — snapping once per axis to avoid sub-pixel seams.\n */\nexport interface NineSlicePrimitiveProps extends TransformProps {\n /** Texture key */\n texture: string\n /** Optional atlas frame name */\n frame?: string | undefined\n /** Optional tint colour */\n tint?: number | undefined\n /** Target display width (resolved by the layout system) */\n width: SizeValue\n /** Target display height (resolved by the layout system) */\n height: SizeValue\n /** Width of the left slice in source texture pixels */\n leftWidth: number\n /** Width of the right slice in source texture pixels */\n rightWidth: number\n /** Height of the top slice in source texture pixels (0 = 3-slice mode) */\n topHeight: number\n /** Height of the bottom slice in source texture pixels (0 = 3-slice mode) */\n bottomHeight: number\n /**\n * Whether image smoothing is enabled when scaling segments.\n * Default: false (prefer crisp rendering for UI textures).\n */\n imageSmoothingEnabled?: boolean | undefined\n /**\n * When true, the element is rendered but excluded from layout calculations.\n * Used for decorative background layers inside stack containers.\n */\n headless?: boolean | undefined\n}\n\n/**\n * Complete NineSlice props including JSX extension props (ref, key).\n */\nexport interface NineSlicePrimitivePropsComplete<TNode = unknown>\n extends NineSlicePrimitiveProps, PropsDefaultExtension<TNode> {}\n\n// ---------------------------------------------------------------------------\n// Slice computation helpers (portable, renderer-neutral)\n// ---------------------------------------------------------------------------\n\n/**\n * Compute the 9 source slice rectangles from a texture frame and slice widths.\n *\n * @param frameW - Source texture / frame width in pixels\n * @param frameH - Source texture / frame height in pixels\n * @param left - Left slice width\n * @param right - Right slice width\n * @param top - Top slice height (0 for 3-slice mode)\n * @param bottom - Bottom slice height (0 for 3-slice mode)\n * @returns 9-element tuple of source slice rects\n */\nexport function computeSourceSlices(\n frameW: number,\n frameH: number,\n left: number,\n right: number,\n top: number,\n bottom: number\n): readonly [\n SliceRect,\n SliceRect,\n SliceRect,\n SliceRect,\n SliceRect,\n SliceRect,\n SliceRect,\n SliceRect,\n SliceRect,\n] {\n const centerW = Math.max(0, frameW - left - right)\n const centerH = Math.max(0, frameH - top - bottom)\n\n return [\n { x: 0, y: 0, w: left, h: top },\n { x: left, y: 0, w: centerW, h: top },\n { x: frameW - right, y: 0, w: right, h: top },\n { x: 0, y: top, w: left, h: centerH },\n { x: left, y: top, w: centerW, h: centerH },\n { x: frameW - right, y: top, w: right, h: centerH },\n { x: 0, y: frameH - bottom, w: left, h: bottom },\n { x: left, y: frameH - bottom, w: centerW, h: bottom },\n { x: frameW - right, y: frameH - bottom, w: right, h: bottom },\n ]\n}\n\n/**\n * Compute 4 shared X edge positions for the target NineSlice.\n *\n * @param x - Left edge of the target area\n * @param width - Total target width\n * @param leftWidth - Left slice width (in target pixels)\n * @param rightWidth - Right slice width (in target pixels)\n * @returns [left, left+leftWidth, right-rightWidth, right]\n */\nexport function computeTargetXs(\n x: number,\n width: number,\n leftWidth: number,\n rightWidth: number\n): readonly [number, number, number, number] {\n return [x, x + leftWidth, x + width - rightWidth, x + width] as const\n}\n\n/**\n * Compute 4 shared Y edge positions for the target NineSlice.\n *\n * @param y - Top edge of the target area\n * @param height - Total target height\n * @param topHeight - Top slice height (in target pixels)\n * @param bottomHeight - Bottom slice height (in target pixels)\n * @returns [top, top+topHeight, bottom-bottomHeight, bottom]\n */\nexport function computeTargetYs(\n y: number,\n height: number,\n topHeight: number,\n bottomHeight: number\n): readonly [number, number, number, number] {\n return [y, y + topHeight, y + height - bottomHeight, y + height] as const\n}\n\n/**\n * Edge index pairs for segment target rectangles.\n * Each entry: [xsLeftIndex, xsRightIndex, ysTopIndex, ysBottomIndex]\n */\nconst SEGMENT_EDGE_MAP: readonly (readonly [number, number, number, number])[] = [\n [0, 1, 0, 1], // TL\n [1, 2, 0, 1], // Top\n [2, 3, 0, 1], // TR\n [0, 1, 1, 2], // Left\n [1, 2, 1, 2], // Center\n [2, 3, 1, 2], // Right\n [0, 1, 2, 3], // BL\n [1, 2, 2, 3], // Bottom\n [2, 3, 2, 3], // BR\n] as const\n\n/**\n * Derive all 9 target rectangles from shared edge arrays.\n *\n * Since adjacent segments share edges, computing target rects this way\n * guarantees zero overlap and zero gaps — provided the edge arrays have\n * been snapped to the physical pixel grid by the renderer backend.\n *\n * @param xs - 4 shared X edges (must be monotonically non-decreasing)\n * @param ys - 4 shared Y edges (must be monotonically non-decreasing)\n * @returns 9 target rectangles with {x, y, w, h}\n */\nexport function deriveTargetRects(\n xs: readonly [number, number, number, number],\n ys: readonly [number, number, number, number]\n): readonly SliceRect[] {\n const rects: SliceRect[] = []\n for (const [x0, x1, y0, y1] of SEGMENT_EDGE_MAP) {\n rects.push({\n x: xs[x0] ?? 0,\n y: ys[y0] ?? 0,\n w: Math.max(0, (xs[x1] ?? 0) - (xs[x0] ?? 0)),\n h: Math.max(0, (ys[y1] ?? 0) - (ys[y0] ?? 0)),\n })\n }\n return rects\n}\n"],"names":[],"mappings":"AA0FO,SAAS,oBACd,QACA,QACA,MACA,OACA,KACA,QAWA;AACA,QAAM,UAAU,KAAK,IAAI,GAAG,SAAS,OAAO,KAAK;AACjD,QAAM,UAAU,KAAK,IAAI,GAAG,SAAS,MAAM,MAAM;AAEjD,SAAO;AAAA,IACL,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,MAAM,GAAG,IAAA;AAAA,IAC1B,EAAE,GAAG,MAAM,GAAG,GAAG,GAAG,SAAS,GAAG,IAAA;AAAA,IAChC,EAAE,GAAG,SAAS,OAAO,GAAG,GAAG,GAAG,OAAO,GAAG,IAAA;AAAA,IACxC,EAAE,GAAG,GAAG,GAAG,KAAK,GAAG,MAAM,GAAG,QAAA;AAAA,IAC5B,EAAE,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,QAAA;AAAA,IAClC,EAAE,GAAG,SAAS,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,QAAA;AAAA,IAC1C,EAAE,GAAG,GAAG,GAAG,SAAS,QAAQ,GAAG,MAAM,GAAG,OAAA;AAAA,IACxC,EAAE,GAAG,MAAM,GAAG,SAAS,QAAQ,GAAG,SAAS,GAAG,OAAA;AAAA,IAC9C,EAAE,GAAG,SAAS,OAAO,GAAG,SAAS,QAAQ,GAAG,OAAO,GAAG,OAAA;AAAA,EAAO;AAEjE;AAWO,SAAS,gBACd,GACA,OACA,WACA,YAC2C;AAC3C,SAAO,CAAC,GAAG,IAAI,WAAW,IAAI,QAAQ,YAAY,IAAI,KAAK;AAC7D;AAWO,SAAS,gBACd,GACA,QACA,WACA,cAC2C;AAC3C,SAAO,CAAC,GAAG,IAAI,WAAW,IAAI,SAAS,cAAc,IAAI,MAAM;AACjE;AAMA,MAAM,mBAA2E;AAAA,EAC/E,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EACX,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EACX,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EACX,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EACX,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EACX,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EACX,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EACX,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AAAA,EACX,CAAC,GAAG,GAAG,GAAG,CAAC;AAAA;AACb;AAaO,SAAS,kBACd,IACA,IACsB;AACtB,QAAM,QAAqB,CAAA;AAC3B,aAAW,CAAC,IAAI,IAAI,IAAI,EAAE,KAAK,kBAAkB;AAC/C,UAAM,KAAK;AAAA,MACT,GAAG,GAAG,EAAE,KAAK;AAAA,MACb,GAAG,GAAG,EAAE,KAAK;AAAA,MACb,GAAG,KAAK,IAAI,IAAI,GAAG,EAAE,KAAK,MAAM,GAAG,EAAE,KAAK,EAAE;AAAA,MAC5C,GAAG,KAAK,IAAI,IAAI,GAAG,EAAE,KAAK,MAAM,GAAG,EAAE,KAAK,EAAE;AAAA,IAAA,CAC7C;AAAA,EACH;AACA,SAAO;AACT;"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ function logNotYetImplemented(componentName, surface) {
4
+ const renderer = surface?.raw ? surface.raw.constructor?.name ?? "unknown" : "unknown";
5
+ if (typeof console !== "undefined" && console.warn) {
6
+ console.warn(
7
+ `[@number10/jsx-core] ${componentName}: not yet implemented for renderer "${renderer}". Using portable fallback.`
8
+ );
9
+ }
10
+ }
11
+ exports.logNotYetImplemented = logNotYetImplemented;
12
+ //# sourceMappingURL=placeholder.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placeholder.cjs","sources":["../src/placeholder.ts"],"sourcesContent":["/**\n * Placeholder utilities — renderer-neutral \"not yet implemented\" fallback.\n *\n * Portable components that need renderer-specific integration (e.g., SVG\n * texture loading, overlay mounting) can use these helpers to provide a\n * visible fallback when a renderer hasn't implemented the feature yet.\n *\n * ## Usage\n *\n * ```typescript\n * // In a portable component that needs renderer integration:\n * import { logNotYetImplemented } from '@number10/jsx-core'\n *\n * export function MyPortableComponent(props: MyProps) {\n * const surface = useSurface()\n *\n * if (!surface?.capabilities.has('svg-texture')) {\n * logNotYetImplemented('Icon', surface)\n * return <Text text=\"[Icon not implemented]\" />\n * }\n *\n * // ... normal implementation\n * }\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// NotYetImplemented — visible fallback component\n// ---------------------------------------------------------------------------\n\n/**\n * Log a structured warning for a renderer that hasn't implemented a feature.\n *\n * The warning includes the component name and renderer identity so developers\n * can track which components are still missing implementation.\n *\n * @param componentName - Name of the component that is not yet implemented\n * @param surface - Optional surface context (used to identify the renderer)\n *\n * @example\n * ```typescript\n * logNotYetImplemented('Icon', surface)\n * // → \"[@number10/jsx-core] Icon: not yet implemented for this renderer\"\n * ```\n */\nexport function logNotYetImplemented(\n componentName: string,\n surface?: { readonly raw?: unknown } | undefined\n): void {\n const renderer = surface?.raw\n ? ((surface.raw as { constructor?: { name?: string } }).constructor?.name ?? 'unknown')\n : 'unknown'\n\n if (typeof console !== 'undefined' && console.warn) {\n console.warn(\n `[@number10/jsx-core] ${componentName}: not yet implemented for renderer \"${renderer}\". ` +\n `Using portable fallback.`\n )\n }\n}\n"],"names":[],"mappings":";;AA6CO,SAAS,qBACd,eACA,SACM;AACN,QAAM,WAAW,SAAS,MACpB,QAAQ,IAA4C,aAAa,QAAQ,YAC3E;AAEJ,MAAI,OAAO,YAAY,eAAe,QAAQ,MAAM;AAClD,YAAQ;AAAA,MACN,wBAAwB,aAAa,uCAAuC,QAAQ;AAAA,IAAA;AAAA,EAGxF;AACF;;"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Placeholder utilities — renderer-neutral "not yet implemented" fallback.
3
+ *
4
+ * Portable components that need renderer-specific integration (e.g., SVG
5
+ * texture loading, overlay mounting) can use these helpers to provide a
6
+ * visible fallback when a renderer hasn't implemented the feature yet.
7
+ *
8
+ * ## Usage
9
+ *
10
+ * ```typescript
11
+ * // In a portable component that needs renderer integration:
12
+ * import { logNotYetImplemented } from '@number10/jsx-core'
13
+ *
14
+ * export function MyPortableComponent(props: MyProps) {
15
+ * const surface = useSurface()
16
+ *
17
+ * if (!surface?.capabilities.has('svg-texture')) {
18
+ * logNotYetImplemented('Icon', surface)
19
+ * return <Text text="[Icon not implemented]" />
20
+ * }
21
+ *
22
+ * // ... normal implementation
23
+ * }
24
+ * ```
25
+ */
26
+ /**
27
+ * Log a structured warning for a renderer that hasn't implemented a feature.
28
+ *
29
+ * The warning includes the component name and renderer identity so developers
30
+ * can track which components are still missing implementation.
31
+ *
32
+ * @param componentName - Name of the component that is not yet implemented
33
+ * @param surface - Optional surface context (used to identify the renderer)
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * logNotYetImplemented('Icon', surface)
38
+ * // → "[@number10/jsx-core] Icon: not yet implemented for this renderer"
39
+ * ```
40
+ */
41
+ export declare function logNotYetImplemented(componentName: string, surface?: {
42
+ readonly raw?: unknown;
43
+ } | undefined): void;
44
+ //# sourceMappingURL=placeholder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placeholder.d.ts","sourceRoot":"","sources":["../src/placeholder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAMH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,EAAE,MAAM,EACrB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,GAC/C,IAAI,CAWN"}
@@ -0,0 +1,12 @@
1
+ function logNotYetImplemented(componentName, surface) {
2
+ const renderer = surface?.raw ? surface.raw.constructor?.name ?? "unknown" : "unknown";
3
+ if (typeof console !== "undefined" && console.warn) {
4
+ console.warn(
5
+ `[@number10/jsx-core] ${componentName}: not yet implemented for renderer "${renderer}". Using portable fallback.`
6
+ );
7
+ }
8
+ }
9
+ export {
10
+ logNotYetImplemented
11
+ };
12
+ //# sourceMappingURL=placeholder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"placeholder.js","sources":["../src/placeholder.ts"],"sourcesContent":["/**\n * Placeholder utilities — renderer-neutral \"not yet implemented\" fallback.\n *\n * Portable components that need renderer-specific integration (e.g., SVG\n * texture loading, overlay mounting) can use these helpers to provide a\n * visible fallback when a renderer hasn't implemented the feature yet.\n *\n * ## Usage\n *\n * ```typescript\n * // In a portable component that needs renderer integration:\n * import { logNotYetImplemented } from '@number10/jsx-core'\n *\n * export function MyPortableComponent(props: MyProps) {\n * const surface = useSurface()\n *\n * if (!surface?.capabilities.has('svg-texture')) {\n * logNotYetImplemented('Icon', surface)\n * return <Text text=\"[Icon not implemented]\" />\n * }\n *\n * // ... normal implementation\n * }\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// NotYetImplemented — visible fallback component\n// ---------------------------------------------------------------------------\n\n/**\n * Log a structured warning for a renderer that hasn't implemented a feature.\n *\n * The warning includes the component name and renderer identity so developers\n * can track which components are still missing implementation.\n *\n * @param componentName - Name of the component that is not yet implemented\n * @param surface - Optional surface context (used to identify the renderer)\n *\n * @example\n * ```typescript\n * logNotYetImplemented('Icon', surface)\n * // → \"[@number10/jsx-core] Icon: not yet implemented for this renderer\"\n * ```\n */\nexport function logNotYetImplemented(\n componentName: string,\n surface?: { readonly raw?: unknown } | undefined\n): void {\n const renderer = surface?.raw\n ? ((surface.raw as { constructor?: { name?: string } }).constructor?.name ?? 'unknown')\n : 'unknown'\n\n if (typeof console !== 'undefined' && console.warn) {\n console.warn(\n `[@number10/jsx-core] ${componentName}: not yet implemented for renderer \"${renderer}\". ` +\n `Using portable fallback.`\n )\n }\n}\n"],"names":[],"mappings":"AA6CO,SAAS,qBACd,eACA,SACM;AACN,QAAM,WAAW,SAAS,MACpB,QAAQ,IAA4C,aAAa,QAAQ,YAC3E;AAEJ,MAAI,OAAO,YAAY,eAAe,QAAQ,MAAM;AAClD,YAAQ;AAAA,MACN,wBAAwB,aAAa,uCAAuC,QAAQ;AAAA,IAAA;AAAA,EAGxF;AACF;"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const hooks = require("./hooks.cjs");
4
+ function usePortalSurface(options = {}) {
5
+ const surfaceContext = hooks.useSurface();
6
+ const adapter = surfaceContext?.adapter;
7
+ const parentSurface = surfaceContext?.rootSurface ?? (surfaceContext?.raw ? findParentSurface(adapter, surfaceContext.raw) : void 0);
8
+ const [portalSurface, setPortalSurface] = hooks.useState(null);
9
+ const portalRef = hooks.useRef(null);
10
+ const [error, setError] = hooks.useState(null);
11
+ const depthRef = hooks.useRef(options.depth ?? 1e3);
12
+ const mountedRef = hooks.useRef(false);
13
+ hooks.useEffect(() => {
14
+ if (!adapter) {
15
+ setError("No SurfaceAdapter available — cannot create portal");
16
+ return;
17
+ }
18
+ if (!parentSurface) {
19
+ setError("No parent RootSurface found — cannot create portal");
20
+ return;
21
+ }
22
+ if (mountedRef.current) return;
23
+ mountedRef.current = true;
24
+ const depth = options.depth ?? 1e3;
25
+ depthRef.current = depth;
26
+ try {
27
+ const created = adapter.createRoot({
28
+ kind: "portal",
29
+ parent: parentSurface,
30
+ zIndex: depth,
31
+ width: surfaceContext?.viewport.width ?? 0,
32
+ height: surfaceContext?.viewport.height ?? 0,
33
+ ...options.id ? { id: options.id } : {}
34
+ });
35
+ portalRef.current = created;
36
+ setPortalSurface(created);
37
+ setError(null);
38
+ } catch (e) {
39
+ setError(`Failed to create portal surface: ${e.message}`);
40
+ }
41
+ return () => {
42
+ mountedRef.current = false;
43
+ const current = portalRef.current;
44
+ if (current) {
45
+ portalRef.current = null;
46
+ setPortalSurface(null);
47
+ try {
48
+ adapter.destroyRoot(current);
49
+ } catch {
50
+ }
51
+ }
52
+ };
53
+ }, []);
54
+ const update = (updates) => {
55
+ const current = portalRef.current;
56
+ if (!current || !adapter) return;
57
+ if (updates.depth !== void 0) depthRef.current = updates.depth;
58
+ adapter.updateRoot(current, {
59
+ ...updates.depth !== void 0 ? { zIndex: updates.depth } : {},
60
+ ...updates.width !== void 0 ? { width: updates.width } : {},
61
+ ...updates.height !== void 0 ? { height: updates.height } : {}
62
+ });
63
+ };
64
+ const destroy = () => {
65
+ const current = portalRef.current;
66
+ if (current && adapter) {
67
+ portalRef.current = null;
68
+ setPortalSurface(null);
69
+ try {
70
+ adapter.destroyRoot(current);
71
+ } catch {
72
+ }
73
+ mountedRef.current = false;
74
+ }
75
+ };
76
+ return {
77
+ surface: portalSurface,
78
+ error,
79
+ update,
80
+ destroy,
81
+ vdom: surfaceContext?.vdom
82
+ };
83
+ }
84
+ function findParentSurface(adapter, target) {
85
+ if (!adapter || !adapter.getRootByTarget) return void 0;
86
+ return adapter.getRootByTarget(target);
87
+ }
88
+ exports.usePortalSurface = usePortalSurface;
89
+ //# sourceMappingURL=portal.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.cjs","sources":["../src/portal.ts"],"sourcesContent":["/**\n * Portable portal surface hook — renderer-neutral overlay root management.\n *\n * Manages the lifecycle of a portal RootSurface (create/update/destroy)\n * using the SurfaceAdapter stored on the current SurfaceContext.\n *\n * The actual VDOM mounting of children is left to the renderer-specific\n * Portal component (e.g., @number10/jsx-phaser's Portal.tsx), which calls\n * this hook and renders into `surface.root`.\n *\n * @example\n * ```typescript\n * // In a Phaser Portal component:\n * import { usePortalSurface } from '@number10/jsx-core'\n *\n * function Portal(props) {\n * const { surface, error } = usePortalSurface({\n * depth: props.depth ?? 1000,\n * id: props.id,\n * })\n *\n * if (error) return null\n * if (!surface) return null\n *\n * // Mount children into surface.root (renderer-specific)\n * // ...\n * }\n * ```\n */\n\nimport { useEffect, useRef, useState, useSurface } from './hooks'\nimport type { RootSurface, SurfaceAdapter, SurfaceContext } from './host-adapter'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Options for usePortalSurface */\nexport interface UsePortalSurfaceOptions {\n /** Z-depth for the portal (higher = on top of others) */\n depth?: number\n /** Optional unique portal ID */\n id?: string\n /**\n * Block input events from passing through the portal area.\n * The renderer adapter is responsible for implementing the actual\n * event blocking (e.g., gesture blocker containers in Phaser).\n */\n blockEvents?: boolean\n}\n\n/** Result of usePortalSurface */\nexport interface UsePortalSurfaceResult {\n /**\n * The portal RootSurface, or null if not yet created.\n * Renderers mount children into `surface.root`.\n */\n surface: RootSurface | null\n /**\n * Error message if portal creation failed.\n * Component should render nothing when this is set.\n */\n error: string | null\n /**\n * Update the portal's depth or viewport dimensions.\n * Call when depth prop changes or on viewport resize.\n */\n update: (updates: { depth?: number; width?: number; height?: number }) => void\n /**\n * Explicitly destroy the portal surface.\n * Automatically called on unmount via useEffect cleanup.\n */\n destroy: () => void\n /**\n * Renderer-bound VDOM operations for mounting children.\n * Portable components use this instead of importing renderer-specific\n * mount/patchVNode/unmount to avoid multi-renderer conflicts.\n */\n vdom: SurfaceContext['vdom']\n}\n\n// ---------------------------------------------------------------------------\n// usePortalSurface\n// ---------------------------------------------------------------------------\n\n/**\n * Portable hook that creates and manages a portal RootSurface.\n *\n * Uses the SurfaceAdapter from the current SurfaceContext to create\n * a portal surface as a child of the parent surface. The hook handles\n * lifecycle: create on mount, update on option changes, destroy on unmount.\n *\n * @param options - Portal configuration\n * @returns Portal surface handle with update/destroy methods\n *\n * @migration(audit:surface-access) Phase 6B — portable portal surface lifecycle.\n * Replaces manual createRoot/destroyRoot in renderer-specific Portal components.\n */\nexport function usePortalSurface(options: UsePortalSurfaceOptions = {}): UsePortalSurfaceResult {\n const surfaceContext = useSurface()\n const adapter: SurfaceAdapter | undefined = surfaceContext?.adapter\n const parentSurface: RootSurface | undefined =\n surfaceContext?.rootSurface ??\n (surfaceContext?.raw ? findParentSurface(adapter, surfaceContext.raw) : undefined)\n\n const [portalSurface, setPortalSurface] = useState<RootSurface | null>(null)\n const portalRef = useRef<RootSurface | null>(null)\n const [error, setError] = useState<string | null>(null)\n const depthRef = useRef(options.depth ?? 1000)\n\n // Track whether we've mounted to avoid double-init\n const mountedRef = useRef(false)\n\n // Create portal surface on mount\n useEffect(() => {\n if (!adapter) {\n setError('No SurfaceAdapter available — cannot create portal')\n return\n }\n\n if (!parentSurface) {\n setError('No parent RootSurface found — cannot create portal')\n return\n }\n\n if (mountedRef.current) return\n mountedRef.current = true\n\n const depth = options.depth ?? 1000\n depthRef.current = depth\n\n try {\n const created = adapter.createRoot({\n kind: 'portal',\n parent: parentSurface,\n zIndex: depth,\n width: surfaceContext?.viewport.width ?? 0,\n height: surfaceContext?.viewport.height ?? 0,\n ...(options.id ? { id: options.id } : {}),\n })\n\n portalRef.current = created\n setPortalSurface(created)\n setError(null)\n } catch (e) {\n setError(`Failed to create portal surface: ${(e as Error).message}`)\n }\n\n return () => {\n mountedRef.current = false\n const current = portalRef.current\n if (current) {\n portalRef.current = null\n setPortalSurface(null)\n try {\n adapter.destroyRoot(current)\n } catch {\n // Surface may already be destroyed by scene shutdown\n }\n }\n }\n // Only run on mount\n }, [])\n\n const update = (updates: { depth?: number; width?: number; height?: number }) => {\n const current = portalRef.current\n if (!current || !adapter) return\n\n if (updates.depth !== undefined) depthRef.current = updates.depth\n\n adapter.updateRoot(current, {\n ...(updates.depth !== undefined ? { zIndex: updates.depth } : {}),\n ...(updates.width !== undefined ? { width: updates.width } : {}),\n ...(updates.height !== undefined ? { height: updates.height } : {}),\n })\n }\n\n const destroy = () => {\n const current = portalRef.current\n if (current && adapter) {\n portalRef.current = null\n setPortalSurface(null)\n try {\n adapter.destroyRoot(current)\n } catch {\n // Surface may already be destroyed\n }\n mountedRef.current = false\n }\n }\n\n return {\n surface: portalSurface,\n error,\n update,\n destroy,\n vdom: surfaceContext?.vdom,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Find the parent RootSurface for a given renderer-native target.\n * Scans the adapter's registered surfaces for one whose raw target matches.\n */\nfunction findParentSurface(\n adapter: SurfaceAdapter | undefined,\n target: unknown\n): RootSurface | undefined {\n if (!adapter || !adapter.getRootByTarget) return undefined\n return adapter.getRootByTarget(target)\n}\n"],"names":["useSurface","useState","useRef","useEffect"],"mappings":";;;AAkGO,SAAS,iBAAiB,UAAmC,IAA4B;AAC9F,QAAM,iBAAiBA,MAAAA,WAAA;AACvB,QAAM,UAAsC,gBAAgB;AAC5D,QAAM,gBACJ,gBAAgB,gBACf,gBAAgB,MAAM,kBAAkB,SAAS,eAAe,GAAG,IAAI;AAE1E,QAAM,CAAC,eAAe,gBAAgB,IAAIC,MAAAA,SAA6B,IAAI;AAC3E,QAAM,YAAYC,MAAAA,OAA2B,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAID,MAAAA,SAAwB,IAAI;AACtD,QAAM,WAAWC,MAAAA,OAAO,QAAQ,SAAS,GAAI;AAG7C,QAAM,aAAaA,MAAAA,OAAO,KAAK;AAG/BC,QAAAA,UAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,eAAS,oDAAoD;AAC7D;AAAA,IACF;AAEA,QAAI,CAAC,eAAe;AAClB,eAAS,oDAAoD;AAC7D;AAAA,IACF;AAEA,QAAI,WAAW,QAAS;AACxB,eAAW,UAAU;AAErB,UAAM,QAAQ,QAAQ,SAAS;AAC/B,aAAS,UAAU;AAEnB,QAAI;AACF,YAAM,UAAU,QAAQ,WAAW;AAAA,QACjC,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,OAAO,gBAAgB,SAAS,SAAS;AAAA,QACzC,QAAQ,gBAAgB,SAAS,UAAU;AAAA,QAC3C,GAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,GAAA,IAAO,CAAA;AAAA,MAAC,CACxC;AAED,gBAAU,UAAU;AACpB,uBAAiB,OAAO;AACxB,eAAS,IAAI;AAAA,IACf,SAAS,GAAG;AACV,eAAS,oCAAqC,EAAY,OAAO,EAAE;AAAA,IACrE;AAEA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,YAAM,UAAU,UAAU;AAC1B,UAAI,SAAS;AACX,kBAAU,UAAU;AACpB,yBAAiB,IAAI;AACrB,YAAI;AACF,kBAAQ,YAAY,OAAO;AAAA,QAC7B,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EAEF,GAAG,CAAA,CAAE;AAEL,QAAM,SAAS,CAAC,YAAiE;AAC/E,UAAM,UAAU,UAAU;AAC1B,QAAI,CAAC,WAAW,CAAC,QAAS;AAE1B,QAAI,QAAQ,UAAU,OAAW,UAAS,UAAU,QAAQ;AAE5D,YAAQ,WAAW,SAAS;AAAA,MAC1B,GAAI,QAAQ,UAAU,SAAY,EAAE,QAAQ,QAAQ,MAAA,IAAU,CAAA;AAAA,MAC9D,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAA,IAAU,CAAA;AAAA,MAC7D,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,WAAW,CAAA;AAAA,IAAC,CAClE;AAAA,EACH;AAEA,QAAM,UAAU,MAAM;AACpB,UAAM,UAAU,UAAU;AAC1B,QAAI,WAAW,SAAS;AACtB,gBAAU,UAAU;AACpB,uBAAiB,IAAI;AACrB,UAAI;AACF,gBAAQ,YAAY,OAAO;AAAA,MAC7B,QAAQ;AAAA,MAER;AACA,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,gBAAgB;AAAA,EAAA;AAE1B;AAUA,SAAS,kBACP,SACA,QACyB;AACzB,MAAI,CAAC,WAAW,CAAC,QAAQ,gBAAiB,QAAO;AACjD,SAAO,QAAQ,gBAAgB,MAAM;AACvC;;"}
@@ -0,0 +1,62 @@
1
+ import { RootSurface, SurfaceContext } from './host-adapter';
2
+ /** Options for usePortalSurface */
3
+ export interface UsePortalSurfaceOptions {
4
+ /** Z-depth for the portal (higher = on top of others) */
5
+ depth?: number;
6
+ /** Optional unique portal ID */
7
+ id?: string;
8
+ /**
9
+ * Block input events from passing through the portal area.
10
+ * The renderer adapter is responsible for implementing the actual
11
+ * event blocking (e.g., gesture blocker containers in Phaser).
12
+ */
13
+ blockEvents?: boolean;
14
+ }
15
+ /** Result of usePortalSurface */
16
+ export interface UsePortalSurfaceResult {
17
+ /**
18
+ * The portal RootSurface, or null if not yet created.
19
+ * Renderers mount children into `surface.root`.
20
+ */
21
+ surface: RootSurface | null;
22
+ /**
23
+ * Error message if portal creation failed.
24
+ * Component should render nothing when this is set.
25
+ */
26
+ error: string | null;
27
+ /**
28
+ * Update the portal's depth or viewport dimensions.
29
+ * Call when depth prop changes or on viewport resize.
30
+ */
31
+ update: (updates: {
32
+ depth?: number;
33
+ width?: number;
34
+ height?: number;
35
+ }) => void;
36
+ /**
37
+ * Explicitly destroy the portal surface.
38
+ * Automatically called on unmount via useEffect cleanup.
39
+ */
40
+ destroy: () => void;
41
+ /**
42
+ * Renderer-bound VDOM operations for mounting children.
43
+ * Portable components use this instead of importing renderer-specific
44
+ * mount/patchVNode/unmount to avoid multi-renderer conflicts.
45
+ */
46
+ vdom: SurfaceContext['vdom'];
47
+ }
48
+ /**
49
+ * Portable hook that creates and manages a portal RootSurface.
50
+ *
51
+ * Uses the SurfaceAdapter from the current SurfaceContext to create
52
+ * a portal surface as a child of the parent surface. The hook handles
53
+ * lifecycle: create on mount, update on option changes, destroy on unmount.
54
+ *
55
+ * @param options - Portal configuration
56
+ * @returns Portal surface handle with update/destroy methods
57
+ *
58
+ * @migration(audit:surface-access) Phase 6B — portable portal surface lifecycle.
59
+ * Replaces manual createRoot/destroyRoot in renderer-specific Portal components.
60
+ */
61
+ export declare function usePortalSurface(options?: UsePortalSurfaceOptions): UsePortalSurfaceResult;
62
+ //# sourceMappingURL=portal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../src/portal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAkB,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAMjF,mCAAmC;AACnC,MAAM,WAAW,uBAAuB;IACtC,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,gCAAgC;IAChC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,iCAAiC;AACjC,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;IAC3B;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;OAGG;IACH,MAAM,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IAC9E;;;OAGG;IACH,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB;;;;OAIG;IACH,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAA;CAC7B;AAMD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B,GAAG,sBAAsB,CAoG9F"}
package/dist/portal.js ADDED
@@ -0,0 +1,89 @@
1
+ import { useSurface, useState, useRef, useEffect } from "./hooks.js";
2
+ function usePortalSurface(options = {}) {
3
+ const surfaceContext = useSurface();
4
+ const adapter = surfaceContext?.adapter;
5
+ const parentSurface = surfaceContext?.rootSurface ?? (surfaceContext?.raw ? findParentSurface(adapter, surfaceContext.raw) : void 0);
6
+ const [portalSurface, setPortalSurface] = useState(null);
7
+ const portalRef = useRef(null);
8
+ const [error, setError] = useState(null);
9
+ const depthRef = useRef(options.depth ?? 1e3);
10
+ const mountedRef = useRef(false);
11
+ useEffect(() => {
12
+ if (!adapter) {
13
+ setError("No SurfaceAdapter available — cannot create portal");
14
+ return;
15
+ }
16
+ if (!parentSurface) {
17
+ setError("No parent RootSurface found — cannot create portal");
18
+ return;
19
+ }
20
+ if (mountedRef.current) return;
21
+ mountedRef.current = true;
22
+ const depth = options.depth ?? 1e3;
23
+ depthRef.current = depth;
24
+ try {
25
+ const created = adapter.createRoot({
26
+ kind: "portal",
27
+ parent: parentSurface,
28
+ zIndex: depth,
29
+ width: surfaceContext?.viewport.width ?? 0,
30
+ height: surfaceContext?.viewport.height ?? 0,
31
+ ...options.id ? { id: options.id } : {}
32
+ });
33
+ portalRef.current = created;
34
+ setPortalSurface(created);
35
+ setError(null);
36
+ } catch (e) {
37
+ setError(`Failed to create portal surface: ${e.message}`);
38
+ }
39
+ return () => {
40
+ mountedRef.current = false;
41
+ const current = portalRef.current;
42
+ if (current) {
43
+ portalRef.current = null;
44
+ setPortalSurface(null);
45
+ try {
46
+ adapter.destroyRoot(current);
47
+ } catch {
48
+ }
49
+ }
50
+ };
51
+ }, []);
52
+ const update = (updates) => {
53
+ const current = portalRef.current;
54
+ if (!current || !adapter) return;
55
+ if (updates.depth !== void 0) depthRef.current = updates.depth;
56
+ adapter.updateRoot(current, {
57
+ ...updates.depth !== void 0 ? { zIndex: updates.depth } : {},
58
+ ...updates.width !== void 0 ? { width: updates.width } : {},
59
+ ...updates.height !== void 0 ? { height: updates.height } : {}
60
+ });
61
+ };
62
+ const destroy = () => {
63
+ const current = portalRef.current;
64
+ if (current && adapter) {
65
+ portalRef.current = null;
66
+ setPortalSurface(null);
67
+ try {
68
+ adapter.destroyRoot(current);
69
+ } catch {
70
+ }
71
+ mountedRef.current = false;
72
+ }
73
+ };
74
+ return {
75
+ surface: portalSurface,
76
+ error,
77
+ update,
78
+ destroy,
79
+ vdom: surfaceContext?.vdom
80
+ };
81
+ }
82
+ function findParentSurface(adapter, target) {
83
+ if (!adapter || !adapter.getRootByTarget) return void 0;
84
+ return adapter.getRootByTarget(target);
85
+ }
86
+ export {
87
+ usePortalSurface
88
+ };
89
+ //# sourceMappingURL=portal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.js","sources":["../src/portal.ts"],"sourcesContent":["/**\n * Portable portal surface hook — renderer-neutral overlay root management.\n *\n * Manages the lifecycle of a portal RootSurface (create/update/destroy)\n * using the SurfaceAdapter stored on the current SurfaceContext.\n *\n * The actual VDOM mounting of children is left to the renderer-specific\n * Portal component (e.g., @number10/jsx-phaser's Portal.tsx), which calls\n * this hook and renders into `surface.root`.\n *\n * @example\n * ```typescript\n * // In a Phaser Portal component:\n * import { usePortalSurface } from '@number10/jsx-core'\n *\n * function Portal(props) {\n * const { surface, error } = usePortalSurface({\n * depth: props.depth ?? 1000,\n * id: props.id,\n * })\n *\n * if (error) return null\n * if (!surface) return null\n *\n * // Mount children into surface.root (renderer-specific)\n * // ...\n * }\n * ```\n */\n\nimport { useEffect, useRef, useState, useSurface } from './hooks'\nimport type { RootSurface, SurfaceAdapter, SurfaceContext } from './host-adapter'\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Options for usePortalSurface */\nexport interface UsePortalSurfaceOptions {\n /** Z-depth for the portal (higher = on top of others) */\n depth?: number\n /** Optional unique portal ID */\n id?: string\n /**\n * Block input events from passing through the portal area.\n * The renderer adapter is responsible for implementing the actual\n * event blocking (e.g., gesture blocker containers in Phaser).\n */\n blockEvents?: boolean\n}\n\n/** Result of usePortalSurface */\nexport interface UsePortalSurfaceResult {\n /**\n * The portal RootSurface, or null if not yet created.\n * Renderers mount children into `surface.root`.\n */\n surface: RootSurface | null\n /**\n * Error message if portal creation failed.\n * Component should render nothing when this is set.\n */\n error: string | null\n /**\n * Update the portal's depth or viewport dimensions.\n * Call when depth prop changes or on viewport resize.\n */\n update: (updates: { depth?: number; width?: number; height?: number }) => void\n /**\n * Explicitly destroy the portal surface.\n * Automatically called on unmount via useEffect cleanup.\n */\n destroy: () => void\n /**\n * Renderer-bound VDOM operations for mounting children.\n * Portable components use this instead of importing renderer-specific\n * mount/patchVNode/unmount to avoid multi-renderer conflicts.\n */\n vdom: SurfaceContext['vdom']\n}\n\n// ---------------------------------------------------------------------------\n// usePortalSurface\n// ---------------------------------------------------------------------------\n\n/**\n * Portable hook that creates and manages a portal RootSurface.\n *\n * Uses the SurfaceAdapter from the current SurfaceContext to create\n * a portal surface as a child of the parent surface. The hook handles\n * lifecycle: create on mount, update on option changes, destroy on unmount.\n *\n * @param options - Portal configuration\n * @returns Portal surface handle with update/destroy methods\n *\n * @migration(audit:surface-access) Phase 6B — portable portal surface lifecycle.\n * Replaces manual createRoot/destroyRoot in renderer-specific Portal components.\n */\nexport function usePortalSurface(options: UsePortalSurfaceOptions = {}): UsePortalSurfaceResult {\n const surfaceContext = useSurface()\n const adapter: SurfaceAdapter | undefined = surfaceContext?.adapter\n const parentSurface: RootSurface | undefined =\n surfaceContext?.rootSurface ??\n (surfaceContext?.raw ? findParentSurface(adapter, surfaceContext.raw) : undefined)\n\n const [portalSurface, setPortalSurface] = useState<RootSurface | null>(null)\n const portalRef = useRef<RootSurface | null>(null)\n const [error, setError] = useState<string | null>(null)\n const depthRef = useRef(options.depth ?? 1000)\n\n // Track whether we've mounted to avoid double-init\n const mountedRef = useRef(false)\n\n // Create portal surface on mount\n useEffect(() => {\n if (!adapter) {\n setError('No SurfaceAdapter available — cannot create portal')\n return\n }\n\n if (!parentSurface) {\n setError('No parent RootSurface found — cannot create portal')\n return\n }\n\n if (mountedRef.current) return\n mountedRef.current = true\n\n const depth = options.depth ?? 1000\n depthRef.current = depth\n\n try {\n const created = adapter.createRoot({\n kind: 'portal',\n parent: parentSurface,\n zIndex: depth,\n width: surfaceContext?.viewport.width ?? 0,\n height: surfaceContext?.viewport.height ?? 0,\n ...(options.id ? { id: options.id } : {}),\n })\n\n portalRef.current = created\n setPortalSurface(created)\n setError(null)\n } catch (e) {\n setError(`Failed to create portal surface: ${(e as Error).message}`)\n }\n\n return () => {\n mountedRef.current = false\n const current = portalRef.current\n if (current) {\n portalRef.current = null\n setPortalSurface(null)\n try {\n adapter.destroyRoot(current)\n } catch {\n // Surface may already be destroyed by scene shutdown\n }\n }\n }\n // Only run on mount\n }, [])\n\n const update = (updates: { depth?: number; width?: number; height?: number }) => {\n const current = portalRef.current\n if (!current || !adapter) return\n\n if (updates.depth !== undefined) depthRef.current = updates.depth\n\n adapter.updateRoot(current, {\n ...(updates.depth !== undefined ? { zIndex: updates.depth } : {}),\n ...(updates.width !== undefined ? { width: updates.width } : {}),\n ...(updates.height !== undefined ? { height: updates.height } : {}),\n })\n }\n\n const destroy = () => {\n const current = portalRef.current\n if (current && adapter) {\n portalRef.current = null\n setPortalSurface(null)\n try {\n adapter.destroyRoot(current)\n } catch {\n // Surface may already be destroyed\n }\n mountedRef.current = false\n }\n }\n\n return {\n surface: portalSurface,\n error,\n update,\n destroy,\n vdom: surfaceContext?.vdom,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Find the parent RootSurface for a given renderer-native target.\n * Scans the adapter's registered surfaces for one whose raw target matches.\n */\nfunction findParentSurface(\n adapter: SurfaceAdapter | undefined,\n target: unknown\n): RootSurface | undefined {\n if (!adapter || !adapter.getRootByTarget) return undefined\n return adapter.getRootByTarget(target)\n}\n"],"names":[],"mappings":";AAkGO,SAAS,iBAAiB,UAAmC,IAA4B;AAC9F,QAAM,iBAAiB,WAAA;AACvB,QAAM,UAAsC,gBAAgB;AAC5D,QAAM,gBACJ,gBAAgB,gBACf,gBAAgB,MAAM,kBAAkB,SAAS,eAAe,GAAG,IAAI;AAE1E,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAA6B,IAAI;AAC3E,QAAM,YAAY,OAA2B,IAAI;AACjD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AACtD,QAAM,WAAW,OAAO,QAAQ,SAAS,GAAI;AAG7C,QAAM,aAAa,OAAO,KAAK;AAG/B,YAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,eAAS,oDAAoD;AAC7D;AAAA,IACF;AAEA,QAAI,CAAC,eAAe;AAClB,eAAS,oDAAoD;AAC7D;AAAA,IACF;AAEA,QAAI,WAAW,QAAS;AACxB,eAAW,UAAU;AAErB,UAAM,QAAQ,QAAQ,SAAS;AAC/B,aAAS,UAAU;AAEnB,QAAI;AACF,YAAM,UAAU,QAAQ,WAAW;AAAA,QACjC,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,OAAO,gBAAgB,SAAS,SAAS;AAAA,QACzC,QAAQ,gBAAgB,SAAS,UAAU;AAAA,QAC3C,GAAI,QAAQ,KAAK,EAAE,IAAI,QAAQ,GAAA,IAAO,CAAA;AAAA,MAAC,CACxC;AAED,gBAAU,UAAU;AACpB,uBAAiB,OAAO;AACxB,eAAS,IAAI;AAAA,IACf,SAAS,GAAG;AACV,eAAS,oCAAqC,EAAY,OAAO,EAAE;AAAA,IACrE;AAEA,WAAO,MAAM;AACX,iBAAW,UAAU;AACrB,YAAM,UAAU,UAAU;AAC1B,UAAI,SAAS;AACX,kBAAU,UAAU;AACpB,yBAAiB,IAAI;AACrB,YAAI;AACF,kBAAQ,YAAY,OAAO;AAAA,QAC7B,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EAEF,GAAG,CAAA,CAAE;AAEL,QAAM,SAAS,CAAC,YAAiE;AAC/E,UAAM,UAAU,UAAU;AAC1B,QAAI,CAAC,WAAW,CAAC,QAAS;AAE1B,QAAI,QAAQ,UAAU,OAAW,UAAS,UAAU,QAAQ;AAE5D,YAAQ,WAAW,SAAS;AAAA,MAC1B,GAAI,QAAQ,UAAU,SAAY,EAAE,QAAQ,QAAQ,MAAA,IAAU,CAAA;AAAA,MAC9D,GAAI,QAAQ,UAAU,SAAY,EAAE,OAAO,QAAQ,MAAA,IAAU,CAAA;AAAA,MAC7D,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,WAAW,CAAA;AAAA,IAAC,CAClE;AAAA,EACH;AAEA,QAAM,UAAU,MAAM;AACpB,UAAM,UAAU,UAAU;AAC1B,QAAI,WAAW,SAAS;AACtB,gBAAU,UAAU;AACpB,uBAAiB,IAAI;AACrB,UAAI;AACF,gBAAQ,YAAY,OAAO;AAAA,MAC7B,QAAQ;AAAA,MAER;AACA,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA,MAAM,gBAAgB;AAAA,EAAA;AAE1B;AAUA,SAAS,kBACP,SACA,QACyB;AACzB,MAAI,CAAC,WAAW,CAAC,QAAQ,gBAAiB,QAAO;AACjD,SAAO,QAAQ,gBAAgB,MAAM;AACvC;"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const PRIMITIVE_NODES = {
4
+ View: "view",
5
+ Text: "text",
6
+ Image: "image",
7
+ Sprite: "sprite",
8
+ VectorDrawContent: "vectorDraw",
9
+ NineSlice: "nineslice"
10
+ };
11
+ exports.PRIMITIVE_NODES = PRIMITIVE_NODES;
12
+ //# sourceMappingURL=primitive-contracts.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitive-contracts.cjs","sources":["../src/primitive-contracts.ts"],"sourcesContent":["/**\n * Portable Primitive Contracts — renderer-neutral prop interfaces for\n * View, Text, Image, and Sprite primitives.\n *\n * These contracts define the MINIMAL prop set that every renderer must\n * support. Each renderer package extends these with renderer-specific\n * props (e.g., Phaser adds PhaserProps and onReady).\n *\n * Portable Components MUST only import from this file and other Core modules.\n * They MUST NOT import from renderer-specific packages or Phaser directly.\n *\n * @migration(audit:scene-portal-mount) Phase 4B — defined in @number10/jsx-core.\n * Renderer implementations live in the renderer package.\n */\n\nimport type {\n BackgroundProps,\n GestureProps,\n LayoutProps,\n SizeValue,\n TransformProps,\n} from './core-props'\nimport type { PropsContainerExtension, PropsDefaultExtension } from './types'\n\n// ---------------------------------------------------------------------------\n// Primitive Host-Node Names\n// ---------------------------------------------------------------------------\n\n/**\n * Stable host-node type names for renderer-neutral primitives.\n *\n * These are the `type` values passed to `HostAdapter.create()` and used\n * in JSX as lowercase tags (`<view/>`, `<text/>`, ...).\n * Each renderer registers HostDescriptors for these names.\n *\n * Portable components MUST NOT use these directly in JSX.\n * They use the uppercase wrappers from `@number10/jsx-components/primitives`.\n */\nexport const PRIMITIVE_NODES = {\n View: 'view',\n Text: 'text',\n Image: 'image',\n Sprite: 'sprite',\n VectorDrawContent: 'vectorDraw',\n NineSlice: 'nineslice',\n} as const\n\n/** Host-node type names for renderer-neutral primitives */\nexport type PrimitiveNodeType = (typeof PRIMITIVE_NODES)[keyof typeof PRIMITIVE_NODES]\n\n// ---------------------------------------------------------------------------\n// View — the fundamental layout container\n// ---------------------------------------------------------------------------\n\n/**\n * Portable props for the View primitive.\n * Every renderer must support: layout, transform, background styling, and alpha.\n */\nexport interface ViewPrimitiveProps\n extends TransformProps, LayoutProps, BackgroundProps, GestureProps {\n /**\n * Alpha transparency of the element.\n * - 0: Fully transparent\n * - 1: Fully opaque (default)\n */\n alpha?: number\n}\n\n/**\n * Complete View props including JSX extensions.\n * The generic parameter allows renderers to specify their native node type for Refs.\n *\n * @typeParam TNode - The renderer's native node type (e.g., Phaser.GameObjects.Container)\n */\nexport interface ViewPrimitivePropsComplete<TNode = unknown>\n extends ViewPrimitiveProps, PropsDefaultExtension<TNode>, PropsContainerExtension {}\n\n// ---------------------------------------------------------------------------\n// Text — text rendering\n// ---------------------------------------------------------------------------\n\n/**\n * Portable text style — renderer-neutral text formatting.\n * Each renderer maps this to its native text style type.\n */\nexport interface PortableTextStyle {\n fontSize?: string | number\n fontFamily?: string\n fontStyle?: string\n fontWeight?: string | number\n color?: string | number\n backgroundColor?: string | number\n align?: 'left' | 'center' | 'right' | 'justify'\n lineHeight?: number\n shadow?:\n | {\n color?: string | number\n blur?: number\n offsetX?: number\n offsetY?: number\n alpha?: number\n fill?: boolean\n stroke?: boolean\n }\n | undefined\n}\n\n/**\n * Portable props for the Text primitive.\n */\nexport interface TextPrimitiveProps extends TransformProps, LayoutProps, GestureProps {\n /** The text content to display */\n text: string | undefined\n /** Maximum width for text wrapping */\n maxWidth?: SizeValue\n /** Portable text style — renderer maps to native style. Named 'style' for compat with Phaser's TextProps. */\n style?: PortableTextStyle | undefined\n}\n\n/**\n * Complete Text props including JSX extensions.\n */\nexport interface TextPrimitivePropsComplete<TNode = unknown>\n extends TextPrimitiveProps, PropsDefaultExtension<TNode>, PropsContainerExtension {}\n\n// ---------------------------------------------------------------------------\n// Image — texture-backed image display\n// ---------------------------------------------------------------------------\n\n/**\n * Portable props for the Image primitive.\n */\nexport interface ImagePrimitiveProps extends TransformProps, LayoutProps {\n /** Texture key or URL */\n texture: string\n /** Optional tint color */\n tint?: number | undefined\n /** Optional frame from texture atlas */\n frame?: string | undefined\n /**\n * Optional explicit source region within the texture/frame.\n *\n * When set, the image only renders this sub-rectangle of the source\n * texture. Coordinates are relative to the frame origin (if `frame`\n * is specified) or the texture origin otherwise.\n *\n * Enables portable NineSlice: each slice cell is an `<Image>` with\n * its own `sourceRect` computed from the frame geometry.\n */\n sourceRect?: { x: number; y: number; w: number; h: number } | undefined\n /** Display width — scaled rendering size (defaults to texture width) */\n displayWidth?: number | undefined\n /** Display height — scaled rendering size (defaults to texture height) */\n displayHeight?: number | undefined\n /**\n * How image fits within displayWidth/displayHeight bounds.\n * - 'fill': Stretch to fill (default, may distort aspect ratio)\n * - 'contain': Scale to fit within bounds, preserve aspect ratio\n * - 'cover': Scale to cover bounds, preserve aspect ratio (may crop)\n */\n fit?: 'fill' | 'contain' | 'cover' | undefined\n /** Origin X anchor point (0-1, default 0.5 for headless, 0 for layout) */\n originX?: number | undefined\n /** Origin Y anchor point (0-1, default 0.5 for headless, 0 for layout) */\n originY?: number | undefined\n}\n\n/**\n * Complete Image props including JSX extensions.\n */\nexport interface ImagePrimitivePropsComplete<TNode = unknown>\n extends ImagePrimitiveProps, PropsDefaultExtension<TNode> {}\n\n// ---------------------------------------------------------------------------\n// Sprite — animated texture-backed display\n// ---------------------------------------------------------------------------\n\n/**\n * Portable props for the Sprite primitive.\n */\nexport interface SpritePrimitiveProps extends TransformProps, LayoutProps {\n /** Texture key or URL */\n texture: string\n /** Optional tint color */\n tint?: number\n /** Optional frame from texture atlas */\n frame?: string\n /** Optional animation key to play on mount */\n anim?: string\n}\n\n/**\n * Complete Sprite props including JSX extensions.\n */\nexport interface SpritePrimitivePropsComplete<TNode = unknown>\n extends SpritePrimitiveProps, PropsDefaultExtension<TNode> {}\n\n// ---------------------------------------------------------------------------\n// Component Import Style — the contract for portable components\n// ---------------------------------------------------------------------------\n\n/**\n * Portable Components MUST follow this import style:\n *\n * ```typescript\n * // ✅ CORRECT — only Core imports\n * import type { ViewPrimitiveProps, TextPrimitiveProps } from '@number10/jsx-core'\n * import { useState, useEffect, useTheme } from '@number10/jsx-core'\n * import type { LayoutProps, TransformProps } from '@number10/jsx-core'\n * import type { VNode, Ref } from '@number10/jsx-core'\n *\n * // ✅ CORRECT — renderer provides the actual primitives\n * // (imported by the app, passed via JSX or context, not imported in the component)\n *\n * // ❌ WRONG — Phaser-specific imports in portable components\n * // import type { PhaserProps } from '@number10/jsx-phaser'\n * // import { useScene } from '@number10/jsx-phaser'\n * // import type * as Phaser from 'phaser'\n * ```\n */\n\n// ---------------------------------------------------------------------------\n// Optional Primitive Capabilities\n// ---------------------------------------------------------------------------\n\n/**\n * The following primitives are OPTIONAL capabilities. A renderer MAY support\n * them. Portable components SHOULD NOT depend on them unless the capability\n * is explicitly checked via `SurfaceContext.capabilities`.\n *\n * | Primitive | Capability Key | Type in PRIMITIVE_NODES | Notes |\n * |-------------|-----------------|-------------------------|-------|\n * | NineSlice | `nineslice` | `'nineslice'` | First-class primitive in PRIMITIVE_NODES; single-node native 9-slice |\n * | TileSprite | `tilesprite` | — | Tiled texture background |\n * | VectorDraw | `drawing` | `'vectorDraw'` | Portable imperative drawing API |\n * | Particles | `particles` | — | Renderer-native particle system |\n *\n * Portable components that need these should:\n * 1. Check `SurfaceContext.capabilities.has('nineslice')` before use\n * 2. Provide a fallback (e.g., use View+Image instead of NineSlice)\n * 3. OR accept them as optional child/overlay via props\n */\n\nexport {\n computeSourceSlices,\n computeTargetXs,\n computeTargetYs,\n deriveTargetRects,\n} from './nineslice-contract'\nexport type {\n NineSlicePrimitiveProps,\n NineSlicePrimitivePropsComplete,\n SliceRect,\n} from './nineslice-contract'\n\nexport type { VectorDrawContentPrimitivePropsComplete } from './vector-draw-context'\n"],"names":[],"mappings":";;AAsCO,MAAM,kBAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,mBAAmB;AAAA,EACnB,WAAW;AACb;;"}