@storystick/react 0.0.0-stage → 0.2.0-rc.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 (102) hide show
  1. package/LICENSE +112 -0
  2. package/NOTICE +17 -0
  3. package/PUBLIC-API.md +36 -0
  4. package/README.md +226 -2
  5. package/dist/.source-hash +1 -0
  6. package/dist/file.d.ts +18 -0
  7. package/dist/file.js +44 -0
  8. package/dist/hydrate-slice.d.ts +3 -0
  9. package/dist/hydrate-slice.js +1 -0
  10. package/dist/image-component.d.ts +26 -0
  11. package/dist/image-component.js +79 -0
  12. package/dist/image-element.d.ts +6 -0
  13. package/dist/image-element.js +68 -0
  14. package/dist/image.d.ts +3 -0
  15. package/dist/image.js +3 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +2 -0
  18. package/dist/link-adapter.d.ts +2 -0
  19. package/dist/link-adapter.js +2 -0
  20. package/dist/link-component.d.ts +53 -0
  21. package/dist/link-component.js +97 -0
  22. package/dist/link.d.ts +3 -0
  23. package/dist/link.js +3 -0
  24. package/dist/preview-generation.d.ts +21 -0
  25. package/dist/preview-generation.js +134 -0
  26. package/dist/preview-host/preview-assistant-continuation.d.ts +9 -0
  27. package/dist/preview-host/preview-assistant-continuation.js +39 -0
  28. package/dist/preview-host/preview-authoring-client.d.ts +12 -0
  29. package/dist/preview-host/preview-authoring-client.js +100 -0
  30. package/dist/preview-host/preview-editor-continuation.d.ts +13 -0
  31. package/dist/preview-host/preview-editor-continuation.js +68 -0
  32. package/dist/preview-host/preview-standalone-controls.d.ts +18 -0
  33. package/dist/preview-host/preview-standalone-controls.js +518 -0
  34. package/dist/preview-host/preview-studio-destination.d.ts +20 -0
  35. package/dist/preview-host/preview-studio-destination.js +55 -0
  36. package/dist/preview-host/use-standalone-presentation.d.ts +15 -0
  37. package/dist/preview-host/use-standalone-presentation.js +223 -0
  38. package/dist/preview-icons.d.ts +10 -0
  39. package/dist/preview-icons.js +8 -0
  40. package/dist/preview-interaction-control.d.ts +6 -0
  41. package/dist/preview-interaction-control.js +39 -0
  42. package/dist/preview-panel-state.d.ts +15 -0
  43. package/dist/preview-panel-state.js +28 -0
  44. package/dist/preview-provenance-publisher-model.d.ts +5 -0
  45. package/dist/preview-provenance-publisher-model.js +9 -0
  46. package/dist/preview-provenance-publisher.d.ts +15 -0
  47. package/dist/preview-provenance-publisher.js +68 -0
  48. package/dist/preview-provenance.d.ts +6 -0
  49. package/dist/preview-provenance.js +4 -0
  50. package/dist/preview-scope.d.ts +128 -0
  51. package/dist/preview-scope.js +161 -0
  52. package/dist/preview-slice-scope.d.ts +32 -0
  53. package/dist/preview-slice-scope.js +33 -0
  54. package/dist/preview-text-props.d.ts +18 -0
  55. package/dist/preview-text-props.js +13 -0
  56. package/dist/preview-toolbar-layout.d.ts +42 -0
  57. package/dist/preview-toolbar-layout.js +139 -0
  58. package/dist/preview-toolbar-panel.d.ts +13 -0
  59. package/dist/preview-toolbar-panel.js +6 -0
  60. package/dist/preview-toolbar-resize-handles.d.ts +9 -0
  61. package/dist/preview-toolbar-resize-handles.js +14 -0
  62. package/dist/preview-toolbar-shell.d.ts +18 -0
  63. package/dist/preview-toolbar-shell.js +138 -0
  64. package/dist/public-rich-text.d.ts +15 -0
  65. package/dist/public-rich-text.js +11 -0
  66. package/dist/public-slice-zone.d.ts +7 -0
  67. package/dist/public-slice-zone.js +6 -0
  68. package/dist/query-cache.d.ts +14 -0
  69. package/dist/query-cache.js +289 -0
  70. package/dist/query-preview-loader.d.ts +9 -0
  71. package/dist/query-preview-loader.js +28 -0
  72. package/dist/query-preview-runtime.d.ts +3 -0
  73. package/dist/query-preview-runtime.js +3 -0
  74. package/dist/rendering.d.ts +29 -0
  75. package/dist/rendering.js +17 -0
  76. package/dist/resize-edge.d.ts +8 -0
  77. package/dist/resize-edge.js +33 -0
  78. package/dist/rich-text.d.ts +150 -0
  79. package/dist/rich-text.js +618 -0
  80. package/dist/slice-render-failure.d.ts +8 -0
  81. package/dist/slice-render-failure.js +46 -0
  82. package/dist/slice-zone.d.ts +82 -0
  83. package/dist/slice-zone.js +273 -0
  84. package/dist/slice.d.ts +16 -0
  85. package/dist/slice.js +46 -0
  86. package/dist/storystick-mark.d.ts +3 -0
  87. package/dist/storystick-mark.js +5 -0
  88. package/dist/use-generated-query.d.ts +37 -0
  89. package/dist/use-generated-query.js +308 -0
  90. package/dist/use-momentary-preview-navigate-mode.d.ts +11 -0
  91. package/dist/use-momentary-preview-navigate-mode.js +158 -0
  92. package/dist/use-preview-perspective-intent.d.ts +53 -0
  93. package/dist/use-preview-perspective-intent.js +227 -0
  94. package/dist/use-preview-pointer-drag.d.ts +16 -0
  95. package/dist/use-preview-pointer-drag.js +98 -0
  96. package/dist/use-preview-toolbar-layout.d.ts +36 -0
  97. package/dist/use-preview-toolbar-layout.js +141 -0
  98. package/dist/use-render-mode-debounce.d.ts +31 -0
  99. package/dist/use-render-mode-debounce.js +143 -0
  100. package/dist/video.d.ts +21 -0
  101. package/dist/video.js +63 -0
  102. package/package.json +146 -3
