react-native-gtkx 0.1.0-alpha.4 → 0.2.0-alpha.2

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 (257) hide show
  1. package/README.md +22 -9
  2. package/dist/adw/index.d.ts +3 -0
  3. package/dist/adw/index.js +41 -0
  4. package/dist/adw/index.js.map +1 -0
  5. package/dist/adw/widgets.generated.d.ts +63 -0
  6. package/dist/adw/widgets.generated.js +125 -0
  7. package/dist/adw/widgets.generated.js.map +1 -0
  8. package/dist/animated/create-animated.d.ts +2 -0
  9. package/dist/animated/create-animated.js +2 -0
  10. package/dist/animated/create-animated.js.map +1 -1
  11. package/dist/animated/index.d.ts +2 -0
  12. package/dist/animated/index.js +1 -0
  13. package/dist/animated/index.js.map +1 -1
  14. package/dist/animated/value-xy.d.ts +41 -0
  15. package/dist/animated/value-xy.js +90 -0
  16. package/dist/animated/value-xy.js.map +1 -0
  17. package/dist/animated/value.d.ts +9 -0
  18. package/dist/animated/value.js +30 -2
  19. package/dist/animated/value.js.map +1 -1
  20. package/dist/common/content.d.ts +69 -0
  21. package/dist/common/content.js +84 -0
  22. package/dist/common/content.js.map +1 -0
  23. package/dist/common/icon.d.ts +23 -0
  24. package/dist/common/icon.js +14 -0
  25. package/dist/common/icon.js.map +1 -0
  26. package/dist/common/index.d.ts +5 -0
  27. package/dist/common/index.js +21 -0
  28. package/dist/common/index.js.map +1 -0
  29. package/dist/common/list.d.ts +82 -0
  30. package/dist/common/list.js +166 -0
  31. package/dist/common/list.js.map +1 -0
  32. package/dist/common/navigation-stack.d.ts +84 -0
  33. package/dist/common/navigation-stack.js +313 -0
  34. package/dist/common/navigation-stack.js.map +1 -0
  35. package/dist/common/widget.d.ts +62 -0
  36. package/dist/common/widget.js +209 -0
  37. package/dist/common/widget.js.map +1 -0
  38. package/dist/components/animated.d.ts +7 -4
  39. package/dist/components/animated.js +88 -67
  40. package/dist/components/animated.js.map +1 -1
  41. package/dist/components/app-registry.d.ts +36 -1
  42. package/dist/components/app-registry.js +8 -3
  43. package/dist/components/app-registry.js.map +1 -1
  44. package/dist/components/host-node.d.ts +7 -0
  45. package/dist/components/host-node.js +26 -1
  46. package/dist/components/host-node.js.map +1 -1
  47. package/dist/components/index.d.ts +6 -2
  48. package/dist/components/index.js +2 -1
  49. package/dist/components/index.js.map +1 -1
  50. package/dist/components/measure.d.ts +13 -0
  51. package/dist/components/measure.js +51 -0
  52. package/dist/components/measure.js.map +1 -0
  53. package/dist/components/modal.d.ts +1 -1
  54. package/dist/components/press-event.d.ts +28 -0
  55. package/dist/components/press-event.js +46 -0
  56. package/dist/components/press-event.js.map +1 -0
  57. package/dist/components/pressable.d.ts +2 -6
  58. package/dist/components/pressable.js +119 -20
  59. package/dist/components/pressable.js.map +1 -1
  60. package/dist/components/rect-store.d.ts +11 -1
  61. package/dist/components/rect-store.js +27 -3
  62. package/dist/components/rect-store.js.map +1 -1
  63. package/dist/components/scroll-view.d.ts +4 -0
  64. package/dist/components/scroll-view.js +8 -2
  65. package/dist/components/scroll-view.js.map +1 -1
  66. package/dist/components/svg/animated-support.d.ts +19 -0
  67. package/dist/components/svg/animated-support.js +64 -0
  68. package/dist/components/svg/animated-support.js.map +1 -0
  69. package/dist/components/svg/context.d.ts +8 -0
  70. package/dist/components/svg/context.js +15 -0
  71. package/dist/components/svg/context.js.map +1 -0
  72. package/dist/components/svg/gradients.d.ts +31 -0
  73. package/dist/components/svg/gradients.js +93 -0
  74. package/dist/components/svg/gradients.js.map +1 -0
  75. package/dist/components/svg/group.d.ts +8 -0
  76. package/dist/components/svg/group.js +30 -0
  77. package/dist/components/svg/group.js.map +1 -0
  78. package/dist/components/svg/index.d.ts +6 -0
  79. package/dist/components/svg/index.js +5 -0
  80. package/dist/components/svg/index.js.map +1 -0
  81. package/dist/components/svg/node.d.ts +7 -0
  82. package/dist/components/svg/node.js +7 -0
  83. package/dist/components/svg/node.js.map +1 -0
  84. package/dist/components/svg/paint.d.ts +24 -0
  85. package/dist/components/svg/paint.js +57 -0
  86. package/dist/components/svg/paint.js.map +1 -0
  87. package/dist/components/svg/shapes.d.ts +47 -0
  88. package/dist/components/svg/shapes.js +51 -0
  89. package/dist/components/svg/shapes.js.map +1 -0
  90. package/dist/components/svg/svg.d.ts +14 -0
  91. package/dist/components/svg/svg.js +48 -0
  92. package/dist/components/svg/svg.js.map +1 -0
  93. package/dist/components/svg/use-shape-node.d.ts +12 -0
  94. package/dist/components/svg/use-shape-node.js +52 -0
  95. package/dist/components/svg/use-shape-node.js.map +1 -0
  96. package/dist/components/text-input.js +1 -1
  97. package/dist/components/text-input.js.map +1 -1
  98. package/dist/components/text.js +36 -3
  99. package/dist/components/text.js.map +1 -1
  100. package/dist/components/use-layout-child.js +50 -4
  101. package/dist/components/use-layout-child.js.map +1 -1
  102. package/dist/components/view.d.ts +9 -4
  103. package/dist/components/view.js +8 -2
  104. package/dist/components/view.js.map +1 -1
  105. package/dist/contracts.d.ts +31 -4
  106. package/dist/contracts.js.map +1 -1
  107. package/dist/gtk/index.d.ts +7 -0
  108. package/dist/gtk/index.js +46 -0
  109. package/dist/gtk/index.js.map +1 -0
  110. package/dist/gtk/widgets.generated.d.ts +103 -0
  111. package/dist/gtk/widgets.generated.js +205 -0
  112. package/dist/gtk/widgets.generated.js.map +1 -0
  113. package/dist/gtk/window-actions.d.ts +77 -0
  114. package/dist/gtk/window-actions.js +193 -0
  115. package/dist/gtk/window-actions.js.map +1 -0
  116. package/dist/gtkx/bridge/geometry.d.ts +15 -0
  117. package/dist/gtkx/bridge/geometry.js +38 -0
  118. package/dist/gtkx/bridge/geometry.js.map +1 -0
  119. package/dist/gtkx/bridge/index.d.ts +11 -4
  120. package/dist/gtkx/bridge/index.js +47 -6
  121. package/dist/gtkx/bridge/index.js.map +1 -1
  122. package/dist/gtkx/bridge/layout-manager.d.ts +2 -1
  123. package/dist/gtkx/bridge/layout-manager.js +31 -9
  124. package/dist/gtkx/bridge/layout-manager.js.map +1 -1
  125. package/dist/gtkx/bridge/slot-portal.d.ts +12 -0
  126. package/dist/gtkx/bridge/slot-portal.js +17 -0
  127. package/dist/gtkx/bridge/slot-portal.js.map +1 -0
  128. package/dist/gtkx/bridge/svg-node.d.ts +79 -0
  129. package/dist/gtkx/bridge/svg-node.js +323 -0
  130. package/dist/gtkx/bridge/svg-node.js.map +1 -0
  131. package/dist/gtkx/bridge/use-signal.js +19 -9
  132. package/dist/gtkx/bridge/use-signal.js.map +1 -1
  133. package/dist/gtkx/bridge/widgets.generated.d.ts +2 -0
  134. package/dist/gtkx/bridge/widgets.generated.js +14 -0
  135. package/dist/gtkx/bridge/widgets.generated.js.map +1 -0
  136. package/dist/index.d.ts +2 -2
  137. package/dist/index.js +1 -1
  138. package/dist/index.js.map +1 -1
  139. package/dist/mcp/bin.d.ts +2 -0
  140. package/dist/mcp/bin.js +17 -0
  141. package/dist/mcp/bin.js.map +1 -0
  142. package/dist/mcp/data/generated.d.ts +1304 -0
  143. package/dist/mcp/data/generated.js +1179 -0
  144. package/dist/mcp/data/generated.js.map +1 -0
  145. package/dist/mcp/index.d.ts +5 -0
  146. package/dist/mcp/index.js +9 -0
  147. package/dist/mcp/index.js.map +1 -0
  148. package/dist/mcp/list.d.ts +4 -0
  149. package/dist/mcp/list.js +56 -0
  150. package/dist/mcp/list.js.map +1 -0
  151. package/dist/mcp/resolve.d.ts +55 -0
  152. package/dist/mcp/resolve.js +151 -0
  153. package/dist/mcp/resolve.js.map +1 -0
  154. package/dist/mcp/search.d.ts +13 -0
  155. package/dist/mcp/search.js +48 -0
  156. package/dist/mcp/search.js.map +1 -0
  157. package/dist/mcp/server.d.ts +7 -0
  158. package/dist/mcp/server.js +135 -0
  159. package/dist/mcp/server.js.map +1 -0
  160. package/dist/mcp/tool.d.ts +21 -0
  161. package/dist/mcp/tool.js +36 -0
  162. package/dist/mcp/tool.js.map +1 -0
  163. package/dist/metro/index.js +10 -0
  164. package/dist/metro/index.js.map +1 -1
  165. package/dist/navigation/index.d.ts +50 -13
  166. package/dist/navigation/index.js +183 -148
  167. package/dist/navigation/index.js.map +1 -1
  168. package/dist/navigation/option-warnings.js +9 -1
  169. package/dist/navigation/option-warnings.js.map +1 -1
  170. package/dist/navigation/sidebar.d.ts +207 -9
  171. package/dist/navigation/sidebar.js +294 -10
  172. package/dist/navigation/sidebar.js.map +1 -1
  173. package/dist/responder/system.d.ts +27 -0
  174. package/dist/responder/system.js +184 -0
  175. package/dist/responder/system.js.map +1 -0
  176. package/dist/responder/touch-history.d.ts +10 -0
  177. package/dist/responder/touch-history.js +78 -0
  178. package/dist/responder/touch-history.js.map +1 -0
  179. package/dist/responder/types.d.ts +63 -0
  180. package/dist/responder/types.js +29 -0
  181. package/dist/responder/types.js.map +1 -0
  182. package/dist/responder/use-responder.d.ts +12 -0
  183. package/dist/responder/use-responder.js +98 -0
  184. package/dist/responder/use-responder.js.map +1 -0
  185. package/dist/runner/host-dev.js +3 -1
  186. package/dist/runner/host-dev.js.map +1 -1
  187. package/dist/runner/host.js +5 -1
  188. package/dist/runner/host.js.map +1 -1
  189. package/dist/runner/index.d.ts +22 -2
  190. package/dist/runner/index.js +163 -13
  191. package/dist/runner/index.js.map +1 -1
  192. package/dist/sea/assemble.d.ts +29 -0
  193. package/dist/sea/assemble.js +129 -0
  194. package/dist/sea/assemble.js.map +1 -0
  195. package/dist/sea/bundle.d.ts +61 -0
  196. package/dist/sea/bundle.js +202 -0
  197. package/dist/sea/bundle.js.map +1 -0
  198. package/dist/sea/gtkx-config-module.d.ts +18 -0
  199. package/dist/sea/gtkx-config-module.js +67 -0
  200. package/dist/sea/gtkx-config-module.js.map +1 -0
  201. package/dist/sea/native-shim.d.ts +40 -0
  202. package/dist/sea/native-shim.js +121 -0
  203. package/dist/sea/native-shim.js.map +1 -0
  204. package/dist/sea/react-anchor-plugin.d.ts +2 -0
  205. package/dist/sea/react-anchor-plugin.js +30 -0
  206. package/dist/sea/react-anchor-plugin.js.map +1 -0
  207. package/dist/style/box-shadow.d.ts +7 -0
  208. package/dist/style/box-shadow.js +197 -0
  209. package/dist/style/box-shadow.js.map +1 -0
  210. package/dist/style/index.d.ts +3 -0
  211. package/dist/style/index.js +3 -0
  212. package/dist/style/index.js.map +1 -1
  213. package/dist/style/split-style.d.ts +5 -2
  214. package/dist/style/split-style.js +33 -5
  215. package/dist/style/split-style.js.map +1 -1
  216. package/dist/style/text-decoration.d.ts +15 -0
  217. package/dist/style/text-decoration.js +26 -0
  218. package/dist/style/text-decoration.js.map +1 -0
  219. package/dist/style/transform.d.ts +22 -0
  220. package/dist/style/transform.js +137 -0
  221. package/dist/style/transform.js.map +1 -0
  222. package/dist/style/visual-css.js +40 -3
  223. package/dist/style/visual-css.js.map +1 -1
  224. package/dist/svg/geometry.d.ts +19 -0
  225. package/dist/svg/geometry.js +80 -0
  226. package/dist/svg/geometry.js.map +1 -0
  227. package/dist/svg/gradient-geometry.d.ts +15 -0
  228. package/dist/svg/gradient-geometry.js +15 -0
  229. package/dist/svg/gradient-geometry.js.map +1 -0
  230. package/dist/svg/transform.d.ts +22 -0
  231. package/dist/svg/transform.js +74 -0
  232. package/dist/svg/transform.js.map +1 -0
  233. package/dist/svg/view-box.d.ts +34 -0
  234. package/dist/svg/view-box.js +108 -0
  235. package/dist/svg/view-box.js.map +1 -0
  236. package/dist/svg-compat/index.d.ts +1 -0
  237. package/dist/svg-compat/index.js +16 -0
  238. package/dist/svg-compat/index.js.map +1 -0
  239. package/dist/testing/index.d.ts +14 -0
  240. package/dist/testing/index.js +14 -0
  241. package/dist/testing/index.js.map +1 -0
  242. package/dist/vendor/react-native/pan-responder.d.ts +102 -0
  243. package/dist/vendor/react-native/pan-responder.js +196 -0
  244. package/dist/vendor/react-native/pan-responder.js.map +1 -0
  245. package/dist/vendor/react-native/touch-history-math.d.ts +18 -0
  246. package/dist/vendor/react-native/touch-history-math.js +102 -0
  247. package/dist/vendor/react-native/touch-history-math.js.map +1 -0
  248. package/dist/vite/index.d.ts +7 -3
  249. package/dist/vite/index.js +16 -4
  250. package/dist/vite/index.js.map +1 -1
  251. package/dist/vitest/index.d.ts +52 -0
  252. package/dist/vitest/index.js +58 -0
  253. package/dist/vitest/index.js.map +1 -0
  254. package/dist/vitest/setup.d.ts +4 -0
  255. package/dist/vitest/setup.js +22 -0
  256. package/dist/vitest/setup.js.map +1 -0
  257. package/package.json +52 -13
