@crossworks/share-ui 0.232.67 → 0.232.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crossworks/share-ui",
3
- "version": "0.232.67",
3
+ "version": "0.232.68",
4
4
  "description": "The server-rendered share surface — the /s/<token> presenters, view-payload contract, mini-app sandbox, and the few primitives they need. Lives in MANTLE (the server renders these), published for jackdaw to consume; may depend only on @mantle/{client-types,content-core} (the jackdaw-repo-split boundary).",
5
5
  "exports": {
6
6
  "./app-presenter": "./src/app-presenter.tsx",
@@ -36,8 +36,8 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@firecms/neat": "1.0.2",
39
- "@mantle/client-types": "npm:@crossworks/client-types@0.232.67",
40
- "@mantle/content-core": "npm:@crossworks/content-core@0.232.67",
39
+ "@mantle/client-types": "npm:@crossworks/client-types@0.232.68",
40
+ "@mantle/content-core": "npm:@crossworks/content-core@0.232.68",
41
41
  "@radix-ui/react-label": "^2.1.12",
42
42
  "@radix-ui/react-slot": "^1.3.0",
43
43
  "class-variance-authority": "^0.7.1",
@@ -49,3 +49,49 @@ describe('buildAppFrameHtml — error reporting', () => {
49
49
  expect(html).toContain('<script type="importmap">{"imports":{}}</script>');
50
50
  });
51
51
  });
