@mrrocket/sdk 0.16.0 → 0.18.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/CHANGELOG.md +25 -0
  2. package/README.md +11 -2
  3. package/dist/api-client.d.ts +7 -1
  4. package/dist/api-client.js.map +1 -1
  5. package/dist/browser/mrrocket-flows.js +16 -2
  6. package/dist/browser/mrrocket-messenger.js +43 -44
  7. package/dist/browser/mrrocket.js +1 -1
  8. package/dist/browser.d.ts +1 -1
  9. package/dist/browser.js +2 -1
  10. package/dist/browser.js.map +1 -1
  11. package/dist/client.d.ts +23 -3
  12. package/dist/client.js +15 -1
  13. package/dist/client.js.map +1 -1
  14. package/dist/context.d.ts +3 -1
  15. package/dist/context.js.map +1 -1
  16. package/dist/core.d.ts +3 -1
  17. package/dist/core.js +1 -1
  18. package/dist/core.js.map +1 -1
  19. package/dist/finish-web.d.ts +18 -0
  20. package/dist/finish-web.js +139 -0
  21. package/dist/finish-web.js.map +1 -0
  22. package/dist/flows-ui.d.ts +1 -10
  23. package/dist/flows-ui.js +165 -16
  24. package/dist/flows-ui.js.map +1 -1
  25. package/dist/flows-web.d.ts +8 -1
  26. package/dist/flows-web.js +8 -0
  27. package/dist/flows-web.js.map +1 -1
  28. package/dist/flows.d.ts +203 -5
  29. package/dist/flows.js +306 -15
  30. package/dist/flows.js.map +1 -1
  31. package/dist/icons.d.ts +24 -0
  32. package/dist/icons.js +61 -0
  33. package/dist/icons.js.map +1 -0
  34. package/dist/index.d.ts +4 -2
  35. package/dist/index.js +10 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/messenger-ui.d.ts +10 -2
  38. package/dist/messenger-ui.js +134 -98
  39. package/dist/messenger-ui.js.map +1 -1
  40. package/dist/onboarding-state.d.ts +23 -0
  41. package/dist/onboarding-state.js +43 -0
  42. package/dist/onboarding-state.js.map +1 -0
  43. package/dist/react-native/index.d.cts +216 -15
  44. package/dist/react-native/index.d.ts +216 -15
  45. package/dist/react-native/index.js +880 -270
  46. package/dist/react-native/index.js.map +1 -1
  47. package/dist/styles-css.d.ts +1 -1
  48. package/dist/styles-css.js +40 -41
  49. package/dist/styles-css.js.map +1 -1
  50. package/package.json +1 -1