@@ -0,0 +1,121 @@
1
+ // The gtkx native addon (@gtkx/native-<platform>-<libc>, a Rust/NAPI addon
2
+ // loaded through dlopen — see docs/getting-started.md's "Shipping an app"
3
+ // section for the full design reasoning) cannot be embedded as bundled JS:
4
+ // a Node SEA is a V8 code cache blob, and dlopen needs a real file on a
5
+ // real filesystem, not bytes inside the executable.
6
+ //
7
+ // So it has to be carried as bytes and written back to disk before use.
8
+ // This plugin swaps every import of a given specifier (in practice,
9
+ // "@gtkx/native" — see ../sea/bundle.ts) for a shim that does exactly
10
+ // that: extract to a per-user cache directory (content-hash-keyed, so
11
+ // repeat launches reuse the file instead of re-extracting ~1.6 MB every
12
+ // start) and dlopen it from there. Where the bytes come from is the one
13
+ // difference between the two single-file artifacts — Node's SEA "assets"
14
+ // mechanism for the executable, a base64 literal for the standalone .cjs;
15
+ // see {@link NativeAddonSource}.
16
+ //
17
+ // Cache location: XDG_CACHE_HOME (or ~/.cache) first, os.tmpdir() as a
18
+ // fallback for a read-only $HOME. Deliberately NOT the SEA executable's
19
+ // own directory — that may be read-only (a mounted image, a root-owned
20
+ // install under /opt) or simply a different place than where the addon
21
+ // was extracted the first time, if the binary moved. This is the same
22
+ // question gtkx's own tutorial answers by placing the addon BESIDE the
23
+ // executable (dist/gtkx.node next to dist/app) — a real divergence, not an
24
+ // oversight: that keeps two files, which is exactly what this epic set
25
+ // out to stop doing.
26
+ import { join } from "node:path";
27
+ export const NATIVE_ASSET_KEY = "gtkx-native.node";
28
+ const bytesExpression = (source, assetKey) => source === "sea-asset"
29
+ ? `Buffer.from(require("node:sea").getAsset(${JSON.stringify(assetKey)}))`
30
+ : `Buffer.from(__gtkxNativeBase64, "base64")`;
31
+ const shimSource = (source, assetKey, base64) => `
32
+ const { createHash } = require("node:crypto");
33
+ const { mkdirSync, existsSync, writeFileSync, renameSync } = require("node:fs");
34
+ const { join } = require("node:path");
35
+ const os = require("node:os");
36
+ ${source === "inline" ? `const __gtkxNativeBase64 = ${JSON.stringify(base64)};` : ""}
37
+
38
+ const bytes = ${bytesExpression(source, assetKey)};
39
+ const hash = createHash("sha256").update(bytes).digest("hex").slice(0, 16);
40
+ const fileName = "native-" + hash + ".node";
41
+
42
+ const candidateDirs = [
43
+ join(process.env.XDG_CACHE_HOME || join(os.homedir(), ".cache"), "react-native-gtkx-sea"),
44
+ join(os.tmpdir(), "react-native-gtkx-sea"),
45
+ ];
46
+
47
+ let target;
48
+ let lastError;
49
+ for (const dir of candidateDirs) {
50
+ try {
51
+ mkdirSync(dir, { recursive: true });
52
+ const candidate = join(dir, fileName);
53
+ if (!existsSync(candidate)) {
54
+ const tmp = candidate + "." + process.pid + ".tmp";
55
+ writeFileSync(tmp, bytes, { mode: 0o755 });
56
+ renameSync(tmp, candidate);
57
+ }
58
+ target = candidate;
59
+ break;
60
+ } catch (error) {
61
+ lastError = error;
62
+ }
63
+ }
64
+ if (!target) {
65
+ throw new Error(
66
+ "react-native-gtkx: could not extract the native addon to a writable " +
67
+ "cache directory (tried " + candidateDirs.join(", ") + "): " + String(lastError),
68
+ );
69
+ }
70
+
71
+ // Not require(target): the require() available to a SEA's main script is
72
+ // restricted to built-ins and embedded assets (verified — requiring an
73
+ // arbitrary absolute path from here throws ERR_UNKNOWN_BUILTIN_MODULE, the
74
+ // same error Node throws for an unrecognized built-in specifier). That
75
+ // restriction doesn't apply to process.dlopen(), the lower-level
76
+ // primitive require() itself uses for ".node" files (Module._extensions
77
+ // [".node"]) — it works on any real path, which is exactly what makes
78
+ // this shim work at all. The "inline" build could use a plain require()
79
+ // here, but shares dlopen() rather than branching: one code path that is
80
+ // proven by both artifacts beats two, one of which is rarely exercised.
81
+ const nativeModule = { exports: {} };
82
+ process.dlopen(nativeModule, target);
83
+ // The vite path's own build (dist/bundle.js, gtkx's CLI plugin) calls
84
+ // this immediately after loading the addon, before anything else touches
85
+ // it; @gtkx/runtime's compiled JS never calls it itself (confirmed by
86
+ // inspecting its dist — it only re-exports a handful of low-level
87
+ // functions), so ordinary require() of the addon through Node's own
88
+ // napi module registration must be relying on something dlopen() alone
89
+ // does not trigger. Without this call the addon has no thread registered
90
+ // as "the Node environment's owner" and the first GTK-driven callback
91
+ // into JS panics: "the Node environment was accessed from a thread it is
92
+ // not installed on" (reproduced and fixed by adding this line).
93
+ nativeModule.exports.init();
94
+ module.exports = nativeModule.exports;
95
+ `;
96
+ /**
97
+ * Intercepts every import/require of `specifier` (an exact bare module
98
+ * specifier, e.g. "@gtkx/native") and replaces it with the extraction shim
99
+ * above. The real module never reaches the bundle graph — only its
100
+ * re-exported surface, loaded from the extracted file at runtime.
101
+ *
102
+ * The shim is given an id inside `appRoot` with a `.cjs` extension rather
103
+ * than a `\0`-prefixed virtual one: the shim IS CommonJS (it assigns
104
+ * `module.exports` so consumers' NAMED imports of the addon still resolve
105
+ * through the bundler's interop), and a real directory is what lets any
106
+ * specifier inside it resolve normally.
107
+ */
108
+ export const nativeAddonShimPlugin = (options) => {
109
+ const { specifier, appRoot, source } = options;
110
+ if (source === "inline" && !options.addonBytes) {
111
+ throw new Error('the "inline" native addon source needs addonBytes');
112
+ }
113
+ const shimId = join(appRoot, "__gtkx-sea-native-shim.cjs");
114
+ const contents = shimSource(source, options.assetKey ?? NATIVE_ASSET_KEY, options.addonBytes?.toString("base64") ?? "");
115
+ return {
116
+ name: "gtkx-native-addon-shim",
117
+ resolveId: (id) => (id === specifier ? shimId : null),
118
+ load: (id) => (id === shimId ? contents : null),
119
+ };
120
+ };
121
+ //# sourceMappingURL=native-shim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-shim.js","sourceRoot":"","sources":["../../src/sea/native-shim.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,0EAA0E;AAC1E,2EAA2E;AAC3E,wEAAwE;AACxE,oDAAoD;AACpD,EAAE;AACF,wEAAwE;AACxE,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,iCAAiC;AACjC,EAAE;AACF,uEAAuE;AACvE,wEAAwE;AACxE,uEAAuE;AACvE,uEAAuE;AACvE,sEAAsE;AACtE,uEAAuE;AACvE,2EAA2E;AAC3E,uEAAuE;AACvE,qBAAqB;AACrB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAGhC,MAAM,CAAC,MAAM,gBAAgB,GAAG,kBAAkB,CAAA;AAgBlD,MAAM,eAAe,GAAG,CACtB,MAAyB,EACzB,QAAgB,EACR,EAAE,CACV,MAAM,KAAK,WAAW;IACpB,CAAC,CAAC,4CAA4C,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;IAC1E,CAAC,CAAC,2CAA2C,CAAA;AAEjD,MAAM,UAAU,GAAG,CACjB,MAAyB,EACzB,QAAgB,EAChB,MAAc,EACN,EAAE,CAAC;;;;;EAKX,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,8BAA8B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;;gBAEpE,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyDhD,CAAA;AAeD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,OAA+B,EACvB,EAAE;IACV,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAA;IAC9C,IAAI,MAAM,KAAK,QAAQ,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAA;IACtE,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAA;IAC1D,MAAM,QAAQ,GAAG,UAAU,CACzB,MAAM,EACN,OAAO,CAAC,QAAQ,IAAI,gBAAgB,EACpC,OAAO,CAAC,UAAU,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,CAC7C,CAAA;IACD,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QACrD,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;KAChD,CAAA;AACH,CAAC,CAAA","sourcesContent":["// The gtkx native addon (@gtkx/native-<platform>-<libc>, a Rust/NAPI addon\n// loaded through dlopen — see docs/getting-started.md's \"Shipping an app\"\n// section for the full design reasoning) cannot be embedded as bundled JS:\n// a Node SEA is a V8 code cache blob, and dlopen needs a real file on a\n// real filesystem, not bytes inside the executable.\n//\n// So it has to be carried as bytes and written back to disk before use.\n// This plugin swaps every import of a given specifier (in practice,\n// \"@gtkx/native\" — see ../sea/bundle.ts) for a shim that does exactly\n// that: extract to a per-user cache directory (content-hash-keyed, so\n// repeat launches reuse the file instead of re-extracting ~1.6 MB every\n// start) and dlopen it from there. Where the bytes come from is the one\n// difference between the two single-file artifacts — Node's SEA \"assets\"\n// mechanism for the executable, a base64 literal for the standalone .cjs;\n// see {@link NativeAddonSource}.\n//\n// Cache location: XDG_CACHE_HOME (or ~/.cache) first, os.tmpdir() as a\n// fallback for a read-only $HOME. Deliberately NOT the SEA executable's\n// own directory — that may be read-only (a mounted image, a root-owned\n// install under /opt) or simply a different place than where the addon\n// was extracted the first time, if the binary moved. This is the same\n// question gtkx's own tutorial answers by placing the addon BESIDE the\n// executable (dist/gtkx.node next to dist/app) — a real divergence, not an\n// oversight: that keeps two files, which is exactly what this epic set\n// out to stop doing.\nimport { join } from \"node:path\"\nimport type { Plugin } from \"rolldown\"\n\nexport const NATIVE_ASSET_KEY = \"gtkx-native.node\"\n\n/**\n * Where the shim gets the addon's bytes from — the only difference between\n * the two single-file artifacts:\n *\n * - `\"sea-asset\"`: from the SEA blob, via node:sea. Only works inside a\n * real single executable.\n * - `\"inline\"`: from a base64 literal in the bundle itself, so the CJS\n * file is self-contained under a plain `node app.cjs` — the same\n * artifact minus the embedded Node runtime. Costs ~4/3 of the addon's\n * size in source text (the addon is ~1.6 MB) and is parsed once at\n * startup; the extraction and dlopen below are shared.\n */\nexport type NativeAddonSource = \"sea-asset\" | \"inline\"\n\nconst bytesExpression = (\n source: NativeAddonSource,\n assetKey: string,\n): string =>\n source === \"sea-asset\"\n ? `Buffer.from(require(\"node:sea\").getAsset(${JSON.stringify(assetKey)}))`\n : `Buffer.from(__gtkxNativeBase64, \"base64\")`\n\nconst shimSource = (\n source: NativeAddonSource,\n assetKey: string,\n base64: string,\n): string => `\nconst { createHash } = require(\"node:crypto\");\nconst { mkdirSync, existsSync, writeFileSync, renameSync } = require(\"node:fs\");\nconst { join } = require(\"node:path\");\nconst os = require(\"node:os\");\n${source === \"inline\" ? `const __gtkxNativeBase64 = ${JSON.stringify(base64)};` : \"\"}\n\nconst bytes = ${bytesExpression(source, assetKey)};\nconst hash = createHash(\"sha256\").update(bytes).digest(\"hex\").slice(0, 16);\nconst fileName = \"native-\" + hash + \".node\";\n\nconst candidateDirs = [\n join(process.env.XDG_CACHE_HOME || join(os.homedir(), \".cache\"), \"react-native-gtkx-sea\"),\n join(os.tmpdir(), \"react-native-gtkx-sea\"),\n];\n\nlet target;\nlet lastError;\nfor (const dir of candidateDirs) {\n try {\n mkdirSync(dir, { recursive: true });\n const candidate = join(dir, fileName);\n if (!existsSync(candidate)) {\n const tmp = candidate + \".\" + process.pid + \".tmp\";\n writeFileSync(tmp, bytes, { mode: 0o755 });\n renameSync(tmp, candidate);\n }\n target = candidate;\n break;\n } catch (error) {\n lastError = error;\n }\n}\nif (!target) {\n throw new Error(\n \"react-native-gtkx: could not extract the native addon to a writable \" +\n \"cache directory (tried \" + candidateDirs.join(\", \") + \"): \" + String(lastError),\n );\n}\n\n// Not require(target): the require() available to a SEA's main script is\n// restricted to built-ins and embedded assets (verified — requiring an\n// arbitrary absolute path from here throws ERR_UNKNOWN_BUILTIN_MODULE, the\n// same error Node throws for an unrecognized built-in specifier). That\n// restriction doesn't apply to process.dlopen(), the lower-level\n// primitive require() itself uses for \".node\" files (Module._extensions\n// [\".node\"]) — it works on any real path, which is exactly what makes\n// this shim work at all. The \"inline\" build could use a plain require()\n// here, but shares dlopen() rather than branching: one code path that is\n// proven by both artifacts beats two, one of which is rarely exercised.\nconst nativeModule = { exports: {} };\nprocess.dlopen(nativeModule, target);\n// The vite path's own build (dist/bundle.js, gtkx's CLI plugin) calls\n// this immediately after loading the addon, before anything else touches\n// it; @gtkx/runtime's compiled JS never calls it itself (confirmed by\n// inspecting its dist — it only re-exports a handful of low-level\n// functions), so ordinary require() of the addon through Node's own\n// napi module registration must be relying on something dlopen() alone\n// does not trigger. Without this call the addon has no thread registered\n// as \"the Node environment's owner\" and the first GTK-driven callback\n// into JS panics: \"the Node environment was accessed from a thread it is\n// not installed on\" (reproduced and fixed by adding this line).\nnativeModule.exports.init();\nmodule.exports = nativeModule.exports;\n`\n\nexport type NativeAddonShimOptions = {\n /** The bare specifier to replace, in practice \"@gtkx/native\". */\n specifier: string\n /** The app root — the shim's id lives here, see above. */\n appRoot: string\n /** Where the addon's bytes come from at runtime. */\n source: NativeAddonSource\n /** SEA asset key; unused when `source` is \"inline\". */\n assetKey?: string\n /** The addon's bytes; required when `source` is \"inline\". */\n addonBytes?: Buffer\n}\n\n/**\n * Intercepts every import/require of `specifier` (an exact bare module\n * specifier, e.g. \"@gtkx/native\") and replaces it with the extraction shim\n * above. The real module never reaches the bundle graph — only its\n * re-exported surface, loaded from the extracted file at runtime.\n *\n * The shim is given an id inside `appRoot` with a `.cjs` extension rather\n * than a `\\0`-prefixed virtual one: the shim IS CommonJS (it assigns\n * `module.exports` so consumers' NAMED imports of the addon still resolve\n * through the bundler's interop), and a real directory is what lets any\n * specifier inside it resolve normally.\n */\nexport const nativeAddonShimPlugin = (\n options: NativeAddonShimOptions,\n): Plugin => {\n const { specifier, appRoot, source } = options\n if (source === \"inline\" && !options.addonBytes) {\n throw new Error('the \"inline\" native addon source needs addonBytes')\n }\n const shimId = join(appRoot, \"__gtkx-sea-native-shim.cjs\")\n const contents = shimSource(\n source,\n options.assetKey ?? NATIVE_ASSET_KEY,\n options.addonBytes?.toString(\"base64\") ?? \"\",\n )\n return {\n name: \"gtkx-native-addon-shim\",\n resolveId: (id) => (id === specifier ? shimId : null),\n load: (id) => (id === shimId ? contents : null),\n }\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from "rolldown";
2
+ export declare const reactAnchorPlugin: (appRoot: string) => Plugin;
@@ -0,0 +1,30 @@
1
+ // react (and its jsx runtimes) must resolve to the exact same instance
2
+ // @gtkx/react's reconciler uses — the reconciler and the app share one
3
+ // React, and anchoring resolution at the app's own node_modules can pick
4
+ // up a second copy (npm's peer-dependency auto-install, or a differently
5
+ // hoisted layout). runner/host.ts hits the identical problem and solves it
6
+ // the same way: resolve "react"/"react/jsx-runtime"/"react/jsx-dev-runtime"
7
+ // from @gtkx/react's own location, not the app's.
8
+ //
9
+ // Confirmed necessary empirically, not just by analogy with host.ts:
10
+ // without this plugin the built SEA threw React's "Invalid hook call"
11
+ // (two React module instances in the bundle graph) — the bundler had
12
+ // resolved the entry's top-level `import("react")` from the app root and
13
+ // @gtkx/react's own internal `import "react"` from @gtkx/react's
14
+ // directory, landing on two different files were the two roots not
15
+ // deduplicated to the same node_modules/react.
16
+ import { createRequire } from "node:module";
17
+ const REACT_SPECIFIERS = new Set([
18
+ "react",
19
+ "react/jsx-runtime",
20
+ "react/jsx-dev-runtime",
21
+ ]);
22
+ export const reactAnchorPlugin = (appRoot) => {
23
+ const appRequire = createRequire(`${appRoot}/package.json`);
24
+ const fromGtkxReact = createRequire(appRequire.resolve("@gtkx/react"));
25
+ return {
26
+ name: "gtkx-react-anchor",
27
+ resolveId: (source) => REACT_SPECIFIERS.has(source) ? fromGtkxReact.resolve(source) : null,
28
+ };
29
+ };
30
+ //# sourceMappingURL=react-anchor-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react-anchor-plugin.js","sourceRoot":"","sources":["../../src/sea/react-anchor-plugin.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,uEAAuE;AACvE,yEAAyE;AACzE,yEAAyE;AACzE,2EAA2E;AAC3E,4EAA4E;AAC5E,kDAAkD;AAClD,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,qEAAqE;AACrE,yEAAyE;AACzE,iEAAiE;AACjE,mEAAmE;AACnE,+CAA+C;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAG3C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC;IAC/B,OAAO;IACP,mBAAmB;IACnB,uBAAuB;CACxB,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAe,EAAU,EAAE;IAC3D,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,OAAO,eAAe,CAAC,CAAA;IAC3D,MAAM,aAAa,GAAG,aAAa,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAA;IACtE,OAAO;QACL,IAAI,EAAE,mBAAmB;QACzB,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CACpB,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI;KACtE,CAAA;AACH,CAAC,CAAA","sourcesContent":["// react (and its jsx runtimes) must resolve to the exact same instance\n// @gtkx/react's reconciler uses — the reconciler and the app share one\n// React, and anchoring resolution at the app's own node_modules can pick\n// up a second copy (npm's peer-dependency auto-install, or a differently\n// hoisted layout). runner/host.ts hits the identical problem and solves it\n// the same way: resolve \"react\"/\"react/jsx-runtime\"/\"react/jsx-dev-runtime\"\n// from @gtkx/react's own location, not the app's.\n//\n// Confirmed necessary empirically, not just by analogy with host.ts:\n// without this plugin the built SEA threw React's \"Invalid hook call\"\n// (two React module instances in the bundle graph) — the bundler had\n// resolved the entry's top-level `import(\"react\")` from the app root and\n// @gtkx/react's own internal `import \"react\"` from @gtkx/react's\n// directory, landing on two different files were the two roots not\n// deduplicated to the same node_modules/react.\nimport { createRequire } from \"node:module\"\nimport type { Plugin } from \"rolldown\"\n\nconst REACT_SPECIFIERS = new Set([\n \"react\",\n \"react/jsx-runtime\",\n \"react/jsx-dev-runtime\",\n])\n\nexport const reactAnchorPlugin = (appRoot: string): Plugin => {\n const appRequire = createRequire(`${appRoot}/package.json`)\n const fromGtkxReact = createRequire(appRequire.resolve(\"@gtkx/react\"))\n return {\n name: \"gtkx-react-anchor\",\n resolveId: (source) =>\n REACT_SPECIFIERS.has(source) ? fromGtkxReact.resolve(source) : null,\n }\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import type { BoxShadowValue } from "../contracts";
2
+ /**
3
+ * Renders RN's `boxShadow` as a GTK CSS `box-shadow` value, or null when
4
+ * nothing usable came out of it (the caller then emits no declaration).
5
+ * Both RN forms are accepted; the string is parsed rather than forwarded.
6
+ */
7
+ export declare const boxShadowToCss: (value: string | readonly BoxShadowValue[]) => string | null;
@@ -0,0 +1,197 @@
1
+ // Compiles React Native's `boxShadow` into a GTK4 CSS `box-shadow` value.
2
+ // Pure module — no bridge imports, unit-testable on any OS.
3
+ //
4
+ // WHY this is not a passthrough of the string form. RN accepts both a CSS
5
+ // string and a structured array, and the string is the form apps actually
6
+ // write. Handing that string straight to GTK would work for the easy cases
7
+ // and fail silently for the ones that matter here: a `PlatformColor` becomes
8
+ // `var(--card-shade-color)` only after ./colors.ts has seen it, a named
9
+ // colour like `rebeccapurple` is not in GTK's vocabulary at all, and an
10
+ // unparseable value would poison the whole declaration block with a GTK CSS
11
+ // warning rather than being dropped the way an invalid `backgroundColor`
12
+ // already is. Parsing here means both forms take the same path, colours go
13
+ // through the same normalizer as every other colour prop, and a bad shadow
14
+ // costs one dev warning instead of the rest of the style.
15
+ //
16
+ // What GTK4 CSS supports, and therefore what this emits: offset-x offset-y
17
+ // [blur] [spread] [color], plus the `inset` keyword, comma-separated. That
18
+ // is the same grammar as CSS, so the ordering rule is CSS's — the FIRST
19
+ // shadow paints on top — and RN follows it too.
20
+ import { parseColor } from "./colors";
21
+ import { warnOnce } from "./dev-warning";
22
+ /** RN's own shadow-length grammar, from `processBoxShadow.js`: a bare number
23
+ * or a number with `px`, nothing else. No percentages, no em — matching it
24
+ * exactly means a style that RN rejects is rejected here too, rather than
25
+ * quietly working on one platform. */
26
+ const LENGTH = /^[+-]?(\d*\.?\d+)(px)?$/;
27
+ const length = (value) => {
28
+ if (typeof value === "number") {
29
+ return Number.isFinite(value) ? `${value}px` : null;
30
+ }
31
+ const trimmed = value.trim();
32
+ const match = LENGTH.exec(trimmed);
33
+ return match ? `${match[1]}px` : null;
34
+ };
35
+ /** Blur is the one length CSS (and RN's parser) refuses to take negative. */
36
+ const nonNegativeLength = (value) => {
37
+ const rendered = length(value);
38
+ if (rendered === null || rendered.startsWith("-")) {
39
+ return null;
40
+ }
41
+ return rendered;
42
+ };
43
+ /** Splits on commas that are not inside parentheses, so `rgba(0, 0, 0, .5)`
44
+ * stays one token. */
45
+ const splitShadows = (value) => {
46
+ const parts = [];
47
+ let depth = 0;
48
+ let current = "";
49
+ for (const char of value) {
50
+ if (char === "(") {
51
+ depth += 1;
52
+ }
53
+ else if (char === ")") {
54
+ depth = Math.max(0, depth - 1);
55
+ }
56
+ if (char === "," && depth === 0) {
57
+ parts.push(current);
58
+ current = "";
59
+ continue;
60
+ }
61
+ current += char;
62
+ }
63
+ parts.push(current);
64
+ return parts.map((part) => part.trim()).filter((part) => part !== "");
65
+ };
66
+ /** Same rule as splitShadows, one level down: whitespace outside
67
+ * parentheses separates a shadow's own components. */
68
+ const splitTokens = (shadow) => {
69
+ const tokens = [];
70
+ let depth = 0;
71
+ let current = "";
72
+ for (const char of shadow) {
73
+ if (char === "(") {
74
+ depth += 1;
75
+ }
76
+ else if (char === ")") {
77
+ depth = Math.max(0, depth - 1);
78
+ }
79
+ if (/\s/.test(char) && depth === 0) {
80
+ if (current !== "") {
81
+ tokens.push(current);
82
+ current = "";
83
+ }
84
+ continue;
85
+ }
86
+ current += char;
87
+ }
88
+ if (current !== "") {
89
+ tokens.push(current);
90
+ }
91
+ return tokens;
92
+ };
93
+ /**
94
+ * Parses one CSS shadow into the structured form. Returns null when the
95
+ * shadow is not something CSS (or RN) would accept: too few or too many
96
+ * lengths, two colours, an unparseable token.
97
+ */
98
+ const parseShadow = (shadow) => {
99
+ const tokens = splitTokens(shadow);
100
+ const lengths = [];
101
+ let color;
102
+ let inset = false;
103
+ for (const token of tokens) {
104
+ if (token.toLowerCase() === "inset") {
105
+ if (inset) {
106
+ return null;
107
+ }
108
+ inset = true;
109
+ continue;
110
+ }
111
+ if (length(token) !== null) {
112
+ lengths.push(token);
113
+ continue;
114
+ }
115
+ if (color !== undefined) {
116
+ return null;
117
+ }
118
+ color = token;
119
+ }
120
+ // CSS requires offset-x and offset-y and allows blur and spread after
121
+ // them; anything else is a malformed shadow.
122
+ if (lengths.length < 2 || lengths.length > 4) {
123
+ return null;
124
+ }
125
+ return {
126
+ offsetX: lengths[0],
127
+ offsetY: lengths[1],
128
+ ...(lengths[2] !== undefined ? { blurRadius: lengths[2] } : {}),
129
+ ...(lengths[3] !== undefined ? { spreadDistance: lengths[3] } : {}),
130
+ ...(color !== undefined ? { color } : {}),
131
+ ...(inset ? { inset: true } : {}),
132
+ };
133
+ };
134
+ /** One structured shadow as a GTK CSS value, or null when a part of it is
135
+ * not usable. */
136
+ const shadowToCss = (shadow) => {
137
+ const offsetX = length(shadow.offsetX);
138
+ const offsetY = length(shadow.offsetY);
139
+ if (offsetX === null || offsetY === null) {
140
+ return null;
141
+ }
142
+ const parts = [];
143
+ if (shadow.inset === true) {
144
+ parts.push("inset");
145
+ }
146
+ parts.push(offsetX, offsetY);
147
+ if (shadow.blurRadius !== undefined) {
148
+ const blur = nonNegativeLength(shadow.blurRadius);
149
+ if (blur === null) {
150
+ return null;
151
+ }
152
+ parts.push(blur);
153
+ }
154
+ if (shadow.spreadDistance !== undefined) {
155
+ // CSS positions spread after blur, so a spread with no blur needs an
156
+ // explicit zero blur rather than silently becoming one.
157
+ if (shadow.blurRadius === undefined) {
158
+ parts.push("0px");
159
+ }
160
+ const spread = length(shadow.spreadDistance);
161
+ if (spread === null) {
162
+ return null;
163
+ }
164
+ parts.push(spread);
165
+ }
166
+ // RN's documented deviation from CSS: an omitted shadow colour is BLACK,
167
+ // not the inherited `currentColor`. Emitting it explicitly is what keeps
168
+ // this platform on RN's side of that difference — leaving it out would
169
+ // silently hand the app CSS's behaviour instead.
170
+ const parsed = parseColor(shadow.color ?? "black");
171
+ if (parsed === null) {
172
+ return null;
173
+ }
174
+ parts.push(parsed);
175
+ return parts.join(" ");
176
+ };
177
+ /**
178
+ * Renders RN's `boxShadow` as a GTK CSS `box-shadow` value, or null when
179
+ * nothing usable came out of it (the caller then emits no declaration).
180
+ * Both RN forms are accepted; the string is parsed rather than forwarded.
181
+ */
182
+ export const boxShadowToCss = (value) => {
183
+ const shadows = typeof value === "string"
184
+ ? splitShadows(value).map(parseShadow)
185
+ : value.map((shadow) => shadow);
186
+ const rendered = [];
187
+ for (const shadow of shadows) {
188
+ const css = shadow === null ? null : shadowToCss(shadow);
189
+ if (css === null) {
190
+ warnOnce(`invalid-box-shadow:${JSON.stringify(value)}`, `[react-native-gtkx] Invalid boxShadow ${JSON.stringify(value)} — declaration dropped`);
191
+ return null;
192
+ }
193
+ rendered.push(css);
194
+ }
195
+ return rendered.length > 0 ? rendered.join(", ") : null;
196
+ };
197
+ //# sourceMappingURL=box-shadow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"box-shadow.js","sourceRoot":"","sources":["../../src/style/box-shadow.ts"],"names":[],"mappings":"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","sourcesContent":["// Compiles React Native's `boxShadow` into a GTK4 CSS `box-shadow` value.\n// Pure module — no bridge imports, unit-testable on any OS.\n//\n// WHY this is not a passthrough of the string form. RN accepts both a CSS\n// string and a structured array, and the string is the form apps actually\n// write. Handing that string straight to GTK would work for the easy cases\n// and fail silently for the ones that matter here: a `PlatformColor` becomes\n// `var(--card-shade-color)` only after ./colors.ts has seen it, a named\n// colour like `rebeccapurple` is not in GTK's vocabulary at all, and an\n// unparseable value would poison the whole declaration block with a GTK CSS\n// warning rather than being dropped the way an invalid `backgroundColor`\n// already is. Parsing here means both forms take the same path, colours go\n// through the same normalizer as every other colour prop, and a bad shadow\n// costs one dev warning instead of the rest of the style.\n//\n// What GTK4 CSS supports, and therefore what this emits: offset-x offset-y\n// [blur] [spread] [color], plus the `inset` keyword, comma-separated. That\n// is the same grammar as CSS, so the ordering rule is CSS's — the FIRST\n// shadow paints on top — and RN follows it too.\n\nimport type { BoxShadowValue } from \"../contracts\"\nimport { parseColor } from \"./colors\"\nimport { warnOnce } from \"./dev-warning\"\n\n/** RN's own shadow-length grammar, from `processBoxShadow.js`: a bare number\n * or a number with `px`, nothing else. No percentages, no em — matching it\n * exactly means a style that RN rejects is rejected here too, rather than\n * quietly working on one platform. */\nconst LENGTH = /^[+-]?(\\d*\\.?\\d+)(px)?$/\n\nconst length = (value: number | string): string | null => {\n if (typeof value === \"number\") {\n return Number.isFinite(value) ? `${value}px` : null\n }\n const trimmed = value.trim()\n const match = LENGTH.exec(trimmed)\n return match ? `${match[1]}px` : null\n}\n\n/** Blur is the one length CSS (and RN's parser) refuses to take negative. */\nconst nonNegativeLength = (value: number | string): string | null => {\n const rendered = length(value)\n if (rendered === null || rendered.startsWith(\"-\")) {\n return null\n }\n return rendered\n}\n\n/** Splits on commas that are not inside parentheses, so `rgba(0, 0, 0, .5)`\n * stays one token. */\nconst splitShadows = (value: string): string[] => {\n const parts: string[] = []\n let depth = 0\n let current = \"\"\n for (const char of value) {\n if (char === \"(\") {\n depth += 1\n } else if (char === \")\") {\n depth = Math.max(0, depth - 1)\n }\n if (char === \",\" && depth === 0) {\n parts.push(current)\n current = \"\"\n continue\n }\n current += char\n }\n parts.push(current)\n return parts.map((part) => part.trim()).filter((part) => part !== \"\")\n}\n\n/** Same rule as splitShadows, one level down: whitespace outside\n * parentheses separates a shadow's own components. */\nconst splitTokens = (shadow: string): string[] => {\n const tokens: string[] = []\n let depth = 0\n let current = \"\"\n for (const char of shadow) {\n if (char === \"(\") {\n depth += 1\n } else if (char === \")\") {\n depth = Math.max(0, depth - 1)\n }\n if (/\\s/.test(char) && depth === 0) {\n if (current !== \"\") {\n tokens.push(current)\n current = \"\"\n }\n continue\n }\n current += char\n }\n if (current !== \"\") {\n tokens.push(current)\n }\n return tokens\n}\n\n/**\n * Parses one CSS shadow into the structured form. Returns null when the\n * shadow is not something CSS (or RN) would accept: too few or too many\n * lengths, two colours, an unparseable token.\n */\nconst parseShadow = (shadow: string): BoxShadowValue | null => {\n const tokens = splitTokens(shadow)\n const lengths: (number | string)[] = []\n let color: string | undefined\n let inset = false\n for (const token of tokens) {\n if (token.toLowerCase() === \"inset\") {\n if (inset) {\n return null\n }\n inset = true\n continue\n }\n if (length(token) !== null) {\n lengths.push(token)\n continue\n }\n if (color !== undefined) {\n return null\n }\n color = token\n }\n // CSS requires offset-x and offset-y and allows blur and spread after\n // them; anything else is a malformed shadow.\n if (lengths.length < 2 || lengths.length > 4) {\n return null\n }\n return {\n offsetX: lengths[0]!,\n offsetY: lengths[1]!,\n ...(lengths[2] !== undefined ? { blurRadius: lengths[2] } : {}),\n ...(lengths[3] !== undefined ? { spreadDistance: lengths[3] } : {}),\n ...(color !== undefined ? { color } : {}),\n ...(inset ? { inset: true } : {}),\n }\n}\n\n/** One structured shadow as a GTK CSS value, or null when a part of it is\n * not usable. */\nconst shadowToCss = (shadow: BoxShadowValue): string | null => {\n const offsetX = length(shadow.offsetX)\n const offsetY = length(shadow.offsetY)\n if (offsetX === null || offsetY === null) {\n return null\n }\n const parts: string[] = []\n if (shadow.inset === true) {\n parts.push(\"inset\")\n }\n parts.push(offsetX, offsetY)\n if (shadow.blurRadius !== undefined) {\n const blur = nonNegativeLength(shadow.blurRadius)\n if (blur === null) {\n return null\n }\n parts.push(blur)\n }\n if (shadow.spreadDistance !== undefined) {\n // CSS positions spread after blur, so a spread with no blur needs an\n // explicit zero blur rather than silently becoming one.\n if (shadow.blurRadius === undefined) {\n parts.push(\"0px\")\n }\n const spread = length(shadow.spreadDistance)\n if (spread === null) {\n return null\n }\n parts.push(spread)\n }\n // RN's documented deviation from CSS: an omitted shadow colour is BLACK,\n // not the inherited `currentColor`. Emitting it explicitly is what keeps\n // this platform on RN's side of that difference — leaving it out would\n // silently hand the app CSS's behaviour instead.\n const parsed = parseColor(shadow.color ?? \"black\")\n if (parsed === null) {\n return null\n }\n parts.push(parsed)\n return parts.join(\" \")\n}\n\n/**\n * Renders RN's `boxShadow` as a GTK CSS `box-shadow` value, or null when\n * nothing usable came out of it (the caller then emits no declaration).\n * Both RN forms are accepted; the string is parsed rather than forwarded.\n */\nexport const boxShadowToCss = (\n value: string | readonly BoxShadowValue[],\n): string | null => {\n const shadows =\n typeof value === \"string\"\n ? splitShadows(value).map(parseShadow)\n : value.map((shadow) => shadow)\n const rendered: string[] = []\n for (const shadow of shadows) {\n const css = shadow === null ? null : shadowToCss(shadow)\n if (css === null) {\n warnOnce(\n `invalid-box-shadow:${JSON.stringify(value)}`,\n `[react-native-gtkx] Invalid boxShadow ${JSON.stringify(value)} — declaration dropped`,\n )\n return null\n }\n rendered.push(css)\n }\n return rendered.length > 0 ? rendered.join(\", \") : null\n}\n"]}
@@ -1,7 +1,10 @@
1
+ export { boxShadowToCss } from "./box-shadow";
1
2
  export { parseColor, PlatformColor } from "./colors";