52
+
53
+ describe('buildAppFrameHtml — the Neat backdrop', () => {
54
+ const SPEC = '{"v":1,"seed":42,"tone":"auto","speed":2}';
55
+ const withNeat = (neatLicense?: string | null) =>
56
+ buildAppFrameHtml({
57
+ bundleCode: 'console.log(1)',
58
+ appCss: '',
59
+ importMapJson: '{"imports":{}}',
60
+ cls: 'dark',
61
+ colorTheme: 'darkmatter',
62
+ viewport: true,
63
+ neatSpec: SPEC,
64
+ neatLicense,
65
+ });
66
+
67
+ it('renders no host div and no runtime script without a spec', () => {
68
+ const html = frame('console.log(1)');
69
+ expect(html).not.toContain('data-neat-spec');
70
+ expect(html).not.toContain('share-page.js');
71
+ });
72
+
73
+ it('renders the host div at z-index:-1 plus the runtime script with a spec', () => {
74
+ const html = withNeat();
75
+ expect(html).toContain('data-neat-spec="{&quot;v&quot;:1');
76
+ expect(html).toContain('z-index:-1');
77
+ expect(html).toContain('src="/share-runtime/share-page.js"');
78
+ // No reader attribute — the runtime must never mount its toggle in a frame.
79
+ expect(html).not.toContain('data-share-mode-default');
80
+ });
81
+
82
+ it('keeps html transparent so the body ground propagates BENEATH the canvas', () => {
83
+ const html = withNeat();
84
+ // Body ground + transparent html is the /s arrangement: the background
85
+ // propagates to the canvas layer, under the z-index:-1 backdrop. An html
86
+ // background (or a bg-background class painting body as an element)
87
+ // would sit above the backdrop and hide it.
88
+ expect(html).toContain('body{background:var(--background)}');
89
+ expect(html).not.toContain('html{background');
90
+ expect(html).not.toContain('<body class="bg-background');
91
+ });
92
+
93
+ it('carries the licence only when set', () => {
94
+ expect(withNeat('k-1')).toContain('data-neat-license="k-1"');
95
+ expect(withNeat()).not.toContain('data-neat-license');
96
+ });
97
+ });
@@ -38,7 +38,10 @@ export function buildAppFrameCsp(origin: string): string {
38
38
  // inside the AppSandbox iframe it composes with the identical attribute.
39
39
  `sandbox allow-scripts; ` +
40
40
  `default-src 'none'; style-src 'unsafe-inline' ${origin}/share-runtime/; ` +
41
- `script-src 'unsafe-inline' ${origin}/app-runtime/; ` +
41
+ // share-runtime scripts joined app-runtime for the Neat backdrop
42
+ // (share-page.js + its lazy WebGL chunk) — same-origin built assets,
43
+ // no new capability.
44
+ `script-src 'unsafe-inline' ${origin}/app-runtime/ ${origin}/share-runtime/; ` +
42
45
  `img-src data: blob: ${origin}; ` +
43
46
  `font-src data: ${origin}/share-runtime/ ${origin}/fonts/; ` +
44
47
  "connect-src 'none'; base-uri 'none'; form-action 'none'"
@@ -185,6 +188,14 @@ export function buildAppFrameHtml(opts: {
185
188
  colorTheme: string | null;
186
189
  /** Viewport frame (iframe IS the viewport) vs card frame (auto-sized). */
187
190
  viewport: boolean;
191
+ /** The brain's saved Neat background spec (canonical encoding), or
192
+ * null/absent for the plain themed ground. Resolved server-side by the
193
+ * frame route — owner frames from the prefs alone, shared frames gated by
194
+ * the shareNeat switch. */
195
+ neatSpec?: string | null;
196
+ /** Neat licence key for watermark removal — same env-sourced value the
197
+ * other surfaces ride. */
198
+ neatLicense?: string | null;
188
199
  }): string {
189
200
  const colorThemeAttr = opts.colorTheme ? ` data-color-theme="${attr(opts.colorTheme)}"` : '';
190
201
  return `<!doctype html>
@@ -205,8 +216,14 @@ export function buildAppFrameHtml(opts: {
205
216
  <style>/* Paint the iframe canvas with the theme background, NOT transparent: a
206
217
  sandboxed (opaque-origin) iframe renders WHITE where it's transparent, so any
207
218
  gap between the app content and the iframe height showed a white strip. With
208
- the themed background, any such gap is invisible (matches the app + host). */
209
- html,body{margin:0;background:var(--background)}#root{padding:0}
219
+ the themed background, any such gap is invisible (matches the app + host).
220
+ The ground lives on BODY with html left transparent — the /s reader's exact
221
+ arrangement, and load-bearing for the Neat backdrop: a body background on a
222
+ transparent html PROPAGATES to the canvas, painted beneath everything
223
+ including the backdrop's z-index:-1 host, while a background on html (or a
224
+ non-propagating body background) paints in the normal layers, above the
225
+ backdrop, and hides it. */
226
+ html,body{margin:0}body{background:var(--background)}#root{padding:0}
210
227
  /* Themed scrollbars for the WHOLE app. The host only styles scrollbars behind an
211
228
  opt-in .scrollbar-thin class, so an app's own scroll containers otherwise fall
212
229
  back to the default wide OS scrollbar with a white/grey track that clashes with
@@ -234,11 +251,27 @@ body{overflow:auto}`
234
251
  .h-screen,.h-dvh,.h-svh,.h-lvh{height:auto!important}`
235
252
  }</style>
236
253
  </head>
237
- <body class="bg-background text-foreground">
238
- <div id="root"></div>
254
+ <body class="text-foreground">
255
+ ${
256
+ /* The brain's saved Neat gradient, rendered INSIDE the frame document —
257
+ the iframe is opaque-origin (sandbox without allow-same-origin), so
258
+ transparency renders WHITE and the host page's backdrop can never show
259
+ through; the only way the background "transfers" into an app is to
260
+ paint it here. Same host-div + share-page.js contract as the /s reader
261
+ (no mode toggle mounts — that requires the reader's
262
+ data-share-mode-default, which frames deliberately lack), and the
263
+ runtime's class observer repaints it when the parent posts a theme
264
+ change. z-index:-1 sits above the html ground (the canvas layer) and
265
+ below all app content. */
266
+ opts.neatSpec
267
+ ? `<div data-neat-spec="${attr(opts.neatSpec)}"${
268
+ opts.neatLicense ? ` data-neat-license="${attr(opts.neatLicense)}"` : ''
269
+ } style="position:fixed;inset:0;z-index:-1;pointer-events:none" aria-hidden="true"></div>\n`
270
+ : ''
271
+ }<div id="root"></div>
239
272
  <script>${ERROR_REPORTER}</script>
240
273
  <script type="module">${opts.bundleCode}</script>
241
274
  <script>${INSPECTOR}</script>
242
- </body>
275
+ ${opts.neatSpec ? `<script type="module" src="/share-runtime/share-page.js"></script>\n` : ''}</body>
243
276
  </html>`;
244
277
  }