@crossworks/share-ui 0.232.67 → 0.232.69

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.69",
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.69",
40
+ "@mantle/content-core": "npm:@crossworks/content-core@0.232.69",
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,59 @@ 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
+
98
+ it('makes --card translucent over a Neat ground, and ONLY then', () => {
99
+ // The derived token sits on :root and the override on body — a custom
100
+ // property may not reference itself on the same element. No spec ⇒ no
101
+ // override, so apps on a plain brain stay pixel-identical.
102
+ const html = withNeat();
103
+ expect(html).toContain('--card-on-neat:color-mix(in oklab,var(--card) 70%,transparent)');
104
+ expect(html).toContain('body{--card:var(--card-on-neat)}');
105
+ expect(frame('console.log(1)')).not.toContain('--card-on-neat');
106
+ });
107
+ });
@@ -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
@@ -233,12 +250,44 @@ body{overflow:auto}`
233
250
  .min-h-screen,.min-h-dvh,.min-h-svh,.min-h-lvh{min-height:0!important}
234
251
  .h-screen,.h-dvh,.h-svh,.h-lvh{height:auto!important}`
235
252
  }</style>
253
+ ${
254
+ /* Translucent card surfaces — ONLY when a Neat ground is painted below
255
+ (no spec ⇒ no override, existing apps stay pixel-identical). 70% is the
256
+ workspace ListCard's idle alpha, so app cards and host cards read as one
257
+ system. Token-level on purpose: apps consume the card colour through
258
+ var(--card) (Tailwind bg-card), so one override restyles them all with no
259
+ per-app opt-in. The derived token lives on html and the override on body
260
+ because a custom property may not reference itself on the same element —
261
+ html still sees the theme's solid --card, body swaps in the mix, and a
262
+ live theme flip recomputes both. --popover stays solid: floating layers
263
+ need an opaque ground, same as the host app. */
264
+ opts.neatSpec
265
+ ? `<style>:root{--card-on-neat:color-mix(in oklab,var(--card) 70%,transparent)}body{--card:var(--card-on-neat)}</style>
266
+ `
267
+ : ''
268
+ }
236
269
  </head>
237
- <body class="bg-background text-foreground">
238
- <div id="root"></div>
270
+ <body class="text-foreground">
271
+ ${
272
+ /* The brain's saved Neat gradient, rendered INSIDE the frame document —
273
+ the iframe is opaque-origin (sandbox without allow-same-origin), so
274
+ transparency renders WHITE and the host page's backdrop can never show
275
+ through; the only way the background "transfers" into an app is to
276
+ paint it here. Same host-div + share-page.js contract as the /s reader
277
+ (no mode toggle mounts — that requires the reader's
278
+ data-share-mode-default, which frames deliberately lack), and the
279
+ runtime's class observer repaints it when the parent posts a theme
280
+ change. z-index:-1 sits above the html ground (the canvas layer) and
281
+ below all app content. */
282
+ opts.neatSpec
283
+ ? `<div data-neat-spec="${attr(opts.neatSpec)}"${
284
+ opts.neatLicense ? ` data-neat-license="${attr(opts.neatLicense)}"` : ''
285
+ } style="position:fixed;inset:0;z-index:-1;pointer-events:none" aria-hidden="true"></div>\n`
286
+ : ''
287
+ }<div id="root"></div>
239
288
  <script>${ERROR_REPORTER}</script>
240
289
  <script type="module">${opts.bundleCode}</script>
241
290
  <script>${INSPECTOR}</script>
242
- </body>
291
+ ${opts.neatSpec ? `<script type="module" src="/share-runtime/share-page.js"></script>\n` : ''}</body>
243
292
  </html>`;
244
293
  }