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.
- package/README.md +22 -9
- package/dist/adw/index.d.ts +3 -0
- package/dist/adw/index.js +41 -0
- package/dist/adw/index.js.map +1 -0
- package/dist/adw/widgets.generated.d.ts +63 -0
- package/dist/adw/widgets.generated.js +125 -0
- package/dist/adw/widgets.generated.js.map +1 -0
- package/dist/animated/create-animated.d.ts +2 -0
- package/dist/animated/create-animated.js +2 -0
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/index.d.ts +2 -0
- package/dist/animated/index.js +1 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/value-xy.d.ts +41 -0
- package/dist/animated/value-xy.js +90 -0
- package/dist/animated/value-xy.js.map +1 -0
- package/dist/animated/value.d.ts +9 -0
- package/dist/animated/value.js +30 -2
- package/dist/animated/value.js.map +1 -1
- package/dist/common/content.d.ts +69 -0
- package/dist/common/content.js +84 -0
- package/dist/common/content.js.map +1 -0
- package/dist/common/icon.d.ts +23 -0
- package/dist/common/icon.js +14 -0
- package/dist/common/icon.js.map +1 -0
- package/dist/common/index.d.ts +5 -0
- package/dist/common/index.js +21 -0
- package/dist/common/index.js.map +1 -0
- package/dist/common/list.d.ts +82 -0
- package/dist/common/list.js +166 -0
- package/dist/common/list.js.map +1 -0
- package/dist/common/navigation-stack.d.ts +84 -0
- package/dist/common/navigation-stack.js +313 -0
- package/dist/common/navigation-stack.js.map +1 -0
- package/dist/common/widget.d.ts +62 -0
- package/dist/common/widget.js +209 -0
- package/dist/common/widget.js.map +1 -0
- package/dist/components/animated.d.ts +7 -4
- package/dist/components/animated.js +88 -67
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.d.ts +36 -1
- package/dist/components/app-registry.js +8 -3
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/host-node.d.ts +7 -0
- package/dist/components/host-node.js +26 -1
- package/dist/components/host-node.js.map +1 -1
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/measure.d.ts +13 -0
- package/dist/components/measure.js +51 -0
- package/dist/components/measure.js.map +1 -0
- package/dist/components/modal.d.ts +1 -1
- package/dist/components/press-event.d.ts +28 -0
- package/dist/components/press-event.js +46 -0
- package/dist/components/press-event.js.map +1 -0
- package/dist/components/pressable.d.ts +2 -6
- package/dist/components/pressable.js +119 -20
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/rect-store.d.ts +11 -1
- package/dist/components/rect-store.js +27 -3
- package/dist/components/rect-store.js.map +1 -1
- package/dist/components/scroll-view.d.ts +4 -0
- package/dist/components/scroll-view.js +8 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/svg/animated-support.d.ts +19 -0
- package/dist/components/svg/animated-support.js +64 -0
- package/dist/components/svg/animated-support.js.map +1 -0
- package/dist/components/svg/context.d.ts +8 -0
- package/dist/components/svg/context.js +15 -0
- package/dist/components/svg/context.js.map +1 -0
- package/dist/components/svg/gradients.d.ts +31 -0
- package/dist/components/svg/gradients.js +93 -0
- package/dist/components/svg/gradients.js.map +1 -0
- package/dist/components/svg/group.d.ts +8 -0
- package/dist/components/svg/group.js +30 -0
- package/dist/components/svg/group.js.map +1 -0
- package/dist/components/svg/index.d.ts +6 -0
- package/dist/components/svg/index.js +5 -0
- package/dist/components/svg/index.js.map +1 -0
- package/dist/components/svg/node.d.ts +7 -0
- package/dist/components/svg/node.js +7 -0
- package/dist/components/svg/node.js.map +1 -0
- package/dist/components/svg/paint.d.ts +24 -0
- package/dist/components/svg/paint.js +57 -0
- package/dist/components/svg/paint.js.map +1 -0
- package/dist/components/svg/shapes.d.ts +47 -0
- package/dist/components/svg/shapes.js +51 -0
- package/dist/components/svg/shapes.js.map +1 -0
- package/dist/components/svg/svg.d.ts +14 -0
- package/dist/components/svg/svg.js +48 -0
- package/dist/components/svg/svg.js.map +1 -0
- package/dist/components/svg/use-shape-node.d.ts +12 -0
- package/dist/components/svg/use-shape-node.js +52 -0
- package/dist/components/svg/use-shape-node.js.map +1 -0
- package/dist/components/text-input.js +1 -1
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.js +36 -3
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-layout-child.js +50 -4
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/components/view.d.ts +9 -4
- package/dist/components/view.js +8 -2
- package/dist/components/view.js.map +1 -1
- package/dist/contracts.d.ts +31 -4
- package/dist/contracts.js.map +1 -1
- package/dist/gtk/index.d.ts +7 -0
- package/dist/gtk/index.js +46 -0
- package/dist/gtk/index.js.map +1 -0
- package/dist/gtk/widgets.generated.d.ts +103 -0
- package/dist/gtk/widgets.generated.js +205 -0
- package/dist/gtk/widgets.generated.js.map +1 -0
- package/dist/gtk/window-actions.d.ts +77 -0
- package/dist/gtk/window-actions.js +193 -0
- package/dist/gtk/window-actions.js.map +1 -0
- package/dist/gtkx/bridge/geometry.d.ts +15 -0
- package/dist/gtkx/bridge/geometry.js +38 -0
- package/dist/gtkx/bridge/geometry.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +11 -4
- package/dist/gtkx/bridge/index.js +47 -6
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/layout-manager.d.ts +2 -1
- package/dist/gtkx/bridge/layout-manager.js +31 -9
- package/dist/gtkx/bridge/layout-manager.js.map +1 -1
- package/dist/gtkx/bridge/slot-portal.d.ts +12 -0
- package/dist/gtkx/bridge/slot-portal.js +17 -0
- package/dist/gtkx/bridge/slot-portal.js.map +1 -0
- package/dist/gtkx/bridge/svg-node.d.ts +79 -0
- package/dist/gtkx/bridge/svg-node.js +323 -0
- package/dist/gtkx/bridge/svg-node.js.map +1 -0
- package/dist/gtkx/bridge/use-signal.js +19 -9
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.d.ts +2 -0
- package/dist/gtkx/bridge/widgets.generated.js +14 -0
- package/dist/gtkx/bridge/widgets.generated.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/bin.d.ts +2 -0
- package/dist/mcp/bin.js +17 -0
- package/dist/mcp/bin.js.map +1 -0
- package/dist/mcp/data/generated.d.ts +1304 -0
- package/dist/mcp/data/generated.js +1179 -0
- package/dist/mcp/data/generated.js.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/list.d.ts +4 -0
- package/dist/mcp/list.js +56 -0
- package/dist/mcp/list.js.map +1 -0
- package/dist/mcp/resolve.d.ts +55 -0
- package/dist/mcp/resolve.js +151 -0
- package/dist/mcp/resolve.js.map +1 -0
- package/dist/mcp/search.d.ts +13 -0
- package/dist/mcp/search.js +48 -0
- package/dist/mcp/search.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +135 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool.d.ts +21 -0
- package/dist/mcp/tool.js +36 -0
- package/dist/mcp/tool.js.map +1 -0
- package/dist/metro/index.js +10 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/index.d.ts +50 -13
- package/dist/navigation/index.js +183 -148
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/option-warnings.js +9 -1
- package/dist/navigation/option-warnings.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +207 -9
- package/dist/navigation/sidebar.js +294 -10
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/responder/system.d.ts +27 -0
- package/dist/responder/system.js +184 -0
- package/dist/responder/system.js.map +1 -0
- package/dist/responder/touch-history.d.ts +10 -0
- package/dist/responder/touch-history.js +78 -0
- package/dist/responder/touch-history.js.map +1 -0
- package/dist/responder/types.d.ts +63 -0
- package/dist/responder/types.js +29 -0
- package/dist/responder/types.js.map +1 -0
- package/dist/responder/use-responder.d.ts +12 -0
- package/dist/responder/use-responder.js +98 -0
- package/dist/responder/use-responder.js.map +1 -0
- package/dist/runner/host-dev.js +3 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +5 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.d.ts +22 -2
- package/dist/runner/index.js +163 -13
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/assemble.d.ts +29 -0
- package/dist/sea/assemble.js +129 -0
- package/dist/sea/assemble.js.map +1 -0
- package/dist/sea/bundle.d.ts +61 -0
- package/dist/sea/bundle.js +202 -0
- package/dist/sea/bundle.js.map +1 -0
- package/dist/sea/gtkx-config-module.d.ts +18 -0
- package/dist/sea/gtkx-config-module.js +67 -0
- package/dist/sea/gtkx-config-module.js.map +1 -0
- package/dist/sea/native-shim.d.ts +40 -0
- package/dist/sea/native-shim.js +121 -0
- package/dist/sea/native-shim.js.map +1 -0
- package/dist/sea/react-anchor-plugin.d.ts +2 -0
- package/dist/sea/react-anchor-plugin.js +30 -0
- package/dist/sea/react-anchor-plugin.js.map +1 -0
- package/dist/style/box-shadow.d.ts +7 -0
- package/dist/style/box-shadow.js +197 -0
- package/dist/style/box-shadow.js.map +1 -0
- package/dist/style/index.d.ts +3 -0
- package/dist/style/index.js +3 -0
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.d.ts +5 -2
- package/dist/style/split-style.js +33 -5
- package/dist/style/split-style.js.map +1 -1
- package/dist/style/text-decoration.d.ts +15 -0
- package/dist/style/text-decoration.js +26 -0
- package/dist/style/text-decoration.js.map +1 -0
- package/dist/style/transform.d.ts +22 -0
- package/dist/style/transform.js +137 -0
- package/dist/style/transform.js.map +1 -0
- package/dist/style/visual-css.js +40 -3
- package/dist/style/visual-css.js.map +1 -1
- package/dist/svg/geometry.d.ts +19 -0
- package/dist/svg/geometry.js +80 -0
- package/dist/svg/geometry.js.map +1 -0
- package/dist/svg/gradient-geometry.d.ts +15 -0
- package/dist/svg/gradient-geometry.js +15 -0
- package/dist/svg/gradient-geometry.js.map +1 -0
- package/dist/svg/transform.d.ts +22 -0
- package/dist/svg/transform.js +74 -0
- package/dist/svg/transform.js.map +1 -0
- package/dist/svg/view-box.d.ts +34 -0
- package/dist/svg/view-box.js +108 -0
- package/dist/svg/view-box.js.map +1 -0
- package/dist/svg-compat/index.d.ts +1 -0
- package/dist/svg-compat/index.js +16 -0
- package/dist/svg-compat/index.js.map +1 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +14 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/vendor/react-native/pan-responder.d.ts +102 -0
- package/dist/vendor/react-native/pan-responder.js +196 -0
- package/dist/vendor/react-native/pan-responder.js.map +1 -0
- package/dist/vendor/react-native/touch-history-math.d.ts +18 -0
- package/dist/vendor/react-native/touch-history-math.js +102 -0
- package/dist/vendor/react-native/touch-history-math.js.map +1 -0
- package/dist/vite/index.d.ts +7 -3
- package/dist/vite/index.js +16 -4
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +52 -0
- package/dist/vitest/index.js +58 -0
- package/dist/vitest/index.js.map +1 -0
- package/dist/vitest/setup.d.ts +4 -0
- package/dist/vitest/setup.js +22 -0
- package/dist/vitest/setup.js.map +1 -0
- 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,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"]}
|
package/dist/style/index.d.ts
CHANGED
|
@@ -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";
|
package/dist/style/index.js
CHANGED
|
@@ -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
|
package/dist/style/index.js.map
CHANGED
|
@@ -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 }.
|
|
4
|
-
*
|
|
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: `
|
|
51
|
-
//
|
|
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 }.
|
|
84
|
-
*
|
|
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":"AAAA,uEAAuE;AACvE,iEAAiE;AAQjE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,sEAAsE;AACtE,mEAAmE;AACnE,MAAM,WAAW,GAAoC;IACnD,YAAY,EAAE,IAAI;IAClB,UAAU,EAAE,IAAI;IAChB,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,IAAI;IACjB,MAAM,EAAE,IAAI;IACZ,SAAS,EAAE,IAAI;IACf,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;IACb,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,IAAI;IACf,aAAa,EAAE,IAAI;IACnB,QAAQ,EAAE,IAAI;IACd,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,IAAI;IACd,GAAG,EAAE,IAAI;IACT,MAAM,EAAE,IAAI;IACZ,cAAc,EAAE,IAAI;IACpB,IAAI,EAAE,IAAI;IACV,MAAM,EAAE,IAAI;IACZ,YAAY,EAAE,IAAI;IAClB,gBAAgB,EAAE,IAAI;IACtB,UAAU,EAAE,IAAI;IAChB,WAAW,EAAE,IAAI;IACjB,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,IAAI;IACpB,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,IAAI;IACd,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,IAAI;IACd,QAAQ,EAAE,IAAI;IACd,OAAO,EAAE,IAAI;IACb,aAAa,EAAE,IAAI;IACnB,iBAAiB,EAAE,IAAI;IACvB,WAAW,EAAE,IAAI;IACjB,YAAY,EAAE,IAAI;IAClB,UAAU,EAAE,IAAI;IAChB,eAAe,EAAE,IAAI;IACrB,QAAQ,EAAE,IAAI;IACd,KAAK,EAAE,IAAI;IACX,MAAM,EAAE,IAAI;IACZ,GAAG,EAAE,IAAI;IACT,KAAK,EAAE,IAAI;CACZ,CAAA;AAED,
|
|
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"]}
|