@civitai/blocks-react 0.71.0 → 0.72.2
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 +308 -15
- package/dist/hooks/useAppStorage.js +2 -0
- package/dist/hooks/useAppWorkflows.js +2 -0
- package/dist/hooks/useBatchEstimate.d.ts +4 -0
- package/dist/hooks/useBatchEstimate.js +6 -0
- package/dist/hooks/useBlockAnalytics.js +2 -0
- package/dist/hooks/useBlockBreakpoint.js +2 -0
- package/dist/hooks/useBlockContext.js +2 -0
- package/dist/hooks/useBlockResize.js +2 -0
- package/dist/hooks/useBlockSettings.js +2 -0
- package/dist/hooks/useBlockTheme.js +2 -0
- package/dist/hooks/useBlockToken.js +2 -0
- package/dist/hooks/useBuzzAccounts.js +2 -0
- package/dist/hooks/useBuzzBalance.js +2 -0
- package/dist/hooks/useBuzzPurchase.js +2 -0
- package/dist/hooks/useBuzzTransactions.js +2 -0
- package/dist/hooks/useBuzzWorkflow.d.ts +35 -3
- package/dist/hooks/useBuzzWorkflow.js +32 -3
- package/dist/hooks/useCheckpointPicker.d.ts +2 -0
- package/dist/hooks/useCheckpointPicker.js +4 -0
- package/dist/hooks/useCivitaiNavigate.js +2 -0
- package/dist/hooks/useCivitaiRoute.js +2 -0
- package/dist/hooks/useCollectionFollow.js +2 -0
- package/dist/hooks/useConsentUnavailable.js +2 -0
- package/dist/hooks/useCreatePostFromApp.js +2 -0
- package/dist/hooks/useDailyCompensation.js +2 -0
- package/dist/hooks/useDirectLoad.js +2 -0
- package/dist/hooks/useDomainMaturity.js +2 -0
- package/dist/hooks/useEntitlements.js +2 -0
- package/dist/hooks/useGatedImages.js +2 -0
- package/dist/hooks/useGenerationResources.js +2 -0
- package/dist/hooks/useGoodPurchase.js +2 -0
- package/dist/hooks/useHostOrigin.js +2 -0
- package/dist/hooks/useImageUpload.js +2 -0
- package/dist/hooks/useNestedDocument.js +2 -0
- package/dist/hooks/usePrepareTrainingDataset.js +2 -0
- package/dist/hooks/usePublishGenerationOutputs.js +2 -0
- package/dist/hooks/useRequestConsent.js +2 -0
- package/dist/hooks/useRequestSignIn.js +2 -0
- package/dist/hooks/useResourcePicker.js +2 -0
- package/dist/hooks/useRunConfirmedStep.d.ts +209 -0
- package/dist/hooks/useRunConfirmedStep.js +289 -0
- package/dist/hooks/useRunTraining.js +2 -0
- package/dist/hooks/useSaveImage.js +2 -0
- package/dist/hooks/useSharedStorage.js +2 -0
- package/dist/hooks/useTip.js +2 -0
- package/dist/hooks/useTipAllowance.js +2 -0
- package/dist/hooks/useTransportConnection.d.ts +43 -0
- package/dist/hooks/useTransportConnection.js +59 -0
- package/dist/hooks/useUploadImageBytes.js +2 -0
- package/dist/hooks/useViewer.js +2 -0
- package/dist/hooks/useWildcardPack.js +2 -0
- package/dist/hooks/useWorkflowBatch.js +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/internal/hostSurface.d.ts +37 -0
- package/dist/internal/hostSurface.js +38 -0
- package/dist/internal/liveHost.d.ts +15 -6
- package/dist/internal/liveHost.js +151 -69
- package/dist/internal/mockHost.d.ts +123 -10
- package/dist/internal/mockHost.js +377 -14
- package/dist/internal/mockWorkflowGraph.d.ts +23 -10
- package/dist/internal/mockWorkflowGraph.js +34 -11
- package/dist/testing.d.ts +1 -1
- package/dist/transport/iframeTransport.js +13 -0
- package/dist/transport/requestTimeouts.js +6 -0
- package/dist/transport/singleton.d.ts +6 -0
- package/dist/transport/singleton.js +20 -0
- package/dist/transport/validate.d.ts +13 -0
- package/dist/transport/validate.js +76 -0
- package/package.json +4 -4
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
|
|
2
2
|
import { getTransport } from '../transport/singleton.js';
|
|
3
3
|
import { sendTypedRequest } from '../transport/transport.js';
|
|
4
4
|
import { useRequestSequencer } from './useRequestSequencer.js';
|
|
5
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
5
6
|
/** Rehydrate the host's `date`/`cursor` (a `Date` instance OR an ISO string). */
|
|
6
7
|
function toDate(v) {
|
|
7
8
|
return v instanceof Date ? v : new Date(v);
|
|
@@ -31,6 +32,7 @@ function toIso(v) {
|
|
|
31
32
|
* const { transactions, cursor, loading, error } = useBuzzTransactions({ type: 'Tip', limit: 20 });
|
|
32
33
|
*/
|
|
33
34
|
export function useBuzzTransactions(params) {
|
|
35
|
+
useTransportConnection();
|
|
34
36
|
const [transactions, setTransactions] = useState(null);
|
|
35
37
|
const [cursor, setCursor] = useState(null);
|
|
36
38
|
const [loading, setLoading] = useState(true);
|
|
@@ -13,6 +13,22 @@ import type { ConsentRetryOptions } from './consentRetryOptions.js';
|
|
|
13
13
|
* on that class is money-ambiguous by design).
|
|
14
14
|
*/
|
|
15
15
|
export declare const TRAINING_BODY_SUBMIT_REFUSAL = "useBuzzWorkflow.submit() does not run a kind:'training' body \u2014 estimate it here, then run it with useRunTraining().runTraining({ ...body, quoteId }). Nothing was sent.";
|
|
16
|
+
/**
|
|
17
|
+
* Why {@link UseBuzzWorkflow.submit} refuses a pass-through `step` body that
|
|
18
|
+
* carries a `quoteId`.
|
|
19
|
+
*
|
|
20
|
+
* 🔴 REFUSED BEFORE THE SEND, for the same reason as
|
|
21
|
+
* {@link TRAINING_BODY_SUBMIT_REFUSAL}. A `quoteId` on a pass-through body means
|
|
22
|
+
* "charge this job against the price the viewer confirmed", and only
|
|
23
|
+
* `RUN_CONFIRMED_STEP`'s host-chrome dialog can record that confirmation — so a
|
|
24
|
+
* `SUBMIT_WORKFLOW` carrying one can never start the job; the server refuses it
|
|
25
|
+
* as unconfirmed. Nothing is sent and nothing is spent, which is why this too is
|
|
26
|
+
* a plain `Error` and not a {@link WorkflowSubmitError}.
|
|
27
|
+
*
|
|
28
|
+
* The same body WITHOUT `quoteId` is an ordinary pass-through submit and is not
|
|
29
|
+
* affected.
|
|
30
|
+
*/
|
|
31
|
+
export declare const CONFIRMED_STEP_BODY_SUBMIT_REFUSAL = "useBuzzWorkflow.submit() does not run a step body that carries a quoteId \u2014 run it with useRunConfirmedStep().runConfirmedStep(body), or drop quoteId to submit it as an ordinary job. Nothing was sent.";
|
|
16
32
|
/**
|
|
17
33
|
* Default orchestrator-side hold per {@link UseBuzzWorkflow.watch} poll,
|
|
18
34
|
* in SECONDS.
|
|
@@ -486,6 +502,17 @@ export interface UseBuzzWorkflow {
|
|
|
486
502
|
* A `kind: 'training'` body's reply also carries
|
|
487
503
|
* {@link BlockWorkflowSnapshot.trainingQuote} — the `quoteId` to run it with
|
|
488
504
|
* through `useRunTraining()`.
|
|
505
|
+
*
|
|
506
|
+
* A pass-through `step` body's reply carries
|
|
507
|
+
* {@link BlockWorkflowSnapshot.stepQuote} when its FIXED price is above the
|
|
508
|
+
* token's per-call budget (and within the per-job ceiling). That job cannot
|
|
509
|
+
* run through {@link UseBuzzWorkflow.submit}; run it with
|
|
510
|
+
* `useRunConfirmedStep().runConfirmedStep({ ...body, quoteId:
|
|
511
|
+
* stepQuote.quoteId })`, which has the viewer confirm `stepQuote.total`. No
|
|
512
|
+
* `stepQuote` means the ordinary rules apply — it is also what a host without
|
|
513
|
+
* the feature sends. Where a quote would be granted, this REJECTS instead for
|
|
514
|
+
* a training `$type` or an `input` that is not plain JSON (see
|
|
515
|
+
* {@link BlockWorkflowSnapshot.stepQuote} for the two messages).
|
|
489
516
|
*/
|
|
490
517
|
estimate: (body: WorkflowBody) => Promise<BlockWorkflowSnapshot>;
|
|
491
518
|
/**
|
|
@@ -589,6 +616,9 @@ export interface UseBuzzWorkflow {
|
|
|
589
616
|
*
|
|
590
617
|
* 🔴 A `kind: 'training'` BODY IS REFUSED before anything is sent — it runs
|
|
591
618
|
* only through `useRunTraining()`. See {@link TRAINING_BODY_SUBMIT_REFUSAL}.
|
|
619
|
+
* So is a pass-through `step` body that carries a `quoteId` — it runs only
|
|
620
|
+
* through `useRunConfirmedStep()`. See
|
|
621
|
+
* {@link CONFIRMED_STEP_BODY_SUBMIT_REFUSAL}.
|
|
592
622
|
*/
|
|
593
623
|
submit: (body: WorkflowBody, options?: SubmitWorkflowOptions) => Promise<BlockWorkflowSnapshot>;
|
|
594
624
|
/**
|
|
@@ -703,9 +733,11 @@ export interface UseBuzzWorkflow {
|
|
|
703
733
|
* orchestrator steps as one workflow through these same calls, and:
|
|
704
734
|
* - `estimate()`'s `cost.total` is NOT a maximum: the orchestrator re-prices a
|
|
705
735
|
* step once the values it references are known and charges the difference;
|
|
706
|
-
* - a workflow the host cannot price
|
|
707
|
-
*
|
|
708
|
-
*
|
|
736
|
+
* - a workflow the host cannot price FAILS `estimate()` with a
|
|
737
|
+
* `workflow_quote_*` `errorCode` and no `cost`, yet still proceeds on
|
|
738
|
+
* `submit()`, reserved at the app's per-generation budget × the number of
|
|
739
|
+
* steps (one with a training step, or with a `repeat` step at any depth, is
|
|
740
|
+
* refused on both); an older host returns that ceiling as the estimate;
|
|
709
741
|
* - one author fee applies to the whole workflow, not one per step, and it is
|
|
710
742
|
* reversed whole if the workflow does not succeed;
|
|
711
743
|
* - `repeat` iterates only an array in the body's `arguments`, at most 16
|
|
@@ -4,6 +4,7 @@ import { withConsentRetry } from '../internal/withConsentRetry.js';
|
|
|
4
4
|
import { WORKFLOW_REQUEST_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
5
5
|
import { getTransport } from '../transport/singleton.js';
|
|
6
6
|
import { resolveIdempotencyKey, sendTypedRequest } from '../transport/transport.js';
|
|
7
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
7
8
|
/**
|
|
8
9
|
* The consent-gated scope {@link UseBuzzWorkflow.submit} requires.
|
|
9
10
|
*
|
|
@@ -30,6 +31,22 @@ const WORKFLOW_SCOPES = [BLOCK_SCOPES.AI_WRITE_BUDGETED];
|
|
|
30
31
|
* on that class is money-ambiguous by design).
|
|
31
32
|
*/
|
|
32
33
|
export const TRAINING_BODY_SUBMIT_REFUSAL = "useBuzzWorkflow.submit() does not run a kind:'training' body — estimate it here, then run it with useRunTraining().runTraining({ ...body, quoteId }). Nothing was sent.";
|
|
34
|
+
/**
|
|
35
|
+
* Why {@link UseBuzzWorkflow.submit} refuses a pass-through `step` body that
|
|
36
|
+
* carries a `quoteId`.
|
|
37
|
+
*
|
|
38
|
+
* 🔴 REFUSED BEFORE THE SEND, for the same reason as
|
|
39
|
+
* {@link TRAINING_BODY_SUBMIT_REFUSAL}. A `quoteId` on a pass-through body means
|
|
40
|
+
* "charge this job against the price the viewer confirmed", and only
|
|
41
|
+
* `RUN_CONFIRMED_STEP`'s host-chrome dialog can record that confirmation — so a
|
|
42
|
+
* `SUBMIT_WORKFLOW` carrying one can never start the job; the server refuses it
|
|
43
|
+
* as unconfirmed. Nothing is sent and nothing is spent, which is why this too is
|
|
44
|
+
* a plain `Error` and not a {@link WorkflowSubmitError}.
|
|
45
|
+
*
|
|
46
|
+
* The same body WITHOUT `quoteId` is an ordinary pass-through submit and is not
|
|
47
|
+
* affected.
|
|
48
|
+
*/
|
|
49
|
+
export const CONFIRMED_STEP_BODY_SUBMIT_REFUSAL = "useBuzzWorkflow.submit() does not run a step body that carries a quoteId — run it with useRunConfirmedStep().runConfirmedStep(body), or drop quoteId to submit it as an ordinary job. Nothing was sent.";
|
|
33
50
|
/**
|
|
34
51
|
* Snapshot statuses that mean "no further polling is needed."
|
|
35
52
|
* Used by both `submit` (a host can return an instant-fail / cached result)
|
|
@@ -528,9 +545,11 @@ export class WorkflowSubmitError extends Error {
|
|
|
528
545
|
* orchestrator steps as one workflow through these same calls, and:
|
|
529
546
|
* - `estimate()`'s `cost.total` is NOT a maximum: the orchestrator re-prices a
|
|
530
547
|
* step once the values it references are known and charges the difference;
|
|
531
|
-
* - a workflow the host cannot price
|
|
532
|
-
*
|
|
533
|
-
*
|
|
548
|
+
* - a workflow the host cannot price FAILS `estimate()` with a
|
|
549
|
+
* `workflow_quote_*` `errorCode` and no `cost`, yet still proceeds on
|
|
550
|
+
* `submit()`, reserved at the app's per-generation budget × the number of
|
|
551
|
+
* steps (one with a training step, or with a `repeat` step at any depth, is
|
|
552
|
+
* refused on both); an older host returns that ceiling as the estimate;
|
|
534
553
|
* - one author fee applies to the whole workflow, not one per step, and it is
|
|
535
554
|
* reversed whole if the workflow does not succeed;
|
|
536
555
|
* - `repeat` iterates only an array in the body's `arguments`, at most 16
|
|
@@ -604,6 +623,7 @@ export class WorkflowSubmitError extends Error {
|
|
|
604
623
|
* }
|
|
605
624
|
*/
|
|
606
625
|
export function useBuzzWorkflow() {
|
|
626
|
+
useTransportConnection();
|
|
607
627
|
const [status, setStatus] = useState('idle');
|
|
608
628
|
const [result, setResult] = useState(null);
|
|
609
629
|
const [error, setError] = useState(null);
|
|
@@ -765,6 +785,15 @@ export function useBuzzWorkflow() {
|
|
|
765
785
|
setStatus('error');
|
|
766
786
|
throw refused;
|
|
767
787
|
}
|
|
788
|
+
// See `CONFIRMED_STEP_BODY_SUBMIT_REFUSAL`. Keyed on the PRESENCE of a
|
|
789
|
+
// `quoteId`, whatever its shape: a malformed one is no more submittable
|
|
790
|
+
// than a well-formed one, and the server would refuse both.
|
|
791
|
+
if (body.kind === 'step' && body.quoteId !== undefined) {
|
|
792
|
+
const refused = new Error(CONFIRMED_STEP_BODY_SUBMIT_REFUSAL);
|
|
793
|
+
setError(refused);
|
|
794
|
+
setStatus('error');
|
|
795
|
+
throw refused;
|
|
796
|
+
}
|
|
768
797
|
setError(null);
|
|
769
798
|
setStatus('submitting');
|
|
770
799
|
// Idempotency: reuse a caller-supplied stable key across a retry (→ one Buzz
|
|
@@ -54,6 +54,8 @@ export interface UseCheckpointPicker {
|
|
|
54
54
|
* host. Pass `null` to clear the override and fall back to the publisher
|
|
55
55
|
* default at next mount. Throws on host-side validation failure (e.g.
|
|
56
56
|
* "wrong-ecosystem") — surface the message to the user; don't retry blindly.
|
|
57
|
+
* MODEL SLOTS ONLY: on a page (`app.page`) the host refuses every `persist`,
|
|
58
|
+
* so it always throws there; a page app keeps the pick in its own state.
|
|
57
59
|
*
|
|
58
60
|
* Both flows are host-mediated: the block never touches the picker UI or
|
|
59
61
|
* the user-settings table directly. Same trust model as useBuzzPurchase /
|
|
@@ -3,6 +3,7 @@ import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
|
3
3
|
import { throwOnFailedReply } from '../internal/replyError.js';
|
|
4
4
|
import { getTransport } from '../transport/singleton.js';
|
|
5
5
|
import { sendTypedRequest } from '../transport/transport.js';
|
|
6
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
6
7
|
/**
|
|
7
8
|
* Drives the platform-side Checkpoint picker and the persist-override flow.
|
|
8
9
|
*
|
|
@@ -15,6 +16,8 @@ import { sendTypedRequest } from '../transport/transport.js';
|
|
|
15
16
|
* host. Pass `null` to clear the override and fall back to the publisher
|
|
16
17
|
* default at next mount. Throws on host-side validation failure (e.g.
|
|
17
18
|
* "wrong-ecosystem") — surface the message to the user; don't retry blindly.
|
|
19
|
+
* MODEL SLOTS ONLY: on a page (`app.page`) the host refuses every `persist`,
|
|
20
|
+
* so it always throws there; a page app keeps the pick in its own state.
|
|
18
21
|
*
|
|
19
22
|
* Both flows are host-mediated: the block never touches the picker UI or
|
|
20
23
|
* the user-settings table directly. Same trust model as useBuzzPurchase /
|
|
@@ -32,6 +35,7 @@ import { sendTypedRequest } from '../transport/transport.js';
|
|
|
32
35
|
* const { selected } = await open({ baseModelGroup: checkpoint.baseModel });
|
|
33
36
|
*/
|
|
34
37
|
export function useCheckpointPicker() {
|
|
38
|
+
useTransportConnection();
|
|
35
39
|
const open = useCallback(async (opts) => {
|
|
36
40
|
// Both keys are spread conditionally, and an empty or whitespace-only
|
|
37
41
|
// family is normalized to absent.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { getTransport } from '../transport/singleton.js';
|
|
3
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
3
4
|
/**
|
|
4
5
|
* Requests a navigation from the host: the hook sends a `NAVIGATE` message and
|
|
5
6
|
* returns. Fire-and-forget — the host doesn't reply, so the block never learns
|
|
@@ -38,6 +39,7 @@ import { getTransport } from '../transport/singleton.js';
|
|
|
38
39
|
* navigate('detail/7', 'new_tab'); // the pre-`scope` shape, still app-scoped
|
|
39
40
|
*/
|
|
40
41
|
export function useCivitaiNavigate() {
|
|
42
|
+
useTransportConnection();
|
|
41
43
|
const navigate = useCallback((path, options = {}) => {
|
|
42
44
|
// A bare string is the pre-`scope` call shape (`navigate(path, 'new_tab')`)
|
|
43
45
|
// and stays supported: there are live callers, and the whole point of the
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useTransportSnapshot } from './useBlockContext.js';
|
|
2
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
2
3
|
/**
|
|
3
4
|
* The sub-path below your app's root that is CURRENTLY showing, kept live for
|
|
4
5
|
* the whole life of the block ON THE IFRAME TRANSPORT.
|
|
@@ -56,6 +57,7 @@ import { useTransportSnapshot } from './useBlockContext.js';
|
|
|
56
57
|
* moves and nothing renders.
|
|
57
58
|
*/
|
|
58
59
|
export function useCivitaiRoute() {
|
|
60
|
+
useTransportConnection();
|
|
59
61
|
const context = useTransportSnapshot().context;
|
|
60
62
|
// A PRESENCE test, not a slot-id test, and not a cast. `BlockContext` is a
|
|
61
63
|
// union whose `UnknownSlotContext` arm declares `slotId` and nothing else, and
|
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
2
2
|
import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
3
3
|
import { getTransport } from '../transport/singleton.js';
|
|
4
4
|
import { RequestTimeoutError, sendTypedRequest } from '../transport/transport.js';
|
|
5
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
5
6
|
/**
|
|
6
7
|
* The closed set of HOST refusal codes, as a runtime Set.
|
|
7
8
|
*
|
|
@@ -134,6 +135,7 @@ export class CollectionFollowError extends Error {
|
|
|
134
135
|
* };
|
|
135
136
|
*/
|
|
136
137
|
export function useCollectionFollow() {
|
|
138
|
+
useTransportConnection();
|
|
137
139
|
const [pending, setPending] = useState(false);
|
|
138
140
|
const [error, setError] = useState(null);
|
|
139
141
|
// Same guard as `useTip`/`useWildcardPack`: this request can outlive the
|
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
|
|
2
2
|
import { armConsentRefusalLatch, clearConsentRefusalLatch, readConsentRefusalLatch, } from '../internal/consentRefusalLatch.js';
|
|
3
3
|
import { getTransport } from '../transport/singleton.js';
|
|
4
4
|
import { subscribeTyped } from '../transport/transport.js';
|
|
5
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
5
6
|
/**
|
|
6
7
|
* Subscribe to the host's `CONSENT_UNAVAILABLE` push — the signal that a
|
|
7
8
|
* `REQUEST_CONSENT` this block sent can **never** be granted in this
|
|
@@ -62,6 +63,7 @@ import { subscribeTyped } from '../transport/transport.js';
|
|
|
62
63
|
* return <button onClick={() => requestConsent({ scopes: ['ai:write:budgeted'] })}>Generate</button>;
|
|
63
64
|
*/
|
|
64
65
|
export function useConsentUnavailable() {
|
|
66
|
+
useTransportConnection();
|
|
65
67
|
const [refusal, setRefusal] = useState(null);
|
|
66
68
|
useEffect(() => {
|
|
67
69
|
const transport = getTransport();
|
|
@@ -4,6 +4,7 @@ import { withConsentRetry } from '../internal/withConsentRetry.js';
|
|
|
4
4
|
import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
5
5
|
import { getTransport } from '../transport/singleton.js';
|
|
6
6
|
import { RequestTimeoutError, sendTypedRequest } from '../transport/transport.js';
|
|
7
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
7
8
|
/**
|
|
8
9
|
* The consent-gated scope this bridge requires — `posts:write:self`, named in
|
|
9
10
|
* `@civitai/app-sdk`'s own `CREATE_POST_FROM_APP` message docs ("Scope
|
|
@@ -156,6 +157,7 @@ export class CreatePostError extends Error {
|
|
|
156
157
|
* };
|
|
157
158
|
*/
|
|
158
159
|
export function useCreatePostFromApp() {
|
|
160
|
+
useTransportConnection();
|
|
159
161
|
const [pending, setPending] = useState(false);
|
|
160
162
|
const [error, setError] = useState(null);
|
|
161
163
|
// Same guard as `useCollectionFollow`: this request can outlive the component
|
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
|
|
|
2
2
|
import { getTransport } from '../transport/singleton.js';
|
|
3
3
|
import { sendTypedRequest } from '../transport/transport.js';
|
|
4
4
|
import { useRequestSequencer } from './useRequestSequencer.js';
|
|
5
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
5
6
|
/**
|
|
6
7
|
* Read the signed-in viewer's per-modelVersion generation compensation for the
|
|
7
8
|
* MONTH containing `params.date`, through the host-mediated
|
|
@@ -19,6 +20,7 @@ import { useRequestSequencer } from './useRequestSequencer.js';
|
|
|
19
20
|
* const { resources, hasPublishedResources } = useDailyCompensation({ date: '2026-07-01' });
|
|
20
21
|
*/
|
|
21
22
|
export function useDailyCompensation(params) {
|
|
23
|
+
useTransportConnection();
|
|
22
24
|
const [resources, setResources] = useState(null);
|
|
23
25
|
const [hasPublishedResources, setHasPublishedResources] = useState(null);
|
|
24
26
|
const [loading, setLoading] = useState(true);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { DIRECT_LOAD_TIMEOUT_MS } from '../transport/directLoad.js';
|
|
3
3
|
import { useTransportSnapshot } from './useBlockContext.js';
|
|
4
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
4
5
|
/**
|
|
5
6
|
* True iff the current window is the TOP-LEVEL browsing context — i.e. the
|
|
6
7
|
* block is NOT running inside an iframe.
|
|
@@ -47,6 +48,7 @@ function isTopLevel() {
|
|
|
47
48
|
* `ready` is `true`, so a late init can't leave a stuck fallback.
|
|
48
49
|
*/
|
|
49
50
|
export function useDirectLoad(options) {
|
|
51
|
+
useTransportConnection();
|
|
50
52
|
const timeoutMs = options?.timeoutMs ?? DIRECT_LOAD_TIMEOUT_MS;
|
|
51
53
|
const ready = useTransportSnapshot().ready;
|
|
52
54
|
// Sampled once per mount — top-level-ness doesn't change during a page's life.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { effectiveBrowsingCeiling, isSfwCeiling, isLevelAllowed as isLevelAllowedCeiling, } from '@civitai/app-sdk/blocks';
|
|
2
2
|
import { useTransportSnapshot } from './useBlockContext.js';
|
|
3
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
3
4
|
/**
|
|
4
5
|
* Read the maturity ceiling in force for the current viewer, as the host
|
|
5
6
|
* projects it into `BLOCK_INIT`, so a block can hide/blur mature affordances.
|
|
@@ -40,6 +41,7 @@ import { useTransportSnapshot } from './useBlockContext.js';
|
|
|
40
41
|
* const hiddenByYourSettings = effectiveBrowsingLevel !== maxBrowsingLevel;
|
|
41
42
|
*/
|
|
42
43
|
export function useDomainMaturity() {
|
|
44
|
+
useTransportConnection();
|
|
43
45
|
const snap = useTransportSnapshot();
|
|
44
46
|
const maxBrowsingLevel = snap.maxBrowsingLevel;
|
|
45
47
|
// The one number every gate below is computed from. `effectiveBrowsingCeiling`
|
|
@@ -4,6 +4,7 @@ import { useBlockContext } from './useBlockContext.js';
|
|
|
4
4
|
import { useBlockToken } from './useBlockToken.js';
|
|
5
5
|
import { useHostOrigin } from './useHostOrigin.js';
|
|
6
6
|
import { useRequestSequencer } from './useRequestSequencer.js';
|
|
7
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
7
8
|
/**
|
|
8
9
|
* Backstop timeout for the direct REST entitlements GET (see {@link useTip} /
|
|
9
10
|
* {@link useGenerationResources} for why direct-fetch hooks need their own bound).
|
|
@@ -51,6 +52,7 @@ const HOST_ORIGIN_WAIT_MS = 30_000;
|
|
|
51
52
|
* return owns('extra-slots') ? <Unlocked /> : <BuyButton onDone={refetch} />;
|
|
52
53
|
*/
|
|
53
54
|
export function useEntitlements() {
|
|
55
|
+
useTransportConnection();
|
|
54
56
|
const host = useHostOrigin();
|
|
55
57
|
const { raw } = useBlockToken();
|
|
56
58
|
const { ready, viewer } = useBlockContext();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { getTransport } from '../transport/singleton.js';
|
|
3
3
|
import { sendTypedRequest } from '../transport/transport.js';
|
|
4
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
4
5
|
/**
|
|
5
6
|
* Read per-viewer gated display data for a list of image ids via the
|
|
6
7
|
* host-mediated `GET_IMAGES_BY_IDS` → `IMAGES_RESULT` bridge — the read side of
|
|
@@ -39,6 +40,7 @@ import { sendTypedRequest } from '../transport/transport.js';
|
|
|
39
40
|
* }
|
|
40
41
|
*/
|
|
41
42
|
export function useGatedImages() {
|
|
43
|
+
useTransportConnection();
|
|
42
44
|
const getImages = useCallback(async (imageIds) => {
|
|
43
45
|
const reply = await sendTypedRequest(getTransport(), { type: 'GET_IMAGES_BY_IDS', payload: { imageIds } }, 'IMAGES_RESULT');
|
|
44
46
|
if (reply.error || !reply.result) {
|
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef } from 'react';
|
|
|
2
2
|
import { buildGenerationResourcesUrl, responseToResources, } from '../api/generationResources.js';
|
|
3
3
|
import { useHostOrigin } from './useHostOrigin.js';
|
|
4
4
|
import { useBlockToken } from './useBlockToken.js';
|
|
5
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
5
6
|
/**
|
|
6
7
|
* Backstop timeout for the direct REST fetch. Unlike the postMessage hooks
|
|
7
8
|
* (which inherit the transport's 30s request timeout), this hook talks to the
|
|
@@ -32,6 +33,7 @@ const GENERATION_RESOURCES_TIMEOUT_MS = 30_000;
|
|
|
32
33
|
* // resources[0].strength / .minStrength / .maxStrength / .trainedWords / .clipSkip
|
|
33
34
|
*/
|
|
34
35
|
export function useGenerationResources() {
|
|
36
|
+
useTransportConnection();
|
|
35
37
|
const host = useHostOrigin();
|
|
36
38
|
const { raw } = useBlockToken();
|
|
37
39
|
// In-flight AbortControllers, aborted on unmount so a pending fetch never
|
|
@@ -6,6 +6,7 @@ import { resolveIdempotencyKey } from '../transport/transport.js';
|
|
|
6
6
|
import { useBlockToken } from './useBlockToken.js';
|
|
7
7
|
import { useBuzzPurchase } from './useBuzzPurchase.js';
|
|
8
8
|
import { useHostOrigin } from './useHostOrigin.js';
|
|
9
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
9
10
|
/**
|
|
10
11
|
* The consent-gated scope a purchase needs.
|
|
11
12
|
*
|
|
@@ -80,6 +81,7 @@ export class GoodPurchaseRefusal extends Error {
|
|
|
80
81
|
* );
|
|
81
82
|
*/
|
|
82
83
|
export function useGoodPurchase() {
|
|
84
|
+
useTransportConnection();
|
|
83
85
|
const host = useHostOrigin();
|
|
84
86
|
const { raw } = useBlockToken();
|
|
85
87
|
const { openPurchaseModal } = useBuzzPurchase();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useSyncExternalStore } from 'react';
|
|
2
2
|
import { getTransport } from '../transport/singleton.js';
|
|
3
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
3
4
|
/**
|
|
4
5
|
* Returns the validated host (parent) origin the block may safely direct-fetch
|
|
5
6
|
* the civitai App Blocks HTTP API against — `undefined` until the transport
|
|
@@ -33,6 +34,7 @@ import { getTransport } from '../transport/singleton.js';
|
|
|
33
34
|
* moment init lands.
|
|
34
35
|
*/
|
|
35
36
|
export function useHostOrigin() {
|
|
37
|
+
useTransportConnection();
|
|
36
38
|
const transport = getTransport();
|
|
37
39
|
const origin = useSyncExternalStore((cb) => transport.subscribe(cb), () => transport.getHostOrigin(),
|
|
38
40
|
// Server snapshot: no host origin is known before hydration.
|
|
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useRef } from 'react';
|
|
|
2
2
|
import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
3
3
|
import { getTransport } from '../transport/singleton.js';
|
|
4
4
|
import { sendTypedRequest, subscribeTyped } from '../transport/transport.js';
|
|
5
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
5
6
|
/**
|
|
6
7
|
* Generous hook-side backstop for {@link BlockImageScanResult} delivery. The
|
|
7
8
|
* host-side scan POLL budget is authoritative and the consuming app applies its
|
|
@@ -28,6 +29,7 @@ function isTerminalVerdict(v) {
|
|
|
28
29
|
return v.status === 'scanned' || v.status === 'blocked';
|
|
29
30
|
}
|
|
30
31
|
export function useImageUpload(options) {
|
|
32
|
+
useTransportConnection();
|
|
31
33
|
const purpose = options?.purpose;
|
|
32
34
|
const asyncScan = options?.asyncScan === true && purpose !== 'generationSource';
|
|
33
35
|
// Per-instance scan tracking, keyed by imageId. Persists across renders; a
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { fetchNestedDocument } from '@civitai/app-sdk/blocks';
|
|
3
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
3
4
|
const IDLE = { status: 'idle', srcDoc: null, error: null };
|
|
4
5
|
const LOADING = { status: 'loading', srcDoc: null, error: null };
|
|
5
6
|
/**
|
|
@@ -61,6 +62,7 @@ const NESTED_DOCUMENT_TIMEOUT_MS = 30_000;
|
|
|
61
62
|
* ```
|
|
62
63
|
*/
|
|
63
64
|
export function useNestedDocument(options) {
|
|
65
|
+
useTransportConnection();
|
|
64
66
|
const src = options?.src;
|
|
65
67
|
const baseUrl = options?.baseUrl;
|
|
66
68
|
const [state, setState] = useState(src ? LOADING : IDLE);
|
|
@@ -4,6 +4,7 @@ import { withConsentRetry } from '../internal/withConsentRetry.js';
|
|
|
4
4
|
import { WORKFLOW_REQUEST_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
5
5
|
import { getTransport } from '../transport/singleton.js';
|
|
6
6
|
import { RequestTimeoutError, sendTypedRequest } from '../transport/transport.js';
|
|
7
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
7
8
|
/**
|
|
8
9
|
* The consent-gated scope the training flow requires — `ai:write:budgeted`. The
|
|
9
10
|
* server refuses a dataset request from a token without it (it is the spend
|
|
@@ -101,6 +102,7 @@ export class PrepareTrainingDatasetError extends Error {
|
|
|
101
102
|
* }
|
|
102
103
|
*/
|
|
103
104
|
export function usePrepareTrainingDataset() {
|
|
105
|
+
useTransportConnection();
|
|
104
106
|
const [pending, setPending] = useState(false);
|
|
105
107
|
const [error, setError] = useState(null);
|
|
106
108
|
// The consent wait can outlive the component; see `useCreatePostFromApp`.
|
|
@@ -2,6 +2,7 @@ import { useCallback } from 'react';
|
|
|
2
2
|
import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
3
3
|
import { getTransport } from '../transport/singleton.js';
|
|
4
4
|
import { sendTypedRequest } from '../transport/transport.js';
|
|
5
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
5
6
|
/**
|
|
6
7
|
* Publish selected outputs of one of the calling app's OWN generations into bare,
|
|
7
8
|
* real-scanned public `Image` rows via the host-mediated
|
|
@@ -28,6 +29,7 @@ import { sendTypedRequest } from '../transport/transport.js';
|
|
|
28
29
|
* // …store imageIds via useSharedStorage() so the grid can read them back gated.
|
|
29
30
|
*/
|
|
30
31
|
export function usePublishGenerationOutputs() {
|
|
32
|
+
useTransportConnection();
|
|
31
33
|
const publish = useCallback(async (args) => {
|
|
32
34
|
const reply = await sendTypedRequest(getTransport(), {
|
|
33
35
|
type: 'PUBLISH_GENERATION_OUTPUTS',
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { sendRequestConsent } from '../internal/withConsentRetry.js';
|
|
3
3
|
import { getTransport } from '../transport/singleton.js';
|
|
4
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
4
5
|
/**
|
|
5
6
|
* Lazy consent. Asks the host to open civitai.com's consent UI when a
|
|
6
7
|
* LOGGED-IN viewer clicks an action whose consent-gated scope the block token
|
|
@@ -43,6 +44,7 @@ import { getTransport } from '../transport/singleton.js';
|
|
|
43
44
|
* requestConsent({ scopes: ['ai:write:budgeted', 'buzz:read:self'] });
|
|
44
45
|
*/
|
|
45
46
|
export function useRequestConsent() {
|
|
47
|
+
useTransportConnection();
|
|
46
48
|
const requestConsent = useCallback((payload) => {
|
|
47
49
|
// Single-sourced with the SDK's own automatic prompt-and-retry
|
|
48
50
|
// (`internal/withConsentRetry.ts`), which posts the identical message. The
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { getTransport } from '../transport/singleton.js';
|
|
3
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
3
4
|
/**
|
|
4
5
|
* Anonymous conversion. Asks the host to start civitai.com's login flow when a
|
|
5
6
|
* logged-out viewer (`useBlockContext().viewer === null`) clicks an action that
|
|
@@ -19,6 +20,7 @@ import { getTransport } from '../transport/singleton.js';
|
|
|
19
20
|
* if (viewer === null) return <button onClick={() => requestSignIn()}>Sign in</button>;
|
|
20
21
|
*/
|
|
21
22
|
export function useRequestSignIn() {
|
|
23
|
+
useTransportConnection();
|
|
22
24
|
const requestSignIn = useCallback((payload) => {
|
|
23
25
|
getTransport().sendMessage({
|
|
24
26
|
type: 'REQUEST_SIGN_IN',
|
|
@@ -3,6 +3,7 @@ import { throwOnReplyError } from '../internal/replyError.js';
|
|
|
3
3
|
import { HUMAN_INTERACTION_TIMEOUT_MS } from '../transport/requestTimeouts.js';
|
|
4
4
|
import { getTransport } from '../transport/singleton.js';
|
|
5
5
|
import { sendTypedRequest } from '../transport/transport.js';
|
|
6
|
+
import { useTransportConnection } from './useTransportConnection.js';
|
|
6
7
|
/**
|
|
7
8
|
* The most resources ONE multi-select `open({ multiple })` can return — the
|
|
8
9
|
* same cap the generation body puts on `additionalResources`, so a full batch
|
|
@@ -86,6 +87,7 @@ const MULTIPLE_RESOURCE_TYPES = new Set(['lora', 'locon', 'dora']);
|
|
|
86
87
|
* }
|
|
87
88
|
*/
|
|
88
89
|
export function useResourcePicker() {
|
|
90
|
+
useTransportConnection();
|
|
89
91
|
const open = useCallback(async (opts) => {
|
|
90
92
|
if (opts.multiple == null) {
|
|
91
93
|
const { selected } = await sendTypedRequest(getTransport(), {
|