@@ -0,0 +1,139 @@
1
+ // src/finish-web.ts
2
+ var prefersReducedMotion = () => {
3
+ try {
4
+ return typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
5
+ } catch {
6
+ return false;
7
+ }
8
+ };
9
+ var COLORS = ["#f59e0b", "#10b981", "#ef4444", "#8b5cf6", "#06b6d4", "#ec4899"];
10
+ var star = (c, r) => {
11
+ c.beginPath();
12
+ for (let i = 0; i < 10; i++) {
13
+ const a = Math.PI / 5 * i - Math.PI / 2;
14
+ const d = i % 2 ? r * 0.45 : r;
15
+ c[i ? "lineTo" : "moveTo"](Math.cos(a) * d, Math.sin(a) * d);
16
+ }
17
+ c.closePath();
18
+ c.fill();
19
+ };
20
+ var bitsFor = (kind, w, h, colors) => {
21
+ const rnd = (a, b) => a + Math.random() * (b - a);
22
+ const pick = () => colors[Math.floor(Math.random() * colors.length)];
23
+ const out = [];
24
+ if (kind === "confetti")
25
+ for (let i = 0; i < 110; i++) {
26
+ const left = i % 2 === 0;
27
+ out.push({
28
+ x: left ? w * 0.15 : w * 0.85,
29
+ y: h * 0.95,
30
+ vx: (left ? 1 : -1) * rnd(0.12, 0.42) * (w / 900 + 0.4),
31
+ vy: -rnd(0.75, 1.25) * (h / 900 + 0.35),
32
+ r: rnd(4, 7),
33
+ spin: rnd(-0.02, 0.02),
34
+ angle: rnd(0, Math.PI),
35
+ color: pick(),
36
+ born: rnd(0, 180),
37
+ life: rnd(1500, 2100),
38
+ shape: "rect"
39
+ });
40
+ }
41
+ else if (kind === "fireworks")
42
+ for (let b = 0; b < 4; b++) {
43
+ const cx = rnd(w * 0.2, w * 0.8);
44
+ const cy = rnd(h * 0.18, h * 0.5);
45
+ const color = pick();
46
+ for (let i = 0; i < 46; i++) {
47
+ const a = Math.PI * 2 * i / 46 + rnd(-0.05, 0.05);
48
+ const v = rnd(0.12, 0.3) * (Math.min(w, h) / 600 + 0.5);
49
+ out.push({ x: cx, y: cy, vx: Math.cos(a) * v, vy: Math.sin(a) * v, r: rnd(1.6, 2.6), spin: 0, angle: 0, color, born: b * 320, life: rnd(900, 1300), shape: "dot" });
50
+ }
51
+ }
52
+ else
53
+ for (let i = 0; i < 34; i++)
54
+ out.push({
55
+ x: rnd(w * 0.08, w * 0.92),
56
+ y: rnd(h * 0.2, h * 0.85),
57
+ vx: 0,
58
+ vy: -rnd(0.02, 0.06),
59
+ r: rnd(6, 13),
60
+ spin: rnd(-4e-3, 4e-3),
61
+ angle: rnd(0, Math.PI),
62
+ color: pick(),
63
+ born: rnd(0, 700),
64
+ life: rnd(900, 1300),
65
+ shape: "star"
66
+ });
67
+ return out;
68
+ };
69
+ var playFinish = (kind, opts = {}) => {
70
+ if (!kind || kind === "none" || typeof document === "undefined" || !opts.force && prefersReducedMotion()) return () => {
71
+ };
72
+ const canvas = document.createElement("canvas");
73
+ canvas.setAttribute("aria-hidden", "true");
74
+ const host = opts.host;
75
+ canvas.style.cssText = host ? "position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:5" : "position:fixed;inset:0;width:100vw;height:100vh;pointer-events:none;z-index:2147483003";
76
+ (host ?? document.body).appendChild(canvas);
77
+ const w = host ? host.clientWidth : window.innerWidth;
78
+ const h = host ? host.clientHeight : window.innerHeight;
79
+ const dpr = Math.min(2, window.devicePixelRatio || 1);
80
+ canvas.width = Math.max(1, Math.round(w * dpr));
81
+ canvas.height = Math.max(1, Math.round(h * dpr));
82
+ const c = canvas.getContext("2d");
83
+ if (!c) {
84
+ canvas.remove();
85
+ return () => {
86
+ };
87
+ }
88
+ const colors = opts.accent ? [opts.accent, opts.accent, ...COLORS] : COLORS;
89
+ const bits = bitsFor(kind, w, h, colors);
90
+ const end = Math.max(...bits.map((b) => b.born + b.life));
91
+ const gravity = kind === "confetti" ? 16e-4 : kind === "fireworks" ? 35e-5 : 0;
92
+ let frame = 0;
93
+ let start = 0;
94
+ let stopped = false;
95
+ const stop = () => {
96
+ if (stopped) return;
97
+ stopped = true;
98
+ cancelAnimationFrame(frame);
99
+ canvas.remove();
100
+ };
101
+ const draw = (t) => {
102
+ if (stopped) return;
103
+ if (!start) start = t;
104
+ const el = t - start;
105
+ c.setTransform(dpr, 0, 0, dpr, 0, 0);
106
+ c.clearRect(0, 0, w, h);
107
+ for (const b of bits) {
108
+ const age = el - b.born;
109
+ if (age < 0 || age > b.life) continue;
110
+ const x = b.x + b.vx * age;
111
+ const y = b.y + b.vy * age + 0.5 * gravity * age * age;
112
+ const fade = Math.min(1, (b.life - age) / 350);
113
+ c.save();
114
+ c.globalAlpha = Math.max(0, fade);
115
+ c.translate(x, y);
116
+ c.rotate(b.angle + b.spin * age);
117
+ c.fillStyle = b.color;
118
+ if (b.shape === "rect") c.fillRect(-b.r, -b.r / 2.2, b.r * 2, b.r / 1.1);
119
+ else if (b.shape === "dot") {
120
+ c.beginPath();
121
+ c.arc(0, 0, b.r, 0, Math.PI * 2);
122
+ c.fill();
123
+ } else {
124
+ const grow = Math.min(1, age / 220);
125
+ star(c, b.r * (grow < 1 ? grow : 1 - Math.max(0, (age - b.life * 0.6) / (b.life * 0.4)) * 0.6));
126
+ }
127
+ c.restore();
128
+ }
129
+ if (el < end) frame = requestAnimationFrame(draw);
130
+ else stop();
131
+ };
132
+ frame = requestAnimationFrame(draw);
133
+ return stop;
134
+ };
135
+ export {
136
+ playFinish,
137
+ prefersReducedMotion
138
+ };
139
+ //# sourceMappingURL=finish-web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/finish-web.ts"],"sourcesContent":["/**\n * 0.18.0: the finish animation on the web (`FinishAnimation`): confetti, fireworks or stars on one canvas for about two\n * seconds, then gone. No library, no timers left behind, nothing to click through (the canvas lets every click pass).\n * Nothing plays when the visitor asks for less motion (`prefers-reduced-motion`). The flows' dialogs (flows-ui.ts) play\n * it when someone finishes a checklist, a tour or the questions; MRRocket's editor plays the same code in its preview.\n */\nimport type { FinishAnimation } from \"./flows\";\n\n/** Whether the visitor's system asks for less motion. */\nexport const prefersReducedMotion = () => {\n try {\n return typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n } catch {\n return false;\n }\n};\n\nconst COLORS = [\"#f59e0b\", \"#10b981\", \"#ef4444\", \"#8b5cf6\", \"#06b6d4\", \"#ec4899\"];\n\ninterface Bit {\n x: number;\n y: number;\n vx: number;\n vy: number;\n r: number;\n spin: number;\n angle: number;\n color: string;\n /** When it appears and how long it lives, in ms from the start. */\n born: number;\n life: number;\n shape: \"rect\" | \"dot\" | \"star\";\n}\n\nconst star = (c: CanvasRenderingContext2D, r: number) => {\n c.beginPath();\n for (let i = 0; i < 10; i++) {\n const a = (Math.PI / 5) * i - Math.PI / 2;\n const d = i % 2 ? r * 0.45 : r;\n c[i ? \"lineTo\" : \"moveTo\"](Math.cos(a) * d, Math.sin(a) * d);\n }\n c.closePath();\n c.fill();\n};\n\n/** The particles of one animation on a w×h area. */\nconst bitsFor = (kind: Exclude<FinishAnimation, \"none\">, w: number, h: number, colors: string[]): Bit[] => {\n const rnd = (a: number, b: number) => a + Math.random() * (b - a);\n const pick = () => colors[Math.floor(Math.random() * colors.length)];\n const out: Bit[] = [];\n if (kind === \"confetti\")\n for (let i = 0; i < 110; i++) {\n const left = i % 2 === 0;\n out.push({\n x: left ? w * 0.15 : w * 0.85,\n y: h * 0.95,\n vx: (left ? 1 : -1) * rnd(0.12, 0.42) * (w / 900 + 0.4),\n vy: -rnd(0.75, 1.25) * (h / 900 + 0.35),\n r: rnd(4, 7),\n spin: rnd(-0.02, 0.02),\n angle: rnd(0, Math.PI),\n color: pick(),\n born: rnd(0, 180),\n life: rnd(1500, 2100),\n shape: \"rect\",\n });\n }\n else if (kind === \"fireworks\")\n for (let b = 0; b < 4; b++) {\n const cx = rnd(w * 0.2, w * 0.8);\n const cy = rnd(h * 0.18, h * 0.5);\n const color = pick();\n for (let i = 0; i < 46; i++) {\n const a = (Math.PI * 2 * i) / 46 + rnd(-0.05, 0.05);\n const v = rnd(0.12, 0.3) * (Math.min(w, h) / 600 + 0.5);\n out.push({ x: cx, y: cy, vx: Math.cos(a) * v, vy: Math.sin(a) * v, r: rnd(1.6, 2.6), spin: 0, angle: 0, color, born: b * 320, life: rnd(900, 1300), shape: \"dot\" });\n }\n }\n else\n for (let i = 0; i < 34; i++)\n out.push({\n x: rnd(w * 0.08, w * 0.92),\n y: rnd(h * 0.2, h * 0.85),\n vx: 0,\n vy: -rnd(0.02, 0.06),\n r: rnd(6, 13),\n spin: rnd(-0.004, 0.004),\n angle: rnd(0, Math.PI),\n color: pick(),\n born: rnd(0, 700),\n life: rnd(900, 1300),\n shape: \"star\",\n });\n return out;\n};\n\n/**\n * Plays `kind` over `host` (filling it; the host should be positioned) or over the whole window; answers a function\n * that stops it early. `accent` joins the colors.\n */\nexport const playFinish = (kind: FinishAnimation | undefined, opts: { accent?: string; host?: HTMLElement; force?: boolean } = {}): (() => void) => {\n if (!kind || kind === \"none\" || typeof document === \"undefined\" || (!opts.force && prefersReducedMotion())) return () => {};\n const canvas = document.createElement(\"canvas\");\n canvas.setAttribute(\"aria-hidden\", \"true\");\n const host = opts.host;\n canvas.style.cssText = host\n ? \"position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:5\"\n : \"position:fixed;inset:0;width:100vw;height:100vh;pointer-events:none;z-index:2147483003\";\n (host ?? document.body).appendChild(canvas);\n const w = host ? host.clientWidth : window.innerWidth;\n const h = host ? host.clientHeight : window.innerHeight;\n const dpr = Math.min(2, window.devicePixelRatio || 1);\n canvas.width = Math.max(1, Math.round(w * dpr));\n canvas.height = Math.max(1, Math.round(h * dpr));\n const c = canvas.getContext(\"2d\");\n if (!c) {\n canvas.remove();\n return () => {};\n }\n const colors = opts.accent ? [opts.accent, opts.accent, ...COLORS] : COLORS;\n const bits = bitsFor(kind, w, h, colors);\n const end = Math.max(...bits.map((b) => b.born + b.life));\n const gravity = kind === \"confetti\" ? 0.0016 : kind === \"fireworks\" ? 0.00035 : 0;\n let frame = 0;\n let start = 0;\n let stopped = false;\n const stop = () => {\n if (stopped) return;\n stopped = true;\n cancelAnimationFrame(frame);\n canvas.remove();\n };\n const draw = (t: number) => {\n if (stopped) return;\n if (!start) start = t;\n const el = t - start;\n c.setTransform(dpr, 0, 0, dpr, 0, 0);\n c.clearRect(0, 0, w, h);\n for (const b of bits) {\n const age = el - b.born;\n if (age < 0 || age > b.life) continue;\n const x = b.x + b.vx * age;\n const y = b.y + b.vy * age + 0.5 * gravity * age * age;\n const fade = Math.min(1, (b.life - age) / 350);\n c.save();\n c.globalAlpha = Math.max(0, fade);\n c.translate(x, y);\n c.rotate(b.angle + b.spin * age);\n c.fillStyle = b.color;\n if (b.shape === \"rect\") c.fillRect(-b.r, -b.r / 2.2, b.r * 2, b.r / 1.1);\n else if (b.shape === \"dot\") {\n c.beginPath();\n c.arc(0, 0, b.r, 0, Math.PI * 2);\n c.fill();\n } else {\n // A star pops in, then shrinks away.\n const grow = Math.min(1, age / 220);\n star(c, b.r * (grow < 1 ? grow : 1 - Math.max(0, (age - b.life * 0.6) / (b.life * 0.4)) * 0.6));\n }\n c.restore();\n }\n if (el < end) frame = requestAnimationFrame(draw);\n else stop();\n };\n frame = requestAnimationFrame(draw);\n return stop;\n};\n"],"mappings":"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","names":[]}
@@ -1,12 +1,3 @@
1
- /**
2
- * The web renderer of the cancel flow, onboarding and in-app messages (flows.ts): plain DOM, no React, so the Next.js
3
- * provider and the script tag share it. Both load it only when something is about to show (a dynamic import, and
4
- * `sdk-flows.js` for the script tag), so pages that show nothing never download it.
5
- *
6
- * Themeable three ways: `theme` ("light", "dark", "auto") and `accent` from the SDK's options, and CSS variables on
7
- * `.ckf-root` in the site's own stylesheet (`--ckf-accent`, `--ckf-bg`, `--ckf-fg`, `--ckf-muted`, `--ckf-line`,
8
- * `--ckf-radius`, `--ckf-font`). Every text is set with textContent; nothing from the server is parsed as HTML.
9
- */
10
1
  import { type FlowsController } from "./flows.js";
