@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 A. Prokopenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # @symbiote-native/svelte
2
+
3
+ The **Svelte adapter** for [SymbioteNative](../../README.md) — render real native iOS/Android views
4
+ from Svelte 5, on the _same_ untouched core as React, Vue, and Angular, with React Native's own
5
+ renderer never in the path. Svelte ships no official host-renderer API yet (`createRenderer` is
6
+ still an unmerged PR upstream), so instead of hooking a framework-blessed extension point this
7
+ adapter patches `globalThis`'s DOM classes: stock compiled Svelte client output believes it is
8
+ talking to a real DOM, and every one of those calls routes into `@symbiote-native/engine`'s same
9
+ four-call mutation API underneath, which does the clone-on-write commit into Fabric.
10
+
11
+ Svelte is the **sharpest test yet** that the core is genuinely framework-agnostic — unlike Vue's
12
+ `createRenderer` or Angular's `Renderer2`, there was no host-renderer seam to hook here at all.
13
+
14
+ <div align="center">
15
+
16
+ ![Svelte driving real native iOS views through SymbioteNative](../../assets/svelte-demo.gif)
17
+
18
+ </div>
19
+
20
+ > New to SymbioteNative? The [root README](../../README.md) has the architecture.
21
+
22
+ ---
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ npm install @symbiote-native/svelte react-native svelte
28
+ ```
29
+
30
+ `react-native` and `svelte` stay your app's own top-level dependencies — this package only
31
+ replaces the JS renderer that drives them. Every component is a `.svelte` SFC — there's no TSX
32
+ alternative the way Vue offers — so the Metro wiring below isn't optional. There's no
33
+ `create-symbiote` scaffolder yet, so it comes from [`examples/svelte`](../../examples/svelte)
34
+ rather than a generator:
35
+
36
+ - `metro.config.js` — point `babelTransformerPath` at
37
+ `@symbiote-native/svelte/metro-svelte-transformer` (compiles `.svelte` on the way into the
38
+ bundle) and disable `inlineRequires` (see [the gotcha
39
+ below](#a-svelte-specific-gotcha--inlinerequires-vs-the-svelte-runtime)).
40
+ - `svelte.config.js` — registers the `forbidWebOnlyConstructs()` and `scopedStyles()`
41
+ preprocessors, so `svelte-check` and the editor catch the same things Metro's transformer
42
+ already guards against at build time, and sets `compilerOptions: { fragments: 'tree', css:
43
+ 'external' }`.
44
+
45
+ ---
46
+
47
+ ## Use it
48
+
49
+ The native entry reaches the _same_ seam as every other adapter. `createApp(App).mount(appName)`
50
+ wires the native-host seams and RN's own `AppRegistry`, then mounts via `@symbiote-native/engine` —
51
+ RN's own renderer is never in the path:
52
+
53
+ ```js
54
+ // index.js
55
+ import { createApp } from '@symbiote-native/svelte/bootstrap';
56
+ import App from './App.svelte';
57
+ import { name as appName } from './app.json';
58
+
59
+ createApp(App).mount(appName);
60
+ ```
61
+
62
+ The app is ordinary Svelte 5 — it just imports primitives from `@symbiote-native/svelte` instead of
63
+ `react-native`. A tap→increment counter, using runes:
64
+
65
+ ```svelte
66
+ <script lang="ts">
67
+ import { View, Text, Pressable } from '@symbiote-native/svelte';
68
+
69
+ let count = $state(0);
70
+ </script>
71
+
72
+ <View style={{ padding: 24 }}>
73
+ <Text>Taps: {count}</Text>
74
+ <Pressable onPress={() => count++}>
75
+ <Text>Tap me</Text>
76
+ </Pressable>
77
+ </View>
78
+ ```
79
+
80
+ The full canary is [`examples/svelte`](../../examples/svelte) — a stock RN 0.86 app whose
81
+ [`App.svelte`](../../examples/svelte/App.svelte) exercises the same surface as the React, Vue, and
82
+ Angular reference canaries.
83
+
84
+ ---
85
+
86
+ ## Parity — and the one gap
87
+
88
+ Svelte reaches the same primitives, runtime modules, `Animated` on both drivers, gestures,
89
+ accessibility, and the `VirtualizedList` family as React, Vue, and Angular, verified on-device on
90
+ iOS and Android. That parity is **structural, not hand-copied**: the component logic (state
91
+ machines + render functions) is written **once** in `@symbiote-native/components`, and Svelte
92
+ supplies only its lifecycle — runes (`$state` / `$derived` / `$effect`) driving hand-authored
93
+ `.svelte` markup that mirrors each `render-*.ts` directly, rather than a generic descriptor bridge
94
+ the way React and Vue use one.
95
+
96
+ The one deliberate gap, same as every other non-React adapter — **third-party React component
97
+ packages** (`@react-native-community/slider` used directly) run only under the React adapter:
98
+ their body calls React hooks off the React dispatcher, which is null under Svelte.
99
+ `@symbiote-native/slider` (this repo's own wrapper) _does_ ship a real Svelte build
100
+ (`@symbiote-native/slider/svelte`) through the same `createNode`-by-ViewConfig path Svelte uses
101
+ for its own primitives — that wrapper is what makes this one third-party native view usable from
102
+ Svelte at all; any _other_ React-only component package stays React-adapter-only until it gets the
103
+ same treatment.
104
+
105
+ ---
106
+
107
+ ## A Svelte-specific gotcha — inlineRequires vs the Svelte runtime
108
+
109
+ RN's default Metro config turns on `inlineRequires` for every top-level import. Svelte's internal
110
+ client runtime (`svelte/internal/client/**`) is a graph of small files wired through module-scope
111
+ singleton reactivity state (the current-effect/signal graph); under `inlineRequires` that graph
112
+ re-enters itself while mounting and blows the JS stack — `Maximum call stack size exceeded` inside
113
+ `metroRequire`, reproduced on a real device/simulator, not just in theory.
114
+ [`examples/svelte`](../../examples/svelte)'s `metro.config.js` disables it explicitly
115
+ (`experimentalImportSupport: false, inlineRequires: false`); any new Svelte app needs the same
116
+ override, since RN's default Metro config doesn't know to do this on its own.
117
+
118
+ A smaller one: Svelte's compiler only trims LEADING/TRAILING whitespace inside a fragment —
119
+ whitespace strictly _between_ two sibling non-text nodes collapses to a real text node and is
120
+ kept. That's invisible on the web, but a stray text node between siblings under a non-`Text`
121
+ parent is an invalid Fabric child on this DOM shim, so markup inside an `{#each}`/`{#if}` chain
122
+ over host primitives has to avoid it — this repo carries a device-verified regression test
123
+ guarding it.
124
+
125
+ ---
126
+
127
+ ## Run it
128
+
129
+ [`examples/svelte`](../../examples/svelte) is a stock React Native 0.86 app. Requires Node ≥ 22
130
+ and the [RN environment setup](https://reactnative.dev/docs/set-up-your-environment) (Xcode,
131
+ CocoaPods):
132
+
133
+ ```bash
134
+ cd examples/svelte
135
+ npm install
136
+ bundle install # first time only — installs CocoaPods itself
137
+ bundle exec pod install # fetch native pods
138
+
139
+ # terminal 1 — Metro. DEBUG=1 turns on diagnostic logs.
140
+ DEBUG=1 npm run dev
141
+
142
+ # terminal 2 — build + launch
143
+ npm run ios # iOS simulator
144
+ npm run android # Android emulator
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Test it
150
+
151
+ ```bash
152
+ pnpm test # vitest, from the workspace root — headless, fake Fabric slot
153
+
154
+ cd examples/svelte
155
+ npm run e2e:build:ios # build the app for Detox
156
+ npm run e2e:test:ios # run the canary journeys on the iOS simulator
157
+ # …or the android equivalents: e2e:build:android / e2e:test:android
158
+ ```
159
+
160
+ Why these come for free — a SymbioteNative app is a stock RN app underneath, so RN's whole testing
161
+ ecosystem applies unchanged. See [Testing](../../README.md#testing).
@@ -0,0 +1,7 @@
1
+ import type { Component } from 'svelte';
2
+ import { type IBootstrapHostOptions } from '@symbiote-native/components/bootstrap';
3
+ export type { IBootstrapHostOptions } from '@symbiote-native/components/bootstrap';
4
+ export type ISymbioteSvelteApp = {
5
+ mount(appName: string): void;
6
+ };
7
+ export declare function createApp(RootComponent: Component, options?: IBootstrapHostOptions): ISymbioteSvelteApp;
@@ -0,0 +1,17 @@
1
+ // Zero-config app entry, the Svelte twin of adapters/vue/src/bootstrap.ts and
2
+ // adapters/react/src/bootstrap.ts. Lives OUTSIDE the package's main barrel — react-native's
3
+ // own source is Flow syntax Vitest's transform can't parse, so anything importing it directly
4
+ // must stay unreachable from the tested main index.ts.
5
+ import { AppRegistry as RNAppRegistry } from 'react-native';
6
+ import { bootstrapHost } from '@symbiote-native/components/bootstrap';
7
+ import { AppRegistry, setHostRegistrar } from './modules/app-registry/index.js';
8
+ // Mirrors the Vue/React adapters' createApp(App).mount(appName) two-step idiom.
9
+ export function createApp(RootComponent, options = {}) {
10
+ return {
11
+ mount(appName) {
12
+ bootstrapHost(options);
13
+ setHostRegistrar(RNAppRegistry);
14
+ AppRegistry.registerComponent(appName, () => RootComponent);
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,6 @@
1
+ import { resolveClassName, type IClassNameValue } from '@symbiote-native/engine';
2
+ export type IClassMap = Readonly<Record<string, boolean | null | undefined>>;
3
+ export type IClassEntry = IClassNameValue | IClassMap | false;
4
+ export type ISvelteClassValue = IClassEntry | ReadonlyArray<IClassEntry | ReadonlyArray<IClassEntry>>;
5
+ export declare function normalizeSvelteClass(value: unknown): unknown;
6
+ export declare function resolveSvelteClass(value: unknown): ReturnType<typeof resolveClassName>;
@@ -0,0 +1,81 @@
1
+ // clsx-shaped `class` values, normalized to a class string at this adapter's bag boundary.
2
+ //
3
+ // WHY THIS IS THE ADAPTER'S JOB. `class={{ active: isOn }}` and `class={['card', isOn &&
4
+ // 'card-on']}` are idiomatic Svelte, but Svelte only normalizes them on the ELEMENT path:
5
+ // `set_class` runs the value through `clsx()` + `to_class()`
6
+ // (svelte/src/internal/shared/attributes.js). A COMPONENT prop gets no such treatment — it is
7
+ // handed over verbatim. App code here never authors a host element (it composes View/Text/… from
8
+ // @symbiote-native/svelte, skill §7), so every class value in this adapter arrives as a component
9
+ // prop and the normalization Svelte would have done is ours to do.
10
+ //
11
+ // WHY NOT WIDEN THE ENGINE'S resolveClassName INSTEAD. There, a plain object means an
12
+ // ALREADY-RESOLVED STYLE and is returned untouched. That is load-bearing for the other three
13
+ // adapters — React only ever passes a string, and Vue's own `normalizeClass` has already
14
+ // flattened `:class="{ active: x }"` into a string before patchProp is reached — so teaching
15
+ // resolveClassName to read an object as a class map would silently change their behaviour.
16
+ //
17
+ // THE DISAMBIGUATION RULE, and why this one: `{ color: 'red' }` is a resolved style and
18
+ // `{ active: true }` is a clsx map, and the two shapes are only told apart by their VALUES.
19
+ // every own value is boolean / null / undefined -> clsx map
20
+ // anything else (including an empty object) -> resolved style, passed through untouched
21
+ // A style object's values are colours, lengths and numbers — never booleans — while a clsx map
22
+ // is written as `{ name: <condition> }`, and a condition idiomatically evaluates to a boolean.
23
+ // The rule is also expressible in the type below (IClassMap's value type IS the runtime check),
24
+ // so `{ card: someString }` — the one shape that would silently take the style branch — is a
25
+ // compile error at the call site rather than a surprise at runtime. An empty object is
26
+ // deliberately NOT treated as a clsx map: both readings contribute no style, so the cheaper,
27
+ // non-allocating branch is the right one.
28
+ import { isClassNameValue, resolveClassName } from '@symbiote-native/engine';
29
+ // Returns a class STRING when the value is clsx-shaped, and the value UNTOUCHED otherwise, so
30
+ // everything the engine already resolved correctly keeps taking the exact path it took before.
31
+ export function normalizeSvelteClass(value) {
32
+ if (typeof value !== 'object' || value === null)
33
+ return value;
34
+ const parts = [];
35
+ return collectClsxParts(value, parts) ? parts.join(CLASS_SEPARATOR) : value;
36
+ }
37
+ const CLASS_SEPARATOR = ' ';
38
+ // For the handful of components that resolve a class to a style THEMSELVES rather than handing
39
+ // it to the host bag — ScrollView and VirtualizedList split the resolved class into layout vs.
40
+ // visual halves before they know which host tag gets which — so a clsx map reaches the registry
41
+ // there too instead of falling into resolveClassName's "already a style" branch.
42
+ export function resolveSvelteClass(value) {
43
+ const normalized = normalizeSvelteClass(value);
44
+ return resolveClassName(isClassNameValue(normalized) ? normalized : undefined);
45
+ }
46
+ // Mirrors clsx's own `toVal` (node_modules/clsx/dist/clsx.js), which is what Svelte's `clsx()`
47
+ // wrapper delegates to for any object value: a string or number contributes itself, an array
48
+ // recurses over its entries dropping the falsy ones, an object contributes every key whose value
49
+ // is truthy. Returns false the moment it meets a plain object that is not a clsx map — that
50
+ // makes the whole value non-clsx, so the caller hands it on unchanged.
51
+ function collectClsxParts(value, parts) {
52
+ if (value === null || value === undefined || typeof value === 'boolean')
53
+ return true;
54
+ if (typeof value === 'string' || typeof value === 'number') {
55
+ if (value !== '' && value !== 0)
56
+ parts.push(String(value));
57
+ return true;
58
+ }
59
+ if (Array.isArray(value)) {
60
+ for (const entry of value) {
61
+ if (!collectClsxParts(entry, parts))
62
+ return false;
63
+ }
64
+ return true;
65
+ }
66
+ if (typeof value !== 'object')
67
+ return false;
68
+ const entries = Object.entries(value);
69
+ if (!isClassMapEntries(entries))
70
+ return false;
71
+ for (const [name, applies] of entries) {
72
+ if (applies === true)
73
+ parts.push(name);
74
+ }
75
+ return true;
76
+ }
77
+ function isClassMapEntries(entries) {
78
+ if (entries.length === 0)
79
+ return false;
80
+ return entries.every(([, applies]) => typeof applies === 'boolean' || applies === null || applies === undefined);
81
+ }
@@ -0,0 +1,38 @@
1
+ <script lang="ts" module>
2
+ // RefreshControl primitive. On iOS this is the PullToRefreshView Fabric node that lives INSIDE
3
+ // a ScrollView (a sibling of the content container), giving the pull-to-refresh gesture. On
4
+ // Android it is AndroidSwipeRefreshLayout and WRAPS the scroll view (a childless-on-iOS,
5
+ // wraps-on-Android shape — see refresh-control-props.ts). `refreshing` is a controlled prop:
6
+ // the parent owns it and pushes it down each commit; native reports the gesture via the direct
7
+ // `topRefresh` event, which the engine routes to the host's `onRefresh` bag entry.
8
+ import type { IRefreshControlProps } from './refresh-control-props';
9
+
10
+ export type { IRefreshControlProps };
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import { dlog } from '@symbiote-native/engine';
15
+ import { resolveAccessibilityProps } from '@symbiote-native/components';
16
+ import { createAttachmentsSync } from '../runes/attachments';
17
+ import type { ShimElement } from '../dom-shim';
18
+
19
+ let { children, ...rest }: IRefreshControlProps = $props();
20
+
21
+ dlog('RefreshControl -> PullToRefreshView');
22
+ dlog(`RefreshControl refreshing=${String(rest.refreshing)}`);
23
+ if (rest.enabled !== undefined) dlog(`RefreshControl enabled=${String(rest.enabled)} (Android-only)`);
24
+ if (rest.onRefresh !== undefined) dlog('RefreshControl onRefresh listener wired');
25
+
26
+ const bag = $derived(resolveAccessibilityProps(rest));
27
+
28
+ // See View.svelte's note on `{@attach}`.
29
+ let hostShim = $state.raw<ShimElement | null>(null);
30
+ const syncAttachments = createAttachmentsSync();
31
+ $effect(() => {
32
+ syncAttachments(hostShim, rest);
33
+ });
34
+ </script>
35
+
36
+ <symbiote-refresh-control p={bag} bind:this={hostShim}>
37
+ {@render children?.()}
38
+ </symbiote-refresh-control>
@@ -0,0 +1,5 @@
1
+ import type { IRefreshControlProps } from './refresh-control-props';
2
+ export type { IRefreshControlProps };
3
+ declare const RefreshControl: import("svelte").Component<IRefreshControlProps, {}, "">;
4
+ type RefreshControl = ReturnType<typeof RefreshControl>;
5
+ export default RefreshControl;
@@ -0,0 +1,34 @@
1
+ <script lang="ts" module>
2
+ // SafeAreaView primitive. A plain view whose native side insets its children to the safe area
3
+ // (notch, rounded corners, system bars). There is no JS-side translation; RN just renders the
4
+ // native RCTSafeAreaView and lets the host do the inset math, so this maps style + children
5
+ // straight onto the intrinsic — same object-bag pattern as View.svelte.
6
+ import type { ISafeAreaViewProps } from './safe-area-view-props';
7
+
8
+ export type { ISafeAreaViewProps };
9
+ </script>
10
+
11
+ <script lang="ts">
12
+ import { dlog } from '@symbiote-native/engine';
13
+ import { resolveAccessibilityProps } from '@symbiote-native/components';
14
+
15
+ import { createAttachmentsSync } from '../runes/attachments';
16
+ import type { ShimElement } from '../dom-shim';
17
+
18
+ let { children, ...rest }: ISafeAreaViewProps = $props();
19
+
20
+ dlog('SafeAreaView -> SafeAreaView');
21
+
22
+ const bag = $derived(resolveAccessibilityProps(rest));
23
+
24
+ // See View.svelte's note on `{@attach}`.
25
+ let hostShim = $state.raw<ShimElement | null>(null);
26
+ const syncAttachments = createAttachmentsSync();
27
+ $effect(() => {
28
+ syncAttachments(hostShim, rest);
29
+ });
30
+ </script>
31
+
32
+ <symbiote-safe-area-view p={bag} bind:this={hostShim}>
33
+ {@render children?.()}
34
+ </symbiote-safe-area-view>
@@ -0,0 +1,5 @@
1
+ import type { ISafeAreaViewProps } from './safe-area-view-props';
2
+ export type { ISafeAreaViewProps };
3
+ declare const SafeAreaView: import("svelte").Component<ISafeAreaViewProps, {}, "">;
4
+ type SafeAreaView = ReturnType<typeof SafeAreaView>;
5
+ export default SafeAreaView;
@@ -0,0 +1,25 @@
1
+ <script lang="ts" module>
2
+ // See View.svelte's header comment — same object-bag design (svelte-adapter-dom-shim
3
+ // skill §3g(c)). No IResponderProps here, matching every other adapter's ITextProps.
4
+ import type { ITextProps } from './text-props';
5
+
6
+ export type { ITextProps };
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import { createAttachmentsSync } from '../runes/attachments';
11
+ import type { ShimElement } from '../dom-shim';
12
+
13
+ let { children, ...bag }: ITextProps = $props();
14
+
15
+ // See View.svelte's note: `{@attach}` arrives as a symbol-keyed entry in the same rest object.
16
+ let hostShim = $state.raw<ShimElement | null>(null);
17
+ const syncAttachments = createAttachmentsSync();
18
+ $effect(() => {
19
+ syncAttachments(hostShim, bag);
20
+ });
21
+ </script>
22
+
23
+ <symbiote-text p={bag} bind:this={hostShim}>
24
+ {@render children?.()}
25
+ </symbiote-text>
@@ -0,0 +1,5 @@
1
+ import type { ITextProps } from './text-props';
2
+ export type { ITextProps };
3
+ declare const Text: import("svelte").Component<ITextProps, {}, "">;
4
+ type Text = ReturnType<typeof Text>;
5
+ export default Text;
@@ -0,0 +1,41 @@
1
+ <script lang="ts" module>
2
+ // Host primitive for app code. Per svelte-adapter-dom-shim skill §3g(c): Symbiote's
3
+ // custom-element codegen path stringifies individual attributes, so unlike Vue's View
4
+ // (which spreads props onto the intrinsic tag and lets patchProp route each one), this
5
+ // component assembles every prop into ONE object bag and hands it to `<symbiote-view p={bag}>`
6
+ // — the shim element's `p` setter diffs it per key into routeProp. `children` is pulled out
7
+ // first: it is a Svelte-specific field (a Snippet), never forwarded into the bag, mirroring
8
+ // why every adapter's IViewProps is declared per-adapter (CLAUDE.md
9
+ // <prop_types_split_agnostic_vs_per_adapter>) rather than shared verbatim.
10
+ import type { IViewProps } from './view-props';
11
+
12
+ export type { IViewProps };
13
+ </script>
14
+
15
+ <script lang="ts">
16
+ import { createAttachmentsSync } from '../runes/attachments';
17
+ import type { ShimElement } from '../dom-shim';
18
+
19
+ let { children, id, nativeID, ...rest }: IViewProps = $props();
20
+
21
+ // `{@attach fn}` on a component lands as a SYMBOL-keyed prop, so it rides in `rest` alongside
22
+ // the ordinary bag keys and never reaches the engine (routeProp only walks string keys). This
23
+ // is the one directive-shaped Svelte feature that IS legal on a component, hence the only way
24
+ // an author reaches the committed host node — see runes/attachments.ts.
25
+ let hostShim = $state.raw<ShimElement | null>(null);
26
+ const syncAttachments = createAttachmentsSync();
27
+ $effect(() => {
28
+ syncAttachments(hostShim, rest);
29
+ });
30
+
31
+ // RN's modern `id` is just a W3C-named alias for `nativeID`: View.js copies it over
32
+ // (`processedProps.nativeID = id`), so `id` wins when both are set. Fold it here and drop
33
+ // the raw `id` key so it never reaches Fabric (every non-function prop in the bag otherwise
34
+ // passes through to the slot as-is) — mirrors React's `resolveId` in
35
+ // `adapters/react/src/components.ts`, adapted to the object-bag idiom.
36
+ const bag = $derived(id === undefined ? { ...rest, nativeID } : { ...rest, nativeID: id });
37
+ </script>
38
+
39
+ <symbiote-view p={bag} bind:this={hostShim}>
40
+ {@render children?.()}
41
+ </symbiote-view>
@@ -0,0 +1,5 @@
1
+ import type { IViewProps } from './view-props';
2
+ export type { IViewProps };
3
+ declare const View: import("svelte").Component<IViewProps, {}, "">;
4
+ type View = ReturnType<typeof View>;
5
+ export default View;
@@ -0,0 +1,3 @@
1
+ import type { IActivityIndicatorPlatform } from '@symbiote-native/components';
2
+ export type { IActivityIndicatorPlatform };
3
+ export declare const PLATFORM: IActivityIndicatorPlatform;
@@ -0,0 +1,5 @@
1
+ export const PLATFORM = {
2
+ // RN: `color = Platform.OS === 'ios' ? GRAY : null`. Android lets the theme color it.
3
+ defaultColor: null,
4
+ nativeExtras: { styleAttr: 'Normal', indeterminate: true },
5
+ };
@@ -0,0 +1 @@
1
+ export * from './activity-indicator-platform.ios';
@@ -0,0 +1,3 @@
1
+ import type { IActivityIndicatorPlatform } from '@symbiote-native/components';
2
+ export type { IActivityIndicatorPlatform };
3
+ export declare const PLATFORM: IActivityIndicatorPlatform;
@@ -0,0 +1,6 @@
1
+ // RN's iOS default spinner color (Libraries/.../ActivityIndicator.js GRAY).
2
+ const IOS_DEFAULT_COLOR = '#999999';
3
+ export const PLATFORM = {
4
+ defaultColor: IOS_DEFAULT_COLOR,
5
+ nativeExtras: {},
6
+ };
@@ -0,0 +1,5 @@
1
+ // Base / default: re-exports the iOS build. Metro overrides this with
2
+ // activity-indicator-platform.ios.ts / .android.ts on a real host; under tsc/vitest the host
3
+ // config resolves here. Filename is the selector, no Platform.OS read — same convention as
4
+ // switch/switch-platform.ts.
5
+ export * from './activity-indicator-platform.ios.js';
@@ -0,0 +1,5 @@
1
+ import type { IActivityIndicatorProps as IActivityIndicatorBaseProps } from '@symbiote-native/components';
2
+ import type { ISvelteClassValue } from '../../class-value';
3
+ export type IActivityIndicatorProps = IActivityIndicatorBaseProps & {
4
+ class?: ISvelteClassValue;
5
+ };
@@ -0,0 +1,57 @@
1
+ <script lang="ts" module>
2
+ // ActivityIndicator: render-only (no state). Calls the shared renderActivityIndicator()
3
+ // straight from core/components — the size translation, platform default-color fold, and
4
+ // native-extras all live there, once, for every adapter. The root stays a literal template
5
+ // tag (`bind:this` needs a known tag), but its child (the spinner) is materialized and kept
6
+ // in sync via the generic descriptorToSvelte bridge (createDescriptorChildrenSync) instead of
7
+ // a hand-written child tag — see svelte-adapter-dom-shim skill §19 for why this is safe and
8
+ // cheap (create once, update by position, never recreate).
9
+ import type { IActivityIndicatorProps } from './activity-indicator-props';
10
+
11
+ export type { IActivityIndicatorProps };
12
+ </script>
13
+
14
+ <script lang="ts">
15
+ import { renderActivityIndicator, resolveAccessibilityProps } from '@symbiote-native/components';
16
+ import { PLATFORM } from './activity-indicator-platform';
17
+ import { createDescriptorChildrenSync } from '../../descriptor-to-svelte';
18
+ import { createAttachmentsSync } from '../../runes/attachments';
19
+ import type { ShimElement } from '../../dom-shim';
20
+
21
+ let {
22
+ animating = true,
23
+ color,
24
+ hidesWhenStopped = true,
25
+ size = 'small',
26
+ style,
27
+ class: className,
28
+ ...passthrough
29
+ }: IActivityIndicatorProps = $props();
30
+
31
+ const descriptor = $derived(
32
+ renderActivityIndicator(
33
+ {
34
+ animating,
35
+ hidesWhenStopped,
36
+ size,
37
+ color,
38
+ style,
39
+ passthrough: { ...resolveAccessibilityProps(passthrough), class: className },
40
+ },
41
+ PLATFORM,
42
+ ),
43
+ );
44
+ let hostShim = $state.raw<ShimElement | null>(null);
45
+ const syncChildren = createDescriptorChildrenSync();
46
+
47
+ $effect(() => {
48
+ syncChildren(hostShim, descriptor.children);
49
+ });
50
+ // See View.svelte's note on `{@attach}`.
51
+ const syncAttachments = createAttachmentsSync();
52
+ $effect(() => {
53
+ syncAttachments(hostShim, passthrough);
54
+ });
55
+ </script>
56
+
57
+ <symbiote-view p={descriptor.props} bind:this={hostShim} />
@@ -0,0 +1,5 @@
1
+ import type { IActivityIndicatorProps } from './activity-indicator-props';
2
+ export type { IActivityIndicatorProps };
3
+ declare const Index: import("svelte").Component<IActivityIndicatorProps, {}, "">;
4
+ type Index = ReturnType<typeof Index>;
5
+ export default Index;
@@ -0,0 +1,5 @@
1
+ import type { IButtonProps as IButtonBaseProps } from '@symbiote-native/components';
2
+ import type { ISvelteClassValue } from '../class-value';
3
+ export type IButtonProps = IButtonBaseProps & {
4
+ class?: ISvelteClassValue;
5
+ };
@@ -0,0 +1 @@
1
+ export {};