package/LICENSE ADDED
@@ -0,0 +1,112 @@
1
+ # Functional Source License, Version 1.1, MIT Future License
2
+
3
+ ## Abbreviation
4
+
5
+ FSL-1.1-MIT
6
+
7
+ ## Notice
8
+
9
+ Copyright 2026 Tanguy Freycon
10
+
11
+ ## Terms and Conditions
12
+
13
+ ### Licensor ("We")
14
+
15
+ The party offering the Software under these Terms and Conditions.
16
+
17
+ ### The Software
18
+
19
+ The "Software" is each version of the software that we make available under
20
+ these Terms and Conditions, as indicated by our inclusion of these Terms and
21
+ Conditions with the Software.
22
+
23
+ ### License Grant
24
+
25
+ Subject to your compliance with this License Grant and the Patents,
26
+ Redistribution and Trademark clauses below, we hereby grant you the right to
27
+ use, copy, modify, create derivative works, publicly perform, publicly display
28
+ and redistribute the Software for any Permitted Purpose identified below.
29
+
30
+ ### Permitted Purpose
31
+
32
+ A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
33
+ means making the Software available to others in a commercial product or
34
+ service that:
35
+
36
+ 1. substitutes for the Software;
37
+
38
+ 2. substitutes for any other product or service we offer using the Software
39
+ that exists as of the date we make the Software available; or
40
+
41
+ 3. offers the same or substantially similar functionality as the Software.
42
+
43
+ Permitted Purposes specifically include using the Software:
44
+
45
+ 1. for your internal use and access;
46
+
47
+ 2. for non-commercial education;
48
+
49
+ 3. for non-commercial research; and
50
+
51
+ 4. in connection with professional services that you provide to a licensee
52
+ using the Software in accordance with these Terms and Conditions.
53
+
54
+ ### Patents
55
+
56
+ To the extent your use for a Permitted Purpose would necessarily infringe our
57
+ patents, the license grant above includes a license under our patents. If you
58
+ make a claim against any party that the Software infringes or contributes to
59
+ the infringement of any patent, then your patent license to the Software ends
60
+ immediately.
61
+
62
+ ### Redistribution
63
+
64
+ The Terms and Conditions apply to all copies, modifications and derivatives of
65
+ the Software.
66
+
67
+ If you redistribute any copies, modifications or derivatives of the Software,
68
+ you must include a copy of or a link to these Terms and Conditions and not
69
+ remove any copyright notices provided in or with the Software.
70
+
71
+ ### Disclaimer
72
+
73
+ THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
74
+ IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
75
+ PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
76
+
77
+ IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
78
+ SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
79
+ EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
80
+
81
+ ### Trademarks
82
+
83
+ Except for displaying the License Details and identifying us as the origin of
84
+ the Software, you have no right under these Terms and Conditions to use our
85
+ trademarks, trade names, service marks or product names.
86
+
87
+ ## Grant of Future License
88
+
89
+ We hereby irrevocably grant you an additional license to use the Software under
90
+ the MIT license that is effective on the second anniversary of the date we make
91
+ the Software available. On or after that date, you may use the Software under
92
+ the MIT license, in which case the following will apply:
93
+
94
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
95
+ this software and associated documentation files (the "Software"), to deal in
96
+ the Software without restriction, including without limitation the rights to
97
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
98
+ of the Software, and to permit persons to whom the Software is furnished to do
99
+ so, subject to the following conditions:
100
+
101
+ The above copyright notice and this permission notice shall be included in all
102
+ copies or substantial portions of the Software.
103
+
104
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
105
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
106
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
107
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
108
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
109
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
110
+ SOFTWARE.
111
+
112
+ This Agreement shall be governed by the laws of France.
package/NOTICE ADDED
@@ -0,0 +1,17 @@
1
+ Preview control icon geometries are derived from lucide-react 0.469.0.
2
+
3
+ ISC License
4
+
5
+ Copyright (c) for portions of Lucide are held by Cole Bemis 2013-2022 as part of Feather (MIT). All other copyright (c) for Lucide are held by Lucide Contributors 2022.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/PUBLIC-API.md ADDED
@@ -0,0 +1,36 @@
1
+ # @storystick/react API contract
2
+
3
+ Install this candidate explicitly with `@storystick/react@0.2.0-rc.0` after registry bootstrap and publication. This repository alone does not make the candidate or beta channel available on npm.
4
+
5
+ Version 0.2.0-rc.0, cohort `sdk-0.2.0-rc.0`. ESM distribution; no CommonJS or universal Edge compatibility is promised.
6
+
7
+ This package is a product integration facade. Reserved contracts are required between SDK packages and must be upgraded with the same cohort. Their public export paths do not promise independent stability.
8
+
9
+ | Entry | Status |
10
+ | --- | --- |
11
+ | `.` | Supported beta entry |
12
+ | `./preview-text` | Supported beta entry |
13
+ | `./image` | Supported beta entry |
14
+ | `./file` | Supported beta entry |
15
+ | `./video` | Supported beta entry |
16
+ | `./slice` | Supported beta entry |
17
+ | `./link` | Supported beta entry |
18
+ | `./query` | Supported beta entry |
19
+ | `./rich-text` | Supported beta entry |
20
+ | `./slice-zone` | Supported beta entry |
21
+ | `./internal/preview-provenance` | Reserved SDK contract |
22
+ | `./internal/link-adapter` | Reserved SDK contract |
23
+ | `./internal/storystick-mark` | Reserved SDK contract |
24
+ | `./internal/preview-interaction-control` | Reserved SDK contract |
25
+ | `./internal/use-preview-perspective-intent` | Reserved SDK contract |
26
+ | `./internal/use-momentary-preview-navigate-mode` | Reserved SDK contract |
27
+ | `./internal/resize-edge` | Reserved SDK contract |
28
+ | `./internal/preview-toolbar-shell` | Reserved SDK contract |
29
+ | `./internal/preview-panel-state` | Reserved SDK contract |
30
+ | `./internal/preview-host` | Reserved SDK contract |
31
+
32
+ The qualified toolchain is Node 24.19.0 and pnpm 9.15.9. Next uses App Router 15.5.28, Sharp 0.35.5 and React/React DOM 19.2.8; Astro uses 7.4.4 with the Node adapter 11.1.9. Browser delivery is verified by bundling; hosted Preview activation and cross-browser behavior require the staging manual check. Source/CLI are Node tools, server entries belong on the server, and browser entries must never receive a server API key.
33
+
34
+ Use a generated Query V2 definition. A missing document/singleton returns null, an empty collection returns an empty array, and fetch throws on blocked/degraded execution. Execute exposes the complete result envelope. RSC metadata must remain plain JSON data; request authority must not be stored in shared module state.
35
+
36
+ The repository is private. Public package availability does not make the monorepo public, and npm provenance from that repository is unavailable. Product and pilot support: [Storystick](https://storystick.app).
package/README.md CHANGED
@@ -1,3 +1,227 @@
1
- # Temporary Holding Version
1
+ # @storystick/react
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ React 19 rendering primitives and a provider-free hook for generated Query V2 reads.
4
+
5
+ ```bash
6
+ pnpm add @storystick/react@beta react@19.2.8 react-dom@19.2.8
7
+ ```
8
+
9
+ Run `storystick generate` first; it writes
10
+ `generated/storystick/query.ts`. Then create a client with that schema, build a
11
+ query, and pass the executable query directly to `useQuery`. No query provider
12
+ is required.
13
+
14
+ ```tsx
15
+ 'use client'
16
+
17
+ import { createClient, previewTextProps } from '@storystick/react'
18
+ import { useQuery } from '@storystick/react/query'
19
+ import { querySchema } from '@/generated/storystick/query'
20
+
21
+ const client = createClient({
22
+ schema: querySchema,
23
+ projectId: process.env.NEXT_PUBLIC_STORYSTICK_PROJECT_ID!,
24
+ publishableKey: process.env.NEXT_PUBLIC_STORYSTICK_PUBLISHABLE_KEY!,
25
+ })
26
+
27
+ const latestPosts = client
28
+ .query('blogPost')
29
+ .select({ title: true, slug: true })
30
+ .orderBy((fields, { desc }) => [desc(fields.publishedAt)])
31
+ .page({ first: 20 })
32
+
33
+ export function LatestPosts() {
34
+ const result = useQuery(latestPosts)
35
+
36
+ if (result.status === 'pending') return <p>Loading…</p>
37
+ if (result.status === 'error') return <p>{result.error.message}</p>
38
+
39
+ return (
40
+ <ul>
41
+ {result.data.map((post) => (
42
+ <li key={post.documentId} {...previewTextProps(post.fields.title)} />
43
+ ))}
44
+ </ul>
45
+ )
46
+ }
47
+ ```
48
+
49
+ The query owns its generated schema and production client identity. Inside a
50
+ trusted Storystick Preview session, `useQuery` automatically executes the same
51
+ compiled query through the current living, variant, or release authority and
52
+ returns to production when Preview ends. Preview tokens never enter the query
53
+ key or component props.
54
+
55
+ `SliceZone`, `RichText`, links and media components are available from the same
56
+ root entry. Subpaths remain available when an application wants an explicit
57
+ bundle boundary:
58
+
59
+ ```tsx
60
+ import { useQuery } from '@storystick/react/query'
61
+ import { RichText } from '@storystick/react/rich-text'
62
+ import { SliceZone } from '@storystick/react/slice-zone'
63
+ ```
64
+
65
+ The root entry contains rendering and client primitives. Import `useQuery` from
66
+ `@storystick/react/query` so a render-only import never discovers the reactive
67
+ Preview loader. This is an intentional pre-launch API change in the candidate cohort.
68
+
69
+ Generated slice maps can type a whole registry without per-component casts:
70
+
71
+ ```tsx
72
+ import { SliceZone, type SliceComponentRegistry } from '@storystick/react/slice-zone'
73
+ import type { SliceTypeMap } from '@/generated/storystick/types'
74
+
75
+ type LandingSliceMap = Pick<SliceTypeMap, 'hero' | 'cta'>
76
+
77
+ const components = {
78
+ hero: Hero,
79
+ cta: Cta,
80
+ } satisfies SliceComponentRegistry<LandingSliceMap>
81
+
82
+ <SliceZone<LandingSliceMap>
83
+ slices={page.fields.sections}
84
+ components={components}
85
+ fallback={({ reason }) => (
86
+ <p data-slice-fallback={reason}>This section is unavailable.</p>
87
+ )}
88
+ />
89
+ ```
90
+
91
+ Every generated slice carries its exact `_type`, `_key`, fields, and variation
92
+ discriminator. A missing slice array is an ordinary empty render. Malformed or
93
+ unregistered entries invoke `fallback` and emit a development diagnostic;
94
+ production never has to discard them silently.
95
+
96
+ ## Render text fields
97
+
98
+ SDK reads return native strings. During authorized Preview, those strings can
99
+ carry edit provenance. In a component with queried `fields`, spread
100
+ `previewTextProps` on the element that displays the original field:
101
+
102
+ ```tsx
103
+ import { previewTextProps } from '@storystick/react'
104
+
105
+ <h1 {...previewTextProps(fields.title)} />
106
+ ```
107
+
108
+ The helper returns plain `children` and, when the value identifies one complete
109
+ field, a `data-ss-p` attribute. Preview metadata stays in the attribute instead
110
+ of the text node. No wrapper or client lifecycle is added. This avoids relying
111
+ on browsers to render invisible codewords identically; raw JSX containing a
112
+ Preview string is not guaranteed to remain visually unchanged across browsers.
113
+
114
+ Client components that only need this helper can import it from
115
+ `@storystick/react/preview-text`. This direct entry contains the pure text
116
+ adapter without importing the SDK's query or renderer entry points.
117
+
118
+ Pass the original field before formatting or concatenating it. Composed values
119
+ are cleaned without choosing an edit target. Render each field on its own
120
+ element when it needs a separate target, or use explicit field bindings for a
121
+ custom presentation. Use `stripPreviewProvenance` on a copy before string
122
+ computation, custom attributes or structured payloads.
123
+
124
+ ## Autonomous content and optional app context
125
+
126
+ SDK reads prepare the content before components receive it. Models, bindings,
127
+ links, assets and container zones resolve from that content's own origin:
128
+
129
+ ```tsx
130
+ <SliceZone slices={page.fields.sections} components={components} />
131
+ <RichText content={author.fields.bio} />
132
+ <StorystickImage image={slice.image} />
133
+ <StorystickLink link={slice.action} />
134
+ ```
135
+
136
+ Import `Slice` from `@storystick/react` (or `@storystick/react/slice`).
137
+ Keep `<Slice.section slice={slice}>` on a slice's visual root when you want its
138
+ Preview overlay. This is an explicit attribute opt-in, not an extra DOM wrapper.
139
+ An authored slice `_anchor` becomes the root's native `id` automatically.
140
+ Default rich-text headings do the same; custom heading renderers receive an
141
+ optional `id` to forward to their native heading element.
142
+
143
+ `context` is only application data. The SDK passes it through without reading
144
+ or adding keys. For example, when a child needs document and parent fields:
145
+
146
+ ```tsx
147
+ <SliceZone slices={document.fields.sections} components={components}
148
+ context={{ document }} />
149
+
150
+ function ContainerSlice({ slice, context }) {
151
+ return <Slice.section slice={slice}>
152
+ <SliceZone slices={slice.children} components={components}
153
+ context={{ ...context, parent: slice }} />
154
+ </Slice.section>
155
+ }
156
+
157
+ function Child({ slice, context }) {
158
+ return <Slice.section slice={slice}>
159
+ <h2 {...previewTextProps(context.document.fields.title)} />
160
+ <p {...previewTextProps(context.parent.heading)} />
161
+ <RichText content={slice.body} />
162
+ </Slice.section>
163
+ }
164
+ ```
165
+
166
+ Type application context with `SliceComponentProps<MySlice, MyContext>` when
167
+ needed. No context is required for content resolution. An explicitly different
168
+ query locale belongs on the client/query, not in an inferred `context.document`.
169
+
170
+ A **Container Slice** owns `slice.children` and renders another `SliceZone`.
171
+ A **Zone Container** arranges mapped fields of the host document: use
172
+ `<ZoneSlot zone={zones?.main} />` and `<ZoneSlot zone={zones?.aside} />`. Those
173
+ slots are already prepared and receive the caller's application context. The
174
+ SDK does not inject `parent`. Existing nesting and model restrictions still apply.
175
+
176
+ Generated `RichTextValue` is a JSON ProseMirror `doc` object, not a serialized
177
+ string. Storage remains serialized. Do not `JSON.parse` SDK read values.
178
+ SDK objects carry reserved `__ssContent` rendering metadata. Preserve it when
179
+ passing content across JSON/RSC boundaries; arrays have no hidden properties.
180
+ Filtering/reordering arrays and shallow-copying their elements preserve origin.
181
+ Do not write rendering metadata back into editorial fields.
182
+
183
+ For manually reconstructed/raw compositions, the advanced escape hatch is
184
+ `source={{ document, path: 'sections' }}` on `SliceZone`, or
185
+ `source={{ document, path: 'body' }}` on `RichText`. It performs no query.
186
+ Contradicting an autonomous value's origin throws instead of silently borrowing
187
+ another document. Pure media/link resolvers still accept explicit sidecars.
188
+ A raw value with missing dependencies cannot obtain them from unrelated reads.
189
+
190
+ Licensed under [FSL-1.1-MIT](./LICENSE).
191
+
192
+ `Slice` only declares the visual root of a slice, including container slices
193
+ and rich-text embeds. It requires `slice` and accepts the chosen element's
194
+ native props. It does not bind documents or fields. For transformed text,
195
+ clean the display value and spread the existing field attributes:
196
+
197
+ ```tsx
198
+ import { Slice, stripPreviewProvenance } from '@storystick/react'
199
+
200
+ function Heading({ slice, bindings }) {
201
+ return <Slice.section slice={slice}>
202
+ <h2 {...bindings?.fields.headline}>
203
+ {formatHeading(stripPreviewProvenance(slice.headline))}
204
+ </h2>
205
+ </Slice.section>
206
+ }
207
+ ```
208
+
209
+ These attributes are plain data and remain optional outside Preview.
210
+
211
+ `StorystickImage` emits a lazy responsive native image by default, including a
212
+ bounded `srcset` and `sizes="auto, 100vw"`. Use `responsive={false}` for a
213
+ single source, or pass an explicit `options.sizes` for a known layout. Native
214
+ media components accept events and React 19 refs when used in a Client
215
+ Component. The resolvers remain available for custom native rendering.
216
+ `useQuery` allocates observers only after commit; server rendering returns a
217
+ stable pending snapshot without starting a fetch or a cache timer. Its Preview
218
+ participation loads on activation and retains the existing targeted/live updates.
219
+ The public result is deliberately conventional: `pending`, `success`, or
220
+ `error`. Projection rebuilding and targeting remain internal, so applications
221
+ do not need to model Content Plane convergence states.
222
+
223
+ ## Candidate distribution contract
224
+
225
+ See [PUBLIC-API.md](./PUBLIC-API.md) for supported entries, reserved SDK contracts,
226
+ the qualified runtime matrix and the candidate cohort. Upgrade Storystick
227
+ packages together.
@@ -0,0 +1 @@
1
+ 937895c9b9d8b1e9dd734b85c5339eee7d93c3afe545ac5d65f65f5330fb3acb
package/dist/file.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+ import { type ResolveFileOptions, type ResolvedStorystickFile, type StorystickFileField } from '@storystick/media';
3
+ /** Resolve a File field from an explicit or value-owned asset metadata. */
4
+ export { resolveFile } from '@storystick/media';
5
+ type FileAnchorProps = Omit<React.ComponentPropsWithRef<'a'>, 'href' | 'download' | 'type' | 'children'>;
6
+ export type StorystickFileComponentProps = FileAnchorProps & {
7
+ file: StorystickFileField | null | undefined;
8
+ options?: ResolveFileOptions;
9
+ children?: React.ReactNode;
10
+ fallback?: StorystickFileFallback;
11
+ };
12
+ export type StorystickFileFailure = Exclude<ResolvedStorystickFile, {
13
+ readonly status: 'ok';
14
+ }>;
15
+ export type StorystickFileFallback = React.ReactNode | ((resolution: StorystickFileFailure) => React.ReactNode);
16
+ /** Server-safe native download link with resolver-owned href and provenance. */
17
+ export declare function StorystickFile(input: StorystickFileComponentProps): React.ReactElement | null;
18
+ export type { FileStatus, ResolveFileOptions, ResolvedStorystickFile, StorystickFileField, StorystickFileProps, } from '@storystick/media';
package/dist/file.js ADDED
@@ -0,0 +1,44 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { emitMediaDiagnostic, isProduction } from '@storystick/media/internal/render-diagnostics';
3
+ import { cleanPreviewAttributes } from '@storystick/types/internal/preview-provenance';
4
+ import { resolveFile, } from '@storystick/media';
5
+ /** Resolve a File field from an explicit or value-owned asset metadata. */
6
+ export { resolveFile } from '@storystick/media';
7
+ /** Server-safe native download link with resolver-owned href and provenance. */
8
+ export function StorystickFile(input) {
9
+ const { file, options = {}, children, fallback, ...anchorProps } = input;
10
+ const diagnostics = [];
11
+ const resolved = resolveFile(file, {
12
+ ...options,
13
+ warn: (message) => diagnostics.push(message),
14
+ });
15
+ if (resolved.status !== 'ok') {
16
+ if (!isProduction() && resolved.status !== 'no-file') {
17
+ emitMediaDiagnostic(`StorystickWarning: file resolution status=${resolved.status}.${diagnostics.length > 0 ? ` ${diagnostics.join(' ')}` : ''}`, options.warn);
18
+ }
19
+ if (fallback !== undefined) {
20
+ return _jsx(_Fragment, { children: typeof fallback === 'function' ? fallback(resolved) : fallback });
21
+ }
22
+ if (isProduction() || resolved.status === 'no-file')
23
+ return null;
24
+ return (_jsx("span", { "aria-hidden": "true", "data-storystick-file-status": resolved.status, ...('data-ss-e' in resolved
25
+ ? { 'data-ss-e': resolved['data-ss-e'] }
26
+ : {}) }));
27
+ }
28
+ if (!isProduction()) {
29
+ for (const message of diagnostics)
30
+ emitMediaDiagnostic(message, options.warn);
31
+ }
32
+ return (_jsx("a", { ...fileAnchorProps(resolved.props, anchorProps), children: children ?? resolved.filename }));
33
+ }
34
+ function fileAnchorProps(resolved, input) {
35
+ return {
36
+ ...cleanPreviewAttributes(input),
37
+ href: resolved.href,
38
+ download: resolved.download,
39
+ ...(resolved.type ? { type: resolved.type } : {}),
40
+ ...('data-ss-e' in resolved
41
+ ? { 'data-ss-e': resolved['data-ss-e'] }
42
+ : {}),
43
+ };
44
+ }
@@ -0,0 +1,3 @@
1
+ export { hydrateSlice, hydrateSliceWithDiagnostics, getFieldBindings, resolveBinding, getByPath } from '@storystick/client/internal';
2
+ export type { FieldBindingsSidecar, HydrateSliceOptions, SliceHydrationFailure, HydratedSliceResult } from '@storystick/client/internal';
3
+ export type { SerializedModelSidecar } from '@storystick/types';
@@ -0,0 +1 @@
1
+ export { hydrateSlice, hydrateSliceWithDiagnostics, getFieldBindings, resolveBinding, getByPath } from '@storystick/client/internal';
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { type ResolveImageOptions, type ResolvedStorystickImage, type StorystickImageField } from '@storystick/media';
3
+ type StorystickImageDomProps = Omit<React.ComponentPropsWithRef<'img'>, 'children' | 'dangerouslySetInnerHTML' | 'src' | 'alt' | 'width' | 'height' | 'srcSet' | 'sizes'>;
4
+ export type StorystickImageComponentProps = StorystickImageDomProps & {
5
+ image: StorystickImageField | null | undefined;
6
+ /** Resolution and provider-native transformation options. */
7
+ options?: ResolveImageOptions;
8
+ /** Disable responsive candidates for constrained editor surfaces. */
9
+ responsive?: boolean;
10
+ /** Contextual alternative text. Empty string explicitly marks decoration. */
11
+ alt?: string;
12
+ /** Automatically consume the version LQIP unless explicitly disabled. */
13
+ placeholder?: 'auto' | 'none';
14
+ /** Customer-owned output for empty or non-renderable image states. */
15
+ fallback?: StorystickImageFallback;
16
+ };
17
+ export type StorystickImageFailure = Exclude<ResolvedStorystickImage, {
18
+ readonly status: 'ok';
19
+ }>;
20
+ export type StorystickImageFallback = React.ReactNode | ((resolution: StorystickImageFailure) => React.ReactNode);
21
+ /**
22
+ * Framework-neutral native image renderer. It emits lazy responsive images by
23
+ * default while preserving provider transforms, LQIP and Preview provenance.
24
+ */
25
+ export declare function StorystickImage(input: StorystickImageComponentProps): React.JSX.Element | null;
26
+ export {};
@@ -0,0 +1,79 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { emitMediaDiagnostic, isProduction } from '@storystick/media/internal/render-diagnostics';
3
+ import { cleanPreviewAttributes } from '@storystick/types/internal/preview-provenance';
4
+ import { resolveImage, } from '@storystick/media';
5
+ import { StorystickImageElement } from './image-element.js';
6
+ /**
7
+ * Framework-neutral native image renderer. It emits lazy responsive images by
8
+ * default while preserving provider transforms, LQIP and Preview provenance.
9
+ */
10
+ export function StorystickImage(input) {
11
+ const { image, options = {}, responsive = true, alt, placeholder = 'auto', fallback, ...imgProps } = input;
12
+ const diagnostics = [];
13
+ const imageForResolve = alt !== undefined && image ? { ...image, alt } : image;
14
+ const defaultSizes = imgProps.loading === 'eager' ? '100vw' : 'auto, 100vw';
15
+ const resolved = resolveImage(imageForResolve, {
16
+ ...options,
17
+ ...(responsive && options.sizes === undefined ? { sizes: defaultSizes } : {}),
18
+ ...(!responsive ? { widths: [], sizes: undefined } : {}),
19
+ warn: (message) => diagnostics.push(message),
20
+ });
21
+ if (resolved.status !== 'ok') {
22
+ if (!isProduction() && resolved.status !== 'no-image') {
23
+ const detail = diagnostics.length > 0 ? ` ${diagnostics.join(' ')}` : '';
24
+ emitMediaDiagnostic(`StorystickWarning: image resolution status=${resolved.status}.${detail}`, options.warn);
25
+ }
26
+ if (fallback !== undefined) {
27
+ return _jsx(_Fragment, { children: typeof fallback === 'function' ? fallback(resolved) : fallback });
28
+ }
29
+ if (isProduction() || resolved.status === 'no-image')
30
+ return null;
31
+ return (_jsx("span", { "aria-hidden": "true", "data-storystick-image-status": resolved.status, ...('data-ss-e' in resolved
32
+ ? { 'data-ss-e': resolved['data-ss-e'] }
33
+ : {}) }));
34
+ }
35
+ if (!isProduction()) {
36
+ for (const message of diagnostics)
37
+ emitMediaDiagnostic(message, options.warn);
38
+ }
39
+ const props = imageElementProps(resolved.props, imgProps, responsive ? undefined : 'eager');
40
+ if (placeholder === 'none' || !resolved.blurDataURL) {
41
+ return _jsx("img", { ...props });
42
+ }
43
+ return (_jsx(StorystickImageElement, { ...props, blurDataURL: resolved.blurDataURL }, `${props.src}:${resolved.blurDataURL}`));
44
+ }
45
+ function imageElementProps(resolved, input, defaultLoading) {
46
+ const forwarded = { ...input };
47
+ const protectedProps = forwarded;
48
+ for (const key of [
49
+ 'src',
50
+ 'alt',
51
+ 'width',
52
+ 'height',
53
+ 'srcSet',
54
+ 'sizes',
55
+ 'data-ss-e',
56
+ 'children',
57
+ 'dangerouslySetInnerHTML',
58
+ ]) {
59
+ delete protectedProps[key];
60
+ }
61
+ const style = resolved.style || input.style
62
+ ? { ...resolved.style, ...input.style }
63
+ : undefined;
64
+ return {
65
+ ...cleanPreviewAttributes(forwarded),
66
+ src: resolved.src,
67
+ alt: resolved.alt,
68
+ width: resolved.width,
69
+ height: resolved.height,
70
+ ...(resolved.srcSet ? { srcSet: resolved.srcSet } : {}),
71
+ ...(resolved.sizes ? { sizes: resolved.sizes } : {}),
72
+ loading: input.loading ?? defaultLoading ?? resolved.loading,
73
+ decoding: input.decoding ?? resolved.decoding,
74
+ ...(style ? { style } : {}),
75
+ ...('data-ss-e' in resolved
76
+ ? { 'data-ss-e': resolved['data-ss-e'] }
77
+ : {}),
78
+ };
79
+ }
@@ -0,0 +1,6 @@
1
+ import * as React from 'react';
2
+ export type StorystickImageElementProps = React.ComponentPropsWithRef<'img'> & {
3
+ blurDataURL: string;
4
+ };
5
+ /** Owns only the lifecycle required to remove a server-painted LQIP. */
6
+ export declare function StorystickImageElement({ blurDataURL, ref, onLoad, onError, src, style, ...props }: StorystickImageElementProps): React.JSX.Element;
@@ -0,0 +1,68 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from 'react';
4
+ /** Owns only the lifecycle required to remove a server-painted LQIP. */
5
+ export function StorystickImageElement({ blurDataURL, ref, onLoad, onError, src, style, ...props }) {
6
+ const imageRef = React.useRef(null);
7
+ const mountedRef = React.useRef(false);
8
+ const srcKey = typeof src === 'string' ? src : '';
9
+ const currentSrcRef = React.useRef(srcKey);
10
+ currentSrcRef.current = srcKey;
11
+ const [settledSrc, setSettledSrc] = React.useState(null);
12
+ const showsPlaceholder = settledSrc !== srcKey;
13
+ const settle = React.useCallback(async (image) => {
14
+ const expectedSrc = srcKey;
15
+ if (typeof image.decode === 'function') {
16
+ try {
17
+ await image.decode();
18
+ }
19
+ catch {
20
+ // A failed decode must not leave the placeholder over native error UI.
21
+ }
22
+ }
23
+ if (!mountedRef.current || currentSrcRef.current !== expectedSrc)
24
+ return;
25
+ setSettledSrc(expectedSrc);
26
+ }, [srcKey]);
27
+ React.useEffect(() => {
28
+ mountedRef.current = true;
29
+ const image = imageRef.current;
30
+ if (image?.complete) {
31
+ void settle(image);
32
+ }
33
+ return () => {
34
+ mountedRef.current = false;
35
+ };
36
+ }, [settle]);
37
+ const setImageRef = React.useCallback((element) => {
38
+ imageRef.current = element;
39
+ const cleanup = typeof ref === 'function' ? ref(element) : undefined;
40
+ if (ref && typeof ref !== 'function')
41
+ ref.current = element;
42
+ return () => {
43
+ imageRef.current = null;
44
+ if (typeof cleanup === 'function')
45
+ cleanup();
46
+ else if (typeof ref === 'function')
47
+ ref(null);
48
+ else if (ref)
49
+ ref.current = null;
50
+ };
51
+ }, [ref]);
52
+ const placeholderStyle = showsPlaceholder
53
+ ? {
54
+ backgroundImage: `url(${JSON.stringify(blurDataURL)})`,
55
+ backgroundPosition: style?.objectPosition ?? 'center',
56
+ backgroundRepeat: 'no-repeat',
57
+ backgroundSize: 'cover',
58
+ }
59
+ : undefined;
60
+ return (_jsx("img", { ...props, ref: setImageRef, src: src, style: placeholderStyle ? { ...style, ...placeholderStyle } : style, onLoad: (event) => {
61
+ const image = event.currentTarget;
62
+ void settle(image);
63
+ onLoad?.(event);
64
+ }, onError: (event) => {
65
+ setSettledSrc(srcKey);
66
+ onError?.(event);
67
+ } }));
68
+ }
@@ -0,0 +1,3 @@
1
+ /** The media resolver owns both autonomous and explicitly sourced values. */
2
+ export { resolveImage } from '@storystick/media';
3
+ export { StorystickImage, type StorystickImageComponentProps, type StorystickImageFailure, type StorystickImageFallback, } from './image-component.js';
package/dist/image.js ADDED
@@ -0,0 +1,3 @@
1
+ /** The media resolver owns both autonomous and explicitly sourced values. */
2
+ export { resolveImage } from '@storystick/media';
3
+ export { StorystickImage, } from './image-component.js';
@@ -0,0 +1 @@
1
+ export * from './rendering.js';