@symbiote-native/svelte 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +161 -0
  3. package/build/bootstrap.d.ts +7 -0
  4. package/build/bootstrap.js +17 -0
  5. package/build/class-value.d.ts +6 -0
  6. package/build/class-value.js +81 -0
  7. package/build/components/RefreshControl.svelte +38 -0
  8. package/build/components/RefreshControl.svelte.d.ts +5 -0
  9. package/build/components/SafeAreaView.svelte +34 -0
  10. package/build/components/SafeAreaView.svelte.d.ts +5 -0
  11. package/build/components/Text.svelte +25 -0
  12. package/build/components/Text.svelte.d.ts +5 -0
  13. package/build/components/View.svelte +41 -0
  14. package/build/components/View.svelte.d.ts +5 -0
  15. package/build/components/activity-indicator/activity-indicator-platform.android.d.ts +3 -0
  16. package/build/components/activity-indicator/activity-indicator-platform.android.js +5 -0
  17. package/build/components/activity-indicator/activity-indicator-platform.d.ts +1 -0
  18. package/build/components/activity-indicator/activity-indicator-platform.ios.d.ts +3 -0
  19. package/build/components/activity-indicator/activity-indicator-platform.ios.js +6 -0
  20. package/build/components/activity-indicator/activity-indicator-platform.js +5 -0
  21. package/build/components/activity-indicator/activity-indicator-props.d.ts +5 -0
  22. package/build/components/activity-indicator/activity-indicator-props.js +1 -0
  23. package/build/components/activity-indicator/index.svelte +57 -0
  24. package/build/components/activity-indicator/index.svelte.d.ts +5 -0
  25. package/build/components/button-props.d.ts +5 -0
  26. package/build/components/button-props.js +1 -0
  27. package/build/components/button.svelte +71 -0
  28. package/build/components/button.svelte.d.ts +5 -0
  29. package/build/components/flat-list/flat-list-props.d.ts +10 -0
  30. package/build/components/flat-list/flat-list-props.js +1 -0
  31. package/build/components/flat-list/index.svelte +254 -0
  32. package/build/components/flat-list/index.svelte.d.ts +79 -0
  33. package/build/components/image/image-logic.d.ts +2 -0
  34. package/build/components/image/image-logic.js +125 -0
  35. package/build/components/image/image-props.d.ts +5 -0
  36. package/build/components/image/image-props.js +1 -0
  37. package/build/components/image/index.d.ts +3 -0
  38. package/build/components/image/index.js +10 -0
  39. package/build/components/image/index.svelte +66 -0
  40. package/build/components/image/index.svelte.d.ts +5 -0
  41. package/build/components/image-background/image-background-props.d.ts +10 -0
  42. package/build/components/image-background/image-background-props.js +1 -0
  43. package/build/components/image-background/index.svelte +81 -0
  44. package/build/components/image-background/index.svelte.d.ts +5 -0
  45. package/build/components/index.d.ts +50 -0
  46. package/build/components/index.js +24 -0
  47. package/build/components/input-accessory-view/index.svelte +48 -0
  48. package/build/components/input-accessory-view/index.svelte.d.ts +5 -0
  49. package/build/components/input-accessory-view/input-accessory-view-props.d.ts +11 -0
  50. package/build/components/input-accessory-view/input-accessory-view-props.js +1 -0
  51. package/build/components/keyboard-avoiding-view/index.svelte +135 -0
  52. package/build/components/keyboard-avoiding-view/index.svelte.d.ts +5 -0
  53. package/build/components/keyboard-avoiding-view/keyboard-avoiding-view-props.d.ts +15 -0
  54. package/build/components/keyboard-avoiding-view/keyboard-avoiding-view-props.js +1 -0
  55. package/build/components/modal/index.svelte +126 -0
  56. package/build/components/modal/index.svelte.d.ts +5 -0
  57. package/build/components/modal/modal-props.d.ts +24 -0
  58. package/build/components/modal/modal-props.js +1 -0
  59. package/build/components/pressable/index.svelte +160 -0
  60. package/build/components/pressable/index.svelte.d.ts +5 -0
  61. package/build/components/pressable/pressable-props.d.ts +27 -0
  62. package/build/components/pressable/pressable-props.js +1 -0
  63. package/build/components/refresh-control-props.d.ts +19 -0
  64. package/build/components/refresh-control-props.js +1 -0
  65. package/build/components/safe-area-view-props.d.ts +10 -0
  66. package/build/components/safe-area-view-props.js +1 -0
  67. package/build/components/scroll-view/index.svelte +298 -0
  68. package/build/components/scroll-view/index.svelte.d.ts +18 -0
  69. package/build/components/scroll-view/scroll-view-platform-types.d.ts +3 -0
  70. package/build/components/scroll-view/scroll-view-platform-types.js +1 -0
  71. package/build/components/scroll-view/scroll-view-platform.android.d.ts +3 -0
  72. package/build/components/scroll-view/scroll-view-platform.android.js +3 -0
  73. package/build/components/scroll-view/scroll-view-platform.d.ts +1 -0
  74. package/build/components/scroll-view/scroll-view-platform.ios.d.ts +3 -0
  75. package/build/components/scroll-view/scroll-view-platform.ios.js +3 -0
  76. package/build/components/scroll-view/scroll-view-platform.js +4 -0
  77. package/build/components/scroll-view/scroll-view-props.d.ts +78 -0
  78. package/build/components/scroll-view/scroll-view-props.js +1 -0
  79. package/build/components/scroll-view/scroll-view-sticky-context.d.ts +7 -0
  80. package/build/components/scroll-view/scroll-view-sticky-context.js +1 -0
  81. package/build/components/scroll-view/sticky-header-props.d.ts +10 -0
  82. package/build/components/scroll-view/sticky-header-props.js +1 -0
  83. package/build/components/scroll-view/sticky-header.svelte +254 -0
  84. package/build/components/scroll-view/sticky-header.svelte.d.ts +5 -0
  85. package/build/components/section-list/index.svelte +107 -0
  86. package/build/components/section-list/index.svelte.d.ts +57 -0
  87. package/build/components/section-list/section-list-props.d.ts +1 -0
  88. package/build/components/section-list/section-list-props.js +1 -0
  89. package/build/components/switch/index.svelte +123 -0
  90. package/build/components/switch/index.svelte.d.ts +5 -0
  91. package/build/components/switch/switch-platform-types.d.ts +5 -0
  92. package/build/components/switch/switch-platform-types.js +1 -0
  93. package/build/components/switch/switch-platform.android.d.ts +3 -0
  94. package/build/components/switch/switch-platform.android.js +8 -0
  95. package/build/components/switch/switch-platform.d.ts +1 -0
  96. package/build/components/switch/switch-platform.ios.d.ts +3 -0
  97. package/build/components/switch/switch-platform.ios.js +7 -0
  98. package/build/components/switch/switch-platform.js +5 -0
  99. package/build/components/switch/switch-props.d.ts +5 -0
  100. package/build/components/switch/switch-props.js +1 -0
  101. package/build/components/text-input/index.svelte +300 -0
  102. package/build/components/text-input/index.svelte.d.ts +12 -0
  103. package/build/components/text-input/text-input-props.d.ts +5 -0
  104. package/build/components/text-input/text-input-props.js +1 -0
  105. package/build/components/text-props.d.ts +23 -0
  106. package/build/components/text-props.js +1 -0
  107. package/build/components/touchable-base-props.d.ts +10 -0
  108. package/build/components/touchable-base-props.js +1 -0
  109. package/build/components/touchable-highlight/index.svelte +39 -0
  110. package/build/components/touchable-highlight/index.svelte.d.ts +5 -0
  111. package/build/components/touchable-highlight/touchable-highlight-props.d.ts +5 -0
  112. package/build/components/touchable-highlight/touchable-highlight-props.js +1 -0
  113. package/build/components/touchable-native-feedback/index.d.ts +9 -0
  114. package/build/components/touchable-native-feedback/index.js +14 -0
  115. package/build/components/touchable-native-feedback/touchable-native-feedback-props.d.ts +8 -0
  116. package/build/components/touchable-native-feedback/touchable-native-feedback-props.js +1 -0
  117. package/build/components/touchable-native-feedback/touchable-native-feedback.svelte +44 -0
  118. package/build/components/touchable-native-feedback/touchable-native-feedback.svelte.d.ts +5 -0
  119. package/build/components/touchable-opacity/index.svelte +124 -0
  120. package/build/components/touchable-opacity/index.svelte.d.ts +5 -0
  121. package/build/components/touchable-opacity/touchable-opacity-props.d.ts +5 -0
  122. package/build/components/touchable-opacity/touchable-opacity-props.js +1 -0
  123. package/build/components/touchable-without-feedback/index.svelte +19 -0
  124. package/build/components/touchable-without-feedback/index.svelte.d.ts +5 -0
  125. package/build/components/touchable-without-feedback/touchable-without-feedback-props.d.ts +2 -0
  126. package/build/components/touchable-without-feedback/touchable-without-feedback-props.js +1 -0
  127. package/build/components/view-props.d.ts +29 -0
  128. package/build/components/view-props.js +1 -0
  129. package/build/components/virtualized-list/index.svelte +702 -0
  130. package/build/components/virtualized-list/index.svelte.d.ts +79 -0
  131. package/build/components/virtualized-list/virtualized-list-props.d.ts +70 -0
  132. package/build/components/virtualized-list/virtualized-list-props.js +73 -0
  133. package/build/components/virtualized-section-list/index.svelte +206 -0
  134. package/build/components/virtualized-section-list/index.svelte.d.ts +57 -0
  135. package/build/components/virtualized-section-list/virtualized-section-list-props.d.ts +62 -0
  136. package/build/components/virtualized-section-list/virtualized-section-list-props.js +1 -0
  137. package/build/create-tunnel/index.d.ts +3 -0
  138. package/build/create-tunnel/index.js +3 -0
  139. package/build/create-tunnel/tunnel-in.svelte +24 -0
  140. package/build/create-tunnel/tunnel-in.svelte.d.ts +9 -0
  141. package/build/create-tunnel/tunnel-out.svelte +14 -0
  142. package/build/create-tunnel/tunnel-out.svelte.d.ts +7 -0
  143. package/build/create-tunnel/tunnel.d.ts +9 -0
  144. package/build/create-tunnel/tunnel.js +9 -0
  145. package/build/descriptor-to-svelte.d.ts +7 -0
  146. package/build/descriptor-to-svelte.js +104 -0
  147. package/build/dom-shim/comment.d.ts +9 -0
  148. package/build/dom-shim/comment.js +24 -0
  149. package/build/dom-shim/document-fragment.d.ts +8 -0
  150. package/build/dom-shim/document-fragment.js +23 -0
  151. package/build/dom-shim/document.d.ts +23 -0
  152. package/build/dom-shim/document.js +61 -0
  153. package/build/dom-shim/element.d.ts +22 -0
  154. package/build/dom-shim/element.js +130 -0
  155. package/build/dom-shim/index.d.ts +5 -0
  156. package/build/dom-shim/index.js +7 -0
  157. package/build/dom-shim/patch-globals.d.ts +2 -0
  158. package/build/dom-shim/patch-globals.js +94 -0
  159. package/build/dom-shim/shim-node.d.ts +30 -0
  160. package/build/dom-shim/shim-node.js +207 -0
  161. package/build/dom-shim/text.d.ts +14 -0
  162. package/build/dom-shim/text.js +97 -0
  163. package/build/host-instance.d.ts +5 -0
  164. package/build/host-instance.js +48 -0
  165. package/build/index.d.ts +28 -0
  166. package/build/index.js +77 -0
  167. package/build/modules/accessibility-info/index.android.d.ts +2 -0
  168. package/build/modules/accessibility-info/index.android.js +4 -0
  169. package/build/modules/accessibility-info/index.d.ts +1 -0
  170. package/build/modules/accessibility-info/index.ios.d.ts +2 -0
  171. package/build/modules/accessibility-info/index.ios.js +3 -0
  172. package/build/modules/accessibility-info/index.js +5 -0
  173. package/build/modules/animated/AnimatedImage.svelte +115 -0
  174. package/build/modules/animated/AnimatedImage.svelte.d.ts +5 -0
  175. package/build/modules/animated/AnimatedScrollView.svelte +92 -0
  176. package/build/modules/animated/AnimatedScrollView.svelte.d.ts +17 -0
  177. package/build/modules/animated/AnimatedText.svelte +58 -0
  178. package/build/modules/animated/AnimatedText.svelte.d.ts +5 -0
  179. package/build/modules/animated/AnimatedView.svelte +95 -0
  180. package/build/modules/animated/AnimatedView.svelte.d.ts +5 -0
  181. package/build/modules/animated/animated-component-props.d.ts +7 -0
  182. package/build/modules/animated/animated-component-props.js +1 -0
  183. package/build/modules/animated/index.d.ts +58 -0
  184. package/build/modules/animated/index.js +69 -0
  185. package/build/modules/app-registry/index.d.ts +7 -0
  186. package/build/modules/app-registry/index.js +28 -0
  187. package/build/modules/status-bar/index.d.ts +5 -0
  188. package/build/modules/status-bar/index.js +21 -0
  189. package/build/modules/status-bar/index.svelte +20 -0
  190. package/build/modules/status-bar/index.svelte.d.ts +4 -0
  191. package/build/native-view-bridge.d.ts +4 -0
  192. package/build/native-view-bridge.js +27 -0
  193. package/build/preprocessor/forbid-web-only-constructs.d.ts +8 -0
  194. package/build/preprocessor/forbid-web-only-constructs.js +204 -0
  195. package/build/preprocessor/scoped-styles.d.ts +10 -0
  196. package/build/preprocessor/scoped-styles.js +399 -0
  197. package/build/render.d.ts +7 -0
  198. package/build/render.js +46 -0
  199. package/build/root-element.d.ts +3 -0
  200. package/build/root-element.js +27 -0
  201. package/build/runes/attachments.d.ts +5 -0
  202. package/build/runes/attachments.js +73 -0
  203. package/build/runes/dimensions-value.d.ts +4 -0
  204. package/build/runes/dimensions-value.js +30 -0
  205. package/build/runes/media-query.d.ts +8 -0
  206. package/build/runes/media-query.js +38 -0
  207. package/build/runes/use-color-scheme.svelte.d.ts +4 -0
  208. package/build/runes/use-color-scheme.svelte.js +25 -0
  209. package/build/runes/use-window-dimensions.svelte.d.ts +4 -0
  210. package/build/runes/use-window-dimensions.svelte.js +43 -0
  211. package/build/runes/window.d.ts +6 -0
  212. package/build/runes/window.js +28 -0
  213. package/build/scope-token.d.ts +2 -0
  214. package/build/scope-token.js +33 -0
  215. package/build/style-scope.d.ts +1 -0
  216. package/build/style-scope.js +29 -0
  217. package/metro-css-parser.cjs +9 -0
  218. package/metro-svelte-transformer.cjs +156 -0
  219. package/package.json +83 -0
