@tour-kit/studio 0.1.0 → 0.2.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.
- package/dist/index.js +351 -31
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -140,6 +140,12 @@ var checklistTask = z4.object({
|
|
|
140
140
|
]).optional(),
|
|
141
141
|
manualComplete: z4.boolean().optional()
|
|
142
142
|
});
|
|
143
|
+
var checklistPosition = z4.enum([
|
|
144
|
+
"bottom-right",
|
|
145
|
+
"bottom-left",
|
|
146
|
+
"top-right",
|
|
147
|
+
"top-left"
|
|
148
|
+
]);
|
|
143
149
|
var checklistComponent = z4.object({
|
|
144
150
|
kind: z4.literal("checklist"),
|
|
145
151
|
id: z4.string(),
|
|
@@ -148,7 +154,15 @@ var checklistComponent = z4.object({
|
|
|
148
154
|
icon: z4.string().optional(),
|
|
149
155
|
tasks: z4.array(checklistTask).min(1),
|
|
150
156
|
dismissible: z4.boolean().optional(),
|
|
151
|
-
hideOnComplete: z4.boolean().optional()
|
|
157
|
+
hideOnComplete: z4.boolean().optional(),
|
|
158
|
+
// Display surface (Studio-authoring; projected to the rendered ELEMENT, not the
|
|
159
|
+
// SDK `ChecklistConfig` — the CLI strips both before emitting the config const).
|
|
160
|
+
// "launcher" (the DEFAULT when unset) = a floating corner launcher button that
|
|
161
|
+
// pops a compact panel (`<ChecklistLauncher>`) — the real-world checklist UX;
|
|
162
|
+
// "inline" = the in-flow `<Checklist>` panel. `position` places the launcher
|
|
163
|
+
// corner (default "bottom-right"); ignored for inline.
|
|
164
|
+
display: z4.enum(["launcher", "inline"]).optional(),
|
|
165
|
+
position: checklistPosition.optional()
|
|
152
166
|
});
|
|
153
167
|
|
|
154
168
|
// ../recipe/src/components.ts
|
|
@@ -246,7 +260,9 @@ var scaffold = z7.object({
|
|
|
246
260
|
// ../recipe/src/schedule.ts
|
|
247
261
|
import { z as z8 } from "zod";
|
|
248
262
|
var dayOfWeek = z8.number().int().min(0).max(6);
|
|
263
|
+
var scheduleAppliesTo = z8.enum(["announcement", "survey"]);
|
|
249
264
|
var scheduleDef = z8.object({
|
|
265
|
+
appliesTo: scheduleAppliesTo.optional(),
|
|
250
266
|
enabled: z8.boolean().optional(),
|
|
251
267
|
startAt: dateString.optional(),
|
|
252
268
|
endAt: dateString.optional(),
|
|
@@ -487,19 +503,51 @@ var themeVariation = z10.object({
|
|
|
487
503
|
theme: themeTokens
|
|
488
504
|
});
|
|
489
505
|
var recipeTheme = z10.object({
|
|
490
|
-
variations: z10.array(themeVariation).length(1)
|
|
506
|
+
variations: z10.array(themeVariation).length(1),
|
|
507
|
+
// Opt-in CLI-codegen flag (NOT projected to the SDK): when true, `tourkit add`
|
|
508
|
+
// ALSO emits a `THEME_TAILWIND` const — a `plugin(({addBase}) => addBase(…))`
|
|
509
|
+
// object the user merges into `tailwind.config` — beside the always-emitted
|
|
510
|
+
// dual-channel CSS. The CSS path is universal (works in any host); this is for
|
|
511
|
+
// Tailwind users who prefer managing the tokens in their config.
|
|
512
|
+
tailwind: z10.boolean().optional()
|
|
491
513
|
});
|
|
492
|
-
var
|
|
493
|
-
|
|
494
|
-
"--primary
|
|
495
|
-
"--
|
|
496
|
-
"--popover
|
|
497
|
-
"--
|
|
498
|
-
"--
|
|
499
|
-
"--
|
|
500
|
-
"--
|
|
501
|
-
|
|
502
|
-
|
|
514
|
+
var THEME_TOKEN_ENTRIES = [
|
|
515
|
+
// Phase 32 — shadcn-vocab base (8)
|
|
516
|
+
["--primary", "--tour-primary"],
|
|
517
|
+
["--primary-foreground", "--tour-primary-fg"],
|
|
518
|
+
["--popover", "--tour-card-bg"],
|
|
519
|
+
["--popover-foreground", "--tour-card-fg"],
|
|
520
|
+
["--muted-foreground", "--tour-muted-fg"],
|
|
521
|
+
["--border", "--tour-card-border"],
|
|
522
|
+
["--ring", "--tour-ring"],
|
|
523
|
+
["--radius", "--tour-card-radius"],
|
|
524
|
+
// Phase 37 — buttons
|
|
525
|
+
["--primary-hover", "--tour-primary-hover"],
|
|
526
|
+
["--secondary", "--tour-secondary-bg"],
|
|
527
|
+
["--secondary-foreground", "--tour-secondary-fg"],
|
|
528
|
+
["--secondary-border", "--tour-secondary-border"],
|
|
529
|
+
["--secondary-hover", "--tour-secondary-hover"],
|
|
530
|
+
// Phase 37 — card
|
|
531
|
+
["--card-shadow", "--tour-card-shadow"],
|
|
532
|
+
["--card-padding", "--tour-card-padding"],
|
|
533
|
+
["--card-width", "--tour-card-width"],
|
|
534
|
+
// Phase 37 — backdrop / overlay
|
|
535
|
+
["--overlay", "--tour-overlay-bg"],
|
|
536
|
+
["--overlay-blur", "--tour-overlay-blur"],
|
|
537
|
+
// Phase 37 — typography
|
|
538
|
+
["--font-family", "--tour-font-family"],
|
|
539
|
+
["--font-size", "--tour-font-size"],
|
|
540
|
+
["--line-height", "--tour-line-height"]
|
|
541
|
+
];
|
|
542
|
+
var TOKEN_BRIDGE = Object.fromEntries(THEME_TOKEN_ENTRIES);
|
|
543
|
+
var SCOPED_SELECTORS = [
|
|
544
|
+
"[data-tour-step]",
|
|
545
|
+
"[data-survey-modal]",
|
|
546
|
+
"[data-survey-slideout]",
|
|
547
|
+
"[data-survey-banner]",
|
|
548
|
+
"[data-survey-popover]",
|
|
549
|
+
"[data-survey-inline]"
|
|
550
|
+
];
|
|
503
551
|
|
|
504
552
|
// ../recipe/src/recipe.ts
|
|
505
553
|
var SUPPORTED_DEPS = {
|
|
@@ -635,12 +683,21 @@ var recipeSchemaV1 = z11.object({
|
|
|
635
683
|
message: "free recipe must inject TourKitProvider"
|
|
636
684
|
});
|
|
637
685
|
for (const c of r.components)
|
|
638
|
-
if ((c.kind === "announcement" || c.kind === "survey") && c.schedule
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
686
|
+
if ((c.kind === "announcement" || c.kind === "survey") && c.schedule) {
|
|
687
|
+
const sched = r.schedules?.[c.schedule];
|
|
688
|
+
if (!sched)
|
|
689
|
+
ctx.addIssue({
|
|
690
|
+
code: "custom",
|
|
691
|
+
path: ["components"],
|
|
692
|
+
message: `unknown schedule "${c.schedule}"`
|
|
693
|
+
});
|
|
694
|
+
else if (sched.appliesTo && sched.appliesTo !== c.kind)
|
|
695
|
+
ctx.addIssue({
|
|
696
|
+
code: "custom",
|
|
697
|
+
path: ["components"],
|
|
698
|
+
message: `schedule "${c.schedule}" applies to ${sched.appliesTo}, not this ${c.kind}`
|
|
699
|
+
});
|
|
700
|
+
}
|
|
644
701
|
const selected = new Set(r.packages);
|
|
645
702
|
r.components.forEach((c, i) => {
|
|
646
703
|
const missing = requiredPackages(c).filter((p) => !selected.has(p));
|
|
@@ -659,6 +716,217 @@ var recipeSchemaV1 = z11.object({
|
|
|
659
716
|
});
|
|
660
717
|
});
|
|
661
718
|
|
|
719
|
+
// ../recipe/src/shim.ts
|
|
720
|
+
var EMBEDDED_DROP = /^(html|body|button)\b|utk-mock/;
|
|
721
|
+
function whereWrap(selector) {
|
|
722
|
+
const sel = selector.trim();
|
|
723
|
+
if (sel === ":root") return ":where(:root)";
|
|
724
|
+
const pseudoEl = sel.indexOf("::");
|
|
725
|
+
if (pseudoEl !== -1) {
|
|
726
|
+
return `:where(${sel.slice(0, pseudoEl)})${sel.slice(pseudoEl)}`;
|
|
727
|
+
}
|
|
728
|
+
return `:where(${sel})`;
|
|
729
|
+
}
|
|
730
|
+
function embeddedShimCss(css) {
|
|
731
|
+
const out = [];
|
|
732
|
+
let i = 0;
|
|
733
|
+
const n = css.length;
|
|
734
|
+
while (i < n) {
|
|
735
|
+
while (i < n && /\s/.test(css.charAt(i))) i++;
|
|
736
|
+
if (i >= n) break;
|
|
737
|
+
if (css.startsWith("/*", i)) {
|
|
738
|
+
const end = css.indexOf("*/", i + 2);
|
|
739
|
+
i = end === -1 ? n : end + 2;
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
const braceStart = css.indexOf("{", i);
|
|
743
|
+
if (braceStart === -1) break;
|
|
744
|
+
const selector = css.slice(i, braceStart).trim();
|
|
745
|
+
let depth = 1;
|
|
746
|
+
let j = braceStart + 1;
|
|
747
|
+
while (j < n && depth > 0) {
|
|
748
|
+
if (css.charAt(j) === "{") depth++;
|
|
749
|
+
else if (css.charAt(j) === "}") depth--;
|
|
750
|
+
j++;
|
|
751
|
+
}
|
|
752
|
+
const body = css.slice(braceStart + 1, j - 1);
|
|
753
|
+
i = j;
|
|
754
|
+
if (selector.startsWith("@keyframes")) {
|
|
755
|
+
out.push(`${selector}{${body}}`);
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
if (selector.startsWith("@media")) {
|
|
759
|
+
out.push(`${selector}{${embeddedShimCss(body)}}`);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
if (EMBEDDED_DROP.test(selector)) continue;
|
|
763
|
+
out.push(`${selector.split(",").map(whereWrap).join(",")}{${body}}`);
|
|
764
|
+
}
|
|
765
|
+
return out.join("\n");
|
|
766
|
+
}
|
|
767
|
+
var PREVIEW_SHIM_CSS = `
|
|
768
|
+
:root{--background:#fff;--foreground:#0f172a;--popover:#fff;--popover-foreground:#0f172a;--card:#fff;--card-foreground:#0f172a;--primary:#2563eb;--primary-foreground:#fff;--secondary:#f1f5f9;--secondary-foreground:#0f172a;--muted:#f1f5f9;--muted-foreground:#64748b;--accent:#f1f5f9;--accent-foreground:#0f172a;--border:#e2e8f0;--ring:#2563eb;--destructive:#dc2626;--destructive-foreground:#fff;--radius:.5rem;--color-popover:var(--utk-popover, var(--popover));--color-border:var(--utk-border, var(--border))}
|
|
769
|
+
/* Button reset \u2014 FIRST so the zero-specificity :where() utility classes below
|
|
770
|
+
(bg-primary, border, px-4, text-sm, \u2026) WIN at equal specificity in the embedded
|
|
771
|
+
shim, while a bare/un-utilitied button still loses its default chrome (native
|
|
772
|
+
border + background). Host-safe: :where() is zero-specificity, so a host's own
|
|
773
|
+
button rules always win on host UI. This is the "no default button border" reset. */
|
|
774
|
+
:where(button){-webkit-appearance:none;appearance:none;background:transparent;border:0;padding:0;font:inherit;color:inherit;cursor:pointer}
|
|
775
|
+
/* overflow-x hidden: the faux-app surface never scrolls horizontally \u2014 mock
|
|
776
|
+
target boxes wrap and every SDK surface is fixed/portaled, so a horizontal
|
|
777
|
+
scrollbar in the canvas is only ever transient layout noise (e.g. a card
|
|
778
|
+
mid-position before floating-ui settles). Vertical scroll stays. */
|
|
779
|
+
html,body{margin:0;overflow-x:hidden}
|
|
780
|
+
body{font-family:ui-sans-serif,system-ui,-apple-system,sans-serif;background:#f8fafc;color:#0f172a;min-height:100vh}
|
|
781
|
+
#utk-mock-app{padding:24px;display:flex;flex-wrap:wrap;gap:16px;align-items:flex-start}
|
|
782
|
+
.utk-mock-box{min-width:150px;min-height:52px;padding:12px 16px;border:1px dashed #cbd5e1;border-radius:10px;background:#fff;display:flex;flex-direction:column;gap:2px;justify-content:center;font-size:13px;color:#334155;box-shadow:0 1px 2px rgba(15,23,42,.05)}
|
|
783
|
+
.utk-mock-box small{color:#94a3b8;font-size:11px}
|
|
784
|
+
.utk-mock-note{color:#64748b;font-size:14px;padding:24px}
|
|
785
|
+
.flex{display:flex}.inline-flex{display:inline-flex}.flex-col{flex-direction:column}.flex-1{flex:1 1 0%}
|
|
786
|
+
.items-center{align-items:center}.items-start{align-items:flex-start}
|
|
787
|
+
.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}
|
|
788
|
+
.gap-1{gap:.25rem}.gap-2{gap:.5rem}.min-w-0{min-width:0}
|
|
789
|
+
.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{inset:0}.z-50{z-index:50}
|
|
790
|
+
.h-2{height:.5rem}.h-6{height:1.5rem}.h-8{height:2rem}.w-80{width:20rem}.w-full{width:100%}
|
|
791
|
+
.p-4{padding:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}
|
|
792
|
+
.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pt-2{padding-top:.5rem}
|
|
793
|
+
.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mt-2{margin-top:.5rem}
|
|
794
|
+
.text-xs{font-size:.75rem;line-height:1rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-lg{font-size:1.125rem;line-height:1.75rem}
|
|
795
|
+
.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-none{line-height:1}.tracking-tight{letter-spacing:-.015em}
|
|
796
|
+
.tabular-nums{font-variant-numeric:tabular-nums}.text-center{text-align:center}
|
|
797
|
+
.bg-popover{background-color:var(--utk-popover, var(--popover))}.text-popover-foreground{color:var(--utk-popover-foreground, var(--popover-foreground))}
|
|
798
|
+
.bg-primary{background-color:var(--utk-primary, var(--primary))}.text-primary-foreground{color:var(--utk-primary-foreground, var(--primary-foreground))}
|
|
799
|
+
.bg-muted{background-color:var(--utk-muted, var(--muted))}.text-muted-foreground{color:var(--utk-muted-foreground, var(--muted-foreground))}
|
|
800
|
+
.bg-transparent{background-color:transparent}.bg-accent{background-color:var(--utk-accent, var(--accent))}.text-foreground{color:var(--utk-foreground, var(--foreground))}
|
|
801
|
+
.border{border:1px solid var(--utk-border, var(--border))}
|
|
802
|
+
.rounded-lg{border-radius:var(--utk-radius, var(--radius))}.rounded-md{border-radius:calc(var(--utk-radius, var(--radius)) - .125rem)}.rounded-sm{border-radius:calc(var(--utk-radius, var(--radius)) - .25rem)}.rounded-full{border-radius:9999px}
|
|
803
|
+
.shadow{box-shadow:0 1px 3px rgba(0,0,0,.1),0 1px 2px rgba(0,0,0,.06)}
|
|
804
|
+
.shadow-lg{box-shadow:0 10px 15px -3px rgba(0,0,0,.12),0 4px 6px -4px rgba(0,0,0,.1)}
|
|
805
|
+
.opacity-70{opacity:.7}.transition-colors{transition:color .15s,background-color .15s,border-color .15s}.transition-opacity{transition:opacity .15s}
|
|
806
|
+
[class~='hover:bg-primary/90']:hover{background-color:#1d4ed8}
|
|
807
|
+
[class~='hover:bg-accent']:hover{background-color:var(--utk-accent, var(--accent))}
|
|
808
|
+
[class~='hover:text-accent-foreground']:hover{color:var(--utk-accent-foreground, var(--accent-foreground))}
|
|
809
|
+
[class~='hover:text-foreground']:hover{color:var(--utk-foreground, var(--foreground))}
|
|
810
|
+
[class~='hover:opacity-100']:hover{opacity:1}
|
|
811
|
+
[class~='hover:underline']:hover{text-decoration:underline}
|
|
812
|
+
[class~='disabled:opacity-50']:disabled{opacity:.5}
|
|
813
|
+
[class~='disabled:pointer-events-none']:disabled{pointer-events:none}
|
|
814
|
+
[class~='focus-visible:outline-none']:focus-visible,[class~='focus:outline-none']:focus{outline:none}
|
|
815
|
+
[class~='focus-visible:ring-2']:focus-visible,[class~='focus:ring-2']:focus{box-shadow:0 0 0 2px var(--utk-ring, var(--ring))}
|
|
816
|
+
[class~='focus-visible:ring-1']:focus-visible{box-shadow:0 0 0 1px var(--utk-ring, var(--ring))}
|
|
817
|
+
/* tour-kit hints v1.0.6 beacon + tooltip + close button (Phase 22A). Enumerated
|
|
818
|
+
from the cva class lists in the hints dist (P/q/F). The default un-variant
|
|
819
|
+
hotspot is a pulsing dot; the tooltip portals into <body>. */
|
|
820
|
+
.h-3{height:.75rem}.w-3{width:.75rem}.h-4{height:1rem}.w-4{width:1rem}
|
|
821
|
+
.border-2{border-width:2px;border-style:solid}.border-background{border-color:var(--utk-background, var(--background))}
|
|
822
|
+
.cursor-pointer{cursor:pointer}.p-3{padding:.75rem}.pr-4{padding-right:1rem}
|
|
823
|
+
.right-2{right:.5rem}.top-2{top:.5rem}.mb-1{margin-bottom:.25rem}
|
|
824
|
+
.shadow-md{box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1)}
|
|
825
|
+
[class~='max-w-[280px]']{max-width:280px}[class~='z-[9999]']{z-index:9999}
|
|
826
|
+
@keyframes tour-pulse{0%,100%{box-shadow:0 0 0 0 rgba(37,99,235,.7)}50%{box-shadow:0 0 0 8px rgba(37,99,235,0)}}
|
|
827
|
+
.animate-tour-pulse{animation:tour-pulse 1.5s ease-in-out infinite}
|
|
828
|
+
/* tour-kit checklists v0.13.9 \u2014 the inline <Checklist showProgress> panel (Phase 22B).
|
|
829
|
+
We render the panel directly (not the floating ChecklistLauncher), so this covers
|
|
830
|
+
the card container, header, progress bar, task rows + checkbox states, and the
|
|
831
|
+
all-complete footer. Enumerated from the cva lists in the checklists dist
|
|
832
|
+
(fe/Oe/Ue/ze container+header+content+complete \xB7 Ve/Ee/Le/pe progress \xB7 Me/De/He/_e task). */
|
|
833
|
+
.bg-card{background-color:var(--utk-card, var(--card))}.text-card-foreground{color:var(--utk-card-foreground, var(--card-foreground))}
|
|
834
|
+
.text-primary{color:var(--utk-primary, var(--primary))}.border-primary{border-color:var(--utk-primary, var(--primary))}
|
|
835
|
+
.shadow-sm{box-shadow:0 1px 2px rgba(0,0,0,.05)}
|
|
836
|
+
.border-b{border-bottom:1px solid var(--utk-border, var(--border))}.border-t{border-top:1px solid var(--utk-border, var(--border))}
|
|
837
|
+
.gap-3{gap:.75rem}.p-2{padding:.5rem}.w-5{width:1.25rem}.h-5{height:1.25rem}.h-full{height:100%}
|
|
838
|
+
.flex-shrink-0{flex-shrink:0}.overflow-hidden{overflow:hidden}.opacity-0{opacity:0}.opacity-50{opacity:.5}
|
|
839
|
+
.cursor-not-allowed{cursor:not-allowed}.line-through{text-decoration:line-through}
|
|
840
|
+
.line-clamp-2{display:-webkit-box;-webkit-line-clamp:2;line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
841
|
+
.transition-all{transition:all .2s}.duration-300{transition-duration:.3s}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}
|
|
842
|
+
[class~='bg-muted/30']{background-color:#f5f8fb}
|
|
843
|
+
[class~='hover:bg-muted/50']:hover{background-color:#e8eef6}
|
|
844
|
+
[class~='border-muted-foreground/30']{border-color:rgba(100,116,139,.3)}
|
|
845
|
+
[class~='hover:border-primary/50']:hover{border-color:rgba(37,99,235,.5)}
|
|
846
|
+
[class~='group']:hover [class~='group-hover:opacity-100']{opacity:1}
|
|
847
|
+
/* The FLOATING ChecklistLauncher button (closed state). The block above styles the
|
|
848
|
+
OPEN panel; the launcher's own size class (w-14 h-14) is launcher-specific and
|
|
849
|
+
otherwise unshimmed, so the closed button collapses to just its icon. bg-primary
|
|
850
|
+
/ rounded-full / shadow-lg are already covered above. */
|
|
851
|
+
.w-14{width:3.5rem}.h-14{height:3.5rem}
|
|
852
|
+
/* The launcher's task-count BADGE \u2014 absolutely pinned to the button's top-right
|
|
853
|
+
corner (the negative offsets) so it never overlaps the centered icon. Without
|
|
854
|
+
these the badge fell back to its in-flow position on top of the glyph. */
|
|
855
|
+
.-top-1{top:-.25rem}.-right-1{right:-.25rem}.font-bold{font-weight:700}
|
|
856
|
+
.bg-destructive{background-color:var(--utk-destructive, var(--destructive))}.text-destructive-foreground{color:var(--utk-destructive-foreground, var(--destructive-foreground))}
|
|
857
|
+
/* The unlicensed watermark portals to <body> at bottom-right with a MAX z-index +
|
|
858
|
+
pointer-events:none (preview-only \u2014 it clears with a Pro license). A bottom-right
|
|
859
|
+
ChecklistLauncher lands in the same corner; the watermark can't be out-z-indexed,
|
|
860
|
+
so in the PREVIEW we lift it clear of the launcher. The rule overrides the
|
|
861
|
+
watermark's inline bottom:16px \u2014 an important rule beats a non-important inline
|
|
862
|
+
declaration even at the zero specificity the embedded :where() wrap gives it. */
|
|
863
|
+
[data-tourkit-watermark]{bottom:5rem!important}
|
|
864
|
+
/* tour-kit announcements v4.1.6 \u2014 modal/slideout/banner/toast/spotlight variants
|
|
865
|
+
(Phase 22C). The SDK ships Tailwind + tailwindcss-animate utilities; the
|
|
866
|
+
sandboxed iframe has no Tailwind build, so this shims the static layout/intent
|
|
867
|
+
classes each variant emits. Enter/exit animations live behind motion-safe:
|
|
868
|
+
variants we deliberately do NOT shim, so no enter animation runs \u2014 reduced-motion
|
|
869
|
+
safe by construction. Enumerated from the cva lists in the announcements dist. */
|
|
870
|
+
.bg-background{background-color:var(--utk-background, var(--background))}
|
|
871
|
+
.bg-secondary{background-color:var(--utk-secondary, var(--secondary))}.text-secondary-foreground{color:var(--utk-secondary-foreground, var(--secondary-foreground))}
|
|
872
|
+
.border-border{border-color:var(--utk-border, var(--border))}.border-0{border-width:0}
|
|
873
|
+
.grid{display:grid}.flex-row{flex-direction:row}.items-end{align-items:flex-end}
|
|
874
|
+
.gap-4{gap:1rem}.p-6{padding:1.5rem}.p-0{padding:0}.px-8{padding-left:2rem;padding-right:2rem}
|
|
875
|
+
.py-3{padding-top:.75rem;padding-bottom:.75rem}
|
|
876
|
+
.space-y-1>*+*{margin-top:.25rem}.space-y-4>*+*{margin-top:1rem}
|
|
877
|
+
.h-1{height:.25rem}.h-9{height:2.25rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.w-full{width:100%}
|
|
878
|
+
.max-w-sm{max-width:24rem}.max-w-md{max-width:28rem}.max-w-lg{max-width:32rem}.max-w-xl{max-width:36rem}
|
|
879
|
+
[class~='w-3/4']{width:75%}
|
|
880
|
+
.top-0{top:0}.bottom-0{bottom:0}.left-0{left:0}.right-0{right:0}
|
|
881
|
+
.top-4{top:1rem}.bottom-4{bottom:1rem}.left-4{left:1rem}.right-4{right:1rem}
|
|
882
|
+
.z-40{z-index:40}.whitespace-nowrap{white-space:nowrap}.pointer-events-auto{pointer-events:auto}
|
|
883
|
+
.opacity-90{opacity:.9}.underline-offset-4{text-underline-offset:4px}.text-lg{font-size:1.125rem;line-height:1.75rem}
|
|
884
|
+
[class~='bg-black/80']{background-color:rgba(0,0,0,.8)}
|
|
885
|
+
/* modal: the Radix Dialog content is fixed + centered (left/top 50% + translate). */
|
|
886
|
+
[class~='left-[50%]'][class~='top-[50%]']{left:50%;top:50%;transform:translate(-50%,-50%)}
|
|
887
|
+
[class~='max-w-[95vw]']{max-width:95vw}[class~='max-h-[95vh]']{max-height:95vh}
|
|
888
|
+
/* banner/toast intents */
|
|
889
|
+
.bg-blue-50{background-color:#eff6ff}.border-blue-200{border-color:#bfdbfe}.text-blue-900{color:#1e3a8a}
|
|
890
|
+
.bg-green-50{background-color:#f0fdf4}.border-green-200{border-color:#bbf7d0}.text-green-900{color:#14532d}.bg-green-600{background-color:#16a34a}
|
|
891
|
+
.bg-yellow-50{background-color:#fefce8}.border-yellow-200{border-color:#fef08a}.text-yellow-900{color:#713f12}.bg-yellow-600{background-color:#ca8a04}
|
|
892
|
+
.bg-red-50{background-color:#fef2f2}.border-red-200{border-color:#fecaca}.text-red-900{color:#7f1d1d}.bg-red-600{background-color:#dc2626}
|
|
893
|
+
/* tour-kit surveys v3.0.9 \u2014 modal/slideout/banner/popover/inline display modes +
|
|
894
|
+
the question controls (rating/text/select/boolean) and SurveyProgress
|
|
895
|
+
(Phase 30). Enumerated from the cva lists in the surveys src
|
|
896
|
+
(question/modal/slideout/banner variants). dark: variants and the Radix
|
|
897
|
+
data-[state] enter/exit animations are deliberately NOT shimmed (light theme;
|
|
898
|
+
reduced-motion safe by construction, like announcements). */
|
|
899
|
+
.text-base{font-size:1rem;line-height:1.5rem}.text-xl{font-size:1.25rem;line-height:1.75rem}
|
|
900
|
+
.px-6{padding-left:1.5rem;padding-right:1.5rem}
|
|
901
|
+
[class~='py-1.5']{padding-top:.375rem;padding-bottom:.375rem}
|
|
902
|
+
.h-12{height:3rem}.h-auto{height:auto}.w-2{width:.5rem}.w-8{width:2rem}.w-10{width:2.5rem}.w-12{width:3rem}
|
|
903
|
+
.border-l{border-left:1px solid var(--utk-border, var(--border))}.border-r{border-right:1px solid var(--utk-border, var(--border))}
|
|
904
|
+
.border-input{border-color:var(--utk-border, var(--border))}
|
|
905
|
+
[class~='bg-primary/10']{background-color:rgba(37,99,235,.1)}
|
|
906
|
+
.inset-y-0{top:0;bottom:0}
|
|
907
|
+
.flex-wrap{flex-wrap:wrap}.shrink-0{flex-shrink:0}
|
|
908
|
+
.resize-y{resize:vertical}
|
|
909
|
+
.text-destructive{color:#dc2626}.text-right{text-align:right}
|
|
910
|
+
.opacity-80{opacity:.8}.underline-offset-2{text-underline-offset:2px}
|
|
911
|
+
.duration-200{transition-duration:.2s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}
|
|
912
|
+
[class~='min-w-[280px]']{min-width:280px}
|
|
913
|
+
[class~='placeholder:text-muted-foreground']::placeholder{color:var(--utk-muted-foreground, var(--muted-foreground))}
|
|
914
|
+
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}
|
|
915
|
+
@media (min-width:640px){[class~='sm:max-w-sm']{max-width:24rem}[class~='sm:max-w-md']{max-width:28rem}[class~='sm:max-w-lg']{max-width:32rem}[class~='sm:rounded-lg']{border-radius:var(--utk-radius, var(--radius))}}
|
|
916
|
+
/* preview-only inactive-schedule chrome (authoring legibility \u2014 NOT the SDK). The
|
|
917
|
+
variant still renders inside it so the canvas is never blank; the badge states
|
|
918
|
+
the gating truth (role=status for the a11y tree). The badge is fixed + top-z so
|
|
919
|
+
it stays visible even when the variant portals out of flow (modal/toast/slideout
|
|
920
|
+
render into <body>); the wrapper dims its in-flow content (banner/spotlight). */
|
|
921
|
+
.utk-ann-inactive{position:relative;margin:16px;padding:10px;border:1px dashed #cbd5e1;border-radius:10px;background:#fff;opacity:.85}
|
|
922
|
+
.utk-ann-inactive-badge{position:fixed;top:10px;left:50%;transform:translateX(-50%);z-index:100000;display:inline-flex;align-items:center;gap:6px;padding:4px 12px;border-radius:9999px;background:#fef08a;color:#713f12;font-size:12px;font-weight:600;box-shadow:0 2px 6px rgba(15,23,42,.18)}
|
|
923
|
+
.utk-ann-inactive-badge::before{content:"";width:7px;height:7px;border-radius:9999px;background:#a16207}
|
|
924
|
+
/* utk:focus selection highlight (Phase 22A) \u2014 flashes a synthesized target box. */
|
|
925
|
+
@keyframes utk-target-flash{0%{box-shadow:0 0 0 0 rgba(37,99,235,.5)}100%{box-shadow:0 0 0 6px rgba(37,99,235,0)}}
|
|
926
|
+
.utk-target-flash{animation:utk-target-flash .8s ease-out 1;border-color:#2563eb}
|
|
927
|
+
@media (prefers-reduced-motion:reduce){.animate-tour-pulse,.utk-target-flash{animation:none}}
|
|
928
|
+
`;
|
|
929
|
+
|
|
662
930
|
// src/commands/add.ts
|
|
663
931
|
import { defineCommand } from "citty";
|
|
664
932
|
import { consola as consola3 } from "consola";
|
|
@@ -872,8 +1140,14 @@ function imports(recipe) {
|
|
|
872
1140
|
if (kinds.has("hint"))
|
|
873
1141
|
value.push(`import { Hint, HintsProvider } from "@tour-kit/hints";`);
|
|
874
1142
|
if (kinds.has("checklist")) {
|
|
1143
|
+
const cls = recipe.components.filter(
|
|
1144
|
+
(c) => c.kind === "checklist"
|
|
1145
|
+
);
|
|
1146
|
+
const names = /* @__PURE__ */ new Set(["ChecklistProvider"]);
|
|
1147
|
+
for (const c of cls)
|
|
1148
|
+
names.add(c.display === "inline" ? "Checklist" : "ChecklistLauncher");
|
|
875
1149
|
value.push(
|
|
876
|
-
`import {
|
|
1150
|
+
`import { ${[...names].sort().join(", ")} } from "@tour-kit/checklists";`
|
|
877
1151
|
);
|
|
878
1152
|
types.push(`import type { ChecklistConfig } from "@tour-kit/checklists";`);
|
|
879
1153
|
}
|
|
@@ -925,19 +1199,39 @@ function themeCssFor(tokens) {
|
|
|
925
1199
|
const target = TOKEN_BRIDGE[k];
|
|
926
1200
|
return target === void 0 ? [] : [[target, v]];
|
|
927
1201
|
}).sort(([a], [b]) => cmp(a, b));
|
|
1202
|
+
const utk = entries.map(([k, v]) => [`--utk-${k.slice(2)}`, v]).sort(([a], [b]) => cmp(a, b));
|
|
928
1203
|
const all = [...entries].sort(([a], [b]) => cmp(a, b));
|
|
929
1204
|
return [
|
|
930
|
-
cssBlock(":root", bridged),
|
|
931
|
-
|
|
1205
|
+
cssBlock(":root", [...bridged, ...utk]),
|
|
1206
|
+
cssBlock(SCOPED_SELECTORS.join(", "), all)
|
|
932
1207
|
].filter((b) => b !== "").join("\n");
|
|
933
1208
|
}
|
|
1209
|
+
function themeTailwindFor(tokens) {
|
|
1210
|
+
const entries = Object.entries(tokens);
|
|
1211
|
+
const bridged = entries.flatMap(([k, v]) => {
|
|
1212
|
+
const target = TOKEN_BRIDGE[k];
|
|
1213
|
+
return target === void 0 ? [] : [[target, v]];
|
|
1214
|
+
}).sort(([a], [b]) => cmp(a, b));
|
|
1215
|
+
const all = [...entries].sort(([a], [b]) => cmp(a, b));
|
|
1216
|
+
const out = {};
|
|
1217
|
+
if (bridged.length > 0) out[":root"] = Object.fromEntries(bridged);
|
|
1218
|
+
for (const sel of SCOPED_SELECTORS)
|
|
1219
|
+
if (all.length > 0) out[sel] = Object.fromEntries(all);
|
|
1220
|
+
return out;
|
|
1221
|
+
}
|
|
934
1222
|
function emitTheme(theme) {
|
|
935
|
-
const
|
|
936
|
-
|
|
1223
|
+
const tokens = theme.variations[0]?.theme ?? {};
|
|
1224
|
+
const lines = [
|
|
937
1225
|
"// theme: recipe-global ThemeProvider variations + the portal-bridging <style> css",
|
|
938
1226
|
`export const THEME_VARIATIONS: ThemeVariation[] = ${serialize(theme.variations, 0)};`,
|
|
939
|
-
`export const THEME_CSS = ${str(
|
|
940
|
-
]
|
|
1227
|
+
`export const THEME_CSS = ${str(themeCssFor(tokens))};`
|
|
1228
|
+
];
|
|
1229
|
+
if (theme.tailwind)
|
|
1230
|
+
lines.push(
|
|
1231
|
+
"// Tailwind: merge into tailwind.config \u2014 `plugin(({ addBase }) => addBase(THEME_TAILWIND))`",
|
|
1232
|
+
`export const THEME_TAILWIND = ${serialize(themeTailwindFor(tokens), 0)};`
|
|
1233
|
+
);
|
|
1234
|
+
return lines.join("\n");
|
|
941
1235
|
}
|
|
942
1236
|
var jsxAttr = (name, value) => ` ${name}={${serialize(value, 0)}}`;
|
|
943
1237
|
var STEP_FIELDS = [
|
|
@@ -976,7 +1270,19 @@ function emitHint(c) {
|
|
|
976
1270
|
return ` <Hint key={${serialize(c.id, 0)}} id={${serialize(c.id, 0)}}${attrs} />`;
|
|
977
1271
|
}
|
|
978
1272
|
function emitChecklistEl(c) {
|
|
979
|
-
|
|
1273
|
+
const id = serialize(c.id, 0);
|
|
1274
|
+
if (c.display === "inline")
|
|
1275
|
+
return ` <Checklist key={${id}} checklistId={${id}} showProgress />`;
|
|
1276
|
+
const position = c.position ?? "bottom-right";
|
|
1277
|
+
return [
|
|
1278
|
+
` <ChecklistLauncher key={${id}} checklistId={${id}} position=${str(position)}>`,
|
|
1279
|
+
` <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">`,
|
|
1280
|
+
` <rect x="8" y="2" width="8" height="4" rx="1" ry="1" />`,
|
|
1281
|
+
` <path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2" />`,
|
|
1282
|
+
` <path d="m9 14 2 2 4-4" />`,
|
|
1283
|
+
` </svg>`,
|
|
1284
|
+
` </ChecklistLauncher>`
|
|
1285
|
+
].join("\n");
|
|
980
1286
|
}
|
|
981
1287
|
function emitAnnouncementEl(c) {
|
|
982
1288
|
const Comp = ANNOUNCEMENT_COMPONENT[c.variant];
|
|
@@ -1068,7 +1374,7 @@ ${body}
|
|
|
1068
1374
|
);
|
|
1069
1375
|
}`;
|
|
1070
1376
|
}
|
|
1071
|
-
var STRIP = /* @__PURE__ */ new Set(["selectorMeta"]);
|
|
1377
|
+
var STRIP = /* @__PURE__ */ new Set(["selectorMeta", "appliesTo", "display", "position"]);
|
|
1072
1378
|
function clean(value) {
|
|
1073
1379
|
if (value instanceof RawCode) return value;
|
|
1074
1380
|
if (Array.isArray(value)) return value.map(clean);
|
|
@@ -1232,12 +1538,17 @@ function SurveyBody({ surveyId }: { surveyId: string }) {
|
|
|
1232
1538
|
)}
|
|
1233
1539
|
<div className="flex justify-end gap-2">
|
|
1234
1540
|
{state.currentStep > 0 && (
|
|
1235
|
-
<button
|
|
1541
|
+
<button
|
|
1542
|
+
type="button"
|
|
1543
|
+
className="inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium text-muted-foreground transition-colors"
|
|
1544
|
+
onClick={() => survey.prevQuestion()}
|
|
1545
|
+
>
|
|
1236
1546
|
Back
|
|
1237
1547
|
</button>
|
|
1238
1548
|
)}
|
|
1239
1549
|
<button
|
|
1240
1550
|
type="button"
|
|
1551
|
+
className="inline-flex items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors"
|
|
1241
1552
|
onClick={() => (isLast ? survey.complete() : advance())}
|
|
1242
1553
|
>
|
|
1243
1554
|
{isLast ? "Done" : "Next"}
|
|
@@ -1272,6 +1583,7 @@ export const ${hookName(h.ref)}: ${h.signature} = () => {
|
|
|
1272
1583
|
${stubs}
|
|
1273
1584
|
`;
|
|
1274
1585
|
}
|
|
1586
|
+
var BASE_SHIM_CSS = embeddedShimCss(PREVIEW_SHIM_CSS);
|
|
1275
1587
|
function expand(recipe) {
|
|
1276
1588
|
const body = recipe.components.map((c) => emitComponent(c, recipe)).join("\n\n");
|
|
1277
1589
|
const helpers = recipe.components.some((c) => c.kind === "survey") ? `
|
|
@@ -1282,13 +1594,21 @@ ${SURVEY_HELPERS}` : "";
|
|
|
1282
1594
|
${emitTheme(recipe.theme)}` : "";
|
|
1283
1595
|
const contents = `${header(recipe.id, recipe.version)}
|
|
1284
1596
|
"use client";
|
|
1597
|
+
import "./${recipe.id}.css";
|
|
1285
1598
|
${imports(recipe)}
|
|
1286
1599
|
|
|
1287
1600
|
${body}${helpers}${themeConsts}
|
|
1288
1601
|
|
|
1289
1602
|
${emitMount(recipe)}
|
|
1290
1603
|
`;
|
|
1291
|
-
const files = [
|
|
1604
|
+
const files = [
|
|
1605
|
+
{ path: `tours/${recipe.id}.tsx`, contents },
|
|
1606
|
+
{
|
|
1607
|
+
path: `tours/${recipe.id}.css`,
|
|
1608
|
+
contents: `${BASE_SHIM_CSS}
|
|
1609
|
+
`
|
|
1610
|
+
}
|
|
1611
|
+
];
|
|
1292
1612
|
if (recipe.scaffold.hooks.length > 0)
|
|
1293
1613
|
files.push({
|
|
1294
1614
|
path: `tours/${recipe.id}.hooks.ts`,
|
|
@@ -1735,7 +2055,7 @@ runMain(
|
|
|
1735
2055
|
defineCommand6({
|
|
1736
2056
|
meta: {
|
|
1737
2057
|
name: "tourkit",
|
|
1738
|
-
version: "0.
|
|
2058
|
+
version: "0.2.0",
|
|
1739
2059
|
description: "User Tour Kit Studio CLI"
|
|
1740
2060
|
},
|
|
1741
2061
|
subCommands: { add, pull, verify, harden, init }
|
package/package.json
CHANGED