@eintrek/erp-shell 0.1.73 → 0.1.75
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 +63 -0
- package/dist/chunks/{guide-runtime-CLVSNkTN.cjs → format-CVazK0hP.cjs} +1371 -59
- package/dist/chunks/format-CVazK0hP.cjs.map +1 -0
- package/dist/chunks/{guide-runtime-C7Z00QwM.esm.js → format-DCTuLpBU.esm.js} +1360 -61
- package/dist/chunks/format-DCTuLpBU.esm.js.map +1 -0
- package/dist/guide/core.d.ts +5 -3
- package/dist/guide/format.d.ts +26 -0
- package/dist/guide/guide-store.d.ts +72 -6
- package/dist/guide/guide-styles.d.ts +1 -1
- package/dist/guide/guide-ui.d.ts +38 -0
- package/dist/guide/machine.d.ts +139 -3
- package/dist/guide/pages.d.ts +79 -0
- package/dist/guide/progress-sync.d.ts +10 -1
- package/dist/guide/progress.d.ts +27 -0
- package/dist/guide/section-button.d.ts +12 -0
- package/dist/guide/types.d.ts +111 -0
- package/dist/guide-core.cjs +32 -19
- package/dist/guide-core.cjs.map +1 -1
- package/dist/guide-core.esm.js +1 -1
- package/dist/help/help-button.d.ts +6 -3
- package/dist/index.cjs +56 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.esm.js +22 -13
- package/dist/index.esm.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunks/guide-runtime-C7Z00QwM.esm.js.map +0 -1
- package/dist/chunks/guide-runtime-CLVSNkTN.cjs.map +0 -1
package/dist/guide/core.d.ts
CHANGED
|
@@ -10,10 +10,12 @@
|
|
|
10
10
|
* The main entry re-exports the same modules, so a tour defined against
|
|
11
11
|
* either entry is the same data, and the bus and the "?" entry are shared.
|
|
12
12
|
*/
|
|
13
|
-
export type { GuideAbortAction, GuideAnchorKey, GuideApiFailure, GuideApiMatch, GuideAppId, GuideCaptureEvent, GuideConfig, GuideContext, GuideFacts, GuideGate, GuideMission, GuideNothingToSave, GuideOnMissing, GuideStep, GuideStepKind, GuideSuite, GuideText, GuideTrigger, } from "./types";
|
|
13
|
+
export type { GuideAbortAction, GuideAnchorKey, GuideApiFailure, GuideApiMatch, GuideAppId, GuideCaptureEvent, GuideConfig, GuideContext, GuideFacts, GuideGate, GuideMission, GuideNothingToSave, GuideOnMissing, GuidePage, GuideSection, GuideStep, GuideStepKind, GuideSuite, GuideText, GuideTrigger, } from "./types";
|
|
14
14
|
export { API_EVENT as GUIDE_API_EVENT, GUIDE_EVENT_PREFIX, GUIDE_LAYER_ATTRIBUTE, apiCallDetail, emitGuideEvent, guideAnchor, reportApiCall, reportApiEvent, type ApiCallReport, type ApiEventDetail, type GuideAnchorProps, } from "./events";
|
|
15
|
-
export { guideMenuShown, openGuidePanel, useGuideEntry, type GuideEntry } from "./guide-store";
|
|
15
|
+
export { guideMenuItems, guideMenuShown, openGuidePanel, openPageGuide, openPagesPanel, openSectionGuide, setSectionGuideValues, useGuideEntry, usePageGuideEntry, usePageGuides, useSectionGuideValues, type GuideEntry, type GuideMenuItem, type PageGuidesEntry, } from "./guide-store";
|
|
16
|
+
export { SectionGuideButton, type SectionGuideButtonProps } from "./section-button";
|
|
17
|
+
export { THAI_MONTHS_SHORT, formatBaht, thaiMonth, thaiMonthYear } from "./format";
|
|
16
18
|
export { GuideRuntime, type GuideMenu, type GuideRouting, type GuideRuntimeProps, } from "./guide-runtime";
|
|
17
19
|
export { GuideProgressConflict, loadGuides, storeGuides, type GuideProgressAdapter, type GuideProgressRequest, type GuideProgressResponse, } from "./progress-sync";
|
|
18
|
-
export type { GuideAudience, GuideMode } from "./progress";
|
|
20
|
+
export type { GuideAudience, GuideMode, PagesMode } from "./progress";
|
|
19
21
|
export { matchApiPath, matchRoute, fillRoute } from "./patterns";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small formatters for section guides' copy (shell 0.1.75): the app passes
|
|
3
|
+
* its own computed numbers, and the copy says them the way the page does —
|
|
4
|
+
* "ม.ค. รับ 500,000 − จ่าย 320,000 = คงเหลือ 180,000".
|
|
5
|
+
*
|
|
6
|
+
* Pure: the tests run it in Node.
|
|
7
|
+
*/
|
|
8
|
+
export declare const THAI_MONTHS_SHORT: readonly ["ม.ค.", "ก.พ.", "มี.ค.", "เม.ย.", "พ.ค.", "มิ.ย.", "ก.ค.", "ส.ค.", "ก.ย.", "ต.ค.", "พ.ย.", "ธ.ค."];
|
|
9
|
+
/**
|
|
10
|
+
* A baht amount with thousands separators, no currency sign: 500000 →
|
|
11
|
+
* "500,000"; 1234.5 → "1,234.50"; -320000 → "-320,000". Whole amounts
|
|
12
|
+
* have no decimals unless `decimals` says so; others have two. Not a
|
|
13
|
+
* finite number → "" (a step whose value is missing is skipped anyway).
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatBaht(n: unknown, opts?: {
|
|
16
|
+
decimals?: number;
|
|
17
|
+
}): string;
|
|
18
|
+
/** "2026-01" (or a Date, or 1) → "ม.ค."; "" when it is not a month. */
|
|
19
|
+
export declare function thaiMonth(m: unknown): string;
|
|
20
|
+
/**
|
|
21
|
+
* "2026-01" → "ม.ค. 69" (the Buddhist year's last two digits), or with
|
|
22
|
+
* `{ year: "full" }` "ม.ค. 2569"; just the month when no year is known.
|
|
23
|
+
*/
|
|
24
|
+
export declare function thaiMonthYear(m: unknown, opts?: {
|
|
25
|
+
year?: "short" | "full";
|
|
26
|
+
}): string;
|
|
@@ -11,14 +11,80 @@ export interface GuideEntry {
|
|
|
11
11
|
/** Called by the host: the entry to show (null: none), and how to open the panel. */
|
|
12
12
|
export declare function publishGuideEntry(next: GuideEntry | null, openPanel: (() => void) | null): void;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* has nothing to open, and "0/0" led to an empty panel. The entry itself
|
|
17
|
-
* stays published: apps wait on it before they load the facts that make
|
|
18
|
-
* missions visible.
|
|
14
|
+
* Page guides here, for the "?" menu: this page's guide (its title) and how
|
|
15
|
+
* many page guides the panel lists ("คู่มือทุกหน้า"). Shell ≥ 0.1.74.
|
|
19
16
|
*/
|
|
20
|
-
export
|
|
17
|
+
export interface PageGuidesEntry {
|
|
18
|
+
/** This page's guide for this person, or null. */
|
|
19
|
+
page: {
|
|
20
|
+
title: string;
|
|
21
|
+
} | null;
|
|
22
|
+
/** Page guides with an `href` this person has (the panel's list). */
|
|
23
|
+
listed: number;
|
|
24
|
+
/**
|
|
25
|
+
* Section guides on screen here that this person has ("อธิบายส่วนนี้ ·
|
|
26
|
+
* <title>"), in the suite's order. Shell ≥ 0.1.75.
|
|
27
|
+
*/
|
|
28
|
+
sections?: ReadonlyArray<{
|
|
29
|
+
key: string;
|
|
30
|
+
title: string;
|
|
31
|
+
}>;
|
|
32
|
+
}
|
|
33
|
+
/** Called by the host: this page's guide, the listed count, the sections on screen, and how to open them. */
|
|
34
|
+
export declare function publishPageGuides(next: PageGuidesEntry | null, openPage: (() => void) | null, openPages: (() => void) | null, openSection?: ((key: string) => void) | null): void;
|
|
35
|
+
/** The "?" menu's items, in order (pure: the menu and its tests share it). "sections": one entry per section guide. */
|
|
36
|
+
export type GuideMenuItem = "page" | "sections" | "missions" | "pages" | "help";
|
|
37
|
+
export declare function guideMenuItems(e: GuideEntry | null, p?: PageGuidesEntry): GuideMenuItem[];
|
|
38
|
+
/**
|
|
39
|
+
* Whether the "?" press opens a menu rather than the help dialog: a tour
|
|
40
|
+
* with missions shown, a guide for this page, or page guides to list. A tour
|
|
41
|
+
* whose missions are all hidden for this user (`visible`, e.g. Plannex
|
|
42
|
+
* without an employee record) has nothing to open, and "0/0" led to an
|
|
43
|
+
* empty panel. The entry itself stays published: apps wait on it before
|
|
44
|
+
* they load the facts that make missions visible.
|
|
45
|
+
*/
|
|
46
|
+
export declare function guideMenuShown(e: GuideEntry | null, p?: PageGuidesEntry): boolean;
|
|
21
47
|
/** Opens the tour's quest panel, when this user has a tour here. */
|
|
22
48
|
export declare function openGuidePanel(): void;
|
|
23
49
|
/** The "?" menu's entry, or null when there is no tour for this user. */
|
|
24
50
|
export declare function useGuideEntry(): GuideEntry | null;
|
|
51
|
+
/** Opens this page's guide ("คู่มือหน้านี้"), when there is one. */
|
|
52
|
+
export declare function openPageGuide(): void;
|
|
53
|
+
/** Opens the panel on the page guides alone ("คู่มือทุกหน้า"). */
|
|
54
|
+
export declare function openPagesPanel(): void;
|
|
55
|
+
/** Page guides here (this page's, and the listed count); stable until they change. */
|
|
56
|
+
export declare function usePageGuides(): PageGuidesEntry;
|
|
57
|
+
/**
|
|
58
|
+
* This page's guide: `{ title, open }`, or null when the page (or the
|
|
59
|
+
* sub-screen, `when`) has none for this person. For an app that draws its
|
|
60
|
+
* own "?" (LIFF's page head): draw it only when this is not null.
|
|
61
|
+
*/
|
|
62
|
+
export declare function usePageGuideEntry(): {
|
|
63
|
+
title: string;
|
|
64
|
+
open: () => void;
|
|
65
|
+
} | null;
|
|
66
|
+
/**
|
|
67
|
+
* Opens a section guide ("อธิบายส่วนนี้"), when it is on screen here for this
|
|
68
|
+
* person. The values it shows are the ones the app last rendered
|
|
69
|
+
* (`useSectionGuideValues`).
|
|
70
|
+
*/
|
|
71
|
+
export declare function openSectionGuide(key: string): void;
|
|
72
|
+
type Values = Readonly<Record<string, unknown>>;
|
|
73
|
+
/**
|
|
74
|
+
* Sets (or with undefined, clears) a section guide's live values. The
|
|
75
|
+
* engine reads the latest whenever it draws a step, so a page that
|
|
76
|
+
* re-renders mid-guide never leaves an old number on screen.
|
|
77
|
+
*/
|
|
78
|
+
export declare function setSectionGuideValues(key: string, values: Values | undefined): void;
|
|
79
|
+
/** A section guide's live values now (empty when the app has given none). */
|
|
80
|
+
export declare function getSectionGuideValues(key: string | null | undefined): Values;
|
|
81
|
+
/** Re-renders when any section guide's values change (the engine's own use). */
|
|
82
|
+
export declare function useSectionValuesVersion(): number;
|
|
83
|
+
/**
|
|
84
|
+
* Hands a section guide the numbers its copy uses, from the component that
|
|
85
|
+
* computes them: `useSectionGuideValues("cash-flow", { month: "ม.ค.", cashIn,
|
|
86
|
+
* … })`. Call it on every render (the latest wins); they go when the
|
|
87
|
+
* component unmounts. undefined values: nothing.
|
|
88
|
+
*/
|
|
89
|
+
export declare function useSectionGuideValues(key: string, values: Values | undefined): void;
|
|
90
|
+
export {};
|
|
@@ -10,4 +10,4 @@
|
|
|
10
10
|
* before 0.1.68: 16px from the bottom (+ the safe area) on a phone, 22px on
|
|
11
11
|
* desktop.
|
|
12
12
|
*/
|
|
13
|
-
export declare const GUIDE_CSS = "\n[data-erp-guide-layer]{position:fixed;inset:0;z-index:1000;pointer-events:none;font-family:inherit;color:var(--popover-foreground,#1c2634);\n --eg-scrim:rgba(12,20,34,.64);--eg-card:var(--popover,#fff);--eg-fg:var(--popover-foreground,#1c2634);\n --eg-muted:var(--muted-foreground,#6d7886);--eg-soft:var(--muted,#eef3f9);--eg-line:var(--border,#e8ecf1);\n --eg-primary:var(--primary,#3280dd);--eg-on-primary:var(--primary-foreground,#fff);\n --eg-tint:color-mix(in oklab,var(--eg-primary) 13%,var(--eg-card));--eg-strong:color-mix(in oklab,var(--eg-primary) 82%,#000);\n --eg-danger:var(--destructive,#d23b4b);--eg-ok:#1d9a5f;--eg-gold:#e6a417;\n --eg-shadow:0 18px 50px -12px rgba(16,32,64,.35),0 4px 12px rgba(16,32,64,.08);line-height:1.5;font-size:14px}\n[data-erp-guide-layer] *{box-sizing:border-box}\n[data-erp-guide-layer] button{font:inherit;color:inherit;cursor:pointer}\n.eg-i{pointer-events:auto}\n.eg-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n.eg-scrim-svg{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible}\n.eg-scrim-svg .eg-scrim-fill{fill:var(--eg-scrim);pointer-events:none}\n.eg-scrim-svg .eg-scrim-hit{fill:transparent;pointer-events:all}\n.eg-scrim{position:fixed;inset:0;background:var(--eg-scrim);pointer-events:auto}\n.eg-scrim.eg-light{background:rgba(12,20,34,.45)}\n.eg-scrim.eg-faint{background:rgba(12,20,34,.35)}\n.eg-ring{position:fixed;border-radius:12px;pointer-events:none;box-shadow:0 0 0 3px #fff,0 0 0 6px var(--eg-primary)}\n.eg-ring.eg-move{transition:left .26s ease,top .26s ease,width .26s ease,height .26s ease}\n.eg-ring::after{content:\"\";position:absolute;inset:-6px;border-radius:15px;border:3px solid var(--eg-primary);animation:eg-pulse 1.6s ease-out infinite}\n.eg-ring.eg-flash::after{animation:eg-flash .5s ease 1}\n@keyframes eg-pulse{0%{opacity:.9;transform:scale(1)}100%{opacity:0;transform:scale(1.12)}}\n@keyframes eg-flash{0%,100%{opacity:1;transform:none}50%{opacity:.2}}\n.eg-hand{position:fixed;width:46px;height:46px;pointer-events:none;filter:drop-shadow(0 4px 6px rgba(0,0,0,.35));animation:eg-tap 1.1s ease-in-out infinite}\n.eg-hand svg{width:46px;height:46px;fill:#fff;stroke:#1c2634;stroke-width:1.4;stroke-linejoin:round;stroke-linecap:round}\n.eg-hand.eg-flip svg{transform:scaleY(-1)}\n@keyframes eg-tap{0%,100%{transform:translate(6px,8px)}50%{transform:translate(0,0)}}\n.eg-bubble{position:fixed;left:0;top:0;width:320px;background:var(--eg-card);color:var(--eg-fg);border-radius:14px;box-shadow:var(--eg-shadow);padding:14px 16px 12px;border-top:4px solid var(--eg-primary);outline:none;pointer-events:auto}\n.eg-bubble.eg-err{border-top-color:var(--eg-danger)}\n.eg-bubble.eg-nudge{animation:eg-nudge .38s ease}\n@keyframes eg-nudge{0%,100%{translate:0}20%{translate:-7px}40%{translate:6px}60%{translate:-4px}80%{translate:2px}}\n.eg-b-top{display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:12px;color:var(--eg-muted)}\n.eg-b-top .eg-mis{font-weight:600;color:var(--eg-strong);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.eg-b-top .eg-cnt{white-space:nowrap;font-weight:600}\n.eg-pips{display:flex;gap:4px;margin:8px 0 10px}\n.eg-pips i{flex:1;height:4px;border-radius:2px;background:var(--eg-soft)}\n.eg-pips i.eg-d{background:var(--eg-primary)}\n.eg-pips i.eg-c{background:color-mix(in oklab,var(--eg-primary) 45%,var(--eg-card))}\n.eg-b-t{margin:0;font-size:16px;font-weight:600;line-height:1.45}\n.eg-b-w{margin:2px 0 0;font-size:13.5px;color:var(--eg-muted)}\n.eg-bubble.eg-err .eg-b-t{color:var(--eg-danger)}\n.eg-b-s{margin:6px 0 0;font-size:13px;font-weight:600;color:var(--eg-danger)}\n.eg-k{display:inline-block;background:var(--eg-tint);color:var(--eg-strong);border-radius:7px;padding:0 7px;font-weight:600}\n.eg-b-f{display:flex;align-items:center;justify-content:space-between;margin-top:12px;gap:8px}\n.eg-b-f .eg-btn{height:34px;padding:0 14px;font-size:13.5px}\n.eg-exit{border:0;background:none;color:var(--eg-muted);font-size:12.5px;padding:4px 0;text-decoration:underline;text-underline-offset:3px}\n.eg-exit:hover{color:var(--eg-fg)}\n.eg-arrow{position:absolute;width:14px;height:14px;background:var(--eg-card);transform:rotate(45deg);border-radius:2px}\n.eg-arrow.eg-top{background:var(--eg-primary)}\n.eg-bubble.eg-err .eg-arrow.eg-top{background:var(--eg-danger)}\n.eg-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:40px;padding:0 16px;border-radius:10px;border:1px solid transparent;font-size:14px;font-weight:600;white-space:nowrap}\n.eg-btn:focus-visible,.eg-exit:focus-visible,.eg-chip:focus-visible,.eg-x:focus-visible{outline:2px solid var(--eg-primary);outline-offset:2px}\n.eg-primary{background:var(--eg-primary);color:var(--eg-on-primary)!important}\n.eg-primary:hover{background:var(--eg-strong)}\n.eg-primary[disabled]{opacity:.55;cursor:default}\n.eg-ghost{background:var(--eg-card);border-color:var(--eg-line)}\n.eg-ghost:hover{background:var(--eg-soft)}\n.eg-ghost[disabled]{opacity:.55;cursor:default}\n.eg-link{background:none;border:0;color:var(--eg-muted);font-weight:500;padding:0 6px;height:32px}\n.eg-link:hover{color:var(--eg-fg);text-decoration:underline}\n.eg-modal{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:min(440px,calc(100vw - 32px));max-height:calc(100vh - 32px);overflow:auto;background:var(--eg-card);color:var(--eg-fg);border-radius:20px;box-shadow:var(--eg-shadow);padding:28px 28px 22px;text-align:center;outline:none;pointer-events:auto}\n.eg-modal.eg-pop{animation:eg-pop .32s cubic-bezier(.2,.9,.3,1.3)}\n@keyframes eg-pop{from{opacity:0;transform:translate(-50%,-46%) scale(.94)}}\n.eg-modal h2{margin:6px 0 4px;font-size:22px;font-weight:700;letter-spacing:-.2px;line-height:1.3}\n.eg-modal .eg-sub{margin:0;color:var(--eg-muted);font-size:14.5px}\n.eg-eyebrow{margin:0;font-size:12.5px;font-weight:600;letter-spacing:.3px;color:var(--eg-strong)}\n.eg-actions{display:grid;gap:6px;margin-top:20px}\n.eg-actions .eg-btn{height:44px}\n.eg-badge{width:64px;height:64px;border-radius:18px;margin:0 auto 12px;display:grid;place-items:center;background:var(--eg-tint);color:var(--eg-strong)}\n.eg-badge svg.eg-ic{width:30px;height:30px}\n.eg-quests{list-style:none;padding:0;margin:18px 0 0;text-align:left;display:grid;gap:6px}\n.eg-quests li{display:flex;align-items:center;gap:12px;padding:9px 12px;border-radius:11px;background:var(--eg-soft);font-size:14px}\n.eg-quests li .eg-n{width:26px;height:26px;border-radius:50%;background:var(--eg-card);border:1.5px solid var(--eg-line);display:grid;place-items:center;font-size:12px;font-weight:700;color:var(--eg-muted);flex:none}\n.eg-quests li.eg-first{background:var(--eg-tint)}\n.eg-quests li.eg-first .eg-n{border-color:var(--eg-primary);color:var(--eg-strong)}\n.eg-quests li.eg-ok .eg-n{background:var(--eg-ok);border-color:var(--eg-ok);color:#fff}\n.eg-quests li .eg-n svg.eg-ic{width:15px;height:15px;stroke-width:3}\n.eg-quests li .eg-t{flex:1}\n.eg-quests li small{color:var(--eg-muted);font-size:12px;white-space:nowrap}\n.eg-modal.eg-confirm{text-align:left;width:min(380px,calc(100vw - 32px));padding:22px}\n.eg-modal.eg-confirm h2{font-size:18px;margin-top:0}\n.eg-modal.eg-confirm .eg-actions{grid-template-columns:1fr 1fr}\n.eg-medal{width:84px;height:84px;border-radius:50%;margin:4px auto 10px;display:grid;place-items:center;color:#fff;background:radial-gradient(circle at 35% 30%,#ffd666,var(--eg-gold));box-shadow:0 0 0 8px #fff4d6;animation:eg-stamp .5s cubic-bezier(.2,.9,.3,1.4) .1s both}\n.eg-medal svg.eg-ic{width:42px;height:42px;stroke-width:2.6}\n@keyframes eg-stamp{from{transform:scale(.3) rotate(-25deg);opacity:0}}\n.eg-prog{display:flex;align-items:center;gap:10px;margin-top:16px;font-size:13px;color:var(--eg-muted);font-weight:600}\n.eg-prog .eg-bar{flex:1;height:8px;border-radius:4px;background:var(--eg-soft);overflow:hidden}\n.eg-prog .eg-bar i{display:block;height:100%;background:var(--eg-primary);border-radius:4px;transition:width .9s cubic-bezier(.2,.8,.2,1) .35s}\n.eg-burst{position:absolute;left:50%;top:70px;width:0;height:0;pointer-events:none}\n.eg-burst span{position:absolute;width:8px;height:12px;border-radius:2px;opacity:0;animation:eg-conf 1.1s cubic-bezier(.15,.7,.3,1) forwards}\n@keyframes eg-conf{0%{opacity:1;transform:translate(0,0) rotate(0)}100%{opacity:0;transform:translate(var(--x),var(--y)) rotate(var(--r))}}\n.eg-card{position:fixed;left:50%;bottom:calc(var(--erp-guide-bottom-offset,16px) + 12px);transform:translateX(-50%);width:min(360px,calc(100vw - 32px));background:var(--eg-card);color:var(--eg-fg);border-radius:14px;box-shadow:var(--eg-shadow);padding:14px 16px;border-top:4px solid var(--eg-primary);pointer-events:auto;outline:none}\n.eg-card .eg-row{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:12px}\n.eg-loading{display:flex;align-items:center;gap:10px;font-weight:600}\n.eg-spin{width:18px;height:18px;border-radius:50%;border:2.5px solid var(--eg-soft);border-top-color:var(--eg-primary);animation:eg-spin .8s linear infinite;flex:none}\n@keyframes eg-spin{to{transform:rotate(360deg)}}\n.eg-chip{position:fixed;right:22px;bottom:var(--erp-guide-bottom-offset,22px);display:flex;align-items:center;gap:10px;height:48px;padding:0 18px 0 8px;border-radius:24px;border:1px solid var(--eg-line);background:var(--eg-card);color:var(--eg-fg);box-shadow:0 8px 24px -8px rgba(16,32,64,.3);font-weight:600;font-size:14px;pointer-events:auto}\n.eg-chip svg{width:34px;height:34px}\n.eg-chip.eg-hi{animation:eg-hi 1.2s ease 2}\n@keyframes eg-hi{50%{box-shadow:0 0 0 6px color-mix(in oklab,var(--eg-primary) 35%,transparent),0 8px 24px -8px rgba(16,32,64,.3)}}\n.eg-panel{position:fixed;right:22px;bottom:calc(var(--erp-guide-bottom-offset,22px) + 60px);width:360px;max-height:calc(100vh - 110px);overflow:auto;background:var(--eg-card);color:var(--eg-fg);border:1px solid var(--eg-line);border-radius:16px;box-shadow:var(--eg-shadow);outline:none;pointer-events:auto}\n.eg-panel-bd{display:none}\n.eg-q-h{display:flex;align-items:center;gap:10px;padding:16px 12px 4px 18px}\n.eg-q-h h2{margin:0;font-size:16px;font-weight:700;flex:1}\n.eg-q-h .eg-n{font-size:13px;color:var(--eg-muted);font-weight:600}\n.eg-x{width:34px;height:34px;border-radius:10px;border:0;background:none;display:grid;place-items:center;color:var(--eg-muted)}\n.eg-x:hover{background:var(--eg-soft)}\n.eg-x svg.eg-ic{width:18px;height:18px}\n.eg-panel .eg-prog{margin:6px 18px 8px}\n.eg-q-list{list-style:none;margin:0;padding:4px 10px 6px;display:grid;gap:2px}\n.eg-q-item{display:flex;align-items:center;gap:12px;padding:10px 8px;border-radius:11px}\n.eg-q-item.eg-next{background:var(--eg-tint)}\n.eg-q-item .eg-n{width:30px;height:30px;border-radius:50%;border:1.5px solid var(--eg-line);display:grid;place-items:center;font-size:12.5px;font-weight:700;color:var(--eg-muted);flex:none;background:var(--eg-card)}\n.eg-q-item.eg-next .eg-n{border-color:var(--eg-primary);color:var(--eg-strong)}\n.eg-q-item.eg-done .eg-n{background:var(--eg-ok);border-color:var(--eg-ok);color:#fff}\n.eg-q-item.eg-done .eg-n svg.eg-ic{width:16px;height:16px;stroke-width:3}\n.eg-q-item.eg-dim .eg-n{opacity:.55}\n.eg-q-item.eg-just .eg-n{animation:eg-stamp .55s cubic-bezier(.2,.9,.3,1.4) .2s both}\n.eg-q-item .eg-t{flex:1;min-width:0}\n.eg-q-item .eg-t b{display:block;font-size:14px;font-weight:600}\n.eg-q-item.eg-done .eg-t b{color:var(--eg-muted)}\n.eg-q-item .eg-t small{display:block;font-size:12px;color:var(--eg-muted)}\n.eg-q-item .eg-t small.eg-sk{color:#b06a00}\n.eg-q-item .eg-t small.eg-lock{color:var(--eg-fg);font-weight:500}\n.eg-q-item .eg-btn{height:32px;padding:0 12px;font-size:13px}\n.eg-q-acts{display:flex;flex-direction:column;align-items:flex-end;gap:2px}\n.eg-q-acts .eg-link{height:22px;font-size:12px;padding:0 2px}\n.eg-q-f{border-top:1px solid var(--eg-line);padding:8px 12px;display:flex;justify-content:flex-end}\n.eg-toast{position:fixed;left:50%;bottom:calc(var(--erp-guide-bottom-offset,22px) + 62px);transform:translateX(-50%);background:#1c2634;color:#fff;padding:10px 16px;border-radius:10px;font-size:13.5px;box-shadow:var(--eg-shadow);max-width:calc(100vw - 32px);pointer-events:none}\nsvg.eg-ic{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex:none}\n@media (max-width:767px){\n .eg-bubble{width:auto}\n .eg-arrow{display:none}\n .eg-modal{top:auto;bottom:12px;transform:translateX(-50%);width:calc(100vw - 24px);padding:24px 20px 18px}\n .eg-modal.eg-pop{animation:eg-up .3s cubic-bezier(.2,.9,.3,1.1)}\n .eg-modal .eg-actions .eg-btn{height:46px}\n .eg-chip{right:16px;bottom:var(--erp-guide-bottom-offset,calc(16px + env(safe-area-inset-bottom)))}\n .eg-panel-bd{display:block;position:fixed;inset:0;background:rgba(15,23,42,.38);pointer-events:auto}\n .eg-panel{left:0;right:0;bottom:0;width:100%;max-height:85vh;border-radius:18px 18px 0 0;padding-bottom:env(safe-area-inset-bottom)}\n .eg-q-item{padding:12px 8px}\n .eg-card{bottom:calc(var(--erp-guide-bottom-offset,calc(16px + env(safe-area-inset-bottom))) - 4px)}\n .eg-toast{bottom:calc(var(--erp-guide-bottom-offset,calc(16px + env(safe-area-inset-bottom))) + 60px)}\n}\n@keyframes eg-up{from{opacity:0;transform:translate(-50%,30px)}}\n@media (prefers-reduced-motion:reduce){\n [data-erp-guide-layer] *,[data-erp-guide-layer] *::before,[data-erp-guide-layer] *::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}\n .eg-ring::after{animation:none!important;opacity:1;transform:none}\n .eg-ring.eg-flash::after{animation:eg-flash .5s ease 1!important;animation-duration:.5s!important}\n .eg-hand{animation:none!important;transform:translate(4px,6px)}\n .eg-burst{display:none}\n .eg-bubble.eg-nudge{animation:none!important;border-top-color:var(--eg-danger)}\n}\n";
|
|
13
|
+
export declare const GUIDE_CSS = "\n[data-erp-guide-layer]{position:fixed;inset:0;z-index:1000;pointer-events:none;font-family:inherit;color:var(--popover-foreground,#1c2634);\n --eg-scrim:rgba(12,20,34,.64);--eg-card:var(--popover,#fff);--eg-fg:var(--popover-foreground,#1c2634);\n --eg-muted:var(--muted-foreground,#6d7886);--eg-soft:var(--muted,#eef3f9);--eg-line:var(--border,#e8ecf1);\n --eg-primary:var(--primary,#3280dd);--eg-on-primary:var(--primary-foreground,#fff);\n --eg-tint:color-mix(in oklab,var(--eg-primary) 13%,var(--eg-card));--eg-strong:color-mix(in oklab,var(--eg-primary) 82%,#000);\n --eg-danger:var(--destructive,#d23b4b);--eg-ok:#1d9a5f;--eg-gold:#e6a417;\n --eg-shadow:0 18px 50px -12px rgba(16,32,64,.35),0 4px 12px rgba(16,32,64,.08);line-height:1.5;font-size:14px}\n[data-erp-guide-layer] *{box-sizing:border-box}\n[data-erp-guide-layer] button{font:inherit;color:inherit;cursor:pointer}\n.eg-i{pointer-events:auto}\n.eg-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}\n.eg-scrim-svg{position:fixed;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible}\n.eg-scrim-svg .eg-scrim-fill{fill:var(--eg-scrim);pointer-events:none}\n.eg-scrim-svg .eg-scrim-hit{fill:transparent;pointer-events:all}\n.eg-scrim{position:fixed;inset:0;background:var(--eg-scrim);pointer-events:auto}\n.eg-scrim.eg-light{background:rgba(12,20,34,.45)}\n.eg-scrim.eg-faint{background:rgba(12,20,34,.35)}\n.eg-ring{position:fixed;border-radius:12px;pointer-events:none;box-shadow:0 0 0 3px #fff,0 0 0 6px var(--eg-primary)}\n.eg-ring.eg-move{transition:left .26s ease,top .26s ease,width .26s ease,height .26s ease}\n.eg-ring::after{content:\"\";position:absolute;inset:-6px;border-radius:15px;border:3px solid var(--eg-primary);animation:eg-pulse 1.6s ease-out infinite}\n.eg-ring.eg-flash::after{animation:eg-flash .5s ease 1}\n@keyframes eg-pulse{0%{opacity:.9;transform:scale(1)}100%{opacity:0;transform:scale(1.12)}}\n@keyframes eg-flash{0%,100%{opacity:1;transform:none}50%{opacity:.2}}\n.eg-hand{position:fixed;width:46px;height:46px;pointer-events:none;filter:drop-shadow(0 4px 6px rgba(0,0,0,.35));animation:eg-tap 1.1s ease-in-out infinite}\n.eg-hand svg{width:46px;height:46px;fill:#fff;stroke:#1c2634;stroke-width:1.4;stroke-linejoin:round;stroke-linecap:round}\n.eg-hand.eg-flip svg{transform:scaleY(-1)}\n@keyframes eg-tap{0%,100%{transform:translate(6px,8px)}50%{transform:translate(0,0)}}\n.eg-bubble{position:fixed;left:0;top:0;width:320px;background:var(--eg-card);color:var(--eg-fg);border-radius:14px;box-shadow:var(--eg-shadow);padding:14px 16px 12px;border-top:4px solid var(--eg-primary);outline:none;pointer-events:auto}\n.eg-bubble.eg-err{border-top-color:var(--eg-danger)}\n.eg-bubble.eg-nudge{animation:eg-nudge .38s ease}\n@keyframes eg-nudge{0%,100%{translate:0}20%{translate:-7px}40%{translate:6px}60%{translate:-4px}80%{translate:2px}}\n.eg-b-top{display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:12px;color:var(--eg-muted)}\n.eg-b-top .eg-mis{font-weight:600;color:var(--eg-strong);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.eg-b-top .eg-cnt{white-space:nowrap;font-weight:600}\n.eg-pips{display:flex;gap:4px;margin:8px 0 10px}\n.eg-pips i{flex:1;height:4px;border-radius:2px;background:var(--eg-soft)}\n.eg-pips i.eg-d{background:var(--eg-primary)}\n.eg-pips i.eg-c{background:color-mix(in oklab,var(--eg-primary) 45%,var(--eg-card))}\n.eg-b-t{margin:0;font-size:16px;font-weight:600;line-height:1.45}\n.eg-b-w{margin:2px 0 0;font-size:13.5px;color:var(--eg-muted)}\n.eg-bubble.eg-err .eg-b-t{color:var(--eg-danger)}\n.eg-b-s{margin:6px 0 0;font-size:13px;font-weight:600;color:var(--eg-danger)}\n.eg-k{display:inline-block;background:var(--eg-tint);color:var(--eg-strong);border-radius:7px;padding:0 7px;font-weight:600}\n.eg-b-f{display:flex;align-items:center;justify-content:space-between;margin-top:12px;gap:8px}\n.eg-b-f .eg-btn{height:34px;padding:0 14px;font-size:13.5px}\n.eg-exit{border:0;background:none;color:var(--eg-muted);font-size:12.5px;padding:4px 0;text-decoration:underline;text-underline-offset:3px}\n.eg-exit:hover{color:var(--eg-fg)}\n.eg-arrow{position:absolute;width:14px;height:14px;background:var(--eg-card);transform:rotate(45deg);border-radius:2px}\n.eg-arrow.eg-top{background:var(--eg-primary)}\n.eg-bubble.eg-err .eg-arrow.eg-top{background:var(--eg-danger)}\n.eg-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;height:40px;padding:0 16px;border-radius:10px;border:1px solid transparent;font-size:14px;font-weight:600;white-space:nowrap}\n.eg-btn:focus-visible,.eg-exit:focus-visible,.eg-chip:focus-visible,.eg-x:focus-visible{outline:2px solid var(--eg-primary);outline-offset:2px}\n.eg-primary{background:var(--eg-primary);color:var(--eg-on-primary)!important}\n.eg-primary:hover{background:var(--eg-strong)}\n.eg-primary[disabled]{opacity:.55;cursor:default}\n.eg-ghost{background:var(--eg-card);border-color:var(--eg-line)}\n.eg-ghost:hover{background:var(--eg-soft)}\n.eg-ghost[disabled]{opacity:.55;cursor:default}\n.eg-link{background:none;border:0;color:var(--eg-muted);font-weight:500;padding:0 6px;height:32px}\n.eg-link:hover{color:var(--eg-fg);text-decoration:underline}\n.eg-modal{position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);width:min(440px,calc(100vw - 32px));max-height:calc(100vh - 32px);overflow:auto;background:var(--eg-card);color:var(--eg-fg);border-radius:20px;box-shadow:var(--eg-shadow);padding:28px 28px 22px;text-align:center;outline:none;pointer-events:auto}\n.eg-modal.eg-pop{animation:eg-pop .32s cubic-bezier(.2,.9,.3,1.3)}\n@keyframes eg-pop{from{opacity:0;transform:translate(-50%,-46%) scale(.94)}}\n.eg-modal h2{margin:6px 0 4px;font-size:22px;font-weight:700;letter-spacing:-.2px;line-height:1.3}\n.eg-modal .eg-sub{margin:0;color:var(--eg-muted);font-size:14.5px}\n.eg-eyebrow{margin:0;font-size:12.5px;font-weight:600;letter-spacing:.3px;color:var(--eg-strong)}\n.eg-actions{display:grid;gap:6px;margin-top:20px}\n.eg-actions .eg-btn{height:44px}\n.eg-badge{width:64px;height:64px;border-radius:18px;margin:0 auto 12px;display:grid;place-items:center;background:var(--eg-tint);color:var(--eg-strong)}\n.eg-badge svg.eg-ic{width:30px;height:30px}\n.eg-quests{list-style:none;padding:0;margin:18px 0 0;text-align:left;display:grid;gap:6px}\n.eg-quests li{display:flex;align-items:center;gap:12px;padding:9px 12px;border-radius:11px;background:var(--eg-soft);font-size:14px}\n.eg-quests li .eg-n{width:26px;height:26px;border-radius:50%;background:var(--eg-card);border:1.5px solid var(--eg-line);display:grid;place-items:center;font-size:12px;font-weight:700;color:var(--eg-muted);flex:none}\n.eg-quests li.eg-first{background:var(--eg-tint)}\n.eg-quests li.eg-first .eg-n{border-color:var(--eg-primary);color:var(--eg-strong)}\n.eg-quests li.eg-ok .eg-n{background:var(--eg-ok);border-color:var(--eg-ok);color:#fff}\n.eg-quests li .eg-n svg.eg-ic{width:15px;height:15px;stroke-width:3}\n.eg-quests li .eg-t{flex:1}\n.eg-quests li small{color:var(--eg-muted);font-size:12px;white-space:nowrap}\n.eg-modal.eg-confirm{text-align:left;width:min(380px,calc(100vw - 32px));padding:22px}\n.eg-modal.eg-confirm h2{font-size:18px;margin-top:0}\n.eg-modal.eg-confirm .eg-actions{grid-template-columns:1fr 1fr}\n.eg-medal{width:84px;height:84px;border-radius:50%;margin:4px auto 10px;display:grid;place-items:center;color:#fff;background:radial-gradient(circle at 35% 30%,#ffd666,var(--eg-gold));box-shadow:0 0 0 8px #fff4d6;animation:eg-stamp .5s cubic-bezier(.2,.9,.3,1.4) .1s both}\n.eg-medal svg.eg-ic{width:42px;height:42px;stroke-width:2.6}\n@keyframes eg-stamp{from{transform:scale(.3) rotate(-25deg);opacity:0}}\n.eg-prog{display:flex;align-items:center;gap:10px;margin-top:16px;font-size:13px;color:var(--eg-muted);font-weight:600}\n.eg-prog .eg-bar{flex:1;height:8px;border-radius:4px;background:var(--eg-soft);overflow:hidden}\n.eg-prog .eg-bar i{display:block;height:100%;background:var(--eg-primary);border-radius:4px;transition:width .9s cubic-bezier(.2,.8,.2,1) .35s}\n.eg-burst{position:absolute;left:50%;top:70px;width:0;height:0;pointer-events:none}\n.eg-burst span{position:absolute;width:8px;height:12px;border-radius:2px;opacity:0;animation:eg-conf 1.1s cubic-bezier(.15,.7,.3,1) forwards}\n@keyframes eg-conf{0%{opacity:1;transform:translate(0,0) rotate(0)}100%{opacity:0;transform:translate(var(--x),var(--y)) rotate(var(--r))}}\n.eg-card{position:fixed;left:50%;bottom:calc(var(--erp-guide-bottom-offset,16px) + 12px);transform:translateX(-50%);width:min(360px,calc(100vw - 32px));background:var(--eg-card);color:var(--eg-fg);border-radius:14px;box-shadow:var(--eg-shadow);padding:14px 16px;border-top:4px solid var(--eg-primary);pointer-events:auto;outline:none}\n.eg-card .eg-row{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-top:12px}\n.eg-loading{display:flex;align-items:center;gap:10px;font-weight:600}\n.eg-spin{width:18px;height:18px;border-radius:50%;border:2.5px solid var(--eg-soft);border-top-color:var(--eg-primary);animation:eg-spin .8s linear infinite;flex:none}\n@keyframes eg-spin{to{transform:rotate(360deg)}}\n.eg-chip{position:fixed;right:22px;bottom:var(--erp-guide-bottom-offset,22px);display:flex;align-items:center;gap:10px;height:48px;padding:0 18px 0 8px;border-radius:24px;border:1px solid var(--eg-line);background:var(--eg-card);color:var(--eg-fg);box-shadow:0 8px 24px -8px rgba(16,32,64,.3);font-weight:600;font-size:14px;pointer-events:auto}\n.eg-chip svg{width:34px;height:34px}\n.eg-chip.eg-hi{animation:eg-hi 1.2s ease 2}\n@keyframes eg-hi{50%{box-shadow:0 0 0 6px color-mix(in oklab,var(--eg-primary) 35%,transparent),0 8px 24px -8px rgba(16,32,64,.3)}}\n.eg-panel{position:fixed;right:22px;bottom:calc(var(--erp-guide-bottom-offset,22px) + 60px);width:360px;max-height:calc(100vh - 110px);overflow:auto;background:var(--eg-card);color:var(--eg-fg);border:1px solid var(--eg-line);border-radius:16px;box-shadow:var(--eg-shadow);outline:none;pointer-events:auto}\n.eg-panel-bd{display:none}\n.eg-q-h{display:flex;align-items:center;gap:10px;padding:16px 12px 4px 18px}\n.eg-q-h h2{margin:0;font-size:16px;font-weight:700;flex:1}\n.eg-q-h .eg-n{font-size:13px;color:var(--eg-muted);font-weight:600}\n.eg-x{width:34px;height:34px;border-radius:10px;border:0;background:none;display:grid;place-items:center;color:var(--eg-muted)}\n.eg-x:hover{background:var(--eg-soft)}\n.eg-x svg.eg-ic{width:18px;height:18px}\n.eg-panel .eg-prog{margin:6px 18px 8px}\n.eg-q-list{list-style:none;margin:0;padding:4px 10px 6px;display:grid;gap:2px}\n.eg-q-item{display:flex;align-items:center;gap:12px;padding:10px 8px;border-radius:11px}\n.eg-q-item.eg-next{background:var(--eg-tint)}\n.eg-q-item .eg-n{width:30px;height:30px;border-radius:50%;border:1.5px solid var(--eg-line);display:grid;place-items:center;font-size:12.5px;font-weight:700;color:var(--eg-muted);flex:none;background:var(--eg-card)}\n.eg-q-item.eg-next .eg-n{border-color:var(--eg-primary);color:var(--eg-strong)}\n.eg-q-item.eg-done .eg-n{background:var(--eg-ok);border-color:var(--eg-ok);color:#fff}\n.eg-q-item.eg-done .eg-n svg.eg-ic{width:16px;height:16px;stroke-width:3}\n.eg-q-item.eg-dim .eg-n{opacity:.55}\n.eg-q-item.eg-just .eg-n{animation:eg-stamp .55s cubic-bezier(.2,.9,.3,1.4) .2s both}\n.eg-q-item .eg-t{flex:1;min-width:0}\n.eg-q-item .eg-t b{display:block;font-size:14px;font-weight:600}\n.eg-q-item.eg-done .eg-t b{color:var(--eg-muted)}\n.eg-q-item .eg-t small{display:block;font-size:12px;color:var(--eg-muted)}\n.eg-q-item .eg-t small.eg-sk{color:#b06a00}\n.eg-q-item .eg-t small.eg-lock{color:var(--eg-fg);font-weight:500}\n.eg-q-item .eg-btn{height:32px;padding:0 12px;font-size:13px}\n.eg-q-acts{display:flex;flex-direction:column;align-items:flex-end;gap:2px}\n.eg-q-acts .eg-link{height:22px;font-size:12px;padding:0 2px}\n.eg-q-f{border-top:1px solid var(--eg-line);padding:8px 12px;display:flex;justify-content:flex-end}\n.eg-q-sub{margin:6px 0 0;padding:12px 18px 2px;border-top:1px solid var(--eg-line);font-size:13px;font-weight:700;color:var(--eg-muted)}\n.eg-pg-item{padding:8px}\n.eg-pg-item .eg-t small.eg-new{color:var(--eg-strong);font-weight:600}\n.eg-card.eg-offer{left:auto;right:22px;transform:none;bottom:var(--erp-guide-bottom-offset,22px);width:min(320px,calc(100vw - 32px))}\n.eg-offer .eg-po-e{margin:0 0 2px;font-size:12px;font-weight:600;color:var(--eg-strong)}\n.eg-offer .eg-row .eg-btn{height:34px;padding:0 14px;font-size:13.5px}\n.eg-toast{position:fixed;left:50%;bottom:calc(var(--erp-guide-bottom-offset,22px) + 62px);transform:translateX(-50%);background:#1c2634;color:#fff;padding:10px 16px;border-radius:10px;font-size:13.5px;box-shadow:var(--eg-shadow);max-width:calc(100vw - 32px);pointer-events:none}\nsvg.eg-ic{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex:none}\n@media (max-width:767px){\n .eg-bubble{width:auto}\n .eg-arrow{display:none}\n .eg-modal{top:auto;bottom:12px;transform:translateX(-50%);width:calc(100vw - 24px);padding:24px 20px 18px}\n .eg-modal.eg-pop{animation:eg-up .3s cubic-bezier(.2,.9,.3,1.1)}\n .eg-modal .eg-actions .eg-btn{height:46px}\n .eg-chip{right:16px;bottom:var(--erp-guide-bottom-offset,calc(16px + env(safe-area-inset-bottom)))}\n .eg-panel-bd{display:block;position:fixed;inset:0;background:rgba(15,23,42,.38);pointer-events:auto}\n .eg-panel{left:0;right:0;bottom:0;width:100%;max-height:85vh;border-radius:18px 18px 0 0;padding-bottom:env(safe-area-inset-bottom)}\n .eg-q-item{padding:12px 8px}\n .eg-card{bottom:calc(var(--erp-guide-bottom-offset,calc(16px + env(safe-area-inset-bottom))) - 4px)}\n .eg-card.eg-offer{left:16px;right:16px;width:auto;bottom:calc(var(--erp-guide-bottom-offset,calc(16px + env(safe-area-inset-bottom))) - 4px)}\n .eg-toast{bottom:calc(var(--erp-guide-bottom-offset,calc(16px + env(safe-area-inset-bottom))) + 60px)}\n}\n@keyframes eg-up{from{opacity:0;transform:translate(-50%,30px)}}\n@media (prefers-reduced-motion:reduce){\n [data-erp-guide-layer] *,[data-erp-guide-layer] *::before,[data-erp-guide-layer] *::after{animation-duration:.001ms!important;animation-iteration-count:1!important;transition-duration:.001ms!important}\n .eg-ring::after{animation:none!important;opacity:1;transform:none}\n .eg-ring.eg-flash::after{animation:eg-flash .5s ease 1!important;animation-duration:.5s!important}\n .eg-hand{animation:none!important;transform:translate(4px,6px)}\n .eg-burst{display:none}\n .eg-bubble.eg-nudge{animation:none!important;border-top-color:var(--eg-danger)}\n}\n";
|
package/dist/guide/guide-ui.d.ts
CHANGED
|
@@ -60,6 +60,17 @@ export interface BubbleModel {
|
|
|
60
60
|
status?: string | null;
|
|
61
61
|
showNext: boolean;
|
|
62
62
|
showEndWithoutSave: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* The bubble's top line in place of "ภารกิจ n · title": a page guide's
|
|
65
|
+
* "คู่มือหน้านี้ · <title>". Shell ≥ 0.1.74.
|
|
66
|
+
*/
|
|
67
|
+
heading?: string;
|
|
68
|
+
/** The way out, in place of "จบการทบทวน" / "ออกจากไกด์" (a page guide: "ปิดคู่มือ"). */
|
|
69
|
+
exitLabel?: string;
|
|
70
|
+
/** ย้อนกลับ: a page guide's read step with a step before it. */
|
|
71
|
+
showBack?: boolean;
|
|
72
|
+
/** ถัดไป's label in place of "ถัดไป" (a page guide's last step: "เสร็จ"). */
|
|
73
|
+
nextLabel?: string;
|
|
63
74
|
}
|
|
64
75
|
export interface StepLayerProps {
|
|
65
76
|
/** The spotlight (padded target), or null while there is none to show. */
|
|
@@ -101,6 +112,8 @@ export interface StepLayerProps {
|
|
|
101
112
|
moving: boolean;
|
|
102
113
|
onExit: () => void;
|
|
103
114
|
onNext: () => void;
|
|
115
|
+
/** ย้อนกลับ (BubbleModel.showBack). */
|
|
116
|
+
onBack?: () => void;
|
|
104
117
|
onEndWithoutSave: () => void;
|
|
105
118
|
onRetry: () => void;
|
|
106
119
|
onGoResume: () => void;
|
|
@@ -170,6 +183,23 @@ export declare function CompleteModal(p: {
|
|
|
170
183
|
reduced: boolean;
|
|
171
184
|
onFinish: () => void;
|
|
172
185
|
}): import("react/jsx-runtime").JSX.Element;
|
|
186
|
+
/**
|
|
187
|
+
* The offer of this page's guide on a first visit: a small card in the
|
|
188
|
+
* corner, not modal (the page stays usable; focus is not taken). Shell ≥ 0.1.74.
|
|
189
|
+
*/
|
|
190
|
+
export declare function PageOfferCard(p: {
|
|
191
|
+
title: string;
|
|
192
|
+
steps: number;
|
|
193
|
+
onStart: () => void;
|
|
194
|
+
onLater: () => void;
|
|
195
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
196
|
+
/** One page guide in the panel's "คู่มือแต่ละหน้า" list. */
|
|
197
|
+
export interface PageItem {
|
|
198
|
+
key: string;
|
|
199
|
+
title: string;
|
|
200
|
+
/** This person has ended this version (ดูแล้ว); otherwise ใหม่. */
|
|
201
|
+
seen: boolean;
|
|
202
|
+
}
|
|
173
203
|
export declare function QuestChip({ done, total, pulse, onOpen }: {
|
|
174
204
|
done: number;
|
|
175
205
|
total: number;
|
|
@@ -201,6 +231,14 @@ export declare function QuestPanel(p: {
|
|
|
201
231
|
onStart: (key: string, replay: boolean) => void;
|
|
202
232
|
onDismiss: (key: string) => void;
|
|
203
233
|
onHideChip: () => void;
|
|
234
|
+
/**
|
|
235
|
+
* The second section, "คู่มือแต่ละหน้า": the page guides this person has
|
|
236
|
+
* (shell ≥ 0.1.74). Omitted or empty: no section.
|
|
237
|
+
*/
|
|
238
|
+
pages?: PageItem[];
|
|
239
|
+
/** false: the page guides alone (no missions for this person, or "คู่มือทุกหน้า"). */
|
|
240
|
+
showMissions?: boolean;
|
|
241
|
+
onOpenPage?: (key: string) => void;
|
|
204
242
|
}): import("react/jsx-runtime").JSX.Element;
|
|
205
243
|
export declare function GuideToast({ text }: {
|
|
206
244
|
text: string;
|
package/dist/guide/machine.d.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { GuideAbortAction, GuideContext, GuideMission, GuideStep, GuideSuite, GuideText, GuideTrigger } from "./types";
|
|
10
10
|
import { type RouteParams } from "./patterns";
|
|
11
|
-
import type { GuideAudience, GuideProgress, MissionEntry } from "./progress";
|
|
11
|
+
import type { GuideAudience, GuideProgress, MissionEntry, PageRow, PagesMode } from "./progress";
|
|
12
|
+
import { currentPage } from "./pages";
|
|
12
13
|
export type StepPhase =
|
|
13
14
|
/** Looking for the target (dim page, "กำลังโหลด…"). */
|
|
14
15
|
"waiting"
|
|
@@ -55,6 +56,15 @@ export type GuideScreen = {
|
|
|
55
56
|
* need not change a real record to go on. Shell ≥ 0.1.73.
|
|
56
57
|
*/
|
|
57
58
|
replayNext: boolean;
|
|
59
|
+
/** A page guide's step: the page key (the mission is `page:<key>`). Shell ≥ 0.1.74. */
|
|
60
|
+
page?: string;
|
|
61
|
+
/** A section guide's step: the section key (the mission is `section:<key>`). Shell ≥ 0.1.75. */
|
|
62
|
+
section?: string;
|
|
63
|
+
}
|
|
64
|
+
/** The first-visit offer of this page's guide: a small card, not modal. */
|
|
65
|
+
| {
|
|
66
|
+
name: "pageOffer";
|
|
67
|
+
page: string;
|
|
58
68
|
} | {
|
|
59
69
|
name: "celebrate";
|
|
60
70
|
mission: string;
|
|
@@ -84,7 +94,44 @@ export interface GuideState {
|
|
|
84
94
|
* the next idle moment (never mid-step). Not saved: a reload drops it.
|
|
85
95
|
*/
|
|
86
96
|
pendingCelebrate: string[];
|
|
97
|
+
/** Who gets page guides ("optional" for every member while they are on). */
|
|
98
|
+
pageAudience: GuideAudience;
|
|
99
|
+
/** How page guides are offered: off, from "?" only, or also on a first visit. */
|
|
100
|
+
pagesMode: PagesMode;
|
|
101
|
+
/** Page rows by page key, as the server holds them (or as just written). */
|
|
102
|
+
pages: Record<string, PageRow>;
|
|
103
|
+
/** Section rows by section key (`<app>.section.<key>`). Shell ≥ 0.1.75. */
|
|
104
|
+
sections: Record<string, PageRow>;
|
|
105
|
+
/**
|
|
106
|
+
* The page or section guide being played: `write` when its row is still
|
|
107
|
+
* to be written (the offer was taken, or this person has never ended this
|
|
108
|
+
* version); a replay of a recorded one writes nothing.
|
|
109
|
+
*/
|
|
110
|
+
pageRun: {
|
|
111
|
+
key: string;
|
|
112
|
+
write: boolean;
|
|
113
|
+
kind?: ReadKind;
|
|
114
|
+
} | null;
|
|
115
|
+
/** What the panel shows: everything, or the page guides alone ("คู่มือทุกหน้า"). */
|
|
116
|
+
panelSection: "all" | "pages";
|
|
117
|
+
/**
|
|
118
|
+
* Steps of the mission being played that passed by themselves (a click
|
|
119
|
+
* whose outcome already held): not counted in "ขั้นที่ x/y". Shell ≥ 0.1.75.
|
|
120
|
+
*/
|
|
121
|
+
autoPassed: {
|
|
122
|
+
mission: string;
|
|
123
|
+
steps: number[];
|
|
124
|
+
} | null;
|
|
87
125
|
}
|
|
126
|
+
/** The page-guide part of the state, from the progress answer. */
|
|
127
|
+
export interface GuidePagesInit {
|
|
128
|
+
pageAudience: GuideAudience;
|
|
129
|
+
pagesMode: PagesMode;
|
|
130
|
+
pages: Record<string, PageRow>;
|
|
131
|
+
sections?: Record<string, PageRow>;
|
|
132
|
+
}
|
|
133
|
+
/** A read-only guide: a page guide or a section guide. */
|
|
134
|
+
export type ReadKind = "page" | "section";
|
|
88
135
|
export interface ReplayPage {
|
|
89
136
|
mission: string;
|
|
90
137
|
/** The mission's abort anchors that were on the page at the start. */
|
|
@@ -107,6 +154,8 @@ export interface GuideEnv {
|
|
|
107
154
|
targetValue: () => string;
|
|
108
155
|
/** An app dialog (not the tour's) is open. */
|
|
109
156
|
dialogOpen?: () => boolean;
|
|
157
|
+
/** The running (or opening) section guide's live values (GuideContext.values). */
|
|
158
|
+
values?: Readonly<Record<string, unknown>>;
|
|
110
159
|
}
|
|
111
160
|
export type GuideSignal = {
|
|
112
161
|
kind: "api";
|
|
@@ -190,6 +239,7 @@ export type GuideAction = {
|
|
|
190
239
|
type: "dismissComplete";
|
|
191
240
|
} | {
|
|
192
241
|
type: "openPanel";
|
|
242
|
+
section?: "all" | "pages";
|
|
193
243
|
} | {
|
|
194
244
|
type: "closePanel";
|
|
195
245
|
} | {
|
|
@@ -209,6 +259,40 @@ export type GuideAction = {
|
|
|
209
259
|
/** The host is idle: show a waiting `completeOn` celebration. */
|
|
210
260
|
| {
|
|
211
261
|
type: "idleCelebrate";
|
|
262
|
+
}
|
|
263
|
+
/** The host found this page's guide due (pageOfferDue): show the offer card. */
|
|
264
|
+
| {
|
|
265
|
+
type: "pageOffer";
|
|
266
|
+
page: string;
|
|
267
|
+
}
|
|
268
|
+
/** The offer's [เริ่ม]. */
|
|
269
|
+
| {
|
|
270
|
+
type: "pageOfferStart";
|
|
271
|
+
}
|
|
272
|
+
/** The offer's [ไม่ตอนนี้]: recorded, never offered again (this version). */
|
|
273
|
+
| {
|
|
274
|
+
type: "pageOfferLater";
|
|
275
|
+
}
|
|
276
|
+
/** "คู่มือหน้านี้" from "?", or a page in the panel's list (once on its page). */
|
|
277
|
+
| {
|
|
278
|
+
type: "startPage";
|
|
279
|
+
page: string;
|
|
280
|
+
}
|
|
281
|
+
/** ย้อนกลับ on a page guide's read step. */
|
|
282
|
+
| {
|
|
283
|
+
type: "back";
|
|
284
|
+
}
|
|
285
|
+
/** A page row came back from the server. */
|
|
286
|
+
| {
|
|
287
|
+
type: "pageRow";
|
|
288
|
+
key: string;
|
|
289
|
+
row: PageRow;
|
|
290
|
+
kind?: ReadKind;
|
|
291
|
+
}
|
|
292
|
+
/** "อธิบายส่วนนี้": a section guide on screen here (its button, or "?"). */
|
|
293
|
+
| {
|
|
294
|
+
type: "startSection";
|
|
295
|
+
section: string;
|
|
212
296
|
};
|
|
213
297
|
export type GuideEffect = {
|
|
214
298
|
type: "save";
|
|
@@ -230,6 +314,14 @@ export type GuideEffect = {
|
|
|
230
314
|
| {
|
|
231
315
|
type: "abort";
|
|
232
316
|
actions: GuideAbortAction[];
|
|
317
|
+
}
|
|
318
|
+
/** Write a page (or section) guide's row, once: `<app>.page.<key>` / `<app>.section.<key>`. */
|
|
319
|
+
| {
|
|
320
|
+
type: "savePage";
|
|
321
|
+
key: string;
|
|
322
|
+
version: number;
|
|
323
|
+
status: "completed" | "skipped";
|
|
324
|
+
kind?: ReadKind;
|
|
233
325
|
};
|
|
234
326
|
export interface GuideResult {
|
|
235
327
|
state: GuideState;
|
|
@@ -244,11 +336,13 @@ export declare const COPY: {
|
|
|
244
336
|
readonly replayEnded: "จบการทบทวนแล้ว";
|
|
245
337
|
readonly hiddenToast: "ซ่อนแล้ว";
|
|
246
338
|
readonly aborted: "ไกด์ไปต่อไม่ได้ ลองอีกครั้งภายหลัง";
|
|
339
|
+
readonly pageDone: "จบคู่มือหน้านี้";
|
|
340
|
+
readonly sectionDone: "จบคำอธิบายส่วนนี้";
|
|
247
341
|
readonly bridgeTitle: "กดปุ่มเมนู";
|
|
248
342
|
readonly bridgeWhy: (app: string) => string;
|
|
249
343
|
};
|
|
250
344
|
export declare const BRIDGE_TARGET = "shell.menu-toggle";
|
|
251
|
-
export declare function initialGuideState(audience: GuideAudience, progress: GuideProgress): GuideState;
|
|
345
|
+
export declare function initialGuideState(audience: GuideAudience, progress: GuideProgress, pages?: GuidePagesInit): GuideState;
|
|
252
346
|
export declare function findMission(suite: GuideSuite, key: string | null | undefined): GuideMission | undefined;
|
|
253
347
|
/**
|
|
254
348
|
* Whether the user has this mission (GuideMission.visible). Without the
|
|
@@ -321,10 +415,14 @@ export declare function chipVisible(state: GuideState, suite: GuideSuite, ctx: G
|
|
|
321
415
|
* whose `requires` they lack or whose `skipIf` holds is left out (the step on
|
|
322
416
|
* screen always counts).
|
|
323
417
|
*/
|
|
324
|
-
export declare function stepPosition(mission: GuideMission, index: number, env: GuideEnv, context: Readonly<Record<string, string
|
|
418
|
+
export declare function stepPosition(mission: GuideMission, index: number, env: GuideEnv, context: Readonly<Record<string, string>>, autoPassed?: readonly number[]): {
|
|
325
419
|
no: number;
|
|
326
420
|
total: number;
|
|
327
421
|
};
|
|
422
|
+
/** A step entered now would pass at once: a click or select step whose route or visible outcome holds. */
|
|
423
|
+
export declare function wouldAutoPass(step: GuideStep, env: GuideEnv): boolean;
|
|
424
|
+
/** The steps of this mission that passed by themselves in this run. */
|
|
425
|
+
export declare function autoPassedOf(state: GuideState, mission: string): readonly number[];
|
|
328
426
|
/** "กด [บันทึก]" → "กด บันทึก" (for screen readers). */
|
|
329
427
|
export declare function plainTitle(t: string): string;
|
|
330
428
|
/** The step's status line (GuideStep.status): trimmed text, or null. */
|
|
@@ -342,4 +440,42 @@ export declare function completeTriggersOf(mission: GuideMission): GuideTrigger[
|
|
|
342
440
|
export declare function passiveEventNames(suite: GuideSuite): string[];
|
|
343
441
|
/** Missions whose `completeOn` this signal meets ({ api } and { event } only). */
|
|
344
442
|
export declare function passiveMatches(suite: GuideSuite, sig: GuideSignal): GuideMission[];
|
|
443
|
+
/** This page's guide for this person (the most specific match, if theirs). */
|
|
444
|
+
export declare function pageHere(suite: GuideSuite, env: GuideEnv): ReturnType<typeof currentPage>;
|
|
445
|
+
/** The page guide's row says this person has ended this version of it. */
|
|
446
|
+
export declare function pageRecorded(state: GuideState, page: {
|
|
447
|
+
key: string;
|
|
448
|
+
version: number;
|
|
449
|
+
}): boolean;
|
|
450
|
+
/** Whether the first step this person sees has its target on screen (or none). */
|
|
451
|
+
export declare function firstStepReady(suite: GuideSuite, pageKey: string, env: GuideEnv): boolean;
|
|
452
|
+
/**
|
|
453
|
+
* The page guide to offer now, or null. Due when: pages mode is "offer" and
|
|
454
|
+
* this person has page guides; nothing else is up (no step, card, panel or
|
|
455
|
+
* celebration waiting); this page's guide is theirs, may be offered, and has
|
|
456
|
+
* no row of this version; its first step's target is on screen; no app
|
|
457
|
+
* dialog is open. The host adds the rest: the welcome not due on this
|
|
458
|
+
* route, a moment of nothing loading, the primary tab, not hidden.
|
|
459
|
+
*/
|
|
460
|
+
export declare function pageOfferDue(state: GuideState, suite: GuideSuite, env: GuideEnv): string | null;
|
|
461
|
+
/** The section guide's row says this person has ended this version of it. */
|
|
462
|
+
export declare function sectionRecorded(state: GuideState, section: {
|
|
463
|
+
key: string;
|
|
464
|
+
version: number;
|
|
465
|
+
}): boolean;
|
|
466
|
+
/** The section guides on screen here that this person has (the "?" entries, the buttons). */
|
|
467
|
+
export declare function sectionsAvailable(suite: GuideSuite, env: GuideEnv): string[];
|
|
468
|
+
/** The step before this one that this person sees, or -1. */
|
|
469
|
+
export declare function previousShownIndex(mission: GuideMission, index: number, env: GuideEnv, context: Readonly<Record<string, string>>): number;
|
|
470
|
+
/**
|
|
471
|
+
* Where ย้อนกลับ goes from a page guide's step, or null. The step before it
|
|
472
|
+
* that this person sees; from inside what a click opened (not a
|
|
473
|
+
* checkpoint) back to that click, closing it first (the step's abort,
|
|
474
|
+
* Escape by default). A click step whose result is still on screen (a tab
|
|
475
|
+
* switched) is passed over: entering it again would pass it at once.
|
|
476
|
+
*/
|
|
477
|
+
export declare function backTarget(mission: GuideMission, index: number, env: GuideEnv, context: Readonly<Record<string, string>>): {
|
|
478
|
+
index: number;
|
|
479
|
+
close: boolean;
|
|
480
|
+
} | null;
|
|
345
481
|
export declare function reduceGuide(state: GuideState, action: GuideAction, suite: GuideSuite, env: GuideEnv): GuideResult;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page guides ("คู่มือหน้านี้", shell 0.1.74): which page guides a suite
|
|
3
|
+
* really has (validation), which one is this page's (the most specific
|
|
4
|
+
* match), and how one runs as a mission. Pure: the tests run it in Node.
|
|
5
|
+
*
|
|
6
|
+
* A page guide runs through the missions' machinery as a synthetic mission
|
|
7
|
+
* `page:<key>` in replay mode, so nothing it does writes tour progress.
|
|
8
|
+
*/
|
|
9
|
+
import type { GuideContext, GuideMission, GuidePage, GuideSection, GuideSuite } from "./types";
|
|
10
|
+
/** The synthetic mission key of a page guide: `page:<key>`. */
|
|
11
|
+
export declare const PAGE_PREFIX = "page:";
|
|
12
|
+
export declare function isPageMissionKey(key: string | null | undefined): boolean;
|
|
13
|
+
export declare function pageKeyOf(missionKey: string | null | undefined): string | undefined;
|
|
14
|
+
/** Why a page guide cannot run, or null when it can. */
|
|
15
|
+
export declare function pageProblem(app: string, page: GuidePage): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* The suite's page guides that can run. A page guide with a commit, input or
|
|
18
|
+
* select step (it would save), a bad key or no route is dropped, with a
|
|
19
|
+
* warning in development; so is a second one with the same key.
|
|
20
|
+
*/
|
|
21
|
+
export declare function validPages(suite: GuideSuite): GuidePage[];
|
|
22
|
+
export declare function findPage(suite: GuideSuite, key: string | null | undefined): GuidePage | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* The page guide as a mission the machine plays: `page:<key>`. Steps lose
|
|
25
|
+
* any `route` (the guide is the page's, and ends when it is left) and are
|
|
26
|
+
* checkpoints unless they say otherwise.
|
|
27
|
+
*/
|
|
28
|
+
export declare function pageMission(page: GuidePage): GuideMission;
|
|
29
|
+
/** What the page matching needs to know about where the person is. */
|
|
30
|
+
export interface PageWhere {
|
|
31
|
+
code: string;
|
|
32
|
+
pathname: string;
|
|
33
|
+
search: URLSearchParams;
|
|
34
|
+
isVisible: (anchor: string) => boolean;
|
|
35
|
+
}
|
|
36
|
+
/** How specific a route pattern is: its `?k=v` constraints, then its static segments. */
|
|
37
|
+
export declare function routeSpecificity(pattern: string): [number, number];
|
|
38
|
+
/**
|
|
39
|
+
* Whether the page guide applies here: one of its routes matches and its
|
|
40
|
+
* `when` anchor (if any) is on screen. The score ranks matches: `when`
|
|
41
|
+
* first, then query constraints, then static segments. Null when it does
|
|
42
|
+
* not apply.
|
|
43
|
+
*/
|
|
44
|
+
export declare function pageMatchScore(page: GuidePage, where: PageWhere): [number, number, number] | null;
|
|
45
|
+
/** The most specific page guide that applies here, whoever it is for (first in the list on a tie). */
|
|
46
|
+
export declare function matchingPage(suite: GuideSuite, where: PageWhere): GuidePage | null;
|
|
47
|
+
/** Whether this person has the page guide: `visible` (only true) and `requires`. */
|
|
48
|
+
export declare function pageShown(page: GuidePage, ctx: GuideContext): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* This page's guide for this person: the most specific match, when it is
|
|
51
|
+
* theirs. A more specific guide they do not have is not replaced by a less
|
|
52
|
+
* specific one (it would describe what is not on screen).
|
|
53
|
+
*/
|
|
54
|
+
export declare function currentPage(suite: GuideSuite, where: PageWhere, ctx: GuideContext): GuidePage | null;
|
|
55
|
+
/** The page guides this person has, for the panel's list (those with an `href`). */
|
|
56
|
+
export declare function listedPages(suite: GuideSuite, ctx: GuideContext): GuidePage[];
|
|
57
|
+
/** The synthetic mission key of a section guide: `section:<key>`. */
|
|
58
|
+
export declare const SECTION_PREFIX = "section:";
|
|
59
|
+
export declare function isSectionMissionKey(key: string | null | undefined): boolean;
|
|
60
|
+
export declare function sectionKeyOf(missionKey: string | null | undefined): string | undefined;
|
|
61
|
+
/** Why a section guide cannot run, or null when it can. */
|
|
62
|
+
export declare function sectionProblem(app: string, section: GuideSection): string | null;
|
|
63
|
+
/** The suite's section guides that can run (as validPages: the rest dropped, with a warning in development). */
|
|
64
|
+
export declare function validSections(suite: GuideSuite): GuideSection[];
|
|
65
|
+
export declare function findSection(suite: GuideSuite, key: string | null | undefined): GuideSection | undefined;
|
|
66
|
+
/** A live value is missing: undefined, null, "" or a number that is not finite. */
|
|
67
|
+
export declare function valueMissing(v: unknown): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* The section guide as a mission the machine plays: `section:<key>`, read
|
|
70
|
+
* steps only, checkpoints unless they say otherwise. A step is skipped while
|
|
71
|
+
* a value it `needs` is missing, or when its title comes out empty.
|
|
72
|
+
*/
|
|
73
|
+
export declare function sectionMission(section: GuideSection): GuideMission;
|
|
74
|
+
/** Whether the section guide applies here: one of its routes matches and its root is on screen. */
|
|
75
|
+
export declare function sectionApplies(section: GuideSection, where: PageWhere): boolean;
|
|
76
|
+
/** Whether this person has the section guide: `visible` (only true) and `requires`. */
|
|
77
|
+
export declare function sectionShown(section: GuideSection, ctx: GuideContext): boolean;
|
|
78
|
+
/** The section guides on screen here that this person has, in the suite's order. */
|
|
79
|
+
export declare function sectionsHere(suite: GuideSuite, where: PageWhere, ctx: GuideContext): GuideSection[];
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* No runtime imports beyond sibling pure modules: the tests run it in Node.
|
|
9
9
|
*/
|
|
10
10
|
import type { GuideSuite } from "./types";
|
|
11
|
-
import { type GuideProgress, type GuidesAnswer } from "./progress";
|
|
11
|
+
import { type GuideProgress, type GuidesAnswer, type PageRow } from "./progress";
|
|
12
12
|
/** What a progress call is about. */
|
|
13
13
|
export interface GuideProgressRequest {
|
|
14
14
|
/** The suite's app ("peoplx", "liff"). */
|
|
@@ -66,3 +66,12 @@ export declare function loadGuides(adapter: GuideProgressAdapter, suite: GuideSu
|
|
|
66
66
|
* stored row, or null when it could not be saved.
|
|
67
67
|
*/
|
|
68
68
|
export declare function storeGuides(adapter: GuideProgressAdapter, suite: GuideSuite, progress: GuideProgress): Promise<GuideProgress | null>;
|
|
69
|
+
/**
|
|
70
|
+
* Writes a page guide's row (`<app>.page.<key>`), or a section guide's
|
|
71
|
+
* (`<app>.section.<key>`, kind "section"), once: the minimal body,
|
|
72
|
+
* with the same 409 handling as the tour's row. Another tab that already
|
|
73
|
+
* recorded this version wins (its row is returned, nothing is sent again);
|
|
74
|
+
* an older version's row is replaced with its rev; a newer one stops. Returns
|
|
75
|
+
* the stored row, or null when it could not be saved.
|
|
76
|
+
*/
|
|
77
|
+
export declare function storePageRow(adapter: GuideProgressAdapter, app: string, key: string, version: number, status: "completed" | "skipped", rev: number, kind?: "page" | "section"): Promise<PageRow | null>;
|
package/dist/guide/progress.d.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* No runtime imports: the tests run this file in Node as it is.
|
|
7
7
|
*/
|
|
8
8
|
export type GuideMode = "off" | "invite" | "forced";
|
|
9
|
+
/** How page guides are offered (`guides.<app>.pages`): off, from "?" only, or also on a first visit. */
|
|
10
|
+
export type PagesMode = "off" | "menu" | "offer";
|
|
9
11
|
export type GuideAudience = "forced" | "optional" | "none";
|
|
10
12
|
export type GuideRowStatus = "in_progress" | "completed" | "skipped" | "aborted";
|
|
11
13
|
export type MissionStatus = "todo" | "active" | "done" | "skipped";
|
|
@@ -38,9 +40,23 @@ export interface GuideProgress {
|
|
|
38
40
|
/** The status the server holds (to know when a PUT needs restart: true). */
|
|
39
41
|
server_status: GuideRowStatus | null;
|
|
40
42
|
}
|
|
43
|
+
/** A page guide's row (`<app>.page.<key>`), as the client keeps it. */
|
|
44
|
+
export interface PageRow {
|
|
45
|
+
version: number;
|
|
46
|
+
status: GuideRowStatus;
|
|
47
|
+
rev: number;
|
|
48
|
+
}
|
|
41
49
|
export interface GuidesAnswer {
|
|
42
50
|
mode: GuideMode;
|
|
43
51
|
audience: GuideAudience;
|
|
52
|
+
/** The page guides' switch; "off" from an API without it. */
|
|
53
|
+
pagesMode: PagesMode;
|
|
54
|
+
/** Who gets page guides: "optional" for every member while pages are not off. */
|
|
55
|
+
pageAudience: GuideAudience;
|
|
56
|
+
/** Page rows by page key (the part after `<app>.page.`). */
|
|
57
|
+
pages: Record<string, PageRow>;
|
|
58
|
+
/** Section rows by section key (the part after `<app>.section.`). Shell ≥ 0.1.75. */
|
|
59
|
+
sections: Record<string, PageRow>;
|
|
44
60
|
/** The suite's row; a fresh one when the user has none or an older version. */
|
|
45
61
|
progress: GuideProgress;
|
|
46
62
|
/** The server holds a newer version than this app's tour: do not run. */
|
|
@@ -58,6 +74,17 @@ export declare function parseGuidesAnswer(payload: unknown, suite: {
|
|
|
58
74
|
version: number;
|
|
59
75
|
app: string;
|
|
60
76
|
}): GuidesAnswer | null;
|
|
77
|
+
/** The row key of a page guide, `<app>.page.<key>`, or a section guide, `<app>.section.<key>`. */
|
|
78
|
+
export declare function pageRowKey(app: string, key: string, kind?: "page" | "section"): string;
|
|
79
|
+
/** Page (or section) rows among the list's rows, by key. Off-shape rows are left out. */
|
|
80
|
+
export declare function parsePageRows(rows: unknown[], app: string, kind?: "page" | "section"): Record<string, PageRow>;
|
|
81
|
+
/** One page row (a list entry, a PUT answer, a 409's `current`); null when off-shape. */
|
|
82
|
+
export declare function parsePageRow(raw: unknown): PageRow | null;
|
|
83
|
+
/**
|
|
84
|
+
* A page row's PUT body: the least the server takes. Written once per page
|
|
85
|
+
* and version (finished: completed; not now, exited or left: skipped).
|
|
86
|
+
*/
|
|
87
|
+
export declare function pageBody(app: string, version: number, rev: number, status: "completed" | "skipped"): Record<string, unknown>;
|
|
61
88
|
/** The PUT body for the client's state. Drops keys the server would refuse. */
|
|
62
89
|
export declare function putBody(p: GuideProgress, app: string): Record<string, unknown>;
|
|
63
90
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface SectionGuideButtonProps {
|
|
2
|
+
/** The GuideSection's key. */
|
|
3
|
+
guideKey: string;
|
|
4
|
+
/**
|
|
5
|
+
* The numbers the guide's copy uses (`ctx.values`), from this render.
|
|
6
|
+
* Optional: the section may call `useSectionGuideValues` itself instead.
|
|
7
|
+
*/
|
|
8
|
+
values?: Readonly<Record<string, unknown>>;
|
|
9
|
+
/** Replaces the default look (a 22px round icon button in the text colour). */
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function SectionGuideButton({ guideKey, values, className }: SectionGuideButtonProps): import("react/jsx-runtime").JSX.Element | null;
|