@@ -0,0 +1,3 @@
1
+ import type { SymbioteSurface } from '@symbiote-native/engine';
2
+ import { ShimElement } from './dom-shim';
3
+ export declare function createRootShimElement(surface: SymbioteSurface): ShimElement;
@@ -0,0 +1,27 @@
1
+ // The one place a shim node is bound EAGERLY instead of lazily (svelte-adapter-dom-shim skill
2
+ // §9, §10). Every other shim node only gets its ISymbioteNode on first insertion under a live
3
+ // parent; the root has no parent to inherit liveness from — it IS the top of the live tree, so
4
+ // it must be live from the moment it exists. This is what Svelte's `mount(Component, { target })`
5
+ // receives as `target`.
6
+ //
7
+ // `makeLive` already does exactly the right thing generically (create the engine node, replay
8
+ // accumulated state, recurse into children) — it just never appends the result anywhere, since
9
+ // normally an ancestor's `insertOne` does that. For the root there is no ancestor shim node;
10
+ // the surface plays that role, so this file makes the one `surface.appendChild` call directly.
11
+ import { ShimElement } from './dom-shim/index.js';
12
+ const ROOT_INTRINSIC = 'symbiote-view';
13
+ // Unlike Vue/React, whose app root mounts directly onto the surface (its own flex:1
14
+ // class/style reaches the engine's synthetic flex:1 AppContainer with nothing in between),
15
+ // Svelte's compiled output needs a real DOM-like `target` to call appendChild on — this
16
+ // wrapper. Without flex:1 here, a flex:1-styled app root nested one level inside it has no
17
+ // resolved parent height to grow into and the whole tree collapses to 0x0: every prop still
18
+ // commits correctly (nothing throws), so this only shows up as a blank, untappable screen.
19
+ const ROOT_WRAPPER_STYLE = { flex: 1 };
20
+ export function createRootShimElement(surface) {
21
+ const root = new ShimElement(ROOT_INTRINSIC);
22
+ root.p = { style: ROOT_WRAPPER_STYLE };
23
+ const engineNode = root.makeLive(surface);
24
+ surface.appendChild(engineNode);
25
+ surface.requestCommit();
26
+ return root;
27
+ }
@@ -0,0 +1,5 @@
1
+ import type { ShimElement } from '../dom-shim';
2
+ type IAttachmentSync = (host: ShimElement | null | undefined, props: object) => void;
3
+ export declare function createAttachmentsSync(): IAttachmentSync;
4
+ export declare function pickAttachmentProps(props: object): Record<symbol, unknown>;
5
+ export {};
@@ -0,0 +1,73 @@
1
+ // `{@attach fn}` support for this adapter's components.
2
+ //
3
+ // WHY IT IS THE ONLY ROUTE. Svelte's compiler rejects `use:` / `transition:` / `class:` /
4
+ // `style:` on a COMPONENT ("This type of directive is not valid on components"), and app code
5
+ // here never authors a host element (skill §7), so a directive is unreachable by construction.
6
+ // Attachments are the exception: `{@attach fn}` compiles on a component, and `fromAction` from
7
+ // `svelte/attachments` converts any third-party action into one. That makes this the single seam
8
+ // through which a Svelte author reaches the committed native node, the twin of Vue's
9
+ // user-defined custom directive (which works untouched in adapters/vue).
10
+ //
11
+ // THE MECHANISM. `{@attach fn}` compiles to a prop whose key is a SYMBOL created by
12
+ // `createAttachmentKey()`, i.e. `Symbol(ATTACHMENT_KEY)`; Svelte's own spread path identifies one
13
+ // by `symbol.description === ATTACHMENT_KEY` (internal/client/dom/elements/attributes.js). Both
14
+ // `rest_props` and `spread_props` expose symbol keys through their `ownKeys` traps, so
15
+ // `Object.getOwnPropertySymbols()` over `$props()` — or over the `...rest` object — finds them.
16
+ //
17
+ // WHY IT DELEGATES TO SVELTE'S OWN `attach()` RATHER THAN CALLING THE FUNCTION DIRECTLY. A
18
+ // DYNAMIC attachment expression does not compile to a changing prop VALUE; the compiler emits a
19
+ // STABLE wrapper and moves the reactivity inside it:
20
+ //
21
+ // {@attach which === 'first' ? first : second}
22
+ // -> [$.attachment()]: ($$node) => ($.get(which) === 'first' ? first : second)($$node)
23
+ //
24
+ // So identity-diffing the prop can never see the swap — the read that changes lives in the
25
+ // attachment BODY. `attach(node, getFn)` (internal/client/dom/elements/attachments.js) is exactly
26
+ // the machinery that handles this: a managed effect around `getFn()` for the identity case, and a
27
+ // branch effect around `fn(node)` so a reactive read inside the body re-runs it with the previous
28
+ // teardown fired first. Reimplementing it would be reimplementing it wrong.
29
+ //
30
+ // WHY A PLAIN `.ts` HERE AND NOT A `.svelte.ts` RUNE. The `$effect` stays at the component call
31
+ // site; this file holds no rune syntax. `.svelte.ts` files are compiled by `compileModule` in
32
+ // Metro (metro-svelte-transformer.cjs) but NOT by vitest, which has no Svelte plugin — so a rune
33
+ // file imported by every component would break every smoke test in this package. Same split, and
34
+ // the same call shape, as `createDescriptorChildrenSync`.
35
+ import { attach } from 'svelte/internal/client';
36
+ import { createAttachmentKey } from 'svelte/attachments';
37
+ // Read off a real key rather than hardcoded: `ATTACHMENT_KEY` lives in svelte/src/constants.js
38
+ // and is not part of the public export surface, so a rename there would otherwise silently
39
+ // disable every attachment. The literal is the fallback for a Svelte build that stops giving the
40
+ // key a description at all.
41
+ const ATTACHMENT_KEY_DESCRIPTION = createAttachmentKey().description ?? '@attach';
42
+ // Call once during component init, then drive it from an `$effect`:
43
+ //
44
+ // const syncAttachments = createAttachmentsSync();
45
+ // $effect(() => { syncAttachments(hostShim, rest); });
46
+ //
47
+ // That effect depends ONLY on the host ref — the attachment values are read lazily inside
48
+ // `attach`'s own effects, never in the caller's body — so it re-runs only when the host node
49
+ // itself changes, and Svelte then destroys the previous run's child effects (firing every
50
+ // teardown) before the new node is attached to.
51
+ export function createAttachmentsSync() {
52
+ return (host, props) => {
53
+ if (host === null || host === undefined)
54
+ return;
55
+ for (const key of attachmentKeys(props)) {
56
+ attach(host, () => Reflect.get(props, key));
57
+ }
58
+ };
59
+ }
60
+ // For a component that delegates to another Symbiote component by naming individual props
61
+ // instead of spreading (the list family, whose wrappers forward ~30 named props, and anything
62
+ // rebuilding its props through a plain string-keyed Record such as Animated's `reduceProps`).
63
+ // Symbol keys do not survive either, so the wrapper re-spreads just the attachment ones.
64
+ export function pickAttachmentProps(props) {
65
+ const forwarded = {};
66
+ for (const key of attachmentKeys(props)) {
67
+ forwarded[key] = Reflect.get(props, key);
68
+ }
69
+ return forwarded;
70
+ }
71
+ function attachmentKeys(props) {
72
+ return Object.getOwnPropertySymbols(props).filter(key => key.description === ATTACHMENT_KEY_DESCRIPTION);
73
+ }
@@ -0,0 +1,4 @@
1
+ export interface IReactiveValue<TValue> {
2
+ readonly current: TValue;
3
+ }
4
+ export declare function createDimensionsValue<TValue>(read: () => TValue): IReactiveValue<TValue>;
@@ -0,0 +1,30 @@
1
+ // The one plumbing piece behind every reactive screen-metric value in this bucket: a `{ get
2
+ // current() }` box whose read is re-run whenever the engine's Dimensions module emits 'change'.
3
+ //
4
+ // `createSubscriber` (svelte/reactivity) rather than hand-rolled bookkeeping: it is pure — no DOM
5
+ // anywhere in its implementation — and is the integration point Svelte itself documents for an
6
+ // external event source (its own `MediaQuery` is built on it). What it buys us over a `$state` +
7
+ // `$effect` pair is the reason every value here is a module-level singleton rather than a
8
+ // `useX()` call:
9
+ // - `start` runs lazily, on the FIRST reactive read, and the teardown runs when the LAST
10
+ // reading effect is destroyed. So importing this module attaches no Dimensions listener, and
11
+ // N components reading `innerWidth.current` share exactly one.
12
+ // - `.current` is legal OUTSIDE a component too (it just reads through, untracked), whereas
13
+ // `$effect` throws `effect_orphan` anywhere but a component.
14
+ // That is also why these files are plain `.ts`, not `.svelte.ts`: they contain no rune syntax, so
15
+ // there is nothing for svelte/compiler's compileModule step to do (same as runes/attachments.ts).
16
+ import { createSubscriber } from 'svelte/reactivity';
17
+ import { Dimensions } from '@symbiote-native/engine';
18
+ export function createDimensionsValue(read) {
19
+ const subscribe = createSubscriber(update => {
20
+ const subscription = Dimensions.addEventListener('change', update);
21
+ return () => subscription.remove();
22
+ });
23
+ return {
24
+ get current() {
25
+ // Registers the dependency when read inside an effect; a no-op read outside one.
26
+ subscribe();
27
+ return read();
28
+ },
29
+ };
30
+ }
@@ -0,0 +1,8 @@
1
+ import { type IReactiveValue } from './dimensions-value';
2
+ export type IOrientation = 'portrait' | 'landscape';
3
+ export declare const orientation: IReactiveValue<IOrientation>;
4
+ export interface IWidthQueryBounds {
5
+ minWidth?: number;
6
+ maxWidth?: number;
7
+ }
8
+ export declare function createWidthQuery(bounds: IWidthQueryBounds): IReactiveValue<boolean>;
@@ -0,0 +1,38 @@
1
+ // What replaces `MediaQuery` from `svelte/reactivity` here — and why it is deliberately NOT a
2
+ // `MediaQuery`-shaped class.
3
+ //
4
+ // `new MediaQuery('min-width: 800px')` takes an arbitrary CSS media-query STRING and answers it
5
+ // against `window.matchMedia`, which does not exist in React Native. A faithful-looking port
6
+ // would have to accept the same string and answer whatever it could from RN sources. The CSS
7
+ // media-query grammar has dozens of features; RN can honestly answer three or four of them. Every
8
+ // other one — `hover`, `pointer`, `prefers-reduced-motion`, `print`, `color-gamut`, `aspect-ratio`
9
+ // — would have to come back `false`, which is indistinguishable from a legitimate "no". That is
10
+ // precisely the "works by accident" dependency the symbiote-web-lib-portability-check skill says
11
+ // to reject: the failure is silent, on device, in a value the author already believes.
12
+ //
13
+ // So the surface is narrower and each supported feature is its own named export. An unsupported
14
+ // one is not a `false` — it is a name that does not exist, i.e. a compile error at the import.
15
+ // The three media features people actually reach for on a phone map cleanly:
16
+ // (orientation: …) -> `orientation` below
17
+ // (min-width / max-width) -> `createWidthQuery` below
18
+ // (prefers-color-scheme) -> the existing `useColorScheme()` rune, over Appearance.
19
+ // Dark mode is not re-exposed here: `useColorScheme()` already covers it and a second accessor
20
+ // for one value is duplication, not ergonomics.
21
+ import { Dimensions } from '@symbiote-native/engine';
22
+ import { createDimensionsValue } from './dimensions-value.js';
23
+ // A square window counts as portrait, matching the CSS `(orientation: portrait)` definition
24
+ // (height >= width), not a coin flip.
25
+ export const orientation = createDimensionsValue(() => {
26
+ const { width, height } = Dimensions.get('window');
27
+ return height >= width ? 'portrait' : 'landscape';
28
+ });
29
+ export function createWidthQuery(bounds) {
30
+ return createDimensionsValue(() => {
31
+ const { width } = Dimensions.get('window');
32
+ if (bounds.minWidth !== undefined && width < bounds.minWidth)
33
+ return false;
34
+ if (bounds.maxWidth !== undefined && width > bounds.maxWidth)
35
+ return false;
36
+ return true;
37
+ });
38
+ }
@@ -0,0 +1,4 @@
1
+ import { type IColorSchemeName } from '@symbiote-native/engine';
2
+ export declare function useColorScheme(): {
3
+ readonly current: IColorSchemeName | null;
4
+ };
@@ -0,0 +1,25 @@
1
+ // useColorScheme, the Svelte twin of React's hook / Vue's composable
2
+ // (adapters/vue/src/composables/use-color-scheme.ts), over the framework-agnostic Appearance
3
+ // module (@symbiote-native/engine). See use-window-dimensions.svelte.ts's header for why this
4
+ // lives in `runes/` with a `.svelte.ts` extension and returns a boxed getter object instead of a
5
+ // bare `$state` variable.
6
+ import { Appearance, } from '@symbiote-native/engine';
7
+ export function useColorScheme() {
8
+ let colorScheme = $state(Appearance.getColorScheme());
9
+ $effect(() => {
10
+ // Re-read on mount in case the scheme changed between this function's own call and the
11
+ // effect actually running — this write is the effect's only touch of `colorScheme`
12
+ // (never a read), so the effect has no dependency on it and runs exactly once on mount,
13
+ // cleaning up exactly once on unmount.
14
+ colorScheme = Appearance.getColorScheme();
15
+ const subscription = Appearance.addChangeListener(preferences => {
16
+ colorScheme = preferences.colorScheme;
17
+ });
18
+ return () => subscription.remove();
19
+ });
20
+ return {
21
+ get current() {
22
+ return colorScheme;
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,4 @@
1
+ import { type IDisplayMetrics } from '@symbiote-native/engine';
2
+ export declare function useWindowDimensions(): {
3
+ readonly current: IDisplayMetrics;
4
+ };
@@ -0,0 +1,43 @@
1
+ // Svelte twin of React's hook / Vue's composable (use-window-dimensions.ts), over the
2
+ // framework-agnostic Dimensions module. Seeds from Dimensions.get('window'), subscribes to
3
+ // 'change', and re-checks right after subscribing to close the gap between this call's read
4
+ // and the effect's mount-time listener attach.
5
+ //
6
+ // `.svelte.ts`: runes ($state/$effect) only work in files with this extension outside a
7
+ // `.svelte` component. `runes/` is this adapter's bucket name for framework-lifecycle helpers
8
+ // (CLAUDE.md's <adapter_src_follows_framework_idioms>) - Svelte calls $state/$effect "runes".
9
+ //
10
+ // Returns a boxed getter, not a bare `$state` variable: Svelte 5 reactivity is lexically
11
+ // scoped to the declaring module, so returning a raw `let dimensions = $state(...)` loses
12
+ // reactivity for the caller. `{ get current() { ... } }` is the sanctioned pattern - read
13
+ // `useWindowDimensions().current` inside `$derived`/template/`$effect`, like unwrapping Vue's
14
+ // `Ref.value`.
15
+ import { Dimensions, } from '@symbiote-native/engine';
16
+ export function useWindowDimensions() {
17
+ let dimensions = $state(Dimensions.get('window'));
18
+ // Plain closure variable, not $state: reading `dimensions` here instead would make the
19
+ // $effect depend on its own write and re-run itself. This keeps the effect's dependency set
20
+ // empty, so it runs once on mount and cleans up once on unmount.
21
+ let last = dimensions;
22
+ $effect(() => {
23
+ const handleChange = (next) => {
24
+ if (last.width !== next.width ||
25
+ last.height !== next.height ||
26
+ last.scale !== next.scale ||
27
+ last.fontScale !== next.fontScale) {
28
+ last = next;
29
+ dimensions = next;
30
+ }
31
+ };
32
+ const subscription = Dimensions.addEventListener('change', (set) => handleChange(set.window));
33
+ // Covers an update missed between the call-time `get` and subscribing here; the equality
34
+ // guard filters the no-op if nothing changed.
35
+ handleChange(Dimensions.get('window'));
36
+ return () => subscription.remove();
37
+ });
38
+ return {
39
+ get current() {
40
+ return dimensions;
41
+ },
42
+ };
43
+ }
@@ -0,0 +1,6 @@
1
+ import { type IReactiveValue } from './dimensions-value';
2
+ export declare const innerWidth: IReactiveValue<number>;
3
+ export declare const innerHeight: IReactiveValue<number>;
4
+ export declare const outerWidth: IReactiveValue<number>;
5
+ export declare const outerHeight: IReactiveValue<number>;
6
+ export declare const devicePixelRatio: IReactiveValue<number>;
@@ -0,0 +1,28 @@
1
+ // The React Native twin of `svelte/reactivity/window`. Upstream reads `window.innerWidth`,
2
+ // `window.outerWidth`, `window.devicePixelRatio` and friends, all of which are absent here — the
3
+ // dom-shim patches only the DOM classes stock compiled Svelte output touches (svelte-adapter-dom-
4
+ // shim skill), never the browser's window-metric properties — so every one of those values would
5
+ // read `undefined` forever and never update. Same names, same `.current` shape, real engine
6
+ // sources underneath.
7
+ //
8
+ // Two deliberate differences from upstream, both improvements:
9
+ // - the type is `number`, not `number | undefined`. Upstream's `undefined` is its SSR branch;
10
+ // there is no SSR here, and Dimensions always answers (it falls back to zero metrics when
11
+ // DeviceInfo is unresolvable), so a consumer never has to unwrap.
12
+ // - `outerWidth`/`outerHeight` are the physical SCREEN, not a "browser window inside a desktop
13
+ // window" — the closest honest reading of outer-vs-inner on a device, where an app's window
14
+ // is the screen minus system chrome (status bar, navigation bar).
15
+ //
16
+ // NOT provided, on purpose: `scrollX` / `scrollY` / `screenLeft` / `screenTop`. There is no
17
+ // window-level scroll offset in React Native (scrolling is per-ScrollView, observed through its
18
+ // own `onScroll`), and an app has no position within a desktop-style window manager. Rather than
19
+ // export a value that is always 0, or one that throws when it is finally read on a device, these
20
+ // are simply absent — the import fails at compile time, which the author sees immediately.
21
+ import { Dimensions, PixelRatio } from '@symbiote-native/engine';
22
+ import { createDimensionsValue } from './dimensions-value.js';
23
+ export const innerWidth = createDimensionsValue(() => Dimensions.get('window').width);
24
+ export const innerHeight = createDimensionsValue(() => Dimensions.get('window').height);
25
+ export const outerWidth = createDimensionsValue(() => Dimensions.get('screen').width);
26
+ export const outerHeight = createDimensionsValue(() => Dimensions.get('screen').height);
27
+ // PixelRatio.get() IS Dimensions.get('window').scale, so the same 'change' event carries it.
28
+ export const devicePixelRatio = createDimensionsValue(() => PixelRatio.get());
@@ -0,0 +1,2 @@
1
+ export declare function scopeToken(token: string, localNames: ReadonlySet<string>, scopeId: string): string;
2
+ export declare function kebabToCamel(value: string): string;
@@ -0,0 +1,33 @@
1
+ // The `<style>`-scoping rule for ONE class token, and nothing else.
2
+ //
3
+ // WHY THIS IS ITS OWN FILE — it looks like it belongs in `./style-scope`, and it cannot live
4
+ // there. The build-time half (`preprocessor/scoped-styles.ts`) and the runtime half
5
+ // (`./style-scope`) must apply the SAME rule or a static `class="card"` and a dynamic
6
+ // `class={'card'}` would disagree, so it has to be shared. But `svelte.config.js` is loaded
7
+ // DIRECTLY by Node (svelte-check, the language server): Node strips the types off a `.ts` file
8
+ // yet still runs its own ESM resolver over whatever that file imports, where an extensionless
9
+ // relative specifier is a hard `ERR_MODULE_NOT_FOUND`. `./style-scope` imports `./class-value`
10
+ // imports `@symbiote-native/engine` — an entire extensionless graph the preprocessor must never
11
+ // pull in. So the shared rule lives HERE, with ZERO imports of its own, reachable from the
12
+ // preprocessor through the `@symbiote-native/svelte/scope-token` exports subpath (which supplies
13
+ // the extension at both targets, `src` in-workspace and `build` when published).
14
+ //
15
+ // Keep this file import-free. Adding one import to it breaks `svelte-check` for every consuming
16
+ // app, and nothing in this package's own test run would notice.
17
+ const SCOPE_SEPARATOR = '__';
18
+ // A token is matched in its camelCase form because that is the only form the registry is keyed
19
+ // in (@symbiote-native/css-parser's `extractClassName` always camelCases, so `.card-title`
20
+ // registers as `cardTitle`) while markup idiomatically writes the kebab form. A token this file's
21
+ // `<style>` block does NOT define is returned VERBATIM, not camelCased — unlike Vue's rewriter,
22
+ // which camelCases everything it sees. The engine's `resolveOne` already falls back kebab->camel
23
+ // on a miss, so camelCasing here would buy nothing and would silently rename a class the author
24
+ // is forwarding to some other file's global rule.
25
+ export function scopeToken(token, localNames, scopeId) {
26
+ const camelToken = kebabToCamel(token);
27
+ return localNames.has(camelToken) ? camelToken + SCOPE_SEPARATOR + scopeId : token;
28
+ }
29
+ // Duplicated from @symbiote-native/css-parser rather than imported, for the same reason
30
+ // core/engine/src/style-registry/index.ts duplicates it — plus the import-free constraint above.
31
+ export function kebabToCamel(value) {
32
+ return value.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
33
+ }
@@ -0,0 +1 @@
1
+ export declare function scopeSvelteClass(value: unknown, localNames: ReadonlySet<string>, scopeId: string): unknown;
@@ -0,0 +1,29 @@
1
+ // Runtime half of the `<style>` scoping pass. The build-time half — `preprocessor/
2
+ // scoped-styles.ts` — rewrites every STATIC class token in a component's own markup while it
3
+ // still has the source text. A DYNAMIC `class={expr}` has no tokens to read at build time, so
4
+ // its value is wrapped in a call to `scopeSvelteClass` instead. Same split Vue's scoped-style
5
+ // path already uses (`scopeClassName` in @symbiote-native/engine, symbiote-sfc-style-compiler §5).
6
+ // Both halves apply the identical per-token rule by sharing `./scope-token` — see that file for
7
+ // why the rule is not simply declared here.
8
+ //
9
+ // WHY NOT REUSE THE ENGINE'S `scopeClassName` DIRECTLY. Its input surface is Vue's:
10
+ // `string | Record<string, boolean> | Array<those>`. Svelte's is wider — `class={['card', cond &&
11
+ // 'on']}` leaves a literal `false` in the array, which `scopeClassName` hands to `.split()` and
12
+ // throws on. Widening a function the other three adapters depend on, to serve one adapter's
13
+ // syntax, is the trade §22b already refused for `resolveClassName`. Instead this normalizes
14
+ // through the adapter's OWN clsx boundary first (`normalizeSvelteClass`) and only scopes what
15
+ // comes back as a class string; everything else — a resolved style object, `false`, a mixed array
16
+ // holding a style object — is handed on untouched, taking exactly the path it took before.
17
+ import { normalizeSvelteClass } from './class-value.js';
18
+ import { scopeToken } from './scope-token.js';
19
+ const CLASS_SEPARATOR = ' ';
20
+ export function scopeSvelteClass(value, localNames, scopeId) {
21
+ const normalized = normalizeSvelteClass(value);
22
+ if (typeof normalized !== 'string')
23
+ return normalized;
24
+ return normalized
25
+ .split(/\s+/)
26
+ .filter(Boolean)
27
+ .map(token => scopeToken(token, localNames, scopeId))
28
+ .join(CLASS_SEPARATOR);
29
+ }
@@ -0,0 +1,9 @@
1
+ // Re-exports @symbiote-native/css-parser verbatim from INSIDE this package, so a consuming app's
2
+ // own require('@symbiote-native/svelte/metro-css-parser') resolves css-parser relative to THIS
3
+ // file's location (adapters/svelte/node_modules, where css-parser IS a real dependency — see
4
+ // package.json), not relative to the app's own node_modules. Twin of
5
+ // adapters/vue/metro-css-parser.cjs. Unlike Vue's, this adapter's own metro-svelte-transformer.cjs
6
+ // already handles standalone style files itself (its isStyleFile branch), so this subpath exists
7
+ // only for parity with the other adapters and for a future consumer that wants CSS-only handling
8
+ // without the .svelte transform (e.g. importing a plain .css from non-.svelte source).
9
+ module.exports = require('@symbiote-native/css-parser').createCssMetroTransformer();
@@ -0,0 +1,156 @@
1
+ // Metro babel transformer that teaches the bundler to read Svelte components (.svelte). Metro
2
+ // has no Svelte plugin, so we do the single-pass compile here: run svelte/compiler's compile()
3
+ // with this adapter's own compiler options, then hand the generated client JS to RN's own babel
4
+ // transformer. This is the Metro twin of adapters/vue/metro-vue-transformer.cjs.
5
+ //
6
+ // Unlike Vue's transformer, this does NOT rewrite any framework import: the whole
7
+ // svelte-adapter-dom-shim strategy is that STOCK compiled Svelte client output (its own
8
+ // `import * as $ from 'svelte/internal/client'`) runs completely unchanged against a globalThis
9
+ // DOM shim — there is no @symbiote-native/svelte/runtime-helpers retarget the way Vue needs one.
10
+ // Verified against the installed svelte@5.56.8: compiling a trivial component with
11
+ // {fragments:'tree', css:'external', generate:'client'} produces exactly
12
+ // `import * as $ from 'svelte/internal/client'` + `$.from_tree(...)` + `$.set_custom_element_data`
13
+ // calls — the same shape the dom-shim's init_operations patches already target.
14
+ //
15
+ // Ships as a package-level export (`@symbiote-native/svelte/metro-svelte-transformer`) rather
16
+ // than living in each consuming app: a consumer's own metro.config.js just points
17
+ // babelTransformerPath at it.
18
+
19
+ const { compile, compileModule } = require('svelte/compiler');
20
+ const ts = require('typescript');
21
+ const {
22
+ compileCssFile,
23
+ isStyleFile,
24
+ resolveUpstreamTransformer,
25
+ } = require('@symbiote-native/css-parser');
26
+
27
+ const upstreamTransformer = resolveUpstreamTransformer();
28
+
29
+ // Mirrors adapters/svelte/svelte.config.js's own compilerOptions exactly (fragments:'tree' is
30
+ // mandatory per svelte-adapter-dom-shim skill §2 — it makes the compiler emit from_tree(),
31
+ // element-by-element via document.createElement, never from_html()'s innerHTML-on-a-<template>,
32
+ // so the shim needs no HTML parser; css:'external' keeps Svelte from injecting a <style> into a
33
+ // document.head that doesn't meaningfully exist). Duplicated here rather than imported from that
34
+ // file: svelte.config.js is ESM (`export default`, loaded directly by Node-native tooling —
35
+ // svelte-check, the language server, Vite), while this file is `require()`d by Metro directly
36
+ // and must stay pure CommonJS with no build step of its own, exactly like
37
+ // metro-vue-transformer.cjs.
38
+ const COMPILER_OPTIONS = { fragments: 'tree', css: 'external', generate: 'client' };
39
+
40
+ // The web-only-construct guard (svelte-adapter-dom-shim skill §7/§22) RUNS HERE, on every
41
+ // `.svelte` Metro compiles — not just via svelte-check/the language server. `{@html}` compiles
42
+ // to `$.html()`, which assigns to an `innerHTML` the shim does not define, so the content
43
+ // silently never renders; editor tooling only covers a developer who has it wired up, and a
44
+ // consuming app whose own `svelte.config.js` never registers the preprocessor would still ship
45
+ // the broken bundle. A build-time gate is the only one nobody can be missing.
46
+ //
47
+ // Resolved by package self-reference rather than a relative path so the same line works from
48
+ // `src/*.ts` in this workspace and from `build/*.js` in a published install — `exports` and
49
+ // `publishConfig.exports` each map `./preprocessor` at their own target. Loaded lazily and once:
50
+ // `require()` cannot pull an ESM/TS module, but `transform` is already async.
51
+ let preprocessorPromise;
52
+ function webOnlyConstructGuard() {
53
+ preprocessorPromise ??= import('@symbiote-native/svelte/preprocessor').then(mod =>
54
+ mod.forbidWebOnlyConstructs(),
55
+ );
56
+ return preprocessorPromise;
57
+ }
58
+
59
+ // The `<style>` preprocessor, loaded the same lazy way and for the same reason. Unlike the guard
60
+ // above (which only throws and hands the source back untouched), this one REWRITES the source —
61
+ // it compiles the style block into registerStyles() output and scopes every `class` in the
62
+ // component's own markup — so its returned `code` is what compile() must be given. See
63
+ // src/preprocessor/scoped-styles.ts for why Svelte's own `result.css` cannot be used instead.
64
+ let scopedStylesPromise;
65
+ function scopedStylesPreprocessor() {
66
+ scopedStylesPromise ??= import('@symbiote-native/svelte/scoped-styles').then(mod =>
67
+ mod.scopedStyles(),
68
+ );
69
+ return scopedStylesPromise;
70
+ }
71
+ //
72
+ // Svelte 5's compiler strips <script lang="ts"> types structurally, with no external type
73
+ // resolution needed (unlike @vue/compiler-sfc's compileScript, which needs registerTS + a real
74
+ // `fs` for a type-only import from another file) — so no TypeScript/filesystem wiring is needed
75
+ // here the way metro-vue-transformer.cjs needs it.
76
+ function compileSvelteFile(src, filename) {
77
+ const { js } = compile(src, { ...COMPILER_OPTIONS, filename });
78
+ return js.code;
79
+ }
80
+
81
+ // `.svelte.js`/`.svelte.ts` files (adapters/svelte/src/runes/*.svelte.ts and any package that
82
+ // ships its own runes) carry rune syntax ($state/$effect/...) OUTSIDE a component's markup, so
83
+ // they need svelte/compiler's separate MODULE api, not compile() — compile() expects a
84
+ // component and throws a parse error on a bare module. Uncompiled, a literal `$state(...)` call
85
+ // hits svelte/index-client.js's dev-guard export and throws `rune_outside_svelte` the instant it
86
+ // runs — this filename check was previously MISSING entirely, so every .svelte.ts rune file
87
+ // shipped uncompiled and would crash on first call on a real device.
88
+ //
89
+ // Unlike compile() (which strips <script lang="ts"> types itself via bundled acorn-typescript),
90
+ // compileModule() does NOT parse TypeScript at all — it throws js_parse_error on a bare
91
+ // return-type annotation regardless of the .ts filename passed. So TS has to be stripped BEFORE
92
+ // compileModule() ever sees the source. ts.transpileModule() (isolated-file "strip only" mode,
93
+ // module:ESNext/target:ESNext) does exactly this: drops type-only imports/annotations, keeps
94
+ // every used value import, and leaves $state/$effect call expressions untouched — it does not
95
+ // require full-program type information the way metro-vue-transformer.cjs's registerTS step does.
96
+ function stripTypeScript(src, filename) {
97
+ const { outputText } = ts.transpileModule(src, {
98
+ fileName: filename,
99
+ compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ESNext },
100
+ });
101
+ return outputText;
102
+ }
103
+
104
+ function compileSvelteModuleFile(src, filename) {
105
+ const { js } = compileModule(stripTypeScript(src, filename), { generate: 'client', filename });
106
+ return js.code;
107
+ }
108
+
109
+ // Exported separately so tests can assert on the compiled output directly without driving the
110
+ // full upstream RN Babel preset.
111
+ module.exports.compileSvelteFile = compileSvelteFile;
112
+ module.exports.compileSvelteModuleFile = compileSvelteModuleFile;
113
+
114
+ module.exports.transform = async function transform(params) {
115
+ if (params.filename.endsWith('.svelte')) {
116
+ // Throws with a message naming the RN alternative. Deliberately BEFORE compile(), so the
117
+ // author sees the real diagnosis rather than a downstream symptom.
118
+ (await webOnlyConstructGuard()).markup({ content: params.src, filename: params.filename });
119
+ const preprocessed = await (
120
+ await scopedStylesPreprocessor()
121
+ ).markup({ content: params.src, filename: params.filename });
122
+ const code = compileSvelteFile(preprocessed.code, params.filename);
123
+ // Re-label as .tsx so RN's transformer processes the module exactly like app source; Metro
124
+ // tracks the real path separately. Matches metro-vue-transformer.cjs's identical trick.
125
+ return upstreamTransformer.transform({
126
+ ...params,
127
+ src: code,
128
+ filename: params.filename + '.tsx',
129
+ });
130
+ }
131
+ if (params.filename.endsWith('.svelte.ts') || params.filename.endsWith('.svelte.js')) {
132
+ const code = compileSvelteModuleFile(params.src, params.filename);
133
+ return upstreamTransformer.transform({
134
+ ...params,
135
+ src: code,
136
+ filename: params.filename + '.tsx',
137
+ });
138
+ }
139
+ // A standalone style file (as opposed to a component's own <style> block, handled by the
140
+ // preprocessor above) — the framework-agnostic path (core/css-parser's compileCssFile), usable
141
+ // from this adapter's examples exactly like from any other adapter's. isStyleFile recognizes
142
+ // .css/.scss/.sass/.less/.styl (+ .module.*).
143
+ if (isStyleFile(params.filename)) {
144
+ const { code } = await compileCssFile(params.src, params.filename);
145
+ return upstreamTransformer.transform({
146
+ ...params,
147
+ src: code,
148
+ filename: params.filename + '.js',
149
+ });
150
+ }
151
+ return upstreamTransformer.transform(params);
152
+ };
153
+
154
+ // Surface the upstream cache key so RN preset changes still bust Metro's cache. The compile step
155
+ // itself is invalidated by `--reset-cache`.
156
+ module.exports.getCacheKey = upstreamTransformer.getCacheKey;