@civitai/blocks-react 0.4.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 +48 -0
- package/dist/hooks/useAppStorage.d.ts +75 -0
- package/dist/hooks/useAppStorage.d.ts.map +1 -0
- package/dist/hooks/useAppStorage.js +73 -0
- package/dist/hooks/useAppStorage.js.map +1 -0
- package/dist/hooks/useBlockAnalytics.d.ts +9 -0
- package/dist/hooks/useBlockAnalytics.d.ts.map +1 -0
- package/dist/hooks/useBlockAnalytics.js +14 -0
- package/dist/hooks/useBlockAnalytics.js.map +1 -0
- package/dist/hooks/useBlockContext.d.ts +18 -0
- package/dist/hooks/useBlockContext.d.ts.map +1 -0
- package/dist/hooks/useBlockContext.js +39 -0
- package/dist/hooks/useBlockContext.js.map +1 -0
- package/dist/hooks/useBlockResize.d.ts +13 -0
- package/dist/hooks/useBlockResize.d.ts.map +1 -0
- package/dist/hooks/useBlockResize.js +33 -0
- package/dist/hooks/useBlockResize.js.map +1 -0
- package/dist/hooks/useBlockSettings.d.ts +7 -0
- package/dist/hooks/useBlockSettings.d.ts.map +1 -0
- package/dist/hooks/useBlockSettings.js +9 -0
- package/dist/hooks/useBlockSettings.js.map +1 -0
- package/dist/hooks/useBlockToken.d.ts +16 -0
- package/dist/hooks/useBlockToken.d.ts.map +1 -0
- package/dist/hooks/useBlockToken.js +61 -0
- package/dist/hooks/useBlockToken.js.map +1 -0
- package/dist/hooks/useBuzzPurchase.d.ts +12 -0
- package/dist/hooks/useBuzzPurchase.d.ts.map +1 -0
- package/dist/hooks/useBuzzPurchase.js +16 -0
- package/dist/hooks/useBuzzPurchase.js.map +1 -0
- package/dist/hooks/useBuzzWorkflow.d.ts +21 -0
- package/dist/hooks/useBuzzWorkflow.d.ts.map +1 -0
- package/dist/hooks/useBuzzWorkflow.js +76 -0
- package/dist/hooks/useBuzzWorkflow.js.map +1 -0
- package/dist/hooks/useCheckpointPicker.d.ts +34 -0
- package/dist/hooks/useCheckpointPicker.d.ts.map +1 -0
- package/dist/hooks/useCheckpointPicker.js +41 -0
- package/dist/hooks/useCheckpointPicker.js.map +1 -0
- package/dist/hooks/useCivitaiNavigate.d.ts +11 -0
- package/dist/hooks/useCivitaiNavigate.d.ts.map +1 -0
- package/dist/hooks/useCivitaiNavigate.js +16 -0
- package/dist/hooks/useCivitaiNavigate.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/detector.d.ts +31 -0
- package/dist/internal/detector.d.ts.map +1 -0
- package/dist/internal/detector.js +67 -0
- package/dist/internal/detector.js.map +1 -0
- package/dist/internal/iframeTransport.d.ts +55 -0
- package/dist/internal/iframeTransport.d.ts.map +1 -0
- package/dist/internal/iframeTransport.js +200 -0
- package/dist/internal/iframeTransport.js.map +1 -0
- package/dist/internal/inlineTransport.d.ts +18 -0
- package/dist/internal/inlineTransport.d.ts.map +1 -0
- package/dist/internal/inlineTransport.js +36 -0
- package/dist/internal/inlineTransport.js.map +1 -0
- package/dist/internal/singleton.d.ts +18 -0
- package/dist/internal/singleton.d.ts.map +1 -0
- package/dist/internal/singleton.js +28 -0
- package/dist/internal/singleton.js.map +1 -0
- package/dist/internal/transport.d.ts +100 -0
- package/dist/internal/transport.d.ts.map +1 -0
- package/dist/internal/transport.js +67 -0
- package/dist/internal/transport.js.map +1 -0
- package/dist/internal/validate.d.ts +56 -0
- package/dist/internal/validate.d.ts.map +1 -0
- package/dist/internal/validate.js +202 -0
- package/dist/internal/validate.js.map +1 -0
- package/dist/testing.d.ts +14 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +16 -0
- package/dist/testing.js.map +1 -0
- package/dist/ui/SettingsForm.d.ts +79 -0
- package/dist/ui/SettingsForm.d.ts.map +1 -0
- package/dist/ui/SettingsForm.js +199 -0
- package/dist/ui/SettingsForm.js.map +1 -0
- package/dist/ui/index.d.ts +11 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +10 -0
- package/dist/ui/index.js.map +1 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# `@civitai/blocks-react`
|
|
2
|
+
|
|
3
|
+
React hooks and iframe transport for [Civitai App Blocks](https://developer.civitai.com/docs/blocks).
|
|
4
|
+
|
|
5
|
+
Pairs with [`@civitai/app-sdk`](https://www.npmjs.com/package/@civitai/app-sdk)'s
|
|
6
|
+
`/blocks` subpath, which carries the framework-agnostic manifest, scope, and
|
|
7
|
+
`postMessage` contract. This package adds the transport that actually moves
|
|
8
|
+
bytes and the React hooks block authors call.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm add @civitai/blocks-react @civitai/app-sdk react
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`react` and `@civitai/app-sdk` are peer dependencies — bring them yourself so
|
|
17
|
+
your block app and the SDK share a single React tree.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { useBlockContext, useBuzzWorkflow } from '@civitai/blocks-react';
|
|
23
|
+
|
|
24
|
+
export function App() {
|
|
25
|
+
const { ready, context, viewer } = useBlockContext();
|
|
26
|
+
const { estimate, submit, status, result } = useBuzzWorkflow();
|
|
27
|
+
|
|
28
|
+
if (!ready) return <div>Loading…</div>;
|
|
29
|
+
return (
|
|
30
|
+
<div>
|
|
31
|
+
<p>Block for model {context.modelId} ({viewer.username})</p>
|
|
32
|
+
<button onClick={() => submit({ prompt: 'a cat' })}>Generate</button>
|
|
33
|
+
{status === 'done' && result?.imageUrls?.map((u) => <img key={u} src={u} />)}
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## What lives where
|
|
40
|
+
|
|
41
|
+
- [`@civitai/app-sdk/blocks`](https://github.com/civitai/civitai-app-starters/tree/main/packages/civitai-app-sdk/src/blocks): manifest types, `defineBlock`, `BLOCK_SCOPES`, `postMessage` protocol, JSON schema.
|
|
42
|
+
- This package: `IframeTransport`, transport detector, and the eight React hooks (`useBlockContext`, `useBlockToken`, `useBlockSettings`, `useBuzzWorkflow`, `useBlockResize`, `useBuzzPurchase`, `useCivitaiNavigate`, `useBlockAnalytics`).
|
|
43
|
+
|
|
44
|
+
Architecture and contribution notes live in the [in-repo `AGENTS.md`](https://github.com/civitai/civitai-app-starters/blob/main/packages/civitai-blocks-react/AGENTS.md) (not shipped in the published tarball).
|
|
45
|
+
|
|
46
|
+
## License
|
|
47
|
+
|
|
48
|
+
MIT — see [`LICENSE`](../../LICENSE).
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public shape of one entry returned by `list()` — `updatedAt` is a real
|
|
3
|
+
* `Date` once the wire's ISO string has been rehydrated.
|
|
4
|
+
*/
|
|
5
|
+
export interface AppStorageKeyEntry {
|
|
6
|
+
key: string;
|
|
7
|
+
updatedAt: Date;
|
|
8
|
+
}
|
|
9
|
+
export interface AppStorageListResult {
|
|
10
|
+
keys: AppStorageKeyEntry[];
|
|
11
|
+
/** Opaque, base64-encoded last key — pass back as `cursor` to page forward. */
|
|
12
|
+
nextCursor?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AppStorageQuota {
|
|
15
|
+
usedBytes: number;
|
|
16
|
+
rowCount: number;
|
|
17
|
+
/** Host-enforced ceiling (bytes). Surface in UI so callers don't hard-code 50MB. */
|
|
18
|
+
limitBytes: number;
|
|
19
|
+
/** Host-enforced row ceiling (~1M today). */
|
|
20
|
+
limitRows: number;
|
|
21
|
+
}
|
|
22
|
+
export interface UseAppStorage {
|
|
23
|
+
/**
|
|
24
|
+
* Read a key for the current (block instance, viewer) tuple. Returns
|
|
25
|
+
* `null` when the key isn't set OR the viewer is anonymous. The generic
|
|
26
|
+
* is for caller convenience; the host stores arbitrary JSON.
|
|
27
|
+
*/
|
|
28
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Upsert a value. Resolves on host ack. Rejects with the host's
|
|
31
|
+
* `error` string when the value exceeds 64KB, when the per-app 50MB
|
|
32
|
+
* quota would be crossed, or when the viewer is anonymous.
|
|
33
|
+
*/
|
|
34
|
+
set<T = unknown>(key: string, value: T): Promise<{
|
|
35
|
+
ok: true;
|
|
36
|
+
sizeBytes?: number;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Remove a key. Resolves with `{ deleted: true }` when a row was
|
|
40
|
+
* present, `{ deleted: false }` when the key wasn't set (still treated
|
|
41
|
+
* as success — idempotent delete).
|
|
42
|
+
*/
|
|
43
|
+
delete(key: string): Promise<{
|
|
44
|
+
ok: true;
|
|
45
|
+
deleted: boolean;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Cursor-paginated key listing. Values aren't returned — call `get(key)`
|
|
49
|
+
* for the ones you care about.
|
|
50
|
+
*/
|
|
51
|
+
list(opts?: {
|
|
52
|
+
prefix?: string;
|
|
53
|
+
limit?: number;
|
|
54
|
+
cursor?: string;
|
|
55
|
+
}): Promise<AppStorageListResult>;
|
|
56
|
+
/**
|
|
57
|
+
* Diagnostic: current usage + the v0 ceilings. Build a "X of 50 MB used"
|
|
58
|
+
* settings widget against this.
|
|
59
|
+
*/
|
|
60
|
+
getQuota(): Promise<AppStorageQuota>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Per-(block instance, viewer) KV datastore. Calls flow through the host's
|
|
64
|
+
* postMessage bridge — the block never sees the apps DB credentials.
|
|
65
|
+
*
|
|
66
|
+
* Anon viewers get a no-op read path (`get` resolves `null`, `list` returns
|
|
67
|
+
* empty) and a hard reject on writes; the block decides whether to gate
|
|
68
|
+
* its UI on `useBlockContext().viewer`.
|
|
69
|
+
*
|
|
70
|
+
* The hook is stable across renders — it returns the same object identity
|
|
71
|
+
* once the transport singleton is created, so it's safe to put in
|
|
72
|
+
* dependency arrays of `useEffect` / `useMemo`.
|
|
73
|
+
*/
|
|
74
|
+
export declare function useAppStorage(): UseAppStorage;
|
|
75
|
+
//# sourceMappingURL=useAppStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAppStorage.d.ts","sourceRoot":"","sources":["../../src/hooks/useAppStorage.ts"],"names":[],"mappings":"AAKA;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD;;;;OAIG;IACH,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnF;;;;OAIG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC7D;;;OAGG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE;QACV,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAClC;;;OAGG;IACH,QAAQ,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,IAAI,aAAa,CAyE7C"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { getTransport } from '../internal/singleton.js';
|
|
3
|
+
import { sendTypedRequest } from '../internal/transport.js';
|
|
4
|
+
/**
|
|
5
|
+
* Per-(block instance, viewer) KV datastore. Calls flow through the host's
|
|
6
|
+
* postMessage bridge — the block never sees the apps DB credentials.
|
|
7
|
+
*
|
|
8
|
+
* Anon viewers get a no-op read path (`get` resolves `null`, `list` returns
|
|
9
|
+
* empty) and a hard reject on writes; the block decides whether to gate
|
|
10
|
+
* its UI on `useBlockContext().viewer`.
|
|
11
|
+
*
|
|
12
|
+
* The hook is stable across renders — it returns the same object identity
|
|
13
|
+
* once the transport singleton is created, so it's safe to put in
|
|
14
|
+
* dependency arrays of `useEffect` / `useMemo`.
|
|
15
|
+
*/
|
|
16
|
+
export function useAppStorage() {
|
|
17
|
+
return useMemo(() => {
|
|
18
|
+
const transport = getTransport();
|
|
19
|
+
return {
|
|
20
|
+
async get(key) {
|
|
21
|
+
const result = await sendTypedRequest(transport, { type: 'APP_STORAGE_GET', payload: { key } }, 'APP_STORAGE_GET_RESULT');
|
|
22
|
+
if (result.error)
|
|
23
|
+
throw new Error(result.error);
|
|
24
|
+
return (result.value ?? null);
|
|
25
|
+
},
|
|
26
|
+
async set(key, value) {
|
|
27
|
+
const result = await sendTypedRequest(transport, { type: 'APP_STORAGE_SET', payload: { key, value } }, 'APP_STORAGE_SET_RESULT');
|
|
28
|
+
if (!result.ok || result.error) {
|
|
29
|
+
throw new Error(result.error ?? 'storage set failed');
|
|
30
|
+
}
|
|
31
|
+
return { ok: true, sizeBytes: result.sizeBytes };
|
|
32
|
+
},
|
|
33
|
+
async delete(key) {
|
|
34
|
+
const result = await sendTypedRequest(transport, { type: 'APP_STORAGE_DELETE', payload: { key } }, 'APP_STORAGE_DELETE_RESULT');
|
|
35
|
+
if (!result.ok || result.error) {
|
|
36
|
+
throw new Error(result.error ?? 'storage delete failed');
|
|
37
|
+
}
|
|
38
|
+
return { ok: true, deleted: result.deleted };
|
|
39
|
+
},
|
|
40
|
+
async list(opts) {
|
|
41
|
+
const result = await sendTypedRequest(transport, {
|
|
42
|
+
type: 'APP_STORAGE_LIST',
|
|
43
|
+
payload: {
|
|
44
|
+
prefix: opts?.prefix,
|
|
45
|
+
limit: opts?.limit,
|
|
46
|
+
cursor: opts?.cursor,
|
|
47
|
+
},
|
|
48
|
+
}, 'APP_STORAGE_LIST_RESULT');
|
|
49
|
+
if (result.error)
|
|
50
|
+
throw new Error(result.error);
|
|
51
|
+
return {
|
|
52
|
+
keys: result.keys.map((k) => ({
|
|
53
|
+
key: k.key,
|
|
54
|
+
updatedAt: new Date(k.updatedAt),
|
|
55
|
+
})),
|
|
56
|
+
nextCursor: result.nextCursor,
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
async getQuota() {
|
|
60
|
+
const result = await sendTypedRequest(transport, { type: 'APP_STORAGE_QUOTA', payload: {} }, 'APP_STORAGE_QUOTA_RESULT');
|
|
61
|
+
if (result.error)
|
|
62
|
+
throw new Error(result.error);
|
|
63
|
+
return {
|
|
64
|
+
usedBytes: result.usedBytes,
|
|
65
|
+
rowCount: result.rowCount,
|
|
66
|
+
limitBytes: result.limitBytes,
|
|
67
|
+
limitRows: result.limitRows,
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}, []);
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=useAppStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAppStorage.js","sourceRoot":"","sources":["../../src/hooks/useAppStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fire-and-forget analytics tracking. The host forwards events to its
|
|
3
|
+
* analytics pipeline (ClickHouse in production); the block doesn't see
|
|
4
|
+
* acknowledgements and shouldn't block on them.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useBlockAnalytics(): {
|
|
7
|
+
track: (eventName: string, properties?: Record<string, unknown>) => void;
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=useBlockAnalytics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockAnalytics.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockAnalytics.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI;IACnC,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CAC1E,CAKA"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { getTransport } from '../internal/singleton.js';
|
|
3
|
+
/**
|
|
4
|
+
* Fire-and-forget analytics tracking. The host forwards events to its
|
|
5
|
+
* analytics pipeline (ClickHouse in production); the block doesn't see
|
|
6
|
+
* acknowledgements and shouldn't block on them.
|
|
7
|
+
*/
|
|
8
|
+
export function useBlockAnalytics() {
|
|
9
|
+
const track = useCallback((eventName, properties) => {
|
|
10
|
+
getTransport().sendMessage({ type: 'TRACK_EVENT', payload: { eventName, properties } });
|
|
11
|
+
}, []);
|
|
12
|
+
return { track };
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=useBlockAnalytics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockAnalytics.js","sourceRoot":"","sources":["../../src/hooks/useBlockAnalytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAG/B,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,SAAiB,EAAE,UAAoC,EAAE,EAAE;QACpF,YAAY,EAAE,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1F,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BlockSnapshot } from '../internal/transport.js';
|
|
2
|
+
/**
|
|
3
|
+
* Subscribe a hook to the singleton transport. All hooks build on this —
|
|
4
|
+
* `useSyncExternalStore` gives concurrent-mode safety and minimal re-renders.
|
|
5
|
+
*/
|
|
6
|
+
declare function useTransportSnapshot(): BlockSnapshot;
|
|
7
|
+
/**
|
|
8
|
+
* Primary hook for a block app. Returns the full per-instance context
|
|
9
|
+
* delivered by the host plus a `ready` gate the UI should respect — fields
|
|
10
|
+
* other than `ready` are sentinel-empty before `BLOCK_INIT` lands.
|
|
11
|
+
*
|
|
12
|
+
* The transport detection (iframe vs inline) happens on first call and is
|
|
13
|
+
* cached process-wide; block apps don't branch on render mode.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useBlockContext(): Pick<BlockSnapshot, 'ready' | 'renderMode' | 'context' | 'token' | 'settings' | 'viewer' | 'theme' | 'blockId' | 'blockInstanceId' | 'appId'>;
|
|
16
|
+
/** Re-exported so other hooks in this package can share the subscription. */
|
|
17
|
+
export { useTransportSnapshot };
|
|
18
|
+
//# sourceMappingURL=useBlockContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockContext.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockContext.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;GAGG;AACH,iBAAS,oBAAoB,IAAI,aAAa,CAS7C;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,IAAI,CACrC,aAAa,EACX,OAAO,GACP,YAAY,GACZ,SAAS,GACT,OAAO,GACP,UAAU,GACV,QAAQ,GACR,OAAO,GACP,SAAS,GACT,iBAAiB,GACjB,OAAO,CACV,CAcA;AAED,6EAA6E;AAC7E,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useSyncExternalStore } from 'react';
|
|
2
|
+
import { getTransport } from '../internal/singleton.js';
|
|
3
|
+
/**
|
|
4
|
+
* Subscribe a hook to the singleton transport. All hooks build on this —
|
|
5
|
+
* `useSyncExternalStore` gives concurrent-mode safety and minimal re-renders.
|
|
6
|
+
*/
|
|
7
|
+
function useTransportSnapshot() {
|
|
8
|
+
const transport = getTransport();
|
|
9
|
+
return useSyncExternalStore((cb) => transport.subscribe(cb), () => transport.getSnapshot(),
|
|
10
|
+
// Server snapshot: blocks don't SSR meaningful state. Return the
|
|
11
|
+
// pre-init empty snapshot so React's hydration check doesn't warn.
|
|
12
|
+
() => transport.getSnapshot());
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Primary hook for a block app. Returns the full per-instance context
|
|
16
|
+
* delivered by the host plus a `ready` gate the UI should respect — fields
|
|
17
|
+
* other than `ready` are sentinel-empty before `BLOCK_INIT` lands.
|
|
18
|
+
*
|
|
19
|
+
* The transport detection (iframe vs inline) happens on first call and is
|
|
20
|
+
* cached process-wide; block apps don't branch on render mode.
|
|
21
|
+
*/
|
|
22
|
+
export function useBlockContext() {
|
|
23
|
+
const snap = useTransportSnapshot();
|
|
24
|
+
return {
|
|
25
|
+
ready: snap.ready,
|
|
26
|
+
renderMode: snap.renderMode,
|
|
27
|
+
context: snap.context,
|
|
28
|
+
token: snap.token,
|
|
29
|
+
settings: snap.settings,
|
|
30
|
+
viewer: snap.viewer,
|
|
31
|
+
theme: snap.theme,
|
|
32
|
+
blockId: snap.blockId,
|
|
33
|
+
blockInstanceId: snap.blockInstanceId,
|
|
34
|
+
appId: snap.appId,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** Re-exported so other hooks in this package can share the subscription. */
|
|
38
|
+
export { useTransportSnapshot };
|
|
39
|
+
//# sourceMappingURL=useBlockContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockContext.js","sourceRoot":"","sources":["../../src/hooks/useBlockContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD;;;GAGG;AACH,SAAS,oBAAoB;IAC3B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,OAAO,oBAAoB,CACzB,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,EAC/B,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE;IAC7B,iEAAiE;IACjE,mEAAmE;IACnE,GAAG,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,CAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe;IAa7B,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,eAAe,EAAE,IAAI,CAAC,eAAe;QACrC,KAAK,EAAE,IAAI,CAAC,KAAK;KAClB,CAAC;AACJ,CAAC;AAED,6EAA6E;AAC7E,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type RefObject } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Observes the referenced element's height and asks the host to resize on
|
|
4
|
+
* every change. Attach to the block's root DOM element.
|
|
5
|
+
*
|
|
6
|
+
* The ResizeObserver runs on both transports — only the outbound
|
|
7
|
+
* `RESIZE_IFRAME` message differs:
|
|
8
|
+
* - Iframe path: posts `RESIZE_IFRAME` with the integer-rounded height.
|
|
9
|
+
* - Inline path: `InlineTransport.sendMessage` is a no-op (the host DOM
|
|
10
|
+
* reflows naturally), so the observer fires but no message goes out.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useBlockResize(ref: RefObject<HTMLElement | null>): void;
|
|
13
|
+
//# sourceMappingURL=useBlockResize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockResize.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockResize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAIlD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,IAAI,CAiBvE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { getTransport } from '../internal/singleton.js';
|
|
3
|
+
/**
|
|
4
|
+
* Observes the referenced element's height and asks the host to resize on
|
|
5
|
+
* every change. Attach to the block's root DOM element.
|
|
6
|
+
*
|
|
7
|
+
* The ResizeObserver runs on both transports — only the outbound
|
|
8
|
+
* `RESIZE_IFRAME` message differs:
|
|
9
|
+
* - Iframe path: posts `RESIZE_IFRAME` with the integer-rounded height.
|
|
10
|
+
* - Inline path: `InlineTransport.sendMessage` is a no-op (the host DOM
|
|
11
|
+
* reflows naturally), so the observer fires but no message goes out.
|
|
12
|
+
*/
|
|
13
|
+
export function useBlockResize(ref) {
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const el = ref.current;
|
|
16
|
+
if (!el)
|
|
17
|
+
return;
|
|
18
|
+
if (typeof ResizeObserver === 'undefined')
|
|
19
|
+
return;
|
|
20
|
+
const transport = getTransport();
|
|
21
|
+
let lastHeight = -1;
|
|
22
|
+
const observer = new ResizeObserver((entries) => {
|
|
23
|
+
const height = Math.ceil(entries[0]?.contentRect.height ?? el.offsetHeight);
|
|
24
|
+
if (height === lastHeight)
|
|
25
|
+
return;
|
|
26
|
+
lastHeight = height;
|
|
27
|
+
transport.sendMessage({ type: 'RESIZE_IFRAME', payload: { height } });
|
|
28
|
+
});
|
|
29
|
+
observer.observe(el);
|
|
30
|
+
return () => observer.disconnect();
|
|
31
|
+
}, [ref]);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=useBlockResize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockResize.js","sourceRoot":"","sources":["../../src/hooks/useBlockResize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAkB,MAAM,OAAO,CAAC;AAElD,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAExD;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,GAAkC;IAC/D,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC;QACvB,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,IAAI,OAAO,cAAc,KAAK,WAAW;YAAE,OAAO;QAElD,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;QACjC,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC;QACpB,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,CAAC,OAAO,EAAE,EAAE;YAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,IAAI,EAAE,CAAC,YAAY,CAAC,CAAC;YAC5E,IAAI,MAAM,KAAK,UAAU;gBAAE,OAAO;YAClC,UAAU,GAAG,MAAM,CAAC;YACpB,SAAS,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACrB,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BlockSettings } from '@civitai/app-sdk/blocks';
|
|
2
|
+
/**
|
|
3
|
+
* Shorthand for `useBlockContext().settings`. Returns the publisher- and
|
|
4
|
+
* user-controlled settings the host forwarded at init.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useBlockSettings(): BlockSettings;
|
|
7
|
+
//# sourceMappingURL=useBlockSettings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockSettings.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockSettings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAI7D;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useTransportSnapshot } from './useBlockContext.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shorthand for `useBlockContext().settings`. Returns the publisher- and
|
|
4
|
+
* user-controlled settings the host forwarded at init.
|
|
5
|
+
*/
|
|
6
|
+
export function useBlockSettings() {
|
|
7
|
+
return useTransportSnapshot().settings;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=useBlockSettings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockSettings.js","sourceRoot":"","sources":["../../src/hooks/useBlockSettings.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,oBAAoB,EAAE,CAAC,QAAQ,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BlockToken } from '@civitai/app-sdk/blocks';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the current block-scoped JWT and keeps it fresh.
|
|
4
|
+
*
|
|
5
|
+
* Iframe path: schedules a `REQUEST_TOKEN` postMessage T-2min before expiry.
|
|
6
|
+
* The host replies with `TOKEN_REFRESH_RESPONSE`, the transport updates its
|
|
7
|
+
* snapshot, and the hook re-renders with the new token.
|
|
8
|
+
*
|
|
9
|
+
* Consumers also get a `refresh()` callable for the 401-retry path. Call it
|
|
10
|
+
* after any API request returns 401 — it forces an immediate token mint and
|
|
11
|
+
* resolves once the new token is applied to the snapshot.
|
|
12
|
+
*/
|
|
13
|
+
export declare function useBlockToken(): BlockToken & {
|
|
14
|
+
refresh: () => Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=useBlockToken.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockToken.d.ts","sourceRoot":"","sources":["../../src/hooks/useBlockToken.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAS1D;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,IAAI,UAAU,GAAG;IAAE,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAwB7E"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { getTransport } from '../internal/singleton.js';
|
|
3
|
+
import { sendTypedRequest } from '../internal/transport.js';
|
|
4
|
+
import { useTransportSnapshot } from './useBlockContext.js';
|
|
5
|
+
/** Refresh fires `REFRESH_LEAD_MS` before the token's `expiresAt`. */
|
|
6
|
+
const REFRESH_LEAD_MS = 2 * 60 * 1000;
|
|
7
|
+
/**
|
|
8
|
+
* Returns the current block-scoped JWT and keeps it fresh.
|
|
9
|
+
*
|
|
10
|
+
* Iframe path: schedules a `REQUEST_TOKEN` postMessage T-2min before expiry.
|
|
11
|
+
* The host replies with `TOKEN_REFRESH_RESPONSE`, the transport updates its
|
|
12
|
+
* snapshot, and the hook re-renders with the new token.
|
|
13
|
+
*
|
|
14
|
+
* Consumers also get a `refresh()` callable for the 401-retry path. Call it
|
|
15
|
+
* after any API request returns 401 — it forces an immediate token mint and
|
|
16
|
+
* resolves once the new token is applied to the snapshot.
|
|
17
|
+
*/
|
|
18
|
+
export function useBlockToken() {
|
|
19
|
+
const snap = useTransportSnapshot();
|
|
20
|
+
const token = snap.token;
|
|
21
|
+
const instanceId = snap.blockInstanceId;
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!snap.ready)
|
|
24
|
+
return;
|
|
25
|
+
const msUntilRefresh = token.expiresAt.getTime() - Date.now() - REFRESH_LEAD_MS;
|
|
26
|
+
if (msUntilRefresh <= 0) {
|
|
27
|
+
// Already past the refresh window — refresh now.
|
|
28
|
+
// Background refresh — swallow errors. The next 401 will re-trigger
|
|
29
|
+
// via the explicit refresh() callable below.
|
|
30
|
+
requestRefresh(instanceId).catch(() => { });
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const id = setTimeout(() => {
|
|
34
|
+
// Background refresh — swallow errors. The next 401 will re-trigger
|
|
35
|
+
// via the explicit refresh() callable below.
|
|
36
|
+
requestRefresh(instanceId).catch(() => { });
|
|
37
|
+
}, msUntilRefresh);
|
|
38
|
+
return () => clearTimeout(id);
|
|
39
|
+
}, [snap.ready, token.expiresAt, instanceId]);
|
|
40
|
+
return { ...token, refresh: () => requestRefresh(instanceId) };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* In-flight dedup. Both the scheduled-timeout path and the "already past"
|
|
44
|
+
* synchronous path can fire while a previous refresh is still pending —
|
|
45
|
+
* coalescing into a single round-trip avoids fanning out duplicate
|
|
46
|
+
* REQUEST_TOKEN messages, which would otherwise hit the host once per
|
|
47
|
+
* snapshot update.
|
|
48
|
+
*/
|
|
49
|
+
let inFlightRefresh = null;
|
|
50
|
+
async function requestRefresh(blockInstanceId) {
|
|
51
|
+
if (inFlightRefresh)
|
|
52
|
+
return inFlightRefresh;
|
|
53
|
+
const transport = getTransport();
|
|
54
|
+
inFlightRefresh = sendTypedRequest(transport, { type: 'REQUEST_TOKEN', payload: { blockInstanceId } }, 'TOKEN_REFRESH_RESPONSE')
|
|
55
|
+
.then(() => undefined)
|
|
56
|
+
.finally(() => {
|
|
57
|
+
inFlightRefresh = null;
|
|
58
|
+
});
|
|
59
|
+
return inFlightRefresh;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=useBlockToken.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBlockToken.js","sourceRoot":"","sources":["../../src/hooks/useBlockToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIlC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAE5D,sEAAsE;AACtE,MAAM,eAAe,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,MAAM,UAAU,GAAG,IAAI,CAAC,eAAe,CAAC;IAExC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO;QACxB,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;QAChF,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;YACxB,iDAAiD;YACjD,oEAAoE;YACpE,6CAA6C;YAC7C,cAAc,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YAC3C,OAAO;QACT,CAAC;QACD,MAAM,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE;YACzB,oEAAoE;YACpE,6CAA6C;YAC7C,cAAc,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAC7C,CAAC,EAAE,cAAc,CAAC,CAAC;QACnB,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC;IAE9C,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,UAAU,CAAC,EAAE,CAAC;AACjE,CAAC;AAED;;;;;;GAMG;AACH,IAAI,eAAe,GAAyB,IAAI,CAAC;AAEjD,KAAK,UAAU,cAAc,CAAC,eAAuB;IACnD,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAC5C,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,eAAe,GAAG,gBAAgB,CAChC,SAAS,EACT,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,EAAE,EACvD,wBAAwB,CACzB;SACE,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;SACrB,OAAO,CAAC,GAAG,EAAE;QACZ,eAAe,GAAG,IAAI,CAAC;IACzB,CAAC,CAAC,CAAC;IACL,OAAO,eAAe,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Opens the Civitai Buzz purchase modal on the host. Resolves with the
|
|
3
|
+
* outcome when the user closes the modal — `purchased: true` means the
|
|
4
|
+
* balance increased; the new balance is included if the host reports it.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useBuzzPurchase(): {
|
|
7
|
+
openPurchaseModal: (suggestedAmount?: number) => Promise<{
|
|
8
|
+
purchased: boolean;
|
|
9
|
+
newBalance?: number;
|
|
10
|
+
}>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=useBuzzPurchase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBuzzPurchase.d.ts","sourceRoot":"","sources":["../../src/hooks/useBuzzPurchase.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,wBAAgB,eAAe,IAAI;IACjC,iBAAiB,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvG,CAUA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { getTransport } from '../internal/singleton.js';
|
|
3
|
+
import { sendTypedRequest } from '../internal/transport.js';
|
|
4
|
+
/**
|
|
5
|
+
* Opens the Civitai Buzz purchase modal on the host. Resolves with the
|
|
6
|
+
* outcome when the user closes the modal — `purchased: true` means the
|
|
7
|
+
* balance increased; the new balance is included if the host reports it.
|
|
8
|
+
*/
|
|
9
|
+
export function useBuzzPurchase() {
|
|
10
|
+
const openPurchaseModal = useCallback(async (suggestedAmount) => {
|
|
11
|
+
const { purchased, newBalance } = await sendTypedRequest(getTransport(), { type: 'OPEN_BUZZ_PURCHASE', payload: { suggestedAmount } }, 'BUZZ_PURCHASE_RESULT');
|
|
12
|
+
return { purchased, newBalance };
|
|
13
|
+
}, []);
|
|
14
|
+
return { openPurchaseModal };
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=useBuzzPurchase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBuzzPurchase.js","sourceRoot":"","sources":["../../src/hooks/useBuzzPurchase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAEpC,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAG7B,MAAM,iBAAiB,GAAG,WAAW,CAAC,KAAK,EAAE,eAAwB,EAAE,EAAE;QACvE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,MAAM,gBAAgB,CACtD,YAAY,EAAE,EACd,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,EAAE,eAAe,EAAE,EAAE,EAC5D,sBAAsB,CACvB,CAAC;QACF,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC;IACnC,CAAC,EAAE,EAAE,CAAC,CAAC;IACP,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BlockWorkflowSnapshot, WorkflowBody, WorkflowStatus } from '@civitai/app-sdk/blocks';
|
|
2
|
+
interface UseBuzzWorkflowReturn {
|
|
3
|
+
estimate: (body: WorkflowBody) => Promise<BlockWorkflowSnapshot>;
|
|
4
|
+
submit: (body: WorkflowBody) => Promise<BlockWorkflowSnapshot>;
|
|
5
|
+
poll: (workflowId: string) => Promise<BlockWorkflowSnapshot>;
|
|
6
|
+
status: WorkflowStatus;
|
|
7
|
+
result: BlockWorkflowSnapshot | null;
|
|
8
|
+
error: Error | null;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Orchestrates the estimate → confirm → submit → poll dance through the
|
|
12
|
+
* host-mediated `postMessage` path.
|
|
13
|
+
*
|
|
14
|
+
* The host enforces budget rules (`cost_estimate <= token.buzzBudget`)
|
|
15
|
+
* before forwarding to the orchestrator; submit() will reject if the host
|
|
16
|
+
* refuses. Block apps should call `useBuzzPurchase().openPurchaseModal()`
|
|
17
|
+
* when that happens.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useBuzzWorkflow(): UseBuzzWorkflowReturn;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=useBuzzWorkflow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBuzzWorkflow.d.ts","sourceRoot":"","sources":["../../src/hooks/useBuzzWorkflow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAiBnG,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACjE,MAAM,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC/D,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC7D,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACrC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,IAAI,qBAAqB,CAgEvD"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
import { getTransport } from '../internal/singleton.js';
|
|
3
|
+
import { sendTypedRequest } from '../internal/transport.js';
|
|
4
|
+
/**
|
|
5
|
+
* Snapshot statuses that mean "no further polling is needed."
|
|
6
|
+
* Used by both `submit` (a host can return an instant-fail / cached result)
|
|
7
|
+
* and `poll` so the hook can't get stuck in 'polling' after a terminal reply.
|
|
8
|
+
*/
|
|
9
|
+
const TERMINAL_STATUSES = new Set([
|
|
10
|
+
'succeeded',
|
|
11
|
+
'failed',
|
|
12
|
+
'canceled',
|
|
13
|
+
'expired',
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* Orchestrates the estimate → confirm → submit → poll dance through the
|
|
17
|
+
* host-mediated `postMessage` path.
|
|
18
|
+
*
|
|
19
|
+
* The host enforces budget rules (`cost_estimate <= token.buzzBudget`)
|
|
20
|
+
* before forwarding to the orchestrator; submit() will reject if the host
|
|
21
|
+
* refuses. Block apps should call `useBuzzPurchase().openPurchaseModal()`
|
|
22
|
+
* when that happens.
|
|
23
|
+
*/
|
|
24
|
+
export function useBuzzWorkflow() {
|
|
25
|
+
const [status, setStatus] = useState('idle');
|
|
26
|
+
const [result, setResult] = useState(null);
|
|
27
|
+
const [error, setError] = useState(null);
|
|
28
|
+
const estimate = useCallback(async (body) => {
|
|
29
|
+
setError(null);
|
|
30
|
+
setStatus('estimating');
|
|
31
|
+
try {
|
|
32
|
+
const { snapshot } = await sendTypedRequest(getTransport(), { type: 'ESTIMATE_WORKFLOW', payload: { body } }, 'ESTIMATE_RESULT');
|
|
33
|
+
setResult(snapshot);
|
|
34
|
+
setStatus('confirming');
|
|
35
|
+
return snapshot;
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
setError(err);
|
|
39
|
+
setStatus('error');
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
}, []);
|
|
43
|
+
const submit = useCallback(async (body) => {
|
|
44
|
+
setError(null);
|
|
45
|
+
setStatus('submitting');
|
|
46
|
+
try {
|
|
47
|
+
const { snapshot } = await sendTypedRequest(getTransport(), { type: 'SUBMIT_WORKFLOW', payload: { body } }, 'WORKFLOW_SUBMITTED');
|
|
48
|
+
setResult(snapshot);
|
|
49
|
+
setStatus(TERMINAL_STATUSES.has(snapshot.status) ? 'done' : 'polling');
|
|
50
|
+
return snapshot;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
setError(err);
|
|
54
|
+
setStatus('error');
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}, []);
|
|
58
|
+
const poll = useCallback(async (workflowId) => {
|
|
59
|
+
setStatus('polling');
|
|
60
|
+
try {
|
|
61
|
+
const { snapshot } = await sendTypedRequest(getTransport(), { type: 'POLL_WORKFLOW', payload: { workflowId } }, 'WORKFLOW_STATUS');
|
|
62
|
+
setResult(snapshot);
|
|
63
|
+
if (TERMINAL_STATUSES.has(snapshot.status)) {
|
|
64
|
+
setStatus('done');
|
|
65
|
+
}
|
|
66
|
+
return snapshot;
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
setError(err);
|
|
70
|
+
setStatus('error');
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
}, []);
|
|
74
|
+
return { estimate, submit, poll, status, result, error };
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=useBuzzWorkflow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBuzzWorkflow.js","sourceRoot":"","sources":["../../src/hooks/useBuzzWorkflow.ts"],"names":[],"mappings":"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"}
|