11
2
  import type { PaywallHost } from "./paywall.js";
12
3
  export interface FlowsTheme {
@@ -16,7 +7,7 @@ export interface FlowsTheme {
16
7
  /** An extra class on the root, for the site's own CSS. */
17
8
  className?: string;
18
9
  }
19
- export declare const FLOWS_CSS = "\n.ckf-root{--ckf-accent:#3f76ff;--ckf-accent-ink:#fff;--ckf-bg:#fff;--ckf-fg:#1c1d20;--ckf-muted:#5d6068;--ckf-soft:#f4f5f7;--ckf-line:#e4e5e8;--ckf-danger:#d92d20;--ckf-radius:14px;--ckf-font:inherit;--ckf-shadow:0 16px 40px -12px rgba(16,18,24,.28),0 0 0 1px rgba(16,18,24,.07)}\n.ckf-root[data-theme=dark]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}\n@media (prefers-color-scheme:dark){.ckf-root[data-theme=auto]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}}\n.ckf-root,.ckf-root *{box-sizing:border-box}\n.ckf-root{font-family:var(--ckf-font);font-size:14px;line-height:1.45;color:var(--ckf-fg);-webkit-font-smoothing:antialiased}\n.ckf-root :where(button,textarea){font:inherit;color:inherit;margin:0}\n.ckf-root :focus-visible{outline:2px solid var(--ckf-accent);outline-offset:2px}\n.ckf-scrim{position:fixed;inset:0;z-index:2147483002;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center;padding:16px;animation:ckf-fade .15s ease-out}\n.ckf-card{position:relative;width:100%;max-width:400px;max-height:calc(100dvh - 32px);overflow-y:auto;background:var(--ckf-bg);color:var(--ckf-fg);border-radius:var(--ckf-radius);box-shadow:var(--ckf-shadow);padding:18px 20px 20px;display:flex;flex-direction:column;gap:12px;animation:ckf-in .18s ease-out}\n.ckf-float{position:fixed;z-index:2147483001;right:20px;bottom:20px;width:340px;max-width:calc(100vw - 24px)}\n.ckf-top{display:flex;align-items:center;gap:8px;min-height:20px}\n.ckf-dots{display:flex;gap:4px}\n.ckf-dot{width:16px;height:4px;border-radius:4px;background:var(--ckf-line)}\n.ckf-dot[data-on]{background:var(--ckf-accent)}\n.ckf-x{margin-left:auto;width:28px;height:28px;border:0;border-radius:8px;background:none;color:var(--ckf-muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:18px;line-height:1}\n.ckf-x:hover{background:var(--ckf-soft);color:var(--ckf-fg)}\n.ckf-h{margin:0;font-size:17px;font-weight:650;letter-spacing:-.01em;text-wrap:balance}\n.ckf-p{margin:0;color:var(--ckf-muted);text-wrap:pretty;white-space:pre-wrap}\n.ckf-small{margin:0;font-size:12px;color:var(--ckf-muted)}\n.ckf-opts{display:flex;flex-direction:column;gap:6px;margin:0;padding:0;list-style:none}\n.ckf-opt{display:flex;align-items:center;gap:10px;width:100%;min-height:40px;padding:8px 12px;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);text-align:left;cursor:pointer;font-weight:500}\n.ckf-opt:hover{background:var(--ckf-soft)}\n.ckf-opt[aria-checked=true]{border-color:var(--ckf-accent);background:color-mix(in srgb,var(--ckf-accent) 8%,var(--ckf-bg))}\n.ckf-radio{width:16px;height:16px;flex:none;border-radius:50%;border:1.5px solid var(--ckf-line);display:inline-flex;align-items:center;justify-content:center}\n.ckf-opt[aria-checked=true] .ckf-radio{border-color:var(--ckf-accent)}\n.ckf-opt[aria-checked=true] .ckf-radio::after{content:\"\";width:8px;height:8px;border-radius:50%;background:var(--ckf-accent)}\n.ckf-box{border-radius:10px;background:var(--ckf-soft);padding:12px;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}\n.ckf-big{font-size:22px;font-weight:650;font-variant-numeric:tabular-nums}\n.ckf-strike{color:var(--ckf-muted);text-decoration:line-through;font-variant-numeric:tabular-nums}\n.ckf-text{width:100%;min-height:64px;resize:vertical;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);padding:8px 10px}\n.ckf-text::placeholder{color:var(--ckf-muted)}\n.ckf-btns{display:flex;flex-direction:column;gap:6px}\n.ckf-btn{height:38px;padding:0 14px;border-radius:10px;border:1px solid var(--ckf-line);background:var(--ckf-bg);font-weight:600;cursor:pointer;transition:filter .1s linear,background-color .1s linear}\n.ckf-btn:hover{background:var(--ckf-soft)}\n.ckf-btn[disabled]{opacity:.55;cursor:not-allowed}\n.ckf-primary{background:var(--ckf-accent);border-color:var(--ckf-accent);color:var(--ckf-accent-ink)}\n.ckf-primary:hover{background:var(--ckf-accent);filter:brightness(1.07)}\n.ckf-danger{background:var(--ckf-danger);border-color:var(--ckf-danger);color:#fff}\n.ckf-danger:hover{background:var(--ckf-danger);filter:brightness(1.07)}\n.ckf-quiet{border-color:transparent;background:none;color:var(--ckf-muted);height:30px;font-weight:500}\n.ckf-quiet:hover{background:none;color:var(--ckf-fg)}\n.ckf-err{margin:0;color:var(--ckf-danger);font-size:13px;font-weight:500}\n.ckf-row{display:flex;justify-content:flex-end;gap:8px;align-items:center}\n.ckf-row .ckf-small{margin-right:auto}\n.ckf-ring{position:fixed;z-index:2147483000;pointer-events:none;border-radius:10px;box-shadow:0 0 0 2px var(--ckf-accent),0 0 0 6px color-mix(in srgb,var(--ckf-accent) 25%,transparent);transition:all .15s ease-out}\n.ckf-check{display:flex;align-items:flex-start;gap:10px;width:100%;padding:8px 6px;border:0;border-radius:8px;background:none;text-align:left}\n.ckf-check:where(button){cursor:pointer}\n.ckf-check:where(button):hover{background:var(--ckf-soft)}\n.ckf-tick{width:18px;height:18px;flex:none;margin-top:1px;border-radius:50%;border:1.5px solid var(--ckf-line);display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent-ink);font-size:11px;font-weight:700}\n.ckf-check[data-done] .ckf-tick{background:var(--ckf-accent);border-color:var(--ckf-accent)}\n.ckf-check[data-done] b{color:var(--ckf-muted);text-decoration:line-through}\n.ckf-check span{display:flex;flex-direction:column;gap:2px;min-width:0}\n.ckf-bar{height:4px;border-radius:4px;background:var(--ckf-soft);overflow:hidden}\n.ckf-bar i{display:block;height:100%;background:var(--ckf-accent)}\n.ckf-banner{position:fixed;z-index:2147483001;left:50%;top:12px;transform:translateX(-50%);width:min(640px,calc(100vw - 24px));background:var(--ckf-bg);border-radius:12px;box-shadow:var(--ckf-shadow);padding:10px 10px 10px 16px;display:flex;align-items:center;gap:12px;animation:ckf-in .18s ease-out}\n.ckf-banner>span{flex:1;min-width:0;display:flex;flex-direction:column}\n.ckf-banner .ckf-btn{height:32px}\n.ckf-pw{max-width:420px;gap:14px}\n.ckf-root[data-still] :is(.ckf-scrim,.ckf-card){animation:none}\n.ckf-li{display:flex;gap:10px;align-items:flex-start;margin:0}\n.ckf-li::before{content:\"\u2713\";flex:none;width:20px;height:20px;border-radius:50%;background:color-mix(in srgb,var(--ckf-accent) 14%,var(--ckf-bg));color:var(--ckf-accent);font-size:12px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}\n.ckf-quote{margin:0;padding:12px 14px;border-radius:12px;background:var(--ckf-soft)}\n.ckf-quote cite{display:block;margin-top:6px;font-style:normal;font-size:12px;color:var(--ckf-muted)}\n.ckf-plan{justify-content:space-between;min-height:52px}\n.ckf-plan span:nth-child(2){display:flex;flex-direction:column;margin-right:auto}\n.ckf-plan b{font-variant-numeric:tabular-nums}\n@keyframes ckf-fade{from{opacity:0}}\n@keyframes ckf-in{from{opacity:0;transform:translateY(6px)}}\n.ckf-banner{animation-name:ckf-fade}\n@media (max-width:520px){.ckf-scrim{align-items:flex-end;padding:0}.ckf-scrim .ckf-card{max-width:none;border-radius:var(--ckf-radius) var(--ckf-radius) 0 0;max-height:92dvh}.ckf-float{right:12px;bottom:12px}}\n@media (prefers-reduced-motion:reduce){.ckf-root *{animation:none!important;transition:none!important}}\n";
10
+ export declare const FLOWS_CSS = "\n.ckf-root{--ckf-accent:#3f76ff;--ckf-accent-ink:#fff;--ckf-bg:#fff;--ckf-fg:#1c1d20;--ckf-muted:#5d6068;--ckf-soft:#f4f5f7;--ckf-line:#e4e5e8;--ckf-danger:#d92d20;--ckf-radius:14px;--ckf-font:inherit;--ckf-shadow:0 16px 40px -12px rgba(16,18,24,.28),0 0 0 1px rgba(16,18,24,.07)}\n.ckf-root[data-theme=dark]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}\n@media (prefers-color-scheme:dark){.ckf-root[data-theme=auto]{--ckf-bg:#17181b;--ckf-fg:#f1f2f4;--ckf-muted:#a9acb3;--ckf-soft:#232428;--ckf-line:#2e2f35;--ckf-shadow:0 16px 40px -12px rgba(0,0,0,.6),0 0 0 1px rgba(255,255,255,.08)}}\n.ckf-root,.ckf-root *{box-sizing:border-box}\n.ckf-root{font-family:var(--ckf-font);font-size:14px;line-height:1.45;color:var(--ckf-fg);-webkit-font-smoothing:antialiased}\n.ckf-root :where(button,textarea){font:inherit;color:inherit;margin:0}\n.ckf-root :focus-visible{outline:2px solid var(--ckf-accent);outline-offset:2px}\n.ckf-scrim{position:fixed;inset:0;z-index:2147483002;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center;padding:16px;animation:ckf-fade .15s ease-out}\n.ckf-card{position:relative;width:100%;max-width:400px;max-height:calc(100dvh - 32px);overflow-y:auto;background:var(--ckf-bg);color:var(--ckf-fg);border-radius:var(--ckf-radius);box-shadow:var(--ckf-shadow);padding:18px 20px 20px;display:flex;flex-direction:column;gap:12px;animation:ckf-in .18s ease-out}\n.ckf-float{position:fixed;z-index:2147483001;right:20px;bottom:20px;width:340px;max-width:calc(100vw - 24px)}\n.ckf-top{display:flex;align-items:center;gap:8px;min-height:20px}\n.ckf-dots{display:flex;gap:4px}\n.ckf-dot{width:16px;height:4px;border-radius:4px;background:var(--ckf-line)}\n.ckf-dot[data-on]{background:var(--ckf-accent)}\n.ckf-x{margin-left:auto;width:28px;height:28px;border:0;border-radius:8px;background:none;color:var(--ckf-muted);cursor:pointer;display:inline-flex;align-items:center;justify-content:center;font-size:18px;line-height:1}\n.ckf-x:hover{background:var(--ckf-soft);color:var(--ckf-fg)}\n.ckf-h{margin:0;font-size:17px;font-weight:650;letter-spacing:-.01em;text-wrap:balance}\n.ckf-p{margin:0;color:var(--ckf-muted);text-wrap:pretty;white-space:pre-wrap}\n.ckf-small{margin:0;font-size:12px;color:var(--ckf-muted)}\n.ckf-opts{display:flex;flex-direction:column;gap:6px;margin:0;padding:0;list-style:none}\n.ckf-opt{display:flex;align-items:center;gap:10px;width:100%;min-height:40px;padding:8px 12px;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);text-align:left;cursor:pointer;font-weight:500}\n.ckf-opt:hover{background:var(--ckf-soft)}\n.ckf-opt[aria-checked=true]{border-color:var(--ckf-accent);background:color-mix(in srgb,var(--ckf-accent) 8%,var(--ckf-bg))}\n.ckf-radio{width:16px;height:16px;flex:none;border-radius:50%;border:1.5px solid var(--ckf-line);display:inline-flex;align-items:center;justify-content:center}\n.ckf-opt[aria-checked=true] .ckf-radio{border-color:var(--ckf-accent)}\n.ckf-opt[aria-checked=true] .ckf-radio::after{content:\"\";width:8px;height:8px;border-radius:50%;background:var(--ckf-accent)}\n.ckf-box{border-radius:10px;background:var(--ckf-soft);padding:12px;display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}\n.ckf-big{font-size:22px;font-weight:650;font-variant-numeric:tabular-nums}\n.ckf-strike{color:var(--ckf-muted);text-decoration:line-through;font-variant-numeric:tabular-nums}\n.ckf-text{width:100%;min-height:64px;resize:vertical;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);padding:8px 10px}\n.ckf-text::placeholder{color:var(--ckf-muted)}\n.ckf-btns{display:flex;flex-direction:column;gap:6px}\n.ckf-btn{height:38px;padding:0 14px;border-radius:10px;border:1px solid var(--ckf-line);background:var(--ckf-bg);font-weight:600;cursor:pointer;transition:filter .1s linear,background-color .1s linear}\n.ckf-btn:hover{background:var(--ckf-soft)}\n.ckf-btn[disabled]{opacity:.55;cursor:not-allowed}\n.ckf-primary{background:var(--ckf-accent);border-color:var(--ckf-accent);color:var(--ckf-accent-ink)}\n.ckf-primary:hover{background:var(--ckf-accent);filter:brightness(1.07)}\n.ckf-danger{background:var(--ckf-danger);border-color:var(--ckf-danger);color:#fff}\n.ckf-danger:hover{background:var(--ckf-danger);filter:brightness(1.07)}\n.ckf-quiet{border-color:transparent;background:none;color:var(--ckf-muted);height:30px;font-weight:500}\n.ckf-quiet:hover{background:none;color:var(--ckf-fg)}\n.ckf-err{margin:0;color:var(--ckf-danger);font-size:13px;font-weight:500}\n.ckf-row{display:flex;justify-content:flex-end;gap:8px;align-items:center}\n.ckf-row .ckf-small{margin-right:auto}\n.ckf-media{display:block;width:100%;max-height:240px;object-fit:cover;border-radius:10px;background:var(--ckf-soft)}\n.ckf-video{position:relative;width:100%;aspect-ratio:16/9;border-radius:10px;overflow:hidden;background:#000}\n.ckf-video iframe{position:absolute;inset:0;width:100%;height:100%;border:0}\n.ckf-arrow{position:absolute;width:12px;height:12px;margin:-6px;background:var(--ckf-bg);transform:rotate(45deg)}\n[data-side=bottom]>.ckf-arrow{top:0;left:var(--ckf-ap)}[data-side=top]>.ckf-arrow{top:100%;left:var(--ckf-ap)}\n[data-side=right]>.ckf-arrow{left:0;top:var(--ckf-ap)}[data-side=left]>.ckf-arrow{left:100%;top:var(--ckf-ap)}\n.ckf-check .ckf-go{margin-left:auto;align-self:center;color:var(--ckf-accent);font-weight:700}\n.ckf-ic{width:20px;height:20px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent);font-size:16px;line-height:1}\n.ckf-ic svg{width:20px;height:20px}.ckf-ic img{width:20px;height:20px;border-radius:5px;object-fit:cover}\n.ckf-hic{width:40px;height:40px;border-radius:12px;background:color-mix(in srgb,var(--ckf-accent) 12%,var(--ckf-bg));display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent);font-size:22px;line-height:1}\n.ckf-hic svg{width:22px;height:22px}.ckf-hic img{width:40px;height:40px;border-radius:12px;object-fit:cover}\n.ckf-input{width:100%;height:40px;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);padding:0 12px}\n.ckf-input::placeholder{color:var(--ckf-muted)}\n.ckf-ring{position:fixed;z-index:2147483000;pointer-events:none;border-radius:10px;box-shadow:0 0 0 2px var(--ckf-accent),0 0 0 6px color-mix(in srgb,var(--ckf-accent) 25%,transparent);transition:all .15s ease-out}\n.ckf-check{display:flex;align-items:flex-start;gap:10px;width:100%;padding:8px 6px;border:0;border-radius:8px;background:none;text-align:left}\n.ckf-check:where(button){cursor:pointer}\n.ckf-check:where(button):hover{background:var(--ckf-soft)}\n.ckf-tick{width:18px;height:18px;flex:none;margin-top:1px;border-radius:50%;border:1.5px solid var(--ckf-line);display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent-ink);font-size:11px;font-weight:700}\n.ckf-check[data-done] .ckf-tick{background:var(--ckf-accent);border-color:var(--ckf-accent)}\n.ckf-check[data-done] b{color:var(--ckf-muted);text-decoration:line-through}\n.ckf-check span{display:flex;flex-direction:column;gap:2px;min-width:0}\n.ckf-bar{height:4px;border-radius:4px;background:var(--ckf-soft);overflow:hidden}\n.ckf-bar i{display:block;height:100%;background:var(--ckf-accent)}\n.ckf-banner{position:fixed;z-index:2147483001;left:50%;top:12px;transform:translateX(-50%);width:min(640px,calc(100vw - 24px));background:var(--ckf-bg);border-radius:12px;box-shadow:var(--ckf-shadow);padding:10px 10px 10px 16px;display:flex;align-items:center;gap:12px;animation:ckf-in .18s ease-out}\n.ckf-banner>span{flex:1;min-width:0;display:flex;flex-direction:column}\n.ckf-banner .ckf-btn{height:32px}\n.ckf-pw{max-width:420px;gap:14px}\n.ckf-root[data-still] :is(.ckf-scrim,.ckf-card){animation:none}\n.ckf-li{display:flex;gap:10px;align-items:flex-start;margin:0}\n.ckf-li::before{content:\"\u2713\";flex:none;width:20px;height:20px;border-radius:50%;background:color-mix(in srgb,var(--ckf-accent) 14%,var(--ckf-bg));color:var(--ckf-accent);font-size:12px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}\n.ckf-quote{margin:0;padding:12px 14px;border-radius:12px;background:var(--ckf-soft)}\n.ckf-quote cite{display:block;margin-top:6px;font-style:normal;font-size:12px;color:var(--ckf-muted)}\n.ckf-plan{justify-content:space-between;min-height:52px}\n.ckf-plan span:nth-child(2){display:flex;flex-direction:column;margin-right:auto}\n.ckf-plan b{font-variant-numeric:tabular-nums}\n@keyframes ckf-fade{from{opacity:0}}\n@keyframes ckf-in{from{opacity:0;transform:translateY(6px)}}\n.ckf-banner{animation-name:ckf-fade}\n@media (max-width:520px){.ckf-scrim{align-items:flex-end;padding:0}.ckf-scrim .ckf-card{max-width:none;border-radius:var(--ckf-radius) var(--ckf-radius) 0 0;max-height:92dvh}.ckf-float{right:12px;bottom:12px}}\n@media (prefers-reduced-motion:reduce){.ckf-root *{animation:none!important;transition:none!important}}\n";
20
11
  export declare const mountFlows: (ctl: FlowsController, theme?: FlowsTheme) => (() => void);
21
12
  /**
22
13
  * Draws an open paywall (paywall.ts) page by page until the returned function is called: the dots, the page's title and
package/dist/flows-ui.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // src/flows-ui.ts
2
- import { discounted, money, offerButton } from "./flows.js";
2
+ import { playFinish } from "./finish-web.js";
3
+ import { answerOk, discounted, mediaOf, money, offerButton, videoEmbed } from "./flows.js";
4
+ import { FLOW_ICONS, iconOf } from "./icons.js";
3
5
  var STYLE_ID = "ckf-styles";
4
6
  var FLOWS_CSS = `
5
7
  .ckf-root{--ckf-accent:#3f76ff;--ckf-accent-ink:#fff;--ckf-bg:#fff;--ckf-fg:#1c1d20;--ckf-muted:#5d6068;--ckf-soft:#f4f5f7;--ckf-line:#e4e5e8;--ckf-danger:#d92d20;--ckf-radius:14px;--ckf-font:inherit;--ckf-shadow:0 16px 40px -12px rgba(16,18,24,.28),0 0 0 1px rgba(16,18,24,.07)}
@@ -46,6 +48,19 @@ var FLOWS_CSS = `
46
48
  .ckf-err{margin:0;color:var(--ckf-danger);font-size:13px;font-weight:500}
47
49
  .ckf-row{display:flex;justify-content:flex-end;gap:8px;align-items:center}
48
50
  .ckf-row .ckf-small{margin-right:auto}
51
+ .ckf-media{display:block;width:100%;max-height:240px;object-fit:cover;border-radius:10px;background:var(--ckf-soft)}
52
+ .ckf-video{position:relative;width:100%;aspect-ratio:16/9;border-radius:10px;overflow:hidden;background:#000}
53
+ .ckf-video iframe{position:absolute;inset:0;width:100%;height:100%;border:0}
54
+ .ckf-arrow{position:absolute;width:12px;height:12px;margin:-6px;background:var(--ckf-bg);transform:rotate(45deg)}
55
+ [data-side=bottom]>.ckf-arrow{top:0;left:var(--ckf-ap)}[data-side=top]>.ckf-arrow{top:100%;left:var(--ckf-ap)}
56
+ [data-side=right]>.ckf-arrow{left:0;top:var(--ckf-ap)}[data-side=left]>.ckf-arrow{left:100%;top:var(--ckf-ap)}
57
+ .ckf-check .ckf-go{margin-left:auto;align-self:center;color:var(--ckf-accent);font-weight:700}
58
+ .ckf-ic{width:20px;height:20px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent);font-size:16px;line-height:1}
59
+ .ckf-ic svg{width:20px;height:20px}.ckf-ic img{width:20px;height:20px;border-radius:5px;object-fit:cover}
60
+ .ckf-hic{width:40px;height:40px;border-radius:12px;background:color-mix(in srgb,var(--ckf-accent) 12%,var(--ckf-bg));display:inline-flex;align-items:center;justify-content:center;color:var(--ckf-accent);font-size:22px;line-height:1}
61
+ .ckf-hic svg{width:22px;height:22px}.ckf-hic img{width:40px;height:40px;border-radius:12px;object-fit:cover}
62
+ .ckf-input{width:100%;height:40px;border:1px solid var(--ckf-line);border-radius:10px;background:var(--ckf-bg);padding:0 12px}
63
+ .ckf-input::placeholder{color:var(--ckf-muted)}
49
64
  .ckf-ring{position:fixed;z-index:2147483000;pointer-events:none;border-radius:10px;box-shadow:0 0 0 2px var(--ckf-accent),0 0 0 6px color-mix(in srgb,var(--ckf-accent) 25%,transparent);transition:all .15s ease-out}
50
65
  .ckf-check{display:flex;align-items:flex-start;gap:10px;width:100%;padding:8px 6px;border:0;border-radius:8px;background:none;text-align:left}
51
66
  .ckf-check:where(button){cursor:pointer}
@@ -115,10 +130,31 @@ var rootFor = (theme) => {
115
130
  document.body.appendChild(root);
116
131
  return root;
117
132
  };
133
+ var iconEl = (icon, className) => {
134
+ const i = iconOf(icon);
135
+ if (!i) return false;
136
+ const box = h("span", { className, "aria-hidden": "true" });
137
+ if (i.kind === "emoji") box.textContent = i.value;
138
+ else if (i.kind === "image") box.append(h("img", { src: i.url, alt: "" }));
139
+ else {
140
+ const ns = "http://www.w3.org/2000/svg";
141
+ const svg = document.createElementNS(ns, "svg");
142
+ for (const [k, v] of Object.entries({ viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }))
143
+ svg.setAttribute(k, v);
144
+ for (const d of FLOW_ICONS[i.name].paths) {
145
+ const p = document.createElementNS(ns, "path");
146
+ p.setAttribute("d", d);
147
+ svg.append(p);
148
+ }
149
+ box.append(svg);
150
+ }
151
+ return box;
152
+ };
118
153
  var mountFlows = (ctl, theme = {}) => {
119
154
  if (typeof document === "undefined") return () => {
120
155
  };
121
156
  const root = rootFor(theme);
157
+ let played = "";
122
158
  let lastKey = "";
123
159
  let returnFocus = null;
124
160
  let comment = "";
@@ -287,8 +323,80 @@ var mountFlows = (ctl, theme = {}) => {
287
323
  )
288
324
  );
289
325
  };
326
+ const media = (s) => {
327
+ const m = mediaOf(s);
328
+ if (!m) return false;
329
+ if (m.kind === "image") return h("img", { className: "ckf-media", src: m.url, alt: m.alt ?? "", onLoad: () => follow() });
330
+ const v = videoEmbed(m.url);
331
+ return v.kind === "iframe" ? h(
332
+ "div",
333
+ { className: "ckf-video" },
334
+ h("iframe", { src: v.src, title: m.alt || "Video", allow: "fullscreen; picture-in-picture", allowfullscreen: true, loading: "lazy", referrerpolicy: "strict-origin-when-cross-origin" })
335
+ ) : h("video", { className: "ckf-media", src: v.src, controls: true, playsinline: true, preload: "metadata", "aria-label": m.alt || void 0, onLoadedmetadata: () => follow() });
336
+ };
337
+ const questionCard = (v) => {
338
+ const s = v.step;
339
+ const q = s.question;
340
+ const titleId = "ckf-title";
341
+ const ok = answerOk(q, v.answer);
342
+ let field = false;
343
+ if (q?.type === "choice")
344
+ field = h(
345
+ "div",
346
+ { className: "ckf-opts", role: "radiogroup", "aria-labelledby": titleId },
347
+ ...(q.options ?? []).map(
348
+ (o) => h(
349
+ "button",
350
+ {
351
+ type: "button",
352
+ role: "radio",
353
+ className: "ckf-opt",
354
+ "aria-checked": String(v.answer === o.id),
355
+ "data-ckf-id": `q:${o.id}`,
356
+ onClick: () => ctl.onboarding.answer(o.id)
357
+ },
358
+ h("span", { className: "ckf-radio" }),
359
+ o.label
360
+ )
361
+ )
362
+ );
363
+ else if (q) {
364
+ const input = h("input", {
365
+ type: "text",
366
+ className: "ckf-input",
367
+ placeholder: q.placeholder || "Your answer",
368
+ "aria-labelledby": titleId,
369
+ maxlength: "500",
370
+ "data-ckf-id": "answer",
371
+ onInput: (e) => ctl.onboarding.answer(e.target.value),
372
+ onKeydown: (e) => e.key === "Enter" && ctl.onboarding.next()
373
+ });
374
+ input.value = v.answer ?? "";
375
+ field = input;
376
+ }
377
+ return h(
378
+ "div",
379
+ { className: "ckf-scrim" },
380
+ h(
381
+ "div",
382
+ { className: "ckf-card", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId },
383
+ h("div", { className: "ckf-top" }, dots(v.index + 1, v.total), closeButton("Close", ctl.onboarding.dismiss)),
384
+ v.index === 0 ? iconEl(v.flow.icon, "ckf-hic") : false,
385
+ h("h2", { className: "ckf-h", id: titleId }, s.title),
386
+ s.body ? h("p", { className: "ckf-p" }, s.body) : false,
387
+ field,
388
+ h(
389
+ "div",
390
+ { className: "ckf-row" },
391
+ v.back ? button("Back", "quiet", ctl.onboarding.back) : false,
392
+ button(s.button || (v.last ? "Done" : "Continue"), "primary", ctl.onboarding.next, { disabled: !ok, id: "go" })
393
+ )
394
+ )
395
+ );
396
+ };
290
397
  const stepCard = (v) => {
291
398
  const s = v.step;
399
+ if (s.kind === "question") return questionCard(v);
292
400
  const card = h(
293
401
  "div",
294
402
  { className: "ckf-card", role: "dialog", "aria-modal": s.kind === "modal" ? "true" : void 0, "aria-labelledby": "ckf-title" },
@@ -298,6 +406,8 @@ var mountFlows = (ctl, theme = {}) => {
298
406
  v.total > 1 ? h("p", { className: "ckf-small" }, `Step ${v.index + 1} of ${v.total}`) : h("span"),
299
407
  closeButton("Close the tour", ctl.onboarding.dismiss)
300
408
  ),
409
+ s.kind === "modal" ? iconEl(v.flow.icon, "ckf-hic") : false,
410
+ media(s),
301
411
  h("h2", { className: "ckf-h", id: "ckf-title" }, s.title),
302
412
  s.body ? h("p", { className: "ckf-p" }, s.body) : false,
303
413
  h("div", { className: "ckf-row" }, button(s.button || (v.last ? "Done" : "Next"), "primary", ctl.onboarding.next, { id: "go" }))
@@ -307,8 +417,9 @@ var mountFlows = (ctl, theme = {}) => {
307
417
  const float = h("div", { className: "ckf-float" }, card);
308
418
  if (target) {
309
419
  const ring = h("div", { className: "ckf-ring", "aria-hidden": "true" });
310
- placed = { float, ring, target };
311
- place(float, ring, target);
420
+ float.append(h("span", { className: "ckf-arrow", "aria-hidden": "true" }));
421
+ placed = { float, ring, target, side: s.placement };
422
+ place(float, ring, target, s.placement);
312
423
  return h("div", {}, ring, float);
313
424
  }
314
425
  return float;
@@ -325,15 +436,31 @@ var mountFlows = (ctl, theme = {}) => {
325
436
  return null;
326
437
  }
327
438
  };
328
- const place = (float, ring, target) => {
439
+ const place = (float, ring, target, side = "auto") => {
329
440
  const r = target.getBoundingClientRect();
330
441
  Object.assign(ring.style, { left: `${r.left - 4}px`, top: `${r.top - 4}px`, width: `${r.width + 8}px`, height: `${r.height + 8}px` });
331
- const w = Math.min(340, window.innerWidth - 24);
332
- const left = Math.max(12, Math.min(r.left, window.innerWidth - w - 12));
333
- const below = r.bottom + 12;
334
- Object.assign(float.style, { left: `${left}px`, right: "auto", width: `${w}px` });
335
- if (below + 200 < window.innerHeight) Object.assign(float.style, { top: `${below}px`, bottom: "auto" });
336
- else Object.assign(float.style, { top: "auto", bottom: `${Math.max(12, window.innerHeight - r.top + 12)}px` });
442
+ const vw = window.innerWidth;
443
+ const vh = window.innerHeight;
444
+ const w = Math.min(340, vw - 24);
445
+ const ht = float.offsetHeight || 200;
446
+ const gap = 14;
447
+ const room = {
448
+ bottom: vh - r.bottom - gap - 12 >= ht,
449
+ top: r.top - gap - 12 >= ht,
450
+ right: vw - r.right - gap - 12 >= w,
451
+ left: r.left - gap - 12 >= w
452
+ };
453
+ const flip = { top: "bottom", bottom: "top", left: "right", right: "left" };
454
+ const order = side === "auto" || !flip[side] ? ["bottom", "top"] : [side, flip[side], "bottom", "top", "right", "left"];
455
+ const s = order.find((x) => room[x]) ?? (side !== "auto" && flip[side] ? side : r.top > vh - r.bottom ? "top" : "bottom");
456
+ const clamp = (v, hi) => Math.max(12, Math.min(v, hi - 12));
457
+ const across = s === "top" || s === "bottom";
458
+ const left = across ? clamp(side === "auto" ? r.left : r.left + r.width / 2 - w / 2, vw - w) : s === "right" ? r.right + gap : r.left - gap - w;
459
+ const top = across ? s === "bottom" ? r.bottom + gap : r.top - gap - ht : clamp(r.top + r.height / 2 - ht / 2, vh - ht);
460
+ Object.assign(float.style, { left: `${left}px`, top: `${top}px`, right: "auto", bottom: "auto", width: `${w}px` });
461
+ float.setAttribute("data-side", s);
462
+ const at = across ? r.left + r.width / 2 - left : r.top + r.height / 2 - top;
463
+ float.style.setProperty("--ckf-ap", `${Math.max(18, Math.min(at, (across ? w : ht) - 18))}px`);
337
464
  };
338
465
  const checklist = (v) => {
339
466
  const done = v.items.filter((i) => i.done).length;
@@ -358,11 +485,25 @@ var mountFlows = (ctl, theme = {}) => {
358
485
  h(
359
486
  "div",
360
487
  { className: "ckf-opts" },
361
- ...v.items.map(({ step, done: d }) => {
488
+ ...v.items.map(({ step, done: d, opens }) => {
362
489
  const inner = [
363
490
  h("span", { className: "ckf-tick", "aria-hidden": "true" }, d ? "\u2713" : ""),
491
+ iconEl(step.icon, "ckf-ic"),
364
492
  h("span", {}, h("b", {}, step.title), step.body ? h("span", { className: "ckf-small" }, step.body) : false)
365
493
  ];
494
+ if (!d && opens)
495
+ return h(
496
+ "button",
497
+ {
498
+ type: "button",
499
+ className: "ckf-check",
500
+ "aria-label": `Start \u201C${step.title}\u201D`,
501
+ "data-ckf-id": `t:${step.id}`,
502
+ onClick: () => ctl.onboarding.open(step.id)
503
+ },
504
+ ...inner,
505
+ h("span", { className: "ckf-go", "aria-hidden": "true" }, "\u203A")
506
+ );
366
507
  return d || step.event ? h("div", { className: "ckf-check", "data-done": d, role: "listitem" }, ...inner) : h(
367
508
  "button",
368
509
  {
@@ -405,6 +546,13 @@ var mountFlows = (ctl, theme = {}) => {
405
546
  };
406
547
  const draw = () => {
407
548
  const v = ctl.view();
549
+ const accent = theme.accent ?? ctl.accent?.();
550
+ if (accent) root.style.setProperty("--ckf-accent", accent);
551
+ const fin = ctl.finale?.();
552
+ if (fin && fin.key !== played) {
553
+ played = fin.key;
554
+ playFinish(fin.kind, { accent });
555
+ }
408
556
  const key = v ? screenKey(v) : "";
409
557
  const focused = document.activeElement?.closest?.(".ckf-root") ? document.activeElement?.getAttribute("data-ckf-id") : null;
410
558
  if (key && !lastKey && document.activeElement instanceof HTMLElement && !document.activeElement.closest(".ckf-root"))
@@ -415,9 +563,10 @@ var mountFlows = (ctl, theme = {}) => {
415
563
  root.replaceChildren(
416
564
  ...v ? [v.type === "cancel" ? cancelCard(v) : v.type === "step" ? stepCard(v) : v.type === "checklist" ? checklist(v) : message(v)] : []
417
565
  );
566
+ follow();
418
567
  if (key !== lastKey) {
419
568
  const modal = root.querySelector("[aria-modal=true]");
420
- const first = modal?.querySelector("[role=radio], [data-ckf-id=go], textarea") ?? null;
569
+ const first = modal?.querySelector("[role=radio], input, [data-ckf-id=go], textarea") ?? null;
421
570
  first?.focus();
422
571
  if (!key && returnFocus) {
423
572
  returnFocus.focus();
@@ -431,11 +580,11 @@ var mountFlows = (ctl, theme = {}) => {
431
580
  const v = ctl.view();
432
581
  if (v?.type === "cancel") ctl.cancel.close();
433
582
  else if (v?.type === "message") ctl.message.dismiss();
434
- else if (v?.type === "step" && v.step.kind === "modal") ctl.onboarding.dismiss();
435
- };
436
- const follow = () => {
437
- if (placed) place(placed.float, placed.ring, placed.target);
583
+ else if (v?.type === "step" && (v.step.kind === "modal" || v.step.kind === "question")) ctl.onboarding.dismiss();
438
584
  };
585
+ function follow() {
586
+ if (placed) place(placed.float, placed.ring, placed.target, placed.side);
587
+ }
439
588
  document.addEventListener("keydown", onKey);
440
589
  window.addEventListener("resize", follow);
441
590
  window.addEventListener("scroll", follow, { passive: true, capture: true });