@thelol3882/sapar 0.0.0-stage → 0.3.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +380 -2
  3. package/dist/adapter-Y9yB3-q3.d.ts +14 -0
  4. package/dist/adapter-fFZPsINL.d.ts +14 -0
  5. package/dist/chunk-7B66QEMY.js +536 -0
  6. package/dist/chunk-7B66QEMY.js.map +1 -0
  7. package/dist/chunk-FM64UDKJ.js +893 -0
  8. package/dist/chunk-FM64UDKJ.js.map +1 -0
  9. package/dist/chunk-VC6XLWM3.js +176 -0
  10. package/dist/chunk-VC6XLWM3.js.map +1 -0
  11. package/dist/chunk-ZBVQGMR4.js +583 -0
  12. package/dist/chunk-ZBVQGMR4.js.map +1 -0
  13. package/dist/cli/index.js +1335 -0
  14. package/dist/core/index.d.ts +273 -0
  15. package/dist/core/index.js +71 -0
  16. package/dist/core/index.js.map +1 -0
  17. package/dist/expo/index.d.ts +189 -0
  18. package/dist/expo/index.js +39 -0
  19. package/dist/expo/index.js.map +1 -0
  20. package/dist/expo-router/index.d.ts +18 -0
  21. package/dist/expo-router/index.js +46 -0
  22. package/dist/expo-router/index.js.map +1 -0
  23. package/dist/next/index.d.ts +18 -0
  24. package/dist/next/index.js +49 -0
  25. package/dist/next/index.js.map +1 -0
  26. package/dist/presentation-CClsPRT6.d.ts +12 -0
  27. package/dist/react/index.d.ts +35 -0
  28. package/dist/react/index.js +29 -0
  29. package/dist/react/index.js.map +1 -0
  30. package/dist/rtk/index.d.ts +751 -0
  31. package/dist/rtk/index.js +114 -0
  32. package/dist/rtk/index.js.map +1 -0
  33. package/dist/stepCount-CTYYFWiV.d.ts +57 -0
  34. package/dist/types-CHD2pB5X.d.ts +192 -0
  35. package/dist/web/index.d.ts +112 -0
  36. package/dist/web/index.js +34 -0
  37. package/dist/web/index.js.map +1 -0
  38. package/package.json +147 -3
  39. package/templates/expo/AnnouncementModal.tsx +102 -0
  40. package/templates/expo/TourCard.tsx +112 -0
  41. package/templates/expo/WhatsNewButton.tsx +87 -0
  42. package/templates/expo/WhatsNewPanel.tsx +167 -0
  43. package/templates/mantine/AnnouncementModal.tsx +66 -0
  44. package/templates/mantine/TourCard.tsx +84 -0
  45. package/templates/mantine/WhatsNewButton.tsx +62 -0
  46. package/templates/mantine/WhatsNewPanel.tsx +129 -0
  47. package/templates/tailwind/AnnouncementModal.tsx +100 -0
  48. package/templates/tailwind/TourCard.tsx +69 -0
  49. package/templates/tailwind/WhatsNewButton.tsx +56 -0
  50. package/templates/tailwind/WhatsNewPanel.tsx +174 -0
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+ import {
3
+ fromWireAnnouncement,
4
+ fromWireInbox,
5
+ fromWireTour,
6
+ toWireProgress
7
+ } from "../chunk-FM64UDKJ.js";
8
+
9
+ // src/rtk/createSaparRtk.ts
10
+ import { useMemo } from "react";
11
+ import { useDispatch } from "react-redux";
12
+
13
+ // src/rtk/errors.ts
14
+ function isNotFound(error) {
15
+ return typeof error === "object" && error !== null && "status" in error && error.status === 404;
16
+ }
17
+
18
+ // src/rtk/createSaparRtk.ts
19
+ var TAG_TYPE = "Sapar";
20
+ var INBOX_TAG = { type: TAG_TYPE, id: "inbox" };
21
+ var ALWAYS_FRESH = { subscribe: false, forceRefetch: true };
22
+ var discardBody = () => void 0;
23
+ function createSaparRtk(api, { basePath = "/tours" } = {}) {
24
+ const root = basePath.replace(/\/+$/, "");
25
+ const tourPath = (key) => `${root}/tours/${encodeURIComponent(key)}/`;
26
+ const injectedApi = api.enhanceEndpoints({ addTagTypes: [TAG_TYPE] }).injectEndpoints({
27
+ endpoints: (builder) => ({
28
+ getSaparInbox: builder.query({
29
+ query: (params) => ({ url: `${root}/inbox/`, params }),
30
+ transformResponse: (inbox) => fromWireInbox(inbox),
31
+ providesTags: [INBOX_TAG]
32
+ }),
33
+ getTour: builder.query({
34
+ async queryFn(key, _api, _extraOptions, baseQuery) {
35
+ const { data, error } = await baseQuery(tourPath(key));
36
+ if (isNotFound(error)) return { data: null };
37
+ return error ? { error } : { data: fromWireTour(data) };
38
+ }
39
+ }),
40
+ getAnnouncements: builder.query({
41
+ query: () => `${root}/announcements/`,
42
+ transformResponse: (announcements) => announcements.map(fromWireAnnouncement)
43
+ }),
44
+ markAnnouncementsSeen: builder.mutation({
45
+ query: (keys) => ({ url: `${root}/seen/`, method: "POST", body: { keys } }),
46
+ transformResponse: discardBody,
47
+ invalidatesTags: [INBOX_TAG]
48
+ }),
49
+ dismissAnnouncement: builder.mutation({
50
+ query: (key) => ({ url: `${root}/dismiss/`, method: "POST", body: { key } }),
51
+ transformResponse: discardBody,
52
+ invalidatesTags: [INBOX_TAG]
53
+ }),
54
+ recordAnnouncementClick: builder.mutation({
55
+ query: (key) => ({ url: `${root}/announcements/${encodeURIComponent(key)}/click/`, method: "POST" }),
56
+ transformResponse: discardBody
57
+ }),
58
+ recordTourProgress: builder.mutation({
59
+ query: (input) => ({
60
+ url: `${tourPath(input.key)}progress/`,
61
+ method: "POST",
62
+ body: toWireProgress(input)
63
+ }),
64
+ transformResponse: discardBody,
65
+ invalidatesTags: [INBOX_TAG]
66
+ })
67
+ })
68
+ });
69
+ const { endpoints } = injectedApi;
70
+ const {
71
+ useGetSaparInboxQuery,
72
+ useLazyGetSaparInboxQuery,
73
+ useGetTourQuery,
74
+ useLazyGetTourQuery,
75
+ useGetAnnouncementsQuery,
76
+ useMarkAnnouncementsSeenMutation,
77
+ useDismissAnnouncementMutation,
78
+ useRecordAnnouncementClickMutation,
79
+ useRecordTourProgressMutation
80
+ } = injectedApi;
81
+ function createSource(dispatch) {
82
+ return {
83
+ getInbox: (query) => dispatch(endpoints.getSaparInbox.initiate(query, ALWAYS_FRESH)).unwrap(),
84
+ getTour: (key) => dispatch(endpoints.getTour.initiate(key, ALWAYS_FRESH)).unwrap(),
85
+ listAnnouncements: () => dispatch(endpoints.getAnnouncements.initiate(void 0, ALWAYS_FRESH)).unwrap(),
86
+ recordProgress: (input) => dispatch(endpoints.recordTourProgress.initiate(input)).unwrap(),
87
+ markSeen: (keys) => dispatch(endpoints.markAnnouncementsSeen.initiate(keys)).unwrap(),
88
+ dismiss: (key) => dispatch(endpoints.dismissAnnouncement.initiate(key)).unwrap(),
89
+ recordClick: (key) => dispatch(endpoints.recordAnnouncementClick.initiate(key)).unwrap()
90
+ };
91
+ }
92
+ function useSaparSource() {
93
+ const dispatch = useDispatch();
94
+ return useMemo(() => createSource(dispatch), [dispatch]);
95
+ }
96
+ return {
97
+ api: injectedApi,
98
+ useGetSaparInboxQuery,
99
+ useLazyGetSaparInboxQuery,
100
+ useGetTourQuery,
101
+ useLazyGetTourQuery,
102
+ useGetAnnouncementsQuery,
103
+ useMarkAnnouncementsSeenMutation,
104
+ useDismissAnnouncementMutation,
105
+ useRecordAnnouncementClickMutation,
106
+ useRecordTourProgressMutation,
107
+ createSource,
108
+ useSaparSource
109
+ };
110
+ }
111
+ export {
112
+ createSaparRtk
113
+ };
114
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/rtk/createSaparRtk.ts","../../src/rtk/errors.ts"],"sourcesContent":["'use client';\n\nimport type { ThunkDispatch, UnknownAction } from '@reduxjs/toolkit';\nimport type {\n Api,\n BaseQueryFn,\n EndpointDefinitions,\n FetchArgs,\n FetchBaseQueryError,\n coreModuleName,\n reactHooksModuleName,\n} from '@reduxjs/toolkit/query/react';\nimport { useMemo } from 'react';\nimport { useDispatch } from 'react-redux';\nimport { fromWireAnnouncement, fromWireInbox, fromWireTour, toWireProgress } from '../core';\nimport type {\n Announcement,\n Inbox,\n Platform,\n ProgressInput,\n Tour,\n SaparSource,\n WireAnnouncement,\n WireInbox,\n WireTour,\n} from '../core';\nimport { isNotFound } from './errors';\n\nexport type SaparBaseQuery = BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError>;\n\n/** The host's own `createApi` slice, whose base query owns auth, refresh and the base URL. */\nexport type HostApi<\n Definitions extends EndpointDefinitions = EndpointDefinitions,\n ReducerPath extends string = string,\n TagTypes extends string = string,\n> = Api<SaparBaseQuery, Definitions, ReducerPath, TagTypes, typeof coreModuleName | typeof reactHooksModuleName>;\n\nexport type SaparDispatch = ThunkDispatch<unknown, unknown, UnknownAction>;\n\nexport interface SaparRtkOptions {\n /** Prefix of the tours endpoints, relative to the host base query's base URL. Default `/tours`. */\n basePath?: string;\n}\n\nconst TAG_TYPE = 'Sapar';\nconst INBOX_TAG = { type: TAG_TYPE, id: 'inbox' } as const;\nconst ALWAYS_FRESH = { subscribe: false, forceRefetch: true } as const;\n\nconst discardBody = (): void => undefined;\n\n/**\n * Generic over the host slice so any `createApi` result is accepted; the body widens it once,\n * because RTK cannot resolve the hooks module through those generics.\n */\nexport function createSaparRtk<\n Definitions extends EndpointDefinitions,\n ReducerPath extends string,\n TagTypes extends string,\n>(api: HostApi<Definitions, ReducerPath, TagTypes>, { basePath = '/tours' }: SaparRtkOptions = {}) {\n const root = basePath.replace(/\\/+$/, '');\n const tourPath = (key: string): string => `${root}/tours/${encodeURIComponent(key)}/`;\n\n const injectedApi = (api as unknown as HostApi).enhanceEndpoints({ addTagTypes: [TAG_TYPE] }).injectEndpoints({\n endpoints: (builder) => ({\n getSaparInbox: builder.query<Inbox, { route?: string; platform: Platform }>({\n query: (params) => ({ url: `${root}/inbox/`, params }),\n transformResponse: (inbox: WireInbox) => fromWireInbox(inbox),\n providesTags: [INBOX_TAG],\n }),\n getTour: builder.query<Tour | null, string>({\n async queryFn(key, _api, _extraOptions, baseQuery) {\n const { data, error } = await baseQuery(tourPath(key));\n if (isNotFound(error)) return { data: null };\n return error ? { error } : { data: fromWireTour(data as WireTour) };\n },\n }),\n getAnnouncements: builder.query<Announcement[], void>({\n query: () => `${root}/announcements/`,\n transformResponse: (announcements: WireAnnouncement[]) => announcements.map(fromWireAnnouncement),\n }),\n markAnnouncementsSeen: builder.mutation<void, string[]>({\n query: (keys) => ({ url: `${root}/seen/`, method: 'POST', body: { keys } }),\n transformResponse: discardBody,\n invalidatesTags: [INBOX_TAG],\n }),\n dismissAnnouncement: builder.mutation<void, string>({\n query: (key) => ({ url: `${root}/dismiss/`, method: 'POST', body: { key } }),\n transformResponse: discardBody,\n invalidatesTags: [INBOX_TAG],\n }),\n recordAnnouncementClick: builder.mutation<void, string>({\n query: (key) => ({ url: `${root}/announcements/${encodeURIComponent(key)}/click/`, method: 'POST' }),\n transformResponse: discardBody,\n }),\n recordTourProgress: builder.mutation<void, ProgressInput>({\n query: (input) => ({\n url: `${tourPath(input.key)}progress/`,\n method: 'POST',\n body: toWireProgress(input),\n }),\n transformResponse: discardBody,\n invalidatesTags: [INBOX_TAG],\n }),\n }),\n });\n\n const { endpoints } = injectedApi;\n const {\n useGetSaparInboxQuery,\n useLazyGetSaparInboxQuery,\n useGetTourQuery,\n useLazyGetTourQuery,\n useGetAnnouncementsQuery,\n useMarkAnnouncementsSeenMutation,\n useDismissAnnouncementMutation,\n useRecordAnnouncementClickMutation,\n useRecordTourProgressMutation,\n } = injectedApi;\n\n function createSource(dispatch: SaparDispatch): SaparSource {\n return {\n getInbox: (query) => dispatch(endpoints.getSaparInbox.initiate(query, ALWAYS_FRESH)).unwrap(),\n getTour: (key) => dispatch(endpoints.getTour.initiate(key, ALWAYS_FRESH)).unwrap(),\n listAnnouncements: () => dispatch(endpoints.getAnnouncements.initiate(undefined, ALWAYS_FRESH)).unwrap(),\n recordProgress: (input) => dispatch(endpoints.recordTourProgress.initiate(input)).unwrap(),\n markSeen: (keys) => dispatch(endpoints.markAnnouncementsSeen.initiate(keys)).unwrap(),\n dismiss: (key) => dispatch(endpoints.dismissAnnouncement.initiate(key)).unwrap(),\n recordClick: (key) => dispatch(endpoints.recordAnnouncementClick.initiate(key)).unwrap(),\n };\n }\n\n function useSaparSource(): SaparSource {\n const dispatch = useDispatch<SaparDispatch>();\n return useMemo(() => createSource(dispatch), [dispatch]);\n }\n\n return {\n api: injectedApi,\n useGetSaparInboxQuery,\n useLazyGetSaparInboxQuery,\n useGetTourQuery,\n useLazyGetTourQuery,\n useGetAnnouncementsQuery,\n useMarkAnnouncementsSeenMutation,\n useDismissAnnouncementMutation,\n useRecordAnnouncementClickMutation,\n useRecordTourProgressMutation,\n createSource,\n useSaparSource,\n };\n}\n","export function isNotFound(error: unknown): boolean {\n return typeof error === 'object' && error !== null && 'status' in error && error.status === 404;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,57 @@
1
+ import { T as Tour, q as TourStep, k as AnchorHandle, f as Announcement, j as Inbox, m as InboxStatus, o as SaparSnapshot } from './types-CHD2pB5X.js';
2
+
3
+ declare function useSaparSnapshot(): SaparSnapshot;
4
+ interface UseSaparResult {
5
+ startTour: (tourOrKey: Tour | string) => Promise<void>;
6
+ inbox: Inbox | null;
7
+ inboxStatus: InboxStatus;
8
+ isRunning: boolean;
9
+ }
10
+ declare function useSapar(): UseSaparResult;
11
+ interface TourStepState {
12
+ tour: Tour;
13
+ step: TourStep;
14
+ /** One-based position among the steps that are expected to be shown. */
15
+ position: number;
16
+ total: number;
17
+ isFirst: boolean;
18
+ isLast: boolean;
19
+ handle: AnchorHandle | null;
20
+ next: () => Promise<void>;
21
+ previous: () => Promise<void>;
22
+ skip: () => void;
23
+ }
24
+ declare function useTourStep(): TourStepState | null;
25
+ interface UseWhatsNewResult {
26
+ items: Announcement[];
27
+ unreadCount: number;
28
+ status: InboxStatus;
29
+ /** Marks the items that are unread at the moment of the call. */
30
+ markAllSeen: () => Promise<void>;
31
+ markSeen: (keys: string[]) => Promise<void>;
32
+ dismiss: (key: string) => Promise<void>;
33
+ recordClick: (key: string) => void;
34
+ startTour: (tourOrKey: Tour | string) => Promise<void>;
35
+ }
36
+ declare function useWhatsNew(): UseWhatsNewResult;
37
+ interface AnnouncementInterruption {
38
+ /** The modal-tier announcement the source wants shown now, if any. */
39
+ announcement: Announcement | null;
40
+ dismiss: () => Promise<void>;
41
+ /** Marks the announcement seen without recording a dismissal. */
42
+ acknowledge: () => Promise<void>;
43
+ }
44
+ declare function useAnnouncementInterruption(): AnnouncementInterruption;
45
+
46
+ interface StepCount {
47
+ /** One-based. */
48
+ position: number;
49
+ total: number;
50
+ }
51
+ interface StepAnnouncement extends StepCount {
52
+ title: string;
53
+ }
54
+ declare const defaultAnnounceStep: ({ position, total, title }: StepAnnouncement) => string;
55
+ declare const defaultProgress: ({ position, total }: StepCount) => string;
56
+
57
+ export { type AnnouncementInterruption as A, type StepAnnouncement as S, type TourStepState as T, type UseSaparResult as U, type StepCount as a, type UseWhatsNewResult as b, defaultProgress as c, defaultAnnounceStep as d, useSapar as e, useSaparSnapshot as f, useTourStep as g, useWhatsNew as h, useAnnouncementInterruption as u };
@@ -0,0 +1,192 @@
1
+ type Platform = 'web' | 'native';
2
+ type Placement = 'auto' | 'top' | 'bottom' | 'left' | 'right';
3
+ type AnnouncementTier = 'silent' | 'feed' | 'modal';
4
+ type TourStatus = 'started' | 'completed' | 'dismissed';
5
+ /** `'manual'` starts only on request; `{ route }` may auto-start where the pattern matches. */
6
+ type TourTrigger = 'manual' | {
7
+ route: string;
8
+ };
9
+ /** Plain text, or a per-locale map resolved by the source. */
10
+ type LocalizedText = string | Readonly<Record<string, string>>;
11
+ interface TourStep {
12
+ key: string;
13
+ /** Absent means a centered step with no spotlight. */
14
+ anchor?: string;
15
+ /** Route the anchor lives on; the runner navigates there first. */
16
+ route?: string;
17
+ title: string;
18
+ body: string;
19
+ placement: Placement;
20
+ padding: number;
21
+ /** A missing anchor skips the step unless this is set, which aborts the tour when the runner reaches it. */
22
+ required: boolean;
23
+ /** `'press'` advances when the user presses the anchored element itself. */
24
+ advanceOn: 'next' | 'press';
25
+ }
26
+ interface Tour {
27
+ key: string;
28
+ revision: number;
29
+ title: string;
30
+ trigger: TourTrigger;
31
+ steps: TourStep[];
32
+ }
33
+ interface Announcement {
34
+ key: string;
35
+ tier: AnnouncementTier;
36
+ title: string;
37
+ body: string;
38
+ imageUrl?: string;
39
+ cta?: {
40
+ label: string;
41
+ url: string;
42
+ };
43
+ /** ISO 8601. */
44
+ publishedAt: string | null;
45
+ tourKey?: string;
46
+ unread: boolean;
47
+ }
48
+ type Interruption = {
49
+ kind: 'tour';
50
+ tour: Tour;
51
+ } | {
52
+ kind: 'announcement';
53
+ announcement: Announcement;
54
+ };
55
+ interface Inbox {
56
+ /** Unread non-silent announcements. */
57
+ unreadCount: number;
58
+ items: Announcement[];
59
+ /** At most one unbidden thing to show, already filtered by the source's policy. */
60
+ interruption: Interruption | null;
61
+ }
62
+ interface ProgressInput {
63
+ key: string;
64
+ revision: number;
65
+ status: TourStatus;
66
+ stepIndex: number;
67
+ autoStarted: boolean;
68
+ }
69
+ /**
70
+ * Where tours, announcements and per-user state come from.
71
+ * Implementations: `memorySource` (definitions in code, state in storage),
72
+ * `httpSource` (the Django/FastAPI backend over fetch), `createSaparRtk` (same backend through RTK Query).
73
+ */
74
+ interface SaparSource {
75
+ getInbox(query: {
76
+ route?: string;
77
+ platform: Platform;
78
+ }): Promise<Inbox>;
79
+ getTour(key: string): Promise<Tour | null>;
80
+ recordProgress(input: ProgressInput): Promise<void>;
81
+ markSeen(keys: string[]): Promise<void>;
82
+ dismiss(key: string): Promise<void>;
83
+ recordClick(key: string): Promise<void>;
84
+ listAnnouncements(): Promise<Announcement[]>;
85
+ }
86
+ /** Possibly-async key-value storage: localStorage, AsyncStorage, MMKV, memory. */
87
+ interface KeyValueStore {
88
+ get(key: string): string | null | Promise<string | null>;
89
+ set(key: string, value: string): void | Promise<void>;
90
+ remove(key: string): void | Promise<void>;
91
+ }
92
+ /** Synchronous per-session storage for the client-side interruption cap. */
93
+ interface SessionStore {
94
+ get(key: string): string | null;
95
+ set(key: string, value: string): void;
96
+ }
97
+ interface AnchorRect {
98
+ x: number;
99
+ y: number;
100
+ width: number;
101
+ height: number;
102
+ }
103
+ interface AnchorHandle {
104
+ key: string;
105
+ /** Async because native measurement is; live because the element moves. */
106
+ measure(): Promise<AnchorRect | null>;
107
+ /** Resolves on the next press of the anchored element. Returns an unsubscribe. */
108
+ onPress(listener: () => void): () => void;
109
+ }
110
+ /** Everything platform-shaped. The web implementation lives in `/web`, a native one would sit beside it. */
111
+ interface PlatformAdapter {
112
+ platform: Platform;
113
+ /** Resolves `null` on timeout. Must never reject for a missing anchor. */
114
+ resolveAnchor(key: string, options: {
115
+ timeoutMs: number;
116
+ }): Promise<AnchorHandle | null>;
117
+ scrollIntoView(handle: AnchorHandle, options: {
118
+ reducedMotion: boolean;
119
+ }): Promise<void>;
120
+ /** Raw route in the host router's spelling; the controller normalizes it. */
121
+ currentRoute(): string;
122
+ /** Resolves once the destination has rendered. */
123
+ navigate(route: string): Promise<void>;
124
+ prefersReducedMotion(): boolean;
125
+ isCompact(): boolean;
126
+ session: SessionStore;
127
+ now(): number;
128
+ }
129
+ type RunnerPhase = 'idle' | 'preparing' | 'running';
130
+ interface RunnerSnapshot {
131
+ phase: RunnerPhase;
132
+ tour: Tour | null;
133
+ /** Index into `tour.steps` of the step being shown. */
134
+ index: number;
135
+ /** The current step's anchor, or null for a centered step. */
136
+ handle: AnchorHandle | null;
137
+ /** Keys of the steps whose anchor is missing right now. An estimate: the runner still tries each on arrival. */
138
+ skipped: readonly string[];
139
+ autoStarted: boolean;
140
+ }
141
+ type InboxStatus = 'idle' | 'loading' | 'ready' | 'error';
142
+ interface SaparSnapshot {
143
+ inbox: Inbox | null;
144
+ inboxStatus: InboxStatus;
145
+ runner: RunnerSnapshot;
146
+ }
147
+ interface SaparControllerOptions {
148
+ source: SaparSource;
149
+ adapter: PlatformAdapter;
150
+ /** Locale prefixes stripped from routes (`/ru/billing` -> `/billing`). Must match the backend. */
151
+ locales?: readonly string[];
152
+ /** How long to wait for an anchor right after navigating, while the page loads. Default 3000. */
153
+ anchorTimeoutMs?: number;
154
+ /** How long to wait for an anchor on a page that is already rendered. Default 300. */
155
+ presentAnchorTimeoutMs?: number;
156
+ /** How long an auto-started tour waits for its first anchor before giving up quietly. Default 10000. */
157
+ autoStartTimeoutMs?: number;
158
+ /** At most one unbidden interruption per session on top of the source's policy. Default true. */
159
+ sessionCap?: boolean;
160
+ /** Called for swallowed errors (failed progress writes, adapter bugs). Default `console.error`. */
161
+ onError?: (error: unknown, context: string) => void;
162
+ }
163
+ /** Framework-free owner of the inbox, the runner and every behaviour rule. React only binds to it. */
164
+ interface SaparController {
165
+ subscribe(listener: () => void): () => void;
166
+ /** Referentially stable until state changes, for `useSyncExternalStore`. */
167
+ getSnapshot(): SaparSnapshot;
168
+ /** Reports the current route; refetches the inbox and may auto-start the offered interruption. `undefined` opts out of route triggers. */
169
+ setRoute(route: string | undefined): void;
170
+ refresh(): Promise<void>;
171
+ /** A tour the user asked for: never capped, reported with `autoStarted: false`. */
172
+ startTour(tourOrKey: Tour | string): Promise<void>;
173
+ next(): Promise<void>;
174
+ previous(): Promise<void>;
175
+ skip(): void;
176
+ /** Optimistically clears unread state, rolls back if the source rejects. */
177
+ markSeen(keys: string[]): Promise<void>;
178
+ dismiss(key: string): Promise<void>;
179
+ recordClick(key: string): void;
180
+ destroy(): void;
181
+ }
182
+ /** Local interruption policy, used by `memorySource`. Mirrors the backend's settings and defaults. */
183
+ interface InterruptionPolicy {
184
+ /** No interruptions for accounts younger than this. Default 3. */
185
+ newUserGraceDays: number;
186
+ /** Minimum gap between auto interruptions. Default 24. */
187
+ cooldownHours: number;
188
+ /** A dismissed tour is re-offered after this long; 0 means immediately. Default 30. */
189
+ dismissalBackoffDays: number;
190
+ }
191
+
192
+ export type { AnnouncementTier as A, InterruptionPolicy as I, KeyValueStore as K, LocalizedText as L, ProgressInput as P, RunnerSnapshot as R, SaparControllerOptions as S, Tour as T, SaparController as a, PlatformAdapter as b, TourTrigger as c, Placement as d, Platform as e, Announcement as f, TourStatus as g, Interruption as h, SaparSource as i, Inbox as j, AnchorHandle as k, AnchorRect as l, InboxStatus as m, RunnerPhase as n, SaparSnapshot as o, SessionStore as p, TourStep as q };
@@ -0,0 +1,112 @@
1
+ export { W as WebAdapterOptions, c as createWebAdapter } from '../adapter-Y9yB3-q3.js';
2
+ import * as react from 'react';
3
+ import { ReactNode, RefCallback, CSSProperties, ComponentPropsWithoutRef, ReactElement } from 'react';
4
+ import { l as AnchorRect } from '../types-CHD2pB5X.js';
5
+ import { T as TourStepState, S as StepAnnouncement, a as StepCount } from '../stepCount-CTYYFWiV.js';
6
+ import { P as Presentation } from '../presentation-CClsPRT6.js';
7
+
8
+ declare const ANCHOR_ATTRIBUTE = "data-sapar";
9
+ declare function registerAnchor(key: string, element: HTMLElement): () => void;
10
+ declare function getAnchorElement(key: string): HTMLElement | null;
11
+ /** Resolves `null` on timeout. Woken by registration, or by a DOM mutation for plain `data-sapar` markup. */
12
+ declare function waitForAnchor(key: string, timeoutMs: number): Promise<HTMLElement | null>;
13
+
14
+ interface SyncStore {
15
+ get(key: string): string | null;
16
+ set(key: string, value: string): void;
17
+ remove(key: string): void;
18
+ }
19
+ declare const localStorageStore: () => SyncStore;
20
+ declare const browserSessionStore: () => SyncStore;
21
+
22
+ /** Renders nothing on the server and during hydration, then portals into `document.body`. */
23
+ declare function BodyPortal({ children }: {
24
+ children: ReactNode;
25
+ }): react.ReactPortal | null;
26
+
27
+ interface TourAnchorProps<K extends string, E extends HTMLElement> {
28
+ ref: RefCallback<E>;
29
+ [ANCHOR_ATTRIBUTE]: K;
30
+ }
31
+ /** Spread the result onto the element a tour step points at: `<button {...useTourAnchor('billing.upgrade')} />`. */
32
+ declare function useTourAnchor<K extends string = string, E extends HTMLElement = HTMLElement>(key: K): TourAnchorProps<K, E>;
33
+
34
+ /** Live viewport rect of `element`, re-measured at most once per animation frame; null when there is none or it is detached. */
35
+ declare function useElementRect(element: HTMLElement | null): AnchorRect | null;
36
+
37
+ interface TourPopoverProps {
38
+ className?: string;
39
+ style?: CSSProperties;
40
+ /** Gap in px between the anchor and the popover. */
41
+ offset?: number;
42
+ /**
43
+ * `'anchored'` puts the popover next to the anchor (centred without one), `'sheet'` is a bottom sheet, and
44
+ * `'auto'` is a sheet on viewports narrower than the adapter's `compactBreakpointPx` (default 768). Default `'auto'`.
45
+ */
46
+ presentation?: Presentation;
47
+ children?: ReactNode;
48
+ }
49
+ declare function TourPopover(props: TourPopoverProps): react.JSX.Element | null;
50
+
51
+ interface TourOverlayProps extends Pick<TourPopoverProps, 'presentation'> {
52
+ spotlightClassName?: string;
53
+ spotlightStyle?: CSSProperties;
54
+ popoverClassName?: string;
55
+ popoverStyle?: CSSProperties;
56
+ onClickOutside?: () => void;
57
+ /** The step card, either directly or built from the running step. */
58
+ children: ReactNode | ((tourStep: TourStepState) => ReactNode);
59
+ }
60
+ declare function TourOverlay({ spotlightClassName, spotlightStyle, popoverClassName, popoverStyle, onClickOutside, presentation, children, }: TourOverlayProps): react.JSX.Element | null;
61
+
62
+ interface TourSpotlightProps {
63
+ /** Colours the dim layer through `currentColor`, e.g. `text-black/50` (or `style.color`). */
64
+ className?: string;
65
+ style?: CSSProperties;
66
+ /** Corner radius of the cutout in px. */
67
+ radius?: number;
68
+ /** Called when the dim layer is clicked. Defaults to skipping the tour. */
69
+ onClickOutside?: () => void;
70
+ }
71
+ declare function TourSpotlight({ onClickOutside, ...rest }: TourSpotlightProps): react.JSX.Element | null;
72
+
73
+ interface TourStepRootProps extends ComponentPropsWithoutRef<'div'> {
74
+ /** Text read by screen readers on each step change. */
75
+ announceStep?: (step: StepAnnouncement) => string;
76
+ }
77
+ declare function Root(props: TourStepRootProps): react.JSX.Element | null;
78
+ declare function Title({ children, ...rest }: ComponentPropsWithoutRef<'h2'>): react.JSX.Element;
79
+ declare function Body({ children, ...rest }: ComponentPropsWithoutRef<'p'>): react.JSX.Element;
80
+ interface TourStepProgressProps extends Omit<ComponentPropsWithoutRef<'span'>, 'children'> {
81
+ /** Defaults to `"{position} / {total}"`. */
82
+ children?: ReactNode | ((count: StepCount) => ReactNode);
83
+ }
84
+ declare function Progress({ children, ...rest }: TourStepProgressProps): react.JSX.Element;
85
+ type ButtonProps = ComponentPropsWithoutRef<'button'>;
86
+ interface TourStepButtonProps extends ButtonProps {
87
+ /** Renders your own button (a design-system component) with the wired-up props. */
88
+ render?: (props: ButtonProps) => ReactElement;
89
+ }
90
+ interface TourStepNextProps extends TourStepButtonProps {
91
+ /** Labels for the last step and the others. Ignored when `children` is given. */
92
+ doneLabel?: ReactNode;
93
+ nextLabel?: ReactNode;
94
+ /** Steps that advance when the anchor is pressed hide this button unless set. */
95
+ showOnPress?: boolean;
96
+ }
97
+ declare function Next({ doneLabel, nextLabel, showOnPress, children, onClick, ...rest }: TourStepNextProps): react.JSX.Element | null;
98
+ declare function Back({ children, onClick, ...rest }: TourStepButtonProps): react.JSX.Element | null;
99
+ declare function Skip({ children, onClick, ...rest }: TourStepButtonProps): react.JSX.Element;
100
+ declare function Close({ children, ...rest }: TourStepButtonProps): react.JSX.Element;
101
+ declare const TourStep: {
102
+ Root: typeof Root;
103
+ Title: typeof Title;
104
+ Body: typeof Body;
105
+ Progress: typeof Progress;
106
+ Next: typeof Next;
107
+ Back: typeof Back;
108
+ Skip: typeof Skip;
109
+ Close: typeof Close;
110
+ };
111
+
112
+ export { BodyPortal, Presentation, type TourAnchorProps, TourOverlay, type TourOverlayProps, TourPopover, type TourPopoverProps, TourSpotlight, type TourSpotlightProps, TourStep, type TourStepButtonProps, type TourStepNextProps, type TourStepProgressProps, type TourStepRootProps, browserSessionStore, getAnchorElement, localStorageStore, registerAnchor, useElementRect, useTourAnchor, waitForAnchor };
@@ -0,0 +1,34 @@
1
+ 'use client';
2
+ import {
3
+ BodyPortal,
4
+ TourOverlay,
5
+ TourPopover,
6
+ TourSpotlight,
7
+ TourStep,
8
+ browserSessionStore,
9
+ createWebAdapter,
10
+ getAnchorElement,
11
+ localStorageStore,
12
+ registerAnchor,
13
+ useElementRect,
14
+ useTourAnchor,
15
+ waitForAnchor
16
+ } from "../chunk-7B66QEMY.js";
17
+ import "../chunk-VC6XLWM3.js";
18
+ import "../chunk-FM64UDKJ.js";
19
+ export {
20
+ BodyPortal,
21
+ TourOverlay,
22
+ TourPopover,
23
+ TourSpotlight,
24
+ TourStep,
25
+ browserSessionStore,
26
+ createWebAdapter,
27
+ getAnchorElement,
28
+ localStorageStore,
29
+ registerAnchor,
30
+ useElementRect,
31
+ useTourAnchor,
32
+ waitForAnchor
33
+ };
34
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}