@civitai/blocks-react 0.31.0 → 0.32.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.
package/README.md CHANGED
@@ -479,6 +479,47 @@ function MatureSection() {
479
479
  }
480
480
  ```
481
481
 
482
+ ## Direct-load fallback ("Open on Civitai")
483
+
484
+ A block is served from its own origin `<slug>.civit.ai` but is designed to run
485
+ **embedded** in the Civitai host iframe at `civitai.com/apps/run/<slug>`, which
486
+ delivers the runtime context via the `BLOCK_INIT` handshake. If someone opens the
487
+ bare `<slug>.civit.ai` URL **directly** (a shared link, a social crawl), no parent
488
+ ever sends `BLOCK_INIT`, so `ready` never flips and the block hangs on its loading
489
+ spinner forever.
490
+
491
+ Wrap your app root once in `<BlockGate>` (from `/ui`) to degrade that into a
492
+ branded landing instead:
493
+
494
+ ```tsx
495
+ import { BlockGate } from '@civitai/blocks-react/ui';
496
+
497
+ // A DIRECT (unembedded) top-level load shows an "Open on Civitai" card linking to
498
+ // civitai.com/apps/run/<slug>. Embedded — and the dev harness, which posts a fake
499
+ // BLOCK_INIT — are a transparent pass-through, so the happy path is unchanged.
500
+ createRoot(container).render(
501
+ <BlockGate>
502
+ <App />
503
+ </BlockGate>,
504
+ );
505
+ ```
506
+
507
+ The trigger is precise: the fallback shows **only** when the block is top-level
508
+ (`window.self === window.top`) **and** no `BLOCK_INIT` arrives within a short
509
+ timeout (`~2s`, override with `<BlockGate timeoutMs={…}>`). Framed blocks never
510
+ trip it; the harness posts `BLOCK_INIT` immediately, so it never trips there
511
+ either. On a non-`*.civit.ai` host (e.g. `localhost` in dev), it shows a neutral
512
+ "waiting for the host" state — never a broken `apps/run/localhost` link.
513
+
514
+ Building your own landing? The primitives are exported from the package root:
515
+
516
+ ```tsx
517
+ import { useDirectLoad, hostToRunUrl } from '@civitai/blocks-react';
518
+
519
+ const directLoad = useDirectLoad(); // true iff top-level AND no BLOCK_INIT within the timeout
520
+ const runUrl = hostToRunUrl('my-app.civit.ai'); // 'https://civitai.com/apps/run/my-app' | null (null = not a civit.ai host)
521
+ ```
522
+
482
523
  ## The `/ui` subexport
483
524
 
484
525
  Opinionated components, imported separately so a transport-only block stays lean.
@@ -0,0 +1,32 @@
1
+ export interface UseDirectLoadOptions {
2
+ /**
3
+ * Milliseconds to wait for `BLOCK_INIT` before treating a top-level load as a
4
+ * direct (unembedded) load. Defaults to {@link DIRECT_LOAD_TIMEOUT_MS}.
5
+ */
6
+ timeoutMs?: number;
7
+ }
8
+ /**
9
+ * Detect a DIRECT (unembedded) top-level load of a block and, after a short
10
+ * grace period, report it so the SDK can show an "Open on Civitai" fallback
11
+ * instead of hanging on the perpetual loading state.
12
+ *
13
+ * Returns `true` ONLY when BOTH hold:
14
+ * 1. The block is TOP-LEVEL (`window.self === window.top` — not in the host
15
+ * iframe), AND
16
+ * 2. No `BLOCK_INIT` has landed (`ready` is still `false`) within `timeoutMs`.
17
+ *
18
+ * This is precise by construction:
19
+ * - An EMBEDDED block (framed) is never top-level → always `false`, even before
20
+ * `ready`. The embedded happy path is untouched.
21
+ * - The dev harness / `createMockHost` runs the block top-level BUT posts
22
+ * `BLOCK_INIT` immediately (a `setTimeout(0)` macrotask), so `ready` flips
23
+ * long before `timeoutMs` and the timer is cleared → always `false`. The dev
24
+ * flow is untouched.
25
+ * - A real direct load (nobody sends `BLOCK_INIT`) stays top-level + not-ready
26
+ * past `timeoutMs` → `true`.
27
+ *
28
+ * Once `ready` flips it stays authoritative: this can never return `true` while
29
+ * `ready` is `true`, so a late init can't leave a stuck fallback.
30
+ */
31
+ export declare function useDirectLoad(options?: UseDirectLoadOptions): boolean;
32
+ //# sourceMappingURL=useDirectLoad.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDirectLoad.d.ts","sourceRoot":"","sources":["../../src/hooks/useDirectLoad.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAuBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAerE"}
@@ -0,0 +1,64 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { DIRECT_LOAD_TIMEOUT_MS } from '../internal/directLoad.js';
3
+ import { useTransportSnapshot } from './useBlockContext.js';
4
+ /**
5
+ * True iff the current window is the TOP-LEVEL browsing context — i.e. the
6
+ * block is NOT running inside an iframe.
7
+ *
8
+ * Uses the `window.self === window.top` IDENTITY comparison, which is always
9
+ * safe: it never reads a property off a (potentially cross-origin) parent, so
10
+ * it cannot throw a SecurityError the way `window.top.location` would. When
11
+ * embedded in the civitai host, `window.top` is the host's top frame — a
12
+ * different object — so this is `false`; when loaded directly it is `true`.
13
+ */
14
+ function isTopLevel() {
15
+ if (typeof window === 'undefined')
16
+ return false; // SSR / no DOM → not a direct browser load
17
+ try {
18
+ return window.self === window.top;
19
+ }
20
+ catch {
21
+ // Defensive: if some exotic engine throws on the identity read, treat it as
22
+ // embedded (fail safe — never show the fallback to a genuinely-framed block).
23
+ return false;
24
+ }
25
+ }
26
+ /**
27
+ * Detect a DIRECT (unembedded) top-level load of a block and, after a short
28
+ * grace period, report it so the SDK can show an "Open on Civitai" fallback
29
+ * instead of hanging on the perpetual loading state.
30
+ *
31
+ * Returns `true` ONLY when BOTH hold:
32
+ * 1. The block is TOP-LEVEL (`window.self === window.top` — not in the host
33
+ * iframe), AND
34
+ * 2. No `BLOCK_INIT` has landed (`ready` is still `false`) within `timeoutMs`.
35
+ *
36
+ * This is precise by construction:
37
+ * - An EMBEDDED block (framed) is never top-level → always `false`, even before
38
+ * `ready`. The embedded happy path is untouched.
39
+ * - The dev harness / `createMockHost` runs the block top-level BUT posts
40
+ * `BLOCK_INIT` immediately (a `setTimeout(0)` macrotask), so `ready` flips
41
+ * long before `timeoutMs` and the timer is cleared → always `false`. The dev
42
+ * flow is untouched.
43
+ * - A real direct load (nobody sends `BLOCK_INIT`) stays top-level + not-ready
44
+ * past `timeoutMs` → `true`.
45
+ *
46
+ * Once `ready` flips it stays authoritative: this can never return `true` while
47
+ * `ready` is `true`, so a late init can't leave a stuck fallback.
48
+ */
49
+ export function useDirectLoad(options) {
50
+ const timeoutMs = options?.timeoutMs ?? DIRECT_LOAD_TIMEOUT_MS;
51
+ const ready = useTransportSnapshot().ready;
52
+ // Sampled once per mount — top-level-ness doesn't change during a page's life.
53
+ const [topLevel] = useState(isTopLevel);
54
+ const [elapsed, setElapsed] = useState(false);
55
+ useEffect(() => {
56
+ // No timer needed when embedded, or once BLOCK_INIT has already landed.
57
+ if (ready || !topLevel)
58
+ return;
59
+ const id = setTimeout(() => setElapsed(true), timeoutMs);
60
+ return () => clearTimeout(id);
61
+ }, [ready, topLevel, timeoutMs]);
62
+ return topLevel && !ready && elapsed;
63
+ }
64
+ //# sourceMappingURL=useDirectLoad.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDirectLoad.js","sourceRoot":"","sources":["../../src/hooks/useDirectLoad.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAU5D;;;;;;;;;GASG;AACH,SAAS,UAAU;IACjB,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC,CAAC,2CAA2C;IAC5F,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,GAAG,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,4EAA4E;QAC5E,8EAA8E;QAC9E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,aAAa,CAAC,OAA8B;IAC1D,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,IAAI,sBAAsB,CAAC;IAC/D,MAAM,KAAK,GAAG,oBAAoB,EAAE,CAAC,KAAK,CAAC;IAC3C,+EAA+E;IAC/E,MAAM,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAU,UAAU,CAAC,CAAC;IACjD,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9C,SAAS,CAAC,GAAG,EAAE;QACb,wEAAwE;QACxE,IAAI,KAAK,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC/B,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC;QACzD,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;IAEjC,OAAO,QAAQ,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC;AACvC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -52,6 +52,9 @@ export { useDomainMaturity } from './hooks/useDomainMaturity.js';
52
52
  export type { DomainMaturity } from './hooks/useDomainMaturity.js';
53
53
  export { SfwGate } from './hooks/SfwGate.js';
54
54
  export type { SfwGateProps } from './hooks/SfwGate.js';
55
+ export { useDirectLoad } from './hooks/useDirectLoad.js';
56
+ export type { UseDirectLoadOptions } from './hooks/useDirectLoad.js';
57
+ export { hostToRunUrl, DIRECT_LOAD_TIMEOUT_MS } from './internal/directLoad.js';
55
58
  export { useAppStorage } from './hooks/useAppStorage.js';
56
59
  export type { AppStorageKeyEntry, AppStorageListResult, AppStorageQuota, UseAppStorage, } from './hooks/useAppStorage.js';
57
60
  export { useSharedStorage } from './hooks/useSharedStorage.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -38,6 +38,8 @@ export { useRequestConsent } from './hooks/useRequestConsent.js';
38
38
  export { useBlockAnalytics } from './hooks/useBlockAnalytics.js';
39
39
  export { useDomainMaturity } from './hooks/useDomainMaturity.js';
40
40
  export { SfwGate } from './hooks/SfwGate.js';
41
+ export { useDirectLoad } from './hooks/useDirectLoad.js';
42
+ export { hostToRunUrl, DIRECT_LOAD_TIMEOUT_MS } from './internal/directLoad.js';
41
43
  export { useAppStorage } from './hooks/useAppStorage.js';
42
44
  export { useSharedStorage } from './hooks/useSharedStorage.js';
43
45
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAG3F,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAO3D,QAAQ;AACR,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEhF,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAErF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAa3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,EAC3B,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAOzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAGhE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAG3F,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAO3D,QAAQ;AACR,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAEvE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAEhF,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAE7D,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAErF,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAa3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,EAC3B,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAOzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Direct-load (unembedded) detection helpers.
3
+ *
4
+ * A block is served from its OWN origin `<slug>.civit.ai` but is designed to
5
+ * run EMBEDDED inside the civitai.com host iframe at
6
+ * `civitai.com/apps/run/<slug>`, which delivers the runtime context (viewer,
7
+ * scoped token, theme) via the postMessage `BLOCK_INIT` handshake. When a block
8
+ * is opened DIRECTLY — a top-level navigation to its bare `<slug>.civit.ai`
9
+ * origin (shared link, social crawl, someone pasting the URL) — no parent ever
10
+ * sends `BLOCK_INIT`, so the block's `ready` gate never flips and it hangs on a
11
+ * loading spinner forever. These helpers let the SDK degrade that into a
12
+ * branded "Open on Civitai" landing.
13
+ *
14
+ * Pure + framework-agnostic on purpose — {@link hostToRunUrl} is exhaustively
15
+ * unit-testable, and the React glue (the `useDirectLoad` hook + the
16
+ * `<BlockGate>` / `<DirectLoadFallback>` UI) builds on it.
17
+ */
18
+ /**
19
+ * Default milliseconds to wait for `BLOCK_INIT` before concluding a TOP-LEVEL
20
+ * load is a direct (unembedded) load and showing the fallback.
21
+ *
22
+ * Chosen well ABOVE the dev harness's init latency and below anything a human
23
+ * would tolerate staring at a spinner. The `createMockHost` / `<Harness>` dev
24
+ * host posts `BLOCK_INIT` on a `setTimeout(0)` macrotask (see
25
+ * `mockHost.ts` → `after(0, … BLOCK_INIT …)`), so `ready` flips almost
26
+ * immediately there and this timeout NEVER elapses in the harness — the
27
+ * fallback is reserved for the genuine "nobody is ever going to send
28
+ * BLOCK_INIT" case. Overridable via the hook/component `timeoutMs` prop.
29
+ */
30
+ export declare const DIRECT_LOAD_TIMEOUT_MS = 2000;
31
+ /** The Civitai host that serves the embedded block run route. */
32
+ export declare const CIVITAI_HOST = "civitai.com";
33
+ /** The deployed-block origin suffix (`<slug>.civit.ai`). */
34
+ export declare const CIVIT_AI_SUFFIX = ".civit.ai";
35
+ /**
36
+ * Derive the canonical Civitai host route for a block served from a
37
+ * `<slug>.civit.ai` origin.
38
+ *
39
+ * - A deployed block host (`<slug>.civit.ai`) → `https://civitai.com/apps/run/<slug>`,
40
+ * where the slug is the FIRST DNS label of the hostname.
41
+ * - Anything else — `localhost` (dev without the harness), an IP, a bare
42
+ * `civit.ai`, or a non-civit.ai host — → `null`. The caller MUST NOT render a
43
+ * `civitai.com/apps/run/…` redirect in that case (there is no meaningful
44
+ * target — never a broken `apps/run/localhost` link); show a neutral
45
+ * "waiting for the host" state instead.
46
+ *
47
+ * Case-insensitive (hostnames are), tolerant of a trailing FQDN dot.
48
+ *
49
+ * @example hostToRunUrl('model-benchmarking.civit.ai') // 'https://civitai.com/apps/run/model-benchmarking'
50
+ * @example hostToRunUrl('localhost') // null
51
+ * @example hostToRunUrl('civit.ai') // null
52
+ */
53
+ export declare function hostToRunUrl(hostname: string | null | undefined): string | null;
54
+ //# sourceMappingURL=directLoad.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directLoad.d.ts","sourceRoot":"","sources":["../../src/internal/directLoad.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,OAAO,CAAC;AAE3C,iEAAiE;AACjE,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAE1C,4DAA4D;AAC5D,eAAO,MAAM,eAAe,cAAc,CAAC;AAK3C;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAgB/E"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Direct-load (unembedded) detection helpers.
3
+ *
4
+ * A block is served from its OWN origin `<slug>.civit.ai` but is designed to
5
+ * run EMBEDDED inside the civitai.com host iframe at
6
+ * `civitai.com/apps/run/<slug>`, which delivers the runtime context (viewer,
7
+ * scoped token, theme) via the postMessage `BLOCK_INIT` handshake. When a block
8
+ * is opened DIRECTLY — a top-level navigation to its bare `<slug>.civit.ai`
9
+ * origin (shared link, social crawl, someone pasting the URL) — no parent ever
10
+ * sends `BLOCK_INIT`, so the block's `ready` gate never flips and it hangs on a
11
+ * loading spinner forever. These helpers let the SDK degrade that into a
12
+ * branded "Open on Civitai" landing.
13
+ *
14
+ * Pure + framework-agnostic on purpose — {@link hostToRunUrl} is exhaustively
15
+ * unit-testable, and the React glue (the `useDirectLoad` hook + the
16
+ * `<BlockGate>` / `<DirectLoadFallback>` UI) builds on it.
17
+ */
18
+ /**
19
+ * Default milliseconds to wait for `BLOCK_INIT` before concluding a TOP-LEVEL
20
+ * load is a direct (unembedded) load and showing the fallback.
21
+ *
22
+ * Chosen well ABOVE the dev harness's init latency and below anything a human
23
+ * would tolerate staring at a spinner. The `createMockHost` / `<Harness>` dev
24
+ * host posts `BLOCK_INIT` on a `setTimeout(0)` macrotask (see
25
+ * `mockHost.ts` → `after(0, … BLOCK_INIT …)`), so `ready` flips almost
26
+ * immediately there and this timeout NEVER elapses in the harness — the
27
+ * fallback is reserved for the genuine "nobody is ever going to send
28
+ * BLOCK_INIT" case. Overridable via the hook/component `timeoutMs` prop.
29
+ */
30
+ export const DIRECT_LOAD_TIMEOUT_MS = 2000;
31
+ /** The Civitai host that serves the embedded block run route. */
32
+ export const CIVITAI_HOST = 'civitai.com';
33
+ /** The deployed-block origin suffix (`<slug>.civit.ai`). */
34
+ export const CIVIT_AI_SUFFIX = '.civit.ai';
35
+ /** A single DNS label (the slug), lowercased. */
36
+ const DNS_LABEL = /^[a-z0-9-]+$/;
37
+ /**
38
+ * Derive the canonical Civitai host route for a block served from a
39
+ * `<slug>.civit.ai` origin.
40
+ *
41
+ * - A deployed block host (`<slug>.civit.ai`) → `https://civitai.com/apps/run/<slug>`,
42
+ * where the slug is the FIRST DNS label of the hostname.
43
+ * - Anything else — `localhost` (dev without the harness), an IP, a bare
44
+ * `civit.ai`, or a non-civit.ai host — → `null`. The caller MUST NOT render a
45
+ * `civitai.com/apps/run/…` redirect in that case (there is no meaningful
46
+ * target — never a broken `apps/run/localhost` link); show a neutral
47
+ * "waiting for the host" state instead.
48
+ *
49
+ * Case-insensitive (hostnames are), tolerant of a trailing FQDN dot.
50
+ *
51
+ * @example hostToRunUrl('model-benchmarking.civit.ai') // 'https://civitai.com/apps/run/model-benchmarking'
52
+ * @example hostToRunUrl('localhost') // null
53
+ * @example hostToRunUrl('civit.ai') // null
54
+ */
55
+ export function hostToRunUrl(hostname) {
56
+ if (!hostname)
57
+ return null;
58
+ // Normalize: trim, lowercase, strip any trailing FQDN dot(s). The trailing-dot
59
+ // strip is a manual linear trim rather than a `/\.+$/` regex, which backtracks
60
+ // O(n^2) on a string of many dots (ReDoS — CodeQL js/polynomial-redos).
61
+ const normalized = hostname.trim().toLowerCase();
62
+ let end = normalized.length;
63
+ while (end > 0 && normalized.charCodeAt(end - 1) === 46 /* '.' */)
64
+ end -= 1;
65
+ const host = normalized.slice(0, end);
66
+ if (!host.endsWith(CIVIT_AI_SUFFIX))
67
+ return null; // not a deployed block host
68
+ // The slug is the FIRST DNS label (`<slug>.civit.ai`). Extra labels beyond
69
+ // the first are ignored — deployed block origins are single-label — but the
70
+ // first label must be a valid, non-empty DNS label.
71
+ const slug = host.slice(0, host.length - CIVIT_AI_SUFFIX.length).split('.')[0] ?? '';
72
+ if (!slug || !DNS_LABEL.test(slug))
73
+ return null;
74
+ return `https://${CIVITAI_HOST}/apps/run/${slug}`;
75
+ }
76
+ //# sourceMappingURL=directLoad.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directLoad.js","sourceRoot":"","sources":["../../src/internal/directLoad.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAE3C,iEAAiE;AACjE,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CAAC;AAE1C,4DAA4D;AAC5D,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,iDAAiD;AACjD,MAAM,SAAS,GAAG,cAAc,CAAC;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,YAAY,CAAC,QAAmC;IAC9D,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,+EAA+E;IAC/E,+EAA+E;IAC/E,wEAAwE;IACxE,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACjD,IAAI,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC;IAC5B,OAAO,GAAG,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,SAAS;QAAE,GAAG,IAAI,CAAC,CAAC;IAC5E,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACtC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,4BAA4B;IAC9E,2EAA2E;IAC3E,4EAA4E;IAC5E,oDAAoD;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACrF,IAAI,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,WAAW,YAAY,aAAa,IAAI,EAAE,CAAC;AACpD,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * Props for {@link DirectLoadFallback}.
4
+ */
5
+ export interface DirectLoadFallbackProps {
6
+ /**
7
+ * Override the hostname used to derive the `civitai.com/apps/run/<slug>` URL.
8
+ * Defaults to `window.location.hostname`. Primarily a testing seam.
9
+ */
10
+ hostname?: string;
11
+ /**
12
+ * When set (and a run URL could be derived), auto-navigate the top window to
13
+ * the run URL after this many milliseconds. OFF by default — a click-to-open
14
+ * landing is the safe default (no surprise navigation; good for shared links,
15
+ * social unfurls, and SEO). The visible button is always the primary path.
16
+ */
17
+ autoRedirectMs?: number;
18
+ }
19
+ /**
20
+ * The branded "Open on Civitai" landing shown when a block is loaded DIRECTLY
21
+ * (top-level at its bare `<slug>.civit.ai` origin) instead of embedded in the
22
+ * civitai host — the graceful degrade for what would otherwise be a perpetual
23
+ * loading spinner.
24
+ *
25
+ * Two states, chosen from the (overridable) hostname:
26
+ * - Deployed block host (`<slug>.civit.ai`): a card with an "Open on Civitai"
27
+ * link to `https://civitai.com/apps/run/<slug>` (a real anchor — right/middle
28
+ * click, shareable, crawlable). Optional {@link DirectLoadFallbackProps.autoRedirectMs}
29
+ * auto-redirect on top of the button.
30
+ * - Non-civit.ai host (e.g. `localhost` in dev without the harness): a neutral
31
+ * "waiting for the Civitai host" card with a dev hint — NEVER a broken
32
+ * `apps/run/localhost` link.
33
+ *
34
+ * Theme-aware via `prefers-color-scheme` (there is no host `theme` on a direct
35
+ * load) and styled with the `/ui` pack's tokens.
36
+ */
37
+ export declare function DirectLoadFallback({ hostname, autoRedirectMs, }: DirectLoadFallbackProps): React.JSX.Element;
38
+ /**
39
+ * Props for {@link BlockGate}.
40
+ */
41
+ export interface BlockGateProps {
42
+ /** The block app. Rendered whenever the block is NOT a direct (unembedded) load. */
43
+ children: ReactNode;
44
+ /**
45
+ * Milliseconds to wait for `BLOCK_INIT` before treating a top-level load as a
46
+ * direct load. Forwarded to {@link useDirectLoad}. Defaults to 2000ms.
47
+ */
48
+ timeoutMs?: number;
49
+ /** Override the fallback rendered on a direct load. Defaults to {@link DirectLoadFallback}. */
50
+ fallback?: ReactNode;
51
+ /** Forwarded to the default {@link DirectLoadFallback} (testing seam). */
52
+ hostname?: string;
53
+ /** Forwarded to the default {@link DirectLoadFallback}. */
54
+ autoRedirectMs?: number;
55
+ }
56
+ /**
57
+ * Drop-in wrapper that shows the {@link DirectLoadFallback} when a block is
58
+ * loaded DIRECTLY (top-level, no `BLOCK_INIT` within the timeout) and otherwise
59
+ * renders `children` unchanged.
60
+ *
61
+ * Wrap your app root once so EVERY SDK-built block degrades gracefully instead
62
+ * of hanging on a loading spinner when its `<slug>.civit.ai` URL is opened
63
+ * directly:
64
+ *
65
+ * ```tsx
66
+ * import { BlockGate } from '@civitai/blocks-react/ui';
67
+ *
68
+ * createRoot(el).render(
69
+ * <BlockGate>
70
+ * <App />
71
+ * </BlockGate>,
72
+ * );
73
+ * ```
74
+ *
75
+ * The embedded happy path and the dev harness are untouched — see
76
+ * {@link useDirectLoad} for exactly why the trigger can't fire in either.
77
+ */
78
+ export declare function BlockGate({ children, timeoutMs, fallback, hostname, autoRedirectMs, }: BlockGateProps): React.JSX.Element;
79
+ //# sourceMappingURL=BlockGate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockGate.d.ts","sourceRoot":"","sources":["../../src/ui/BlockGate.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAkD5D;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,cAAc,GACf,EAAE,uBAAuB,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CA+D7C;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,QAAQ,EAAE,SAAS,CAAC;IACpB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,cAAc,GACf,EAAE,cAAc,GAAG,KAAK,CAAC,GAAG,CAAC,OAAO,CAQpC"}
@@ -0,0 +1,143 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { hostToRunUrl } from '../internal/directLoad.js';
4
+ import { useDirectLoad } from '../hooks/useDirectLoad.js';
5
+ import { Card } from './Card.js';
6
+ import { Stack } from './Stack.js';
7
+ import { useBlocksStyles } from './styles.js';
8
+ /**
9
+ * Read the OS/browser color-scheme preference, live.
10
+ *
11
+ * A directly-loaded block has NO `BLOCK_INIT`, so there is no host `theme` to
12
+ * set `data-theme` from (the usual gotcha-#60 path). We fall back to
13
+ * `prefers-color-scheme` so the fallback card still themes correctly in light
14
+ * AND dark. Guarded for SSR / engines without `matchMedia`.
15
+ */
16
+ function usePrefersColorScheme() {
17
+ const [dark, setDark] = useState(() => {
18
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')
19
+ return false;
20
+ try {
21
+ return window.matchMedia('(prefers-color-scheme: dark)').matches;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ });
27
+ useEffect(() => {
28
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')
29
+ return;
30
+ let mql;
31
+ try {
32
+ mql = window.matchMedia('(prefers-color-scheme: dark)');
33
+ }
34
+ catch {
35
+ return;
36
+ }
37
+ const onChange = (e) => setDark(e.matches);
38
+ // Modern browsers: addEventListener. Older Safari: addListener.
39
+ if (typeof mql.addEventListener === 'function') {
40
+ mql.addEventListener('change', onChange);
41
+ return () => mql.removeEventListener('change', onChange);
42
+ }
43
+ if (typeof mql.addListener === 'function') {
44
+ mql.addListener(onChange);
45
+ return () => mql.removeListener(onChange);
46
+ }
47
+ return;
48
+ }, []);
49
+ return dark ? 'dark' : 'light';
50
+ }
51
+ const wrapperStyle = {
52
+ minHeight: '100%',
53
+ minWidth: 0,
54
+ display: 'flex',
55
+ alignItems: 'center',
56
+ justifyContent: 'center',
57
+ padding: 24,
58
+ boxSizing: 'border-box',
59
+ background: 'var(--ci-color-surface-2)',
60
+ color: 'var(--ci-color-text)',
61
+ };
62
+ const brandStyle = {
63
+ fontSize: 12,
64
+ fontWeight: 700,
65
+ letterSpacing: '0.08em',
66
+ textTransform: 'uppercase',
67
+ color: 'var(--ci-color-primary)',
68
+ };
69
+ const titleStyle = { fontSize: 18, fontWeight: 700 };
70
+ const bodyStyle = {
71
+ fontSize: 14,
72
+ lineHeight: 1.5,
73
+ color: 'var(--ci-color-text-dimmed)',
74
+ };
75
+ /**
76
+ * The branded "Open on Civitai" landing shown when a block is loaded DIRECTLY
77
+ * (top-level at its bare `<slug>.civit.ai` origin) instead of embedded in the
78
+ * civitai host — the graceful degrade for what would otherwise be a perpetual
79
+ * loading spinner.
80
+ *
81
+ * Two states, chosen from the (overridable) hostname:
82
+ * - Deployed block host (`<slug>.civit.ai`): a card with an "Open on Civitai"
83
+ * link to `https://civitai.com/apps/run/<slug>` (a real anchor — right/middle
84
+ * click, shareable, crawlable). Optional {@link DirectLoadFallbackProps.autoRedirectMs}
85
+ * auto-redirect on top of the button.
86
+ * - Non-civit.ai host (e.g. `localhost` in dev without the harness): a neutral
87
+ * "waiting for the Civitai host" card with a dev hint — NEVER a broken
88
+ * `apps/run/localhost` link.
89
+ *
90
+ * Theme-aware via `prefers-color-scheme` (there is no host `theme` on a direct
91
+ * load) and styled with the `/ui` pack's tokens.
92
+ */
93
+ export function DirectLoadFallback({ hostname, autoRedirectMs, }) {
94
+ useBlocksStyles();
95
+ const theme = usePrefersColorScheme();
96
+ const resolvedHost = hostname ?? (typeof window !== 'undefined' ? window.location?.hostname : undefined);
97
+ const runUrl = hostToRunUrl(resolvedHost);
98
+ useEffect(() => {
99
+ if (!runUrl || autoRedirectMs == null || typeof window === 'undefined')
100
+ return;
101
+ const id = setTimeout(() => {
102
+ try {
103
+ // Navigate the TOP frame (equals self on a direct load; correct either way).
104
+ (window.top ?? window).location.href = runUrl;
105
+ }
106
+ catch {
107
+ window.location.href = runUrl;
108
+ }
109
+ }, autoRedirectMs);
110
+ return () => clearTimeout(id);
111
+ }, [runUrl, autoRedirectMs]);
112
+ return (_jsx("div", { "data-theme": theme, "data-civitai-block-direct-load": "true", style: wrapperStyle, children: _jsx(Card, { withBorder: true, padding: "lg", style: { maxWidth: 420, width: '100%', textAlign: 'center' }, children: _jsxs(Stack, { gap: 12, align: "center", children: [_jsx("span", { style: brandStyle, children: "Civitai App" }), runUrl ? (_jsxs(_Fragment, { children: [_jsx("strong", { style: titleStyle, children: "Open this app on Civitai" }), _jsx("span", { style: bodyStyle, children: "This is a Civitai App. It runs inside Civitai, where you can sign in and use it." }), _jsx("a", { "data-civitai-ui": "button", "data-variant": "filled", "data-size": "lg", "data-full-width": "true", "data-civitai-block-open-on-civitai": "true", href: runUrl, target: "_top", rel: "noopener", children: "Open on Civitai" })] })) : (_jsxs(_Fragment, { children: [_jsx("strong", { style: titleStyle, children: "Waiting for the Civitai host\u2026" }), _jsx("span", { style: bodyStyle, role: "status", children: "This is a Civitai App. It\u2019s meant to run inside the Civitai host. In local development, load it through the block dev harness." })] }))] }) }) }));
113
+ }
114
+ /**
115
+ * Drop-in wrapper that shows the {@link DirectLoadFallback} when a block is
116
+ * loaded DIRECTLY (top-level, no `BLOCK_INIT` within the timeout) and otherwise
117
+ * renders `children` unchanged.
118
+ *
119
+ * Wrap your app root once so EVERY SDK-built block degrades gracefully instead
120
+ * of hanging on a loading spinner when its `<slug>.civit.ai` URL is opened
121
+ * directly:
122
+ *
123
+ * ```tsx
124
+ * import { BlockGate } from '@civitai/blocks-react/ui';
125
+ *
126
+ * createRoot(el).render(
127
+ * <BlockGate>
128
+ * <App />
129
+ * </BlockGate>,
130
+ * );
131
+ * ```
132
+ *
133
+ * The embedded happy path and the dev harness are untouched — see
134
+ * {@link useDirectLoad} for exactly why the trigger can't fire in either.
135
+ */
136
+ export function BlockGate({ children, timeoutMs, fallback, hostname, autoRedirectMs, }) {
137
+ const directLoad = useDirectLoad({ timeoutMs });
138
+ if (directLoad) {
139
+ return (_jsx(_Fragment, { children: fallback ?? _jsx(DirectLoadFallback, { hostname: hostname, autoRedirectMs: autoRedirectMs }) }));
140
+ }
141
+ return _jsx(_Fragment, { children: children });
142
+ }
143
+ //# sourceMappingURL=BlockGate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BlockGate.js","sourceRoot":"","sources":["../../src/ui/BlockGate.tsx"],"names":[],"mappings":"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"}
@@ -17,6 +17,8 @@
17
17
  */
18
18
  export { SettingsForm, SettingsFormError, isFieldVisible } from './SettingsForm.js';
19
19
  export type { SettingsFormProps } from './SettingsForm.js';
20
+ export { BlockGate, DirectLoadFallback } from './BlockGate.js';
21
+ export type { BlockGateProps, DirectLoadFallbackProps } from './BlockGate.js';
20
22
  export { injectBlocksStyles, useBlocksStyles, BLOCKS_UI_STYLES, } from './styles.js';
21
23
  export { Button } from './Button.js';
22
24
  export type { ButtonProps, ButtonVariant, ButtonSize } from './Button.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACpF,YAAY,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAK3D,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC/D,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE1E,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAExD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEtE,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC"}
package/dist/ui/index.js CHANGED
@@ -17,6 +17,10 @@
17
17
  */
18
18
  // W3 — manifest-driven settings form (host-themed native controls).
19
19
  export { SettingsForm, SettingsFormError, isFieldVisible } from './SettingsForm.js';
20
+ // Direct-load (unembedded) fallback — the branded "Open on Civitai" landing
21
+ // that replaces the perpetual loading spinner when a block's <slug>.civit.ai
22
+ // URL is opened top-level instead of embedded in the Civitai host.
23
+ export { BlockGate, DirectLoadFallback } from './BlockGate.js';
20
24
  // W6 — runtime style injection.
21
25
  export { injectBlocksStyles, useBlocksStyles, BLOCKS_UI_STYLES, } from './styles.js';
22
26
  // W6 — component pack.
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,oEAAoE;AACpE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGpF,gCAAgC;AAChC,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,uBAAuB;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,oEAAoE;AACpE,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGpF,4EAA4E;AAC5E,6EAA6E;AAC7E,mEAAmE;AACnE,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG/D,gCAAgC;AAChC,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,uBAAuB;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@civitai/blocks-react",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "React hooks and iframe transport for Civitai Apps. Pairs with @civitai/app-sdk/blocks.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -43,7 +43,7 @@
43
43
  "react-dom": "^19.0.0",
44
44
  "typescript": "^5.9.2",
45
45
  "vitest": "^4.1.7",
46
- "@civitai/app-sdk": "^0.25.0"
46
+ "@civitai/app-sdk": "^0.25.1"
47
47
  },
48
48
  "publishConfig": {
49
49
  "access": "public"