2
3
  export { resetDevWarnings } from "./dev-warning";
3
4
  export { createCssRegistry, type CssFn, type CssRegistry } from "./registry";
4
5
  export { splitStyle } from "./split-style";
5
6
  export { absoluteFill, absoluteFillObject, compose, create, flatten, hairlineWidth, StyleSheet, } from "./style-sheet";
7
+ export { textDecorationToAttrs, type TextDecorations } from "./text-decoration";
6
8
  export { textAlignToLabelProps, type LabelAlignProps, type TextAlign, } from "./text-align";
9
+ export { composeTransform, IDENTITY_TRANSFORM, isIdentityTransform, isTranslationOnly, parseAngle, } from "./transform";
7
10
  export { visualStyleToCss } from "./visual-css";
@@ -4,11 +4,14 @@
4
4
  // Everything exported here is pure (no bridge imports) and unit-testable on
5
5
  // any OS. The production registry that talks to the gtkx `css` helper lives
6
6
  // in ./registry.gtkx.ts and is imported directly by the components.
7
+ export { boxShadowToCss } from "./box-shadow";
7
8
  export { parseColor, PlatformColor } from "./colors";
8
9
  export { resetDevWarnings } from "./dev-warning";
9
10
  export { createCssRegistry } from "./registry";
10
11
  export { splitStyle } from "./split-style";
