@verbumia/feedback 0.2.5 → 0.2.7
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/dist/{chunk-5DZUKJ4M.js → chunk-7KWEI55W.js} +78 -3
- package/dist/chunk-7KWEI55W.js.map +1 -0
- package/dist/{client-CYgwLkiA.d.cts → client-D83qhH0O.d.cts} +85 -1
- package/dist/{client-CYgwLkiA.d.ts → client-D83qhH0O.d.ts} +85 -1
- package/dist/core/index.cjs +77 -2
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +1 -1
- package/dist/{keys-BPAFf8xR.d.ts → keys-BpVB1kcI.d.ts} +1 -1
- package/dist/{keys-BQpczt84.d.cts → keys-D4oJtn64.d.cts} +1 -1
- package/dist/native/index.cjs +184 -23
- package/dist/native/index.cjs.map +1 -1
- package/dist/native/index.d.cts +56 -4
- package/dist/native/index.d.ts +56 -4
- package/dist/native/index.js +108 -22
- package/dist/native/index.js.map +1 -1
- package/dist/react/index.cjs +184 -23
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +135 -5
- package/dist/react/index.d.ts +135 -5
- package/dist/react/index.js +108 -22
- package/dist/react/index.js.map +1 -1
- package/dist/svelte/index.cjs +77 -2
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.d.cts +2 -2
- package/dist/svelte/index.d.ts +2 -2
- package/dist/svelte/index.js +1 -1
- package/dist/vue/index.cjs +77 -2
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.d.cts +2 -2
- package/dist/vue/index.d.ts +2 -2
- package/dist/vue/index.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-5DZUKJ4M.js.map +0 -1
package/dist/react/index.d.cts
CHANGED
|
@@ -1,17 +1,43 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { F as FeedbackClient, D as DeclaredKey } from '../client-
|
|
3
|
-
export { B as BatchResponse, a as FeedbackConfig, b as FeedbackError, c as FeedbackString, R as RatingInput, S as StringsResponse, d as SuggestionInput, T as TokenBundle } from '../client-
|
|
2
|
+
import { F as FeedbackClient, D as DeclaredKey } from '../client-D83qhH0O.cjs';
|
|
3
|
+
export { B as BatchResponse, a as FeedbackConfig, b as FeedbackError, c as FeedbackString, R as RatingInput, S as StringsResponse, d as SuggestionInput, T as TokenBundle } from '../client-D83qhH0O.cjs';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
|
-
export { h as hasKeyRegistry, r as resolveKeys } from '../keys-
|
|
5
|
+
export { h as hasKeyRegistry, r as resolveKeys } from '../keys-D4oJtn64.cjs';
|
|
6
6
|
|
|
7
7
|
/** Structural mirror of `@verbumia/react-i18next`'s VerbumiaPlugin —
|
|
8
|
-
* kept local so feedback has no build-time dep on the i18n SDK.
|
|
8
|
+
* kept local so feedback has no build-time dep on the i18n SDK. The
|
|
9
|
+
* `i18n` and `onLanguageChange` fields are optional so a plugin attached
|
|
10
|
+
* to a non-Verbumia host (or to a pre-1.0.5 react-i18next) keeps
|
|
11
|
+
* working — runtime language re-sync is just disabled. From feedback
|
|
12
|
+
* ≥0.2.6, the peerDep on `@verbumia/react-i18next` is `>=1.0.5`, so the
|
|
13
|
+
* lang-change subscription path is guaranteed available in matched
|
|
14
|
+
* installs. */
|
|
9
15
|
interface I18nPluginContext {
|
|
16
|
+
i18n?: {
|
|
17
|
+
language?: string;
|
|
18
|
+
/** 0.2.7 — direct handle on the underlying `i18next` instance the
|
|
19
|
+
* `@verbumia/react-i18next` provider exposes. Used by the
|
|
20
|
+
* `scope: "current-view"` snapshot path: a same-locale
|
|
21
|
+
* `changeLanguage(language)` re-emits `languageChanged` so every
|
|
22
|
+
* bound consumer re-renders → `t()` calls repopulate the registry.
|
|
23
|
+
* Optional so the plugin still works on non-Verbumia hosts (it
|
|
24
|
+
* falls back to a no-rerender registry snapshot in that case). */
|
|
25
|
+
i18next?: {
|
|
26
|
+
language?: string;
|
|
27
|
+
changeLanguage?: (lng: string) => Promise<unknown>;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
10
30
|
config: {
|
|
11
31
|
apiBase?: string;
|
|
12
32
|
projectUuid: string;
|
|
13
33
|
defaultLocale: string;
|
|
14
34
|
};
|
|
35
|
+
/** #806 — subscribe to runtime language changes (see VerbumiaPluginContext
|
|
36
|
+
* in `@verbumia/react-i18next` ≥1.0.5). Returns an unsubscribe fn the
|
|
37
|
+
* plugin MUST call from teardown. Optional so attaching to a
|
|
38
|
+
* pre-1.0.5 react-i18next falls back gracefully (no auto re-sync;
|
|
39
|
+
* consumer can still pass `options.language` explicitly). */
|
|
40
|
+
onLanguageChange?: (cb: (lng: string) => void) => () => void;
|
|
15
41
|
}
|
|
16
42
|
interface I18nPlugin {
|
|
17
43
|
name: string;
|
|
@@ -19,10 +45,53 @@ interface I18nPlugin {
|
|
|
19
45
|
render?: () => ReactNode;
|
|
20
46
|
}
|
|
21
47
|
interface FeedbackController {
|
|
22
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Opens the panel. From 0.2.7 the call is **async** (returns
|
|
50
|
+
* `Promise<void>`) so the `scope: "current-view"` snapshot sequence
|
|
51
|
+
* can run before the modal mounts (reset registry → force
|
|
52
|
+
* languageChanged → 1-frame yield → snapshot). Hosts that do NOT
|
|
53
|
+
* await still work — the modal mounts when the promise resolves.
|
|
54
|
+
* Under `scope: "all"` (or when the i18next instance isn't reachable)
|
|
55
|
+
* `open()` resolves on the next microtask with no extra work.
|
|
56
|
+
*/
|
|
57
|
+
open: () => Promise<void>;
|
|
23
58
|
close: () => void;
|
|
24
59
|
/** The underlying client (advanced; usually unused). */
|
|
25
60
|
client: FeedbackClient;
|
|
61
|
+
/** ToS version currently required by the backend (SDK build-time
|
|
62
|
+
* constant — see core/tos.ts). 0.2.7+. */
|
|
63
|
+
readonly tosVersion: string;
|
|
64
|
+
/** Whether this end-user has a live session-token bundle (i.e. has
|
|
65
|
+
* accepted the current ToS version). Mirrors the same persisted state
|
|
66
|
+
* the built-in modal writes to, so an external ToS page that calls
|
|
67
|
+
* `acceptTos()` flips this to `true` immediately. 0.2.7+. */
|
|
68
|
+
readonly hasAcceptedTos: boolean;
|
|
69
|
+
/** Programmatically POST `/v1/feedback/tos` and persist the returned
|
|
70
|
+
* token bundle into the SDK's shared store. Used by hosts that
|
|
71
|
+
* build their own ToS page (`feedbackPlugin({ tos: "skip" })`). Thin
|
|
72
|
+
* alias over `FeedbackClient.acceptTos()` — idempotent (a second
|
|
73
|
+
* call returns the in-flight / existing bundle without re-POSTing).
|
|
74
|
+
* 0.2.7+. */
|
|
75
|
+
acceptTos: () => Promise<void>;
|
|
76
|
+
/** 0.2.7 — feedback-addon active flag for this project, sourced from
|
|
77
|
+
* `GET /v1/projects/{p}/feedback-addon/state` + composed with the
|
|
78
|
+
* plugin's `cta` option:
|
|
79
|
+
* - `cta: "auto"` (default): mirrors `state.isActive` (so a host
|
|
80
|
+
* that hides its CTA on `!isActive` does the right thing for
|
|
81
|
+
* Starter / no SKU).
|
|
82
|
+
* - `cta: "show"`: always `true` (force the host's CTA).
|
|
83
|
+
* - `cta: "hide"`: always `false`.
|
|
84
|
+
* `null` before the first state fetch resolves (e.g. on a host that
|
|
85
|
+
* didn't pass `apiKey` and hasn't accepted ToS yet). */
|
|
86
|
+
readonly isActive: boolean | null;
|
|
87
|
+
/** 0.2.7 — the languages the SDK is licensed to rate strings in.
|
|
88
|
+
* Mirrors `state.enabledLanguages` verbatim: `string[]` for Starter
|
|
89
|
+
* / no SKU, literal `"all"` for Unlimited. `null` before the first
|
|
90
|
+
* state fetch resolves. */
|
|
91
|
+
readonly enabledLanguages: string[] | "all" | null;
|
|
92
|
+
/** 0.2.7 — raw SKU code (`feedback_starter` / `feedback_unlimited`
|
|
93
|
+
* / `null`). Useful for downstream UI / billing dashboards. */
|
|
94
|
+
readonly sku: "feedback_starter" | "feedback_unlimited" | null;
|
|
26
95
|
}
|
|
27
96
|
interface FeedbackPluginOptions {
|
|
28
97
|
/** Override language; defaults to the i18n provider's defaultLocale. */
|
|
@@ -48,6 +117,62 @@ interface FeedbackPluginOptions {
|
|
|
48
117
|
};
|
|
49
118
|
/** Injected fetch (tests / RN polyfills). */
|
|
50
119
|
fetchImpl?: typeof fetch;
|
|
120
|
+
/**
|
|
121
|
+
* 0.2.7 — how the SDK handles the Terms of Service prompt:
|
|
122
|
+
* - `"modal"` (DEFAULT, backwards-compatible): the built-in modal
|
|
123
|
+
* renders the SDK's ToS step on first open; tapping Accept calls
|
|
124
|
+
* `POST /v1/feedback/tos` and persists the token bundle.
|
|
125
|
+
* - `"skip"`: the built-in modal SKIPS the ToS step entirely. The
|
|
126
|
+
* host promises to handle consent externally (e.g. their own
|
|
127
|
+
* branded ToS page) and must call `controller.acceptTos()` to
|
|
128
|
+
* mint the session bundle. Until that runs, every `getStrings()`
|
|
129
|
+
* surfaces the existing 401 error state (the SDK does NOT
|
|
130
|
+
* silently auto-accept on the user's behalf).
|
|
131
|
+
*/
|
|
132
|
+
tos?: "modal" | "skip";
|
|
133
|
+
/**
|
|
134
|
+
* 0.2.7 — host's project API key (`vrb_live_…`, scope `project:read`).
|
|
135
|
+
* Lets the SDK call `GET /v1/projects/{p}/feedback-addon/state` at
|
|
136
|
+
* `setup()` time + on language change, BEFORE the end-user accepts
|
|
137
|
+
* ToS. When omitted, addon-state is deferred until the user's
|
|
138
|
+
* session bearer is minted via `acceptTos()`, and `controller.isActive`
|
|
139
|
+
* stays `null` until then.
|
|
140
|
+
*/
|
|
141
|
+
apiKey?: string;
|
|
142
|
+
/**
|
|
143
|
+
* 0.2.7 — controls `controller.isActive` (which hosts wire to their
|
|
144
|
+
* own CTA's `hidden` flag — the SDK does not render a floating CTA
|
|
145
|
+
* itself; see plugin.tsx header).
|
|
146
|
+
* - `"auto"` (DEFAULT): mirrors `state.isActive` from the backend,
|
|
147
|
+
* i.e. hides on Starter / no SKU, shows on Unlimited.
|
|
148
|
+
* - `"show"`: forces `isActive=true` regardless of state.
|
|
149
|
+
* - `"hide"`: forces `isActive=false` regardless of state.
|
|
150
|
+
*/
|
|
151
|
+
cta?: "auto" | "show" | "hide";
|
|
152
|
+
/**
|
|
153
|
+
* 0.2.7 — what the panel calls "on-screen keys":
|
|
154
|
+
* - `"current-view"` (DEFAULT — rebalances the 1.0.3 "strict-better-
|
|
155
|
+
* than-false-empty" trade-off now that we have a better mechanism):
|
|
156
|
+
* `controller.open()` (now async) resets the on-screen key
|
|
157
|
+
* registry, force-emits a same-locale `languageChanged` through
|
|
158
|
+
* i18next to re-render every consumer, awaits a 1-frame yield so
|
|
159
|
+
* their `t()` calls repopulate the registry, snapshots, then opens
|
|
160
|
+
* the modal with that snapshot. Result: the widget lists ONLY keys
|
|
161
|
+
* rendered on the user's current screen at the instant they
|
|
162
|
+
* opened it.
|
|
163
|
+
* - `"all"`: pre-0.2.7 behavior — no reset, no force re-render. The
|
|
164
|
+
* panel reads the accumulated registry as-is (still strictly
|
|
165
|
+
* better than a false-empty; useful if you want the cumulative
|
|
166
|
+
* keys-since-mount view).
|
|
167
|
+
*
|
|
168
|
+
* KNOWN LIMITATION (`"current-view"`): components that call
|
|
169
|
+
* `i18next.t()` OUTSIDE a React tree (cron-like calls, module-load
|
|
170
|
+
* `t()`) are NOT re-collected by the changeLanguage trigger — they
|
|
171
|
+
* have nothing to re-render. Acceptable: those calls aren't strictly
|
|
172
|
+
* "rendered on screen", so excluding them matches the on-screen
|
|
173
|
+
* contract.
|
|
174
|
+
*/
|
|
175
|
+
scope?: "current-view" | "all";
|
|
51
176
|
}
|
|
52
177
|
declare function feedbackPlugin(options: FeedbackPluginOptions): I18nPlugin;
|
|
53
178
|
|
|
@@ -55,6 +180,11 @@ declare function FeedbackPanel(props: {
|
|
|
55
180
|
client: FeedbackClient;
|
|
56
181
|
keys?: DeclaredKey[];
|
|
57
182
|
namespace?: string | string[];
|
|
183
|
+
/** 0.2.7 — `"skip"` removes the built-in ToS step entirely; the host
|
|
184
|
+
* owns consent (via `controller.acceptTos()`). When the user has not
|
|
185
|
+
* yet accepted, the strings fetch surfaces a 401-derived error in the
|
|
186
|
+
* existing error row. */
|
|
187
|
+
tos?: "modal" | "skip";
|
|
58
188
|
onClose: () => void;
|
|
59
189
|
}): react_jsx_runtime.JSX.Element;
|
|
60
190
|
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,17 +1,43 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { F as FeedbackClient, D as DeclaredKey } from '../client-
|
|
3
|
-
export { B as BatchResponse, a as FeedbackConfig, b as FeedbackError, c as FeedbackString, R as RatingInput, S as StringsResponse, d as SuggestionInput, T as TokenBundle } from '../client-
|
|
2
|
+
import { F as FeedbackClient, D as DeclaredKey } from '../client-D83qhH0O.js';
|
|
3
|
+
export { B as BatchResponse, a as FeedbackConfig, b as FeedbackError, c as FeedbackString, R as RatingInput, S as StringsResponse, d as SuggestionInput, T as TokenBundle } from '../client-D83qhH0O.js';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
|
-
export { h as hasKeyRegistry, r as resolveKeys } from '../keys-
|
|
5
|
+
export { h as hasKeyRegistry, r as resolveKeys } from '../keys-BpVB1kcI.js';
|
|
6
6
|
|
|
7
7
|
/** Structural mirror of `@verbumia/react-i18next`'s VerbumiaPlugin —
|
|
8
|
-
* kept local so feedback has no build-time dep on the i18n SDK.
|
|
8
|
+
* kept local so feedback has no build-time dep on the i18n SDK. The
|
|
9
|
+
* `i18n` and `onLanguageChange` fields are optional so a plugin attached
|
|
10
|
+
* to a non-Verbumia host (or to a pre-1.0.5 react-i18next) keeps
|
|
11
|
+
* working — runtime language re-sync is just disabled. From feedback
|
|
12
|
+
* ≥0.2.6, the peerDep on `@verbumia/react-i18next` is `>=1.0.5`, so the
|
|
13
|
+
* lang-change subscription path is guaranteed available in matched
|
|
14
|
+
* installs. */
|
|
9
15
|
interface I18nPluginContext {
|
|
16
|
+
i18n?: {
|
|
17
|
+
language?: string;
|
|
18
|
+
/** 0.2.7 — direct handle on the underlying `i18next` instance the
|
|
19
|
+
* `@verbumia/react-i18next` provider exposes. Used by the
|
|
20
|
+
* `scope: "current-view"` snapshot path: a same-locale
|
|
21
|
+
* `changeLanguage(language)` re-emits `languageChanged` so every
|
|
22
|
+
* bound consumer re-renders → `t()` calls repopulate the registry.
|
|
23
|
+
* Optional so the plugin still works on non-Verbumia hosts (it
|
|
24
|
+
* falls back to a no-rerender registry snapshot in that case). */
|
|
25
|
+
i18next?: {
|
|
26
|
+
language?: string;
|
|
27
|
+
changeLanguage?: (lng: string) => Promise<unknown>;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
10
30
|
config: {
|
|
11
31
|
apiBase?: string;
|
|
12
32
|
projectUuid: string;
|
|
13
33
|
defaultLocale: string;
|
|
14
34
|
};
|
|
35
|
+
/** #806 — subscribe to runtime language changes (see VerbumiaPluginContext
|
|
36
|
+
* in `@verbumia/react-i18next` ≥1.0.5). Returns an unsubscribe fn the
|
|
37
|
+
* plugin MUST call from teardown. Optional so attaching to a
|
|
38
|
+
* pre-1.0.5 react-i18next falls back gracefully (no auto re-sync;
|
|
39
|
+
* consumer can still pass `options.language` explicitly). */
|
|
40
|
+
onLanguageChange?: (cb: (lng: string) => void) => () => void;
|
|
15
41
|
}
|
|
16
42
|
interface I18nPlugin {
|
|
17
43
|
name: string;
|
|
@@ -19,10 +45,53 @@ interface I18nPlugin {
|
|
|
19
45
|
render?: () => ReactNode;
|
|
20
46
|
}
|
|
21
47
|
interface FeedbackController {
|
|
22
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Opens the panel. From 0.2.7 the call is **async** (returns
|
|
50
|
+
* `Promise<void>`) so the `scope: "current-view"` snapshot sequence
|
|
51
|
+
* can run before the modal mounts (reset registry → force
|
|
52
|
+
* languageChanged → 1-frame yield → snapshot). Hosts that do NOT
|
|
53
|
+
* await still work — the modal mounts when the promise resolves.
|
|
54
|
+
* Under `scope: "all"` (or when the i18next instance isn't reachable)
|
|
55
|
+
* `open()` resolves on the next microtask with no extra work.
|
|
56
|
+
*/
|
|
57
|
+
open: () => Promise<void>;
|
|
23
58
|
close: () => void;
|
|
24
59
|
/** The underlying client (advanced; usually unused). */
|
|
25
60
|
client: FeedbackClient;
|
|
61
|
+
/** ToS version currently required by the backend (SDK build-time
|
|
62
|
+
* constant — see core/tos.ts). 0.2.7+. */
|
|
63
|
+
readonly tosVersion: string;
|
|
64
|
+
/** Whether this end-user has a live session-token bundle (i.e. has
|
|
65
|
+
* accepted the current ToS version). Mirrors the same persisted state
|
|
66
|
+
* the built-in modal writes to, so an external ToS page that calls
|
|
67
|
+
* `acceptTos()` flips this to `true` immediately. 0.2.7+. */
|
|
68
|
+
readonly hasAcceptedTos: boolean;
|
|
69
|
+
/** Programmatically POST `/v1/feedback/tos` and persist the returned
|
|
70
|
+
* token bundle into the SDK's shared store. Used by hosts that
|
|
71
|
+
* build their own ToS page (`feedbackPlugin({ tos: "skip" })`). Thin
|
|
72
|
+
* alias over `FeedbackClient.acceptTos()` — idempotent (a second
|
|
73
|
+
* call returns the in-flight / existing bundle without re-POSTing).
|
|
74
|
+
* 0.2.7+. */
|
|
75
|
+
acceptTos: () => Promise<void>;
|
|
76
|
+
/** 0.2.7 — feedback-addon active flag for this project, sourced from
|
|
77
|
+
* `GET /v1/projects/{p}/feedback-addon/state` + composed with the
|
|
78
|
+
* plugin's `cta` option:
|
|
79
|
+
* - `cta: "auto"` (default): mirrors `state.isActive` (so a host
|
|
80
|
+
* that hides its CTA on `!isActive` does the right thing for
|
|
81
|
+
* Starter / no SKU).
|
|
82
|
+
* - `cta: "show"`: always `true` (force the host's CTA).
|
|
83
|
+
* - `cta: "hide"`: always `false`.
|
|
84
|
+
* `null` before the first state fetch resolves (e.g. on a host that
|
|
85
|
+
* didn't pass `apiKey` and hasn't accepted ToS yet). */
|
|
86
|
+
readonly isActive: boolean | null;
|
|
87
|
+
/** 0.2.7 — the languages the SDK is licensed to rate strings in.
|
|
88
|
+
* Mirrors `state.enabledLanguages` verbatim: `string[]` for Starter
|
|
89
|
+
* / no SKU, literal `"all"` for Unlimited. `null` before the first
|
|
90
|
+
* state fetch resolves. */
|
|
91
|
+
readonly enabledLanguages: string[] | "all" | null;
|
|
92
|
+
/** 0.2.7 — raw SKU code (`feedback_starter` / `feedback_unlimited`
|
|
93
|
+
* / `null`). Useful for downstream UI / billing dashboards. */
|
|
94
|
+
readonly sku: "feedback_starter" | "feedback_unlimited" | null;
|
|
26
95
|
}
|
|
27
96
|
interface FeedbackPluginOptions {
|
|
28
97
|
/** Override language; defaults to the i18n provider's defaultLocale. */
|
|
@@ -48,6 +117,62 @@ interface FeedbackPluginOptions {
|
|
|
48
117
|
};
|
|
49
118
|
/** Injected fetch (tests / RN polyfills). */
|
|
50
119
|
fetchImpl?: typeof fetch;
|
|
120
|
+
/**
|
|
121
|
+
* 0.2.7 — how the SDK handles the Terms of Service prompt:
|
|
122
|
+
* - `"modal"` (DEFAULT, backwards-compatible): the built-in modal
|
|
123
|
+
* renders the SDK's ToS step on first open; tapping Accept calls
|
|
124
|
+
* `POST /v1/feedback/tos` and persists the token bundle.
|
|
125
|
+
* - `"skip"`: the built-in modal SKIPS the ToS step entirely. The
|
|
126
|
+
* host promises to handle consent externally (e.g. their own
|
|
127
|
+
* branded ToS page) and must call `controller.acceptTos()` to
|
|
128
|
+
* mint the session bundle. Until that runs, every `getStrings()`
|
|
129
|
+
* surfaces the existing 401 error state (the SDK does NOT
|
|
130
|
+
* silently auto-accept on the user's behalf).
|
|
131
|
+
*/
|
|
132
|
+
tos?: "modal" | "skip";
|
|
133
|
+
/**
|
|
134
|
+
* 0.2.7 — host's project API key (`vrb_live_…`, scope `project:read`).
|
|
135
|
+
* Lets the SDK call `GET /v1/projects/{p}/feedback-addon/state` at
|
|
136
|
+
* `setup()` time + on language change, BEFORE the end-user accepts
|
|
137
|
+
* ToS. When omitted, addon-state is deferred until the user's
|
|
138
|
+
* session bearer is minted via `acceptTos()`, and `controller.isActive`
|
|
139
|
+
* stays `null` until then.
|
|
140
|
+
*/
|
|
141
|
+
apiKey?: string;
|
|
142
|
+
/**
|
|
143
|
+
* 0.2.7 — controls `controller.isActive` (which hosts wire to their
|
|
144
|
+
* own CTA's `hidden` flag — the SDK does not render a floating CTA
|
|
145
|
+
* itself; see plugin.tsx header).
|
|
146
|
+
* - `"auto"` (DEFAULT): mirrors `state.isActive` from the backend,
|
|
147
|
+
* i.e. hides on Starter / no SKU, shows on Unlimited.
|
|
148
|
+
* - `"show"`: forces `isActive=true` regardless of state.
|
|
149
|
+
* - `"hide"`: forces `isActive=false` regardless of state.
|
|
150
|
+
*/
|
|
151
|
+
cta?: "auto" | "show" | "hide";
|
|
152
|
+
/**
|
|
153
|
+
* 0.2.7 — what the panel calls "on-screen keys":
|
|
154
|
+
* - `"current-view"` (DEFAULT — rebalances the 1.0.3 "strict-better-
|
|
155
|
+
* than-false-empty" trade-off now that we have a better mechanism):
|
|
156
|
+
* `controller.open()` (now async) resets the on-screen key
|
|
157
|
+
* registry, force-emits a same-locale `languageChanged` through
|
|
158
|
+
* i18next to re-render every consumer, awaits a 1-frame yield so
|
|
159
|
+
* their `t()` calls repopulate the registry, snapshots, then opens
|
|
160
|
+
* the modal with that snapshot. Result: the widget lists ONLY keys
|
|
161
|
+
* rendered on the user's current screen at the instant they
|
|
162
|
+
* opened it.
|
|
163
|
+
* - `"all"`: pre-0.2.7 behavior — no reset, no force re-render. The
|
|
164
|
+
* panel reads the accumulated registry as-is (still strictly
|
|
165
|
+
* better than a false-empty; useful if you want the cumulative
|
|
166
|
+
* keys-since-mount view).
|
|
167
|
+
*
|
|
168
|
+
* KNOWN LIMITATION (`"current-view"`): components that call
|
|
169
|
+
* `i18next.t()` OUTSIDE a React tree (cron-like calls, module-load
|
|
170
|
+
* `t()`) are NOT re-collected by the changeLanguage trigger — they
|
|
171
|
+
* have nothing to re-render. Acceptable: those calls aren't strictly
|
|
172
|
+
* "rendered on screen", so excluding them matches the on-screen
|
|
173
|
+
* contract.
|
|
174
|
+
*/
|
|
175
|
+
scope?: "current-view" | "all";
|
|
51
176
|
}
|
|
52
177
|
declare function feedbackPlugin(options: FeedbackPluginOptions): I18nPlugin;
|
|
53
178
|
|
|
@@ -55,6 +180,11 @@ declare function FeedbackPanel(props: {
|
|
|
55
180
|
client: FeedbackClient;
|
|
56
181
|
keys?: DeclaredKey[];
|
|
57
182
|
namespace?: string | string[];
|
|
183
|
+
/** 0.2.7 — `"skip"` removes the built-in ToS step entirely; the host
|
|
184
|
+
* owns consent (via `controller.acceptTos()`). When the user has not
|
|
185
|
+
* yet accepted, the strings fetch surfaces a 401-derived error in the
|
|
186
|
+
* existing error row. */
|
|
187
|
+
tos?: "modal" | "skip";
|
|
58
188
|
onClose: () => void;
|
|
59
189
|
}): react_jsx_runtime.JSX.Element;
|
|
60
190
|
|
package/dist/react/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
FeedbackError,
|
|
5
5
|
hasKeyRegistry,
|
|
6
6
|
resolveKeys
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-7KWEI55W.js";
|
|
8
8
|
|
|
9
9
|
// src/react/plugin.tsx
|
|
10
10
|
import { createPortal } from "react-dom";
|
|
@@ -23,8 +23,10 @@ var C = {
|
|
|
23
23
|
emeraldSoft: "#34d399"
|
|
24
24
|
};
|
|
25
25
|
function FeedbackPanel(props) {
|
|
26
|
-
const { client, keys, namespace, onClose } = props;
|
|
27
|
-
const [consented, setConsented] = useState(
|
|
26
|
+
const { client, keys, namespace, tos = "modal", onClose } = props;
|
|
27
|
+
const [consented, setConsented] = useState(
|
|
28
|
+
tos === "skip" ? true : client.hasConsented
|
|
29
|
+
);
|
|
28
30
|
const [busy, setBusy] = useState(false);
|
|
29
31
|
const [error, setError] = useState(null);
|
|
30
32
|
const [strings, setStrings] = useState([]);
|
|
@@ -310,42 +312,73 @@ function StringRow(props) {
|
|
|
310
312
|
// src/react/plugin.tsx
|
|
311
313
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
312
314
|
function makeStore() {
|
|
313
|
-
let
|
|
315
|
+
let state = { isOpen: false, snapshotKeys: void 0 };
|
|
314
316
|
const listeners = /* @__PURE__ */ new Set();
|
|
315
317
|
return {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
open
|
|
320
|
-
|
|
318
|
+
getState: () => state,
|
|
319
|
+
setOpen(open, snapshotKeys) {
|
|
320
|
+
const next = {
|
|
321
|
+
isOpen: open,
|
|
322
|
+
snapshotKeys: open ? snapshotKeys : void 0
|
|
323
|
+
};
|
|
324
|
+
if (state.isOpen === next.isOpen && state.snapshotKeys === next.snapshotKeys) {
|
|
325
|
+
return;
|
|
321
326
|
}
|
|
327
|
+
state = next;
|
|
328
|
+
listeners.forEach((l) => l());
|
|
322
329
|
},
|
|
323
330
|
subscribe(l) {
|
|
324
331
|
listeners.add(l);
|
|
325
|
-
return () =>
|
|
332
|
+
return () => {
|
|
333
|
+
listeners.delete(l);
|
|
334
|
+
};
|
|
326
335
|
}
|
|
327
336
|
};
|
|
328
337
|
}
|
|
338
|
+
async function captureCurrentViewSnapshot(i18next) {
|
|
339
|
+
const reg = globalThis.__verbumia_key_registry__;
|
|
340
|
+
if (!reg) return [];
|
|
341
|
+
const lng = i18next?.language;
|
|
342
|
+
const change = i18next?.changeLanguage;
|
|
343
|
+
if (typeof change === "function" && typeof lng === "string" && lng) {
|
|
344
|
+
reg.reset?.();
|
|
345
|
+
try {
|
|
346
|
+
await change(lng);
|
|
347
|
+
} catch {
|
|
348
|
+
}
|
|
349
|
+
await new Promise((r) => {
|
|
350
|
+
if (typeof requestAnimationFrame === "function") {
|
|
351
|
+
requestAnimationFrame(() => r());
|
|
352
|
+
} else {
|
|
353
|
+
setTimeout(() => r(), 16);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
return reg.snapshot();
|
|
357
|
+
}
|
|
358
|
+
return reg.snapshot();
|
|
359
|
+
}
|
|
329
360
|
function feedbackPlugin(options) {
|
|
330
361
|
const store = makeStore();
|
|
331
362
|
let client = null;
|
|
332
363
|
function Outlet() {
|
|
333
|
-
const
|
|
364
|
+
const state = useSyncExternalStore(
|
|
334
365
|
store.subscribe,
|
|
335
|
-
store.
|
|
336
|
-
store.
|
|
366
|
+
store.getState,
|
|
367
|
+
store.getState
|
|
337
368
|
);
|
|
338
|
-
if (!isOpen || !client || typeof document === "undefined") return null;
|
|
369
|
+
if (!state.isOpen || !client || typeof document === "undefined") return null;
|
|
339
370
|
const c = client;
|
|
371
|
+
const panelKeys = options.keys ?? state.snapshotKeys;
|
|
340
372
|
return createPortal(
|
|
341
373
|
/* @__PURE__ */ jsx2(
|
|
342
374
|
FeedbackPanel,
|
|
343
375
|
{
|
|
344
376
|
client: c,
|
|
345
|
-
keys:
|
|
377
|
+
keys: panelKeys,
|
|
346
378
|
namespace: options.namespace,
|
|
379
|
+
tos: options.tos ?? "modal",
|
|
347
380
|
onClose: () => {
|
|
348
|
-
store.
|
|
381
|
+
store.setOpen(false);
|
|
349
382
|
void c.flush();
|
|
350
383
|
}
|
|
351
384
|
}
|
|
@@ -356,24 +389,77 @@ function feedbackPlugin(options) {
|
|
|
356
389
|
return {
|
|
357
390
|
name: "@verbumia/feedback",
|
|
358
391
|
setup(ctx) {
|
|
392
|
+
const initialLanguage = options.language ?? ctx.i18n?.language ?? ctx.config.defaultLocale;
|
|
393
|
+
const tos = options.tos ?? "modal";
|
|
359
394
|
client = new FeedbackClient({
|
|
360
395
|
apiBase: options.apiBase ?? ctx.config.apiBase ?? "https://api.verbumia.dev",
|
|
361
396
|
projectId: options.projectId ?? ctx.config.projectUuid,
|
|
362
|
-
language:
|
|
397
|
+
language: initialLanguage,
|
|
363
398
|
endUserId: options.endUserId,
|
|
364
|
-
fetchImpl: options.fetchImpl
|
|
399
|
+
fetchImpl: options.fetchImpl,
|
|
400
|
+
apiKey: options.apiKey,
|
|
401
|
+
autoAcceptTos: tos !== "skip"
|
|
365
402
|
});
|
|
403
|
+
const clientRef = client;
|
|
404
|
+
let addonState = null;
|
|
405
|
+
const cta = options.cta ?? "auto";
|
|
406
|
+
const scope = options.scope ?? "current-view";
|
|
407
|
+
const ctxI18next = ctx.i18n?.i18next;
|
|
408
|
+
const refreshState = async () => {
|
|
409
|
+
try {
|
|
410
|
+
const next = await clientRef.getAddonState();
|
|
411
|
+
if (next !== null) addonState = next;
|
|
412
|
+
} catch {
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
if (options.apiKey) void refreshState();
|
|
416
|
+
let langUnsub;
|
|
417
|
+
if (typeof ctx.onLanguageChange === "function") {
|
|
418
|
+
langUnsub = ctx.onLanguageChange((lng) => {
|
|
419
|
+
clientRef.setLanguage(lng);
|
|
420
|
+
void refreshState();
|
|
421
|
+
});
|
|
422
|
+
}
|
|
366
423
|
const controller = {
|
|
367
|
-
open: () =>
|
|
424
|
+
open: async () => {
|
|
425
|
+
if (scope === "current-view") {
|
|
426
|
+
const snapshot = await captureCurrentViewSnapshot(ctxI18next);
|
|
427
|
+
store.setOpen(true, snapshot);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
store.setOpen(true);
|
|
431
|
+
},
|
|
368
432
|
close: () => {
|
|
369
|
-
store.
|
|
370
|
-
void
|
|
433
|
+
store.setOpen(false);
|
|
434
|
+
void clientRef?.flush();
|
|
435
|
+
},
|
|
436
|
+
client: clientRef,
|
|
437
|
+
get tosVersion() {
|
|
438
|
+
return clientRef.tosVersion;
|
|
371
439
|
},
|
|
372
|
-
|
|
440
|
+
get hasAcceptedTos() {
|
|
441
|
+
return clientRef.hasAcceptedTos;
|
|
442
|
+
},
|
|
443
|
+
acceptTos: async () => {
|
|
444
|
+
await clientRef.acceptTos();
|
|
445
|
+
if (!options.apiKey) await refreshState();
|
|
446
|
+
},
|
|
447
|
+
get isActive() {
|
|
448
|
+
if (cta === "show") return true;
|
|
449
|
+
if (cta === "hide") return false;
|
|
450
|
+
return addonState ? addonState.isActive : null;
|
|
451
|
+
},
|
|
452
|
+
get enabledLanguages() {
|
|
453
|
+
return addonState ? addonState.enabledLanguages : null;
|
|
454
|
+
},
|
|
455
|
+
get sku() {
|
|
456
|
+
return addonState ? addonState.sku : null;
|
|
457
|
+
}
|
|
373
458
|
};
|
|
374
459
|
options.onReady?.(controller);
|
|
375
460
|
if (options.controllerRef) options.controllerRef.current = controller;
|
|
376
461
|
return () => {
|
|
462
|
+
langUnsub?.();
|
|
377
463
|
if (options.controllerRef) options.controllerRef.current = null;
|
|
378
464
|
void client?.flush();
|
|
379
465
|
};
|