11
12
  export { absoluteFill, absoluteFillObject, compose, create, flatten, hairlineWidth, StyleSheet, } from "./style-sheet";
13
+ export { textDecorationToAttrs } from "./text-decoration";
12
14
  export { textAlignToLabelProps, } from "./text-align";
15
+ export { composeTransform, IDENTITY_TRANSFORM, isIdentityTransform, isTranslationOnly, parseAngle, } from "./transform";
13
16
  export { visualStyleToCss } from "./visual-css";
14
17
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/style/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,8CAA8C;AAC9C,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,oEAAoE;AAEpE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAgC,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,OAAO,EACP,MAAM,EACN,OAAO,EACP,aAAa,EACb,UAAU,GACX,MAAM,eAAe,CAAA;AACtB,OAAO,EACL,qBAAqB,GAGtB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA","sourcesContent":["// Style pipeline: StyleSheet → flatten → splitStyle →\n// visualStyleToCss → memoized class registry.\n//\n// Everything exported here is pure (no bridge imports) and unit-testable on\n// any OS. The production registry that talks to the gtkx `css` helper lives\n// in ./registry.gtkx.ts and is imported directly by the components.\n\nexport { parseColor, PlatformColor } from \"./colors\"\nexport { resetDevWarnings } from \"./dev-warning\"\nexport { createCssRegistry, type CssFn, type CssRegistry } from \"./registry\"\nexport { splitStyle } from \"./split-style\"\nexport {\n absoluteFill,\n absoluteFillObject,\n compose,\n create,\n flatten,\n hairlineWidth,\n StyleSheet,\n} from \"./style-sheet\"\nexport {\n textAlignToLabelProps,\n type LabelAlignProps,\n type TextAlign,\n} from \"./text-align\"\nexport { visualStyleToCss } from \"./visual-css\"\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/style/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,8CAA8C;AAC9C,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,oEAAoE;AAEpE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAgC,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,OAAO,EACP,MAAM,EACN,OAAO,EACP,aAAa,EACb,UAAU,GACX,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,qBAAqB,EAAwB,MAAM,mBAAmB,CAAA;AAC/E,OAAO,EACL,qBAAqB,GAGtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,UAAU,GACX,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA","sourcesContent":["// Style pipeline: StyleSheet → flatten → splitStyle →\n// visualStyleToCss → memoized class registry.\n//\n// Everything exported here is pure (no bridge imports) and unit-testable on\n// any OS. The production registry that talks to the gtkx `css` helper lives\n// in ./registry.gtkx.ts and is imported directly by the components.\n\nexport { boxShadowToCss } from \"./box-shadow\"\nexport { parseColor, PlatformColor } from \"./colors\"\nexport { resetDevWarnings } from \"./dev-warning\"\nexport { createCssRegistry, type CssFn, type CssRegistry } from \"./registry\"\nexport { splitStyle } from \"./split-style\"\nexport {\n absoluteFill,\n absoluteFillObject,\n compose,\n create,\n flatten,\n hairlineWidth,\n StyleSheet,\n} from \"./style-sheet\"\nexport { textDecorationToAttrs, type TextDecorations } from \"./text-decoration\"\nexport {\n textAlignToLabelProps,\n type LabelAlignProps,\n type TextAlign,\n} from \"./text-align\"\nexport {\n composeTransform,\n IDENTITY_TRANSFORM,\n isIdentityTransform,\n isTranslationOnly,\n parseAngle,\n} from \"./transform\"\nexport { visualStyleToCss } from \"./visual-css\"\n"]}
@@ -1,6 +1,9 @@
1
1
  import type { FlatStyle, SplitStyle } from "../contracts";
2
2
  /**
3
- * Splits a flattened style into { layout, visual }. Unknown properties are
4
- * ignored with a one-time dev warning per key; undefined values are dropped.
3
+ * Splits a flattened style into { layout, visual }. Behavioral props
4
+ * (pointerEvents) belong to neither bucket and are consumed silently the
5
+ * component that owns the behavior reads them from the flattened style.
6
+ * Unknown properties are ignored with a one-time dev warning per key;
7
+ * undefined values are dropped.
5
8
  */
6
9
  export declare const splitStyle: (flat: FlatStyle) => SplitStyle;
@@ -47,11 +47,14 @@ const LAYOUT_KEYS = {
47
47
  top: true,
48
48
  width: true,
49
49
  };
50
- // Note: `transform` is visual by contract it is applied by the layout
51
- // engine through Fixed.Child matrices, not through CSS, and is passed
50
+ // Note: `textDecorationLine` and `textAlign` are visual by contract but never
51
+ // reach CSS Pango carries both, and the Text component applies them.
52
+ // Note: `transform` is visual by contract — it is applied as the
53
+ // GskTransform of the child's allocation, not through CSS, and is passed
52
54
  // through here untouched.
53
55
  const VISUAL_KEYS = {
54
56
  backgroundColor: true,
57
+ boxShadow: true,
55
58
  borderBottomColor: true,
56
59
  borderBottomLeftRadius: true,
57
60
  borderBottomRightRadius: true,
@@ -76,12 +79,37 @@ const VISUAL_KEYS = {
76
79
  letterSpacing: true,
77
80
  lineHeight: true,
78
81
  opacity: true,
82
+ outlineColor: true,
83
+ outlineOffset: true,
84
+ outlineStyle: true,
85
+ outlineWidth: true,
79
86
  textAlign: true,
87
+ textDecorationLine: true,
80
88
  transform: true,
81
89
  };
90
+ // Behavioral props: supported, but by neither half of this pipeline. Since
91
+ // RN 0.71 `pointerEvents` may be written in a style and not only as a prop,
92
+ // and View reads it straight off the flattened style (components/view.tsx)
93
+ // to drive GTK picking — it never reaches Yoga and it has no CSS
94
+ // equivalent. So the splitter must CONSUME it silently: warning here told a
95
+ // user that working code was being ignored, on the very line where
96
+ // tests/gtk/components/pointer-events.gtk.test.tsx proves it is not.
97
+ //
98
+ // Exhaustive the same way the two buckets above are, but over what FlatStyle
99
+ // adds ON TOP of LayoutStyle and VisualStyle — that Omit makes the three
100
+ // records a partition of the contract, so a future fourth kind of style prop
101
+ // cannot be added to FlatStyle without failing compilation right here. That
102
+ // is exactly the drift this list suffered: `pointerEvents` was added to the
103
+ // contract and to View, and nothing forced anyone to classify it.
104
+ const BEHAVIORAL_KEYS = {
105
+ pointerEvents: true,
106
+ };
82
107
  /**
83
- * Splits a flattened style into { layout, visual }. Unknown properties are
84
- * ignored with a one-time dev warning per key; undefined values are dropped.
108
+ * Splits a flattened style into { layout, visual }. Behavioral props
109
+ * (pointerEvents) belong to neither bucket and are consumed silently the
110
+ * component that owns the behavior reads them from the flattened style.
111
+ * Unknown properties are ignored with a one-time dev warning per key;
112
+ * undefined values are dropped.
85
113
  */
86
114
  export const splitStyle = (flat) => {
87
115
  const layout = {};
@@ -98,7 +126,7 @@ export const splitStyle = (flat) => {
98
126
  ;
99
127
  visual[key] = value;
100
128
  }
101
- else {
129
+ else if (!Object.hasOwn(BEHAVIORAL_KEYS, key)) {
102
130
  warnOnce(`unknown-style-prop:${key}`, `[react-native-gtkx] Unknown style property "${key}" is not supported and will be ignored`);
103
131
  }
104
132
  }
@@ -1 +1 @@
1
- {"version":3,"file":"split-style.js","sourceRoot":"","sources":["../../src/style/split-style.ts"],"names":[],"mappings":"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","sourcesContent":["// Classifies a flattened style into layout props (consumed by the Yoga\n// engine) and visual props (compiled into GTK CSS). Pure module.\n\nimport type {\n FlatStyle,\n LayoutStyle,\n SplitStyle,\n VisualStyle,\n} from \"../contracts\"\nimport { warnOnce } from \"./dev-warning\"\n\n// Exhaustive over the frozen contract: adding a key to LayoutStyle in\n// contracts.ts fails compilation here until the key is classified.\nconst LAYOUT_KEYS: Record<keyof LayoutStyle, true> = {\n alignContent: true,\n alignItems: true,\n alignSelf: true,\n aspectRatio: true,\n bottom: true,\n columnGap: true,\n direction: true,\n display: true,\n flex: true,\n flexBasis: true,\n flexDirection: true,\n flexGrow: true,\n flexShrink: true,\n flexWrap: true,\n gap: true,\n height: true,\n justifyContent: true,\n left: true,\n margin: true,\n marginBottom: true,\n marginHorizontal: true,\n marginLeft: true,\n marginRight: true,\n marginTop: true,\n marginVertical: true,\n maxHeight: true,\n maxWidth: true,\n minHeight: true,\n minWidth: true,\n overflow: true,\n padding: true,\n paddingBottom: true,\n paddingHorizontal: true,\n paddingLeft: true,\n paddingRight: true,\n paddingTop: true,\n paddingVertical: true,\n position: true,\n right: true,\n rowGap: true,\n top: true,\n width: true,\n}\n\n// Note: `transform` is visual by contract — it is applied by the layout\n// engine through Fixed.Child matrices, not through CSS, and is passed\n// through here untouched.\nconst VISUAL_KEYS: Record<keyof VisualStyle, true> = {\n backgroundColor: true,\n borderBottomColor: true,\n borderBottomLeftRadius: true,\n borderBottomRightRadius: true,\n borderBottomWidth: true,\n borderColor: true,\n borderLeftColor: true,\n borderLeftWidth: true,\n borderRadius: true,\n borderRightColor: true,\n borderRightWidth: true,\n borderStyle: true,\n borderTopColor: true,\n borderTopLeftRadius: true,\n borderTopRightRadius: true,\n borderTopWidth: true,\n borderWidth: true,\n color: true,\n fontFamily: true,\n fontSize: true,\n fontStyle: true,\n fontWeight: true,\n letterSpacing: true,\n lineHeight: true,\n opacity: true,\n textAlign: true,\n transform: true,\n}\n\n/**\n * Splits a flattened style into { layout, visual }. Unknown properties are\n * ignored with a one-time dev warning per key; undefined values are dropped.\n */\nexport const splitStyle = (flat: FlatStyle): SplitStyle => {\n const layout: LayoutStyle = {}\n const visual: VisualStyle = {}\n for (const [key, value] of Object.entries(flat)) {\n if (value === undefined) {\n continue\n }\n if (Object.hasOwn(LAYOUT_KEYS, key)) {\n ;(layout as Record<string, unknown>)[key] = value\n } else if (Object.hasOwn(VISUAL_KEYS, key)) {\n ;(visual as Record<string, unknown>)[key] = value\n } else {\n warnOnce(\n `unknown-style-prop:${key}`,\n `[react-native-gtkx] Unknown style property \"${key}\" is not supported and will be ignored`,\n )\n }\n }\n return { layout, visual }\n}\n"]}
1
+ {"version":3,"file":"split-style.js","sourceRoot":"","sources":["../../src/style/split-style.ts"],"names":[],"mappings":"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","sourcesContent":["// Classifies a flattened style into layout props (consumed by the Yoga\n// engine) and visual props (compiled into GTK CSS). Pure module.\n\nimport type {\n FlatStyle,\n LayoutStyle,\n SplitStyle,\n VisualStyle,\n} from \"../contracts\"\nimport { warnOnce } from \"./dev-warning\"\n\n// Exhaustive over the frozen contract: adding a key to LayoutStyle in\n// contracts.ts fails compilation here until the key is classified.\nconst LAYOUT_KEYS: Record<keyof LayoutStyle, true> = {\n alignContent: true,\n alignItems: true,\n alignSelf: true,\n aspectRatio: true,\n bottom: true,\n columnGap: true,\n direction: true,\n display: true,\n flex: true,\n flexBasis: true,\n flexDirection: true,\n flexGrow: true,\n flexShrink: true,\n flexWrap: true,\n gap: true,\n height: true,\n justifyContent: true,\n left: true,\n margin: true,\n marginBottom: true,\n marginHorizontal: true,\n marginLeft: true,\n marginRight: true,\n marginTop: true,\n marginVertical: true,\n maxHeight: true,\n maxWidth: true,\n minHeight: true,\n minWidth: true,\n overflow: true,\n padding: true,\n paddingBottom: true,\n paddingHorizontal: true,\n paddingLeft: true,\n paddingRight: true,\n paddingTop: true,\n paddingVertical: true,\n position: true,\n right: true,\n rowGap: true,\n top: true,\n width: true,\n}\n\n// Note: `textDecorationLine` and `textAlign` are visual by contract but never\n// reach CSS — Pango carries both, and the Text component applies them.\n// Note: `transform` is visual by contract — it is applied as the\n// GskTransform of the child's allocation, not through CSS, and is passed\n// through here untouched.\nconst VISUAL_KEYS: Record<keyof VisualStyle, true> = {\n backgroundColor: true,\n boxShadow: true,\n borderBottomColor: true,\n borderBottomLeftRadius: true,\n borderBottomRightRadius: true,\n borderBottomWidth: true,\n borderColor: true,\n borderLeftColor: true,\n borderLeftWidth: true,\n borderRadius: true,\n borderRightColor: true,\n borderRightWidth: true,\n borderStyle: true,\n borderTopColor: true,\n borderTopLeftRadius: true,\n borderTopRightRadius: true,\n borderTopWidth: true,\n borderWidth: true,\n color: true,\n fontFamily: true,\n fontSize: true,\n fontStyle: true,\n fontWeight: true,\n letterSpacing: true,\n lineHeight: true,\n opacity: true,\n outlineColor: true,\n outlineOffset: true,\n outlineStyle: true,\n outlineWidth: true,\n textAlign: true,\n textDecorationLine: true,\n transform: true,\n}\n\n// Behavioral props: supported, but by neither half of this pipeline. Since\n// RN 0.71 `pointerEvents` may be written in a style and not only as a prop,\n// and View reads it straight off the flattened style (components/view.tsx)\n// to drive GTK picking — it never reaches Yoga and it has no CSS\n// equivalent. So the splitter must CONSUME it silently: warning here told a\n// user that working code was being ignored, on the very line where\n// tests/gtk/components/pointer-events.gtk.test.tsx proves it is not.\n//\n// Exhaustive the same way the two buckets above are, but over what FlatStyle\n// adds ON TOP of LayoutStyle and VisualStyle — that Omit makes the three\n// records a partition of the contract, so a future fourth kind of style prop\n// cannot be added to FlatStyle without failing compilation right here. That\n// is exactly the drift this list suffered: `pointerEvents` was added to the\n// contract and to View, and nothing forced anyone to classify it.\nconst BEHAVIORAL_KEYS: Record<\n keyof Omit<FlatStyle, keyof LayoutStyle | keyof VisualStyle>,\n true\n> = {\n pointerEvents: true,\n}\n\n/**\n * Splits a flattened style into { layout, visual }. Behavioral props\n * (pointerEvents) belong to neither bucket and are consumed silently — the\n * component that owns the behavior reads them from the flattened style.\n * Unknown properties are ignored with a one-time dev warning per key;\n * undefined values are dropped.\n */\nexport const splitStyle = (flat: FlatStyle): SplitStyle => {\n const layout: LayoutStyle = {}\n const visual: VisualStyle = {}\n for (const [key, value] of Object.entries(flat)) {\n if (value === undefined) {\n continue\n }\n if (Object.hasOwn(LAYOUT_KEYS, key)) {\n ;(layout as Record<string, unknown>)[key] = value\n } else if (Object.hasOwn(VISUAL_KEYS, key)) {\n ;(visual as Record<string, unknown>)[key] = value\n } else if (!Object.hasOwn(BEHAVIORAL_KEYS, key)) {\n warnOnce(\n `unknown-style-prop:${key}`,\n `[react-native-gtkx] Unknown style property \"${key}\" is not supported and will be ignored`,\n )\n }\n }\n return { layout, visual }\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import type { TextDecorationLine } from "../contracts";
2
+ export type TextDecorations = {
3
+ underline: boolean;
4
+ strikethrough: boolean;
5
+ };
6
+ /**
7
+ * Splits RN's `textDecorationLine` into the two Pango attributes that carry
8
+ * it. `"none"` and `undefined` produce neither, which is what lets the caller
9
+ * skip building an attribute list at all.
10
+ *
11
+ * RN's `"underline line-through"` is the combined value; the words are
12
+ * matched individually so the reversed spelling behaves the same, as it does
13
+ * in CSS.
14
+ */
15
+ export declare const textDecorationToAttrs: (value: TextDecorationLine | undefined) => TextDecorations;
@@ -0,0 +1,26 @@
1
+ // textDecorationLine, classified. Pure module — no bridge imports.
2
+ //
3
+ // GTK4 CSS has no `text-decoration` for widgets, the same way it has no
4
+ // `text-align`: both are Pango's business, not the style sheet's. So this
5
+ // follows text-align.ts's shape exactly — the style system decides WHAT is
6
+ // wanted, and the Text component applies it through the label's Pango
7
+ // attribute list.
8
+ /**
9
+ * Splits RN's `textDecorationLine` into the two Pango attributes that carry
10
+ * it. `"none"` and `undefined` produce neither, which is what lets the caller
11
+ * skip building an attribute list at all.
12
+ *
13
+ * RN's `"underline line-through"` is the combined value; the words are
14
+ * matched individually so the reversed spelling behaves the same, as it does
15
+ * in CSS.
16
+ */
17
+ export const textDecorationToAttrs = (value) => {
18
+ if (value === undefined || value === "none") {
19
+ return { underline: false, strikethrough: false };
20
+ }
21
+ return {
22
+ underline: value.includes("underline"),
23
+ strikethrough: value.includes("line-through"),
24
+ };
25
+ };
26
+ //# sourceMappingURL=text-decoration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text-decoration.js","sourceRoot":"","sources":["../../src/style/text-decoration.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,EAAE;AACF,wEAAwE;AACxE,0EAA0E;AAC1E,2EAA2E;AAC3E,sEAAsE;AACtE,kBAAkB;AASlB;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,KAAqC,EACpB,EAAE;IACnB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;QAC5C,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,CAAA;IACnD,CAAC;IACD,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;QACtC,aAAa,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC;KAC9C,CAAA;AACH,CAAC,CAAA","sourcesContent":["// textDecorationLine, classified. Pure module — no bridge imports.\n//\n// GTK4 CSS has no `text-decoration` for widgets, the same way it has no\n// `text-align`: both are Pango's business, not the style sheet's. So this\n// follows text-align.ts's shape exactly — the style system decides WHAT is\n// wanted, and the Text component applies it through the label's Pango\n// attribute list.\n\nimport type { TextDecorationLine } from \"../contracts\"\n\nexport type TextDecorations = {\n underline: boolean\n strikethrough: boolean\n}\n\n/**\n * Splits RN's `textDecorationLine` into the two Pango attributes that carry\n * it. `\"none\"` and `undefined` produce neither, which is what lets the caller\n * skip building an attribute list at all.\n *\n * RN's `\"underline line-through\"` is the combined value; the words are\n * matched individually so the reversed spelling behaves the same, as it does\n * in CSS.\n */\nexport const textDecorationToAttrs = (\n value: TextDecorationLine | undefined,\n): TextDecorations => {\n if (value === undefined || value === \"none\") {\n return { underline: false, strikethrough: false }\n }\n return {\n underline: value.includes(\"underline\"),\n strikethrough: value.includes(\"line-through\"),\n }\n}\n"]}