@syntrologie/runtime-sdk 2.4.1 → 2.6.0-canary.1

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 (62) hide show
  1. package/CAPABILITIES.md +66 -1
  2. package/dist/SmartCanvasApp.d.ts +5 -3
  3. package/dist/actions/executors/index.d.ts +1 -2
  4. package/dist/actions/index.d.ts +2 -2
  5. package/dist/actions/schema.d.ts +19111 -20709
  6. package/dist/actions/schema.js +5 -2
  7. package/dist/actions/types.d.ts +45 -48
  8. package/dist/anchor/AnchorResolver.d.ts +18 -0
  9. package/dist/anchor/index.d.ts +2 -0
  10. package/dist/api.d.ts +3 -8
  11. package/dist/apps/examples/gamification-app.example.d.ts +8 -8
  12. package/dist/{chunk-4NYS7GAW.js → chunk-BU4Z6PD7.js} +45 -7
  13. package/dist/chunk-BU4Z6PD7.js.map +7 -0
  14. package/dist/{chunk-QGWATS3Z.js → chunk-Q4WGXNKC.js} +2775 -1052
  15. package/dist/chunk-Q4WGXNKC.js.map +7 -0
  16. package/dist/{chunk-OGTCFYR3.js → chunk-R5DNAIRI.js} +45 -34
  17. package/dist/chunk-R5DNAIRI.js.map +7 -0
  18. package/dist/chunk-XDYJ64IN.js +178 -0
  19. package/dist/chunk-XDYJ64IN.js.map +7 -0
  20. package/dist/components/ShadowCanvasOverlay.d.ts +2 -20
  21. package/dist/components/TileIcon.d.ts +14 -0
  22. package/dist/config/schema.d.ts +5529 -3620
  23. package/dist/config/schema.js +21 -61
  24. package/dist/config/schema.js.map +3 -3
  25. package/dist/context/schema.d.ts +16 -16
  26. package/dist/decisions/schema.d.ts +357 -2789
  27. package/dist/decisions/schema.js +7 -1
  28. package/dist/decisions/types.d.ts +24 -2
  29. package/dist/events/registerConfigPredicates.d.ts +7 -10
  30. package/dist/events/schema.d.ts +12 -12
  31. package/dist/experiments/adapters/growthbook.d.ts +2 -0
  32. package/dist/experiments/types.d.ts +7 -0
  33. package/dist/hooks/useShadowCanvasConfig.d.ts +6 -4
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +154 -186
  36. package/dist/index.js.map +3 -3
  37. package/dist/overlays/schema.d.ts +70 -70
  38. package/dist/react.js +4 -3
  39. package/dist/react.js.map +1 -1
  40. package/dist/runtime.d.ts +3 -0
  41. package/dist/smart-canvas.esm.js +123 -84
  42. package/dist/smart-canvas.esm.js.map +4 -4
  43. package/dist/smart-canvas.js +3271 -1210
  44. package/dist/smart-canvas.js.map +4 -4
  45. package/dist/smart-canvas.min.js +123 -84
  46. package/dist/smart-canvas.min.js.map +4 -4
  47. package/dist/surfaces/types.d.ts +2 -1
  48. package/dist/theme/ThemeProvider.d.ts +11 -16
  49. package/dist/theme/defaultTheme.d.ts +6 -1
  50. package/dist/theme/index.d.ts +3 -4
  51. package/dist/theme/types.d.ts +11 -0
  52. package/dist/version.d.ts +1 -1
  53. package/dist/widgets/WidgetRegistry.d.ts +1 -0
  54. package/package.json +9 -8
  55. package/schema/canvas-config.schema.json +10583 -1049
  56. package/scripts/validate-config.mjs +180 -0
  57. package/dist/actions/executors/tour.d.ts +0 -18
  58. package/dist/chunk-4NYS7GAW.js.map +0 -7
  59. package/dist/chunk-OGTCFYR3.js.map +0 -7
  60. package/dist/chunk-QGWATS3Z.js.map +0 -7
  61. package/dist/hooks/useHostPatches.d.ts +0 -9
  62. package/dist/theme/extractHostTheme.d.ts +0 -14
package/dist/index.js CHANGED
@@ -30,10 +30,9 @@ import {
30
30
  appRegistry,
31
31
  applyStaticSlotStyles,
32
32
  base,
33
- border,
34
- brand,
35
33
  cleanupAppContext,
36
34
  createActionEngine,
35
+ createAnchorResolver,
37
36
  createAppContext,
38
37
  createAppLoader,
39
38
  createCanvasConfigFetcher,
@@ -101,9 +100,10 @@ import {
101
100
  validateAction,
102
101
  validateActions,
103
102
  widgetRegistry
104
- } from "./chunk-QGWATS3Z.js";
103
+ } from "./chunk-Q4WGXNKC.js";
105
104
  import {
106
105
  AddClassZ,
106
+ AnchorIdZ,
107
107
  BadgePositionZ,
108
108
  BadgeZ,
109
109
  CtaButtonZ,
@@ -134,18 +134,21 @@ import {
134
134
  WaitZ,
135
135
  WidgetConfigZ,
136
136
  coreActionStepSchemas
137
- } from "./chunk-OGTCFYR3.js";
137
+ } from "./chunk-R5DNAIRI.js";
138
+ import "./chunk-XDYJ64IN.js";
138
139
  import {
139
140
  ActivationConfigZ,
140
141
  AnchorVisibleConditionZ,
141
142
  ConditionZ,
142
143
  CooldownActiveConditionZ,
144
+ CounterDefZ,
143
145
  DecisionStrategyZ,
144
146
  DismissedConditionZ,
145
147
  EventCountConditionZ,
146
148
  EventOccurredConditionZ,
147
149
  ExternalStrategyZ,
148
150
  FrequencyLimitConditionZ,
151
+ MatchOpZ,
149
152
  ModelStrategyZ,
150
153
  PageUrlConditionZ,
151
154
  RouteConditionZ,
@@ -155,12 +158,13 @@ import {
155
158
  ScoreStrategyZ,
156
159
  SessionMetricConditionZ,
157
160
  StateEqualsConditionZ,
161
+ TriggerWhenZ,
158
162
  ViewportConditionZ,
159
163
  decisionSchemas,
160
164
  validateActivationConfig,
161
165
  validateCondition,
162
166
  validateStrategy
163
- } from "./chunk-4NYS7GAW.js";
167
+ } from "./chunk-BU4Z6PD7.js";
164
168
 
165
169
  // src/index.ts
166
170
  import React4 from "react";
@@ -656,12 +660,12 @@ function getFeedbackPrompt(feedbackConfig) {
656
660
  }
657
661
  var baseStyles = {
658
662
  container: {
659
- fontFamily: "system-ui, -apple-system, sans-serif",
663
+ fontFamily: "var(--sc-font-family, system-ui, -apple-system, sans-serif)",
660
664
  maxWidth: "800px",
661
665
  margin: "0 auto"
662
666
  },
663
667
  searchWrapper: {
664
- marginBottom: "16px"
668
+ marginBottom: "8px"
665
669
  },
666
670
  searchInput: {
667
671
  width: "100%",
@@ -669,38 +673,41 @@ var baseStyles = {
669
673
  borderRadius: "8px",
670
674
  fontSize: "14px",
671
675
  outline: "none",
672
- transition: "border-color 0.15s ease"
676
+ transition: "border-color 0.15s ease",
677
+ backgroundColor: "var(--sc-content-search-background)",
678
+ color: "var(--sc-content-search-color)"
673
679
  },
674
680
  accordion: {
675
681
  display: "flex",
676
682
  flexDirection: "column",
677
- gap: "8px"
683
+ gap: "var(--sc-content-item-gap, 6px)"
678
684
  },
679
685
  item: {
680
- borderRadius: "8px",
686
+ borderRadius: "var(--sc-content-border-radius, 8px)",
681
687
  overflow: "hidden",
682
688
  transition: "box-shadow 0.15s ease"
683
689
  },
684
690
  question: {
685
691
  width: "100%",
686
- padding: "16px 20px",
692
+ padding: "var(--sc-content-item-padding, 12px 16px)",
687
693
  display: "flex",
688
694
  alignItems: "center",
689
695
  justifyContent: "space-between",
690
696
  border: "none",
691
697
  cursor: "pointer",
692
- fontSize: "15px",
698
+ fontSize: "var(--sc-content-item-font-size, 15px)",
693
699
  fontWeight: 500,
694
700
  textAlign: "left",
695
701
  transition: "background-color 0.15s ease"
696
702
  },
697
703
  chevron: {
698
- fontSize: "18px",
699
- transition: "transform 0.2s ease"
704
+ fontSize: "20px",
705
+ transition: "transform 0.2s ease",
706
+ color: "var(--sc-content-chevron-color, currentColor)"
700
707
  },
701
708
  answer: {
702
- padding: "0 20px 16px 20px",
703
- fontSize: "14px",
709
+ padding: "var(--sc-content-body-padding, 0 16px 12px 16px)",
710
+ fontSize: "var(--sc-content-body-font-size, 14px)",
704
711
  lineHeight: 1.6,
705
712
  overflow: "hidden",
706
713
  transition: "max-height 0.2s ease, padding 0.2s ease"
@@ -716,12 +723,12 @@ var baseStyles = {
716
723
  marginBottom: "8px"
717
724
  },
718
725
  categoryHeader: {
719
- fontSize: "13px",
726
+ fontSize: "var(--sc-content-category-font-size, 12px)",
720
727
  fontWeight: 700,
721
728
  textTransform: "uppercase",
722
729
  letterSpacing: "0.05em",
723
- padding: "12px 4px 6px 4px",
724
- marginTop: "8px"
730
+ padding: "var(--sc-content-category-padding, 8px 4px 4px 4px)",
731
+ marginTop: "var(--sc-content-category-gap, 4px)"
725
732
  },
726
733
  feedback: {
727
734
  display: "flex",
@@ -759,30 +766,28 @@ var baseStyles = {
759
766
  var themeStyles = {
760
767
  light: {
761
768
  container: {
762
- backgroundColor: base.white,
763
- color: slateGrey[1]
769
+ backgroundColor: "transparent",
770
+ color: "inherit"
764
771
  },
765
772
  searchInput: {
766
- backgroundColor: slateGrey[12],
767
- border: `1px solid ${slateGrey[11]}`,
768
- color: slateGrey[1]
773
+ border: `1px solid ${slateGrey[11]}`
769
774
  },
770
775
  item: {
771
- backgroundColor: slateGrey[12],
772
- border: `1px solid ${slateGrey[11]}`
776
+ backgroundColor: "var(--sc-content-background)",
777
+ border: "var(--sc-content-border)"
773
778
  },
774
779
  itemExpanded: {
775
780
  boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)"
776
781
  },
777
782
  question: {
778
783
  backgroundColor: "transparent",
779
- color: slateGrey[1]
784
+ color: "var(--sc-content-text-color)"
780
785
  },
781
786
  questionHover: {
782
- backgroundColor: slateGrey[12]
787
+ backgroundColor: "var(--sc-content-background-hover)"
783
788
  },
784
789
  answer: {
785
- color: slateGrey[6]
790
+ color: "var(--sc-content-text-secondary-color)"
786
791
  },
787
792
  category: {
788
793
  backgroundColor: purple[8],
@@ -800,30 +805,28 @@ var themeStyles = {
800
805
  },
801
806
  dark: {
802
807
  container: {
803
- backgroundColor: slateGrey[1],
804
- color: slateGrey[12]
808
+ backgroundColor: "transparent",
809
+ color: "inherit"
805
810
  },
806
811
  searchInput: {
807
- backgroundColor: slateGrey[3],
808
- border: `1px solid ${slateGrey[5]}`,
809
- color: slateGrey[12]
812
+ border: `1px solid ${slateGrey[5]}`
810
813
  },
811
814
  item: {
812
- backgroundColor: slateGrey[3],
813
- border: `1px solid ${slateGrey[5]}`
815
+ backgroundColor: "var(--sc-content-background)",
816
+ border: "var(--sc-content-border)"
814
817
  },
815
818
  itemExpanded: {
816
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)"
819
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)"
817
820
  },
818
821
  question: {
819
822
  backgroundColor: "transparent",
820
- color: slateGrey[12]
823
+ color: "var(--sc-content-text-color)"
821
824
  },
822
825
  questionHover: {
823
- backgroundColor: slateGrey[5]
826
+ backgroundColor: "var(--sc-content-background-hover)"
824
827
  },
825
828
  answer: {
826
- color: slateGrey[8]
829
+ color: "var(--sc-content-text-secondary-color)"
827
830
  },
828
831
  category: {
829
832
  backgroundColor: purple[0],
@@ -840,14 +843,19 @@ var themeStyles = {
840
843
  }
841
844
  }
842
845
  };
843
- function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackValue, onFeedback }) {
846
+ function FAQItem({ item, isExpanded, isHighlighted, isLast, onToggle, theme, feedbackConfig, feedbackValue, onFeedback }) {
844
847
  const [isHovered, setIsHovered] = useState2(false);
845
848
  const colors = themeStyles[theme];
846
849
  const { question, answer } = item.config;
847
850
  const itemStyle = {
848
851
  ...baseStyles.item,
849
852
  ...colors.item,
850
- ...isExpanded ? colors.itemExpanded : {}
853
+ ...isExpanded ? colors.itemExpanded : {},
854
+ ...isHighlighted ? {
855
+ boxShadow: "0 0 0 2px #6366f1, 0 0 12px rgba(99, 102, 241, 0.4)",
856
+ transition: "box-shadow 0.3s ease"
857
+ } : {},
858
+ ...!isLast ? { borderBottom: "var(--sc-content-item-divider, none)" } : {}
851
859
  };
852
860
  const questionStyle = {
853
861
  ...baseStyles.question,
@@ -856,7 +864,7 @@ function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackVa
856
864
  };
857
865
  const chevronStyle = {
858
866
  ...baseStyles.chevron,
859
- transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)"
867
+ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)"
860
868
  };
861
869
  const answerStyle = {
862
870
  ...baseStyles.answer,
@@ -868,7 +876,7 @@ function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackVa
868
876
  ...baseStyles.feedback,
869
877
  ...colors.feedbackPrompt
870
878
  };
871
- return _jsxs2("div", { style: itemStyle, "data-faq-item-id": item.config.id, children: [_jsxs2("button", { type: "button", style: questionStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [_jsx2("span", { children: question }), _jsx2("span", { style: chevronStyle, children: "\u25BC" })] }), _jsxs2("div", { style: answerStyle, "aria-hidden": !isExpanded, children: [renderAnswer(answer), isExpanded && feedbackConfig && _jsxs2("div", { style: feedbackStyle, children: [_jsx2("span", { children: getFeedbackPrompt(feedbackConfig) }), _jsx2("button", { type: "button", style: {
879
+ return _jsxs2("div", { style: itemStyle, "data-faq-item-id": item.config.id, children: [_jsxs2("button", { type: "button", style: questionStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [_jsx2("span", { children: question }), _jsx2("span", { style: chevronStyle, children: "\u203A" })] }), _jsxs2("div", { style: answerStyle, "aria-hidden": !isExpanded, children: [renderAnswer(answer), isExpanded && feedbackConfig && _jsxs2("div", { style: feedbackStyle, children: [_jsx2("span", { children: getFeedbackPrompt(feedbackConfig) }), _jsx2("button", { type: "button", style: {
872
880
  ...baseStyles.feedbackButton,
873
881
  ...feedbackValue === "up" ? baseStyles.feedbackButtonSelected : {}
874
882
  }, "aria-label": "Thumbs up", onClick: () => onFeedback(item.config.id, question, "up"), children: "\u{1F44D}" }), _jsx2("button", { type: "button", style: {
@@ -879,6 +887,7 @@ function FAQItem({ item, isExpanded, onToggle, theme, feedbackConfig, feedbackVa
879
887
  function FAQWidget({ config, runtime: runtime7, instanceId }) {
880
888
  const [renderTick, forceUpdate] = useReducer((x) => x + 1, 0);
881
889
  const [expandedIds, setExpandedIds] = useState2(/* @__PURE__ */ new Set());
890
+ const [highlightId, setHighlightId] = useState2(null);
882
891
  const [searchQuery, setSearchQuery] = useState2("");
883
892
  const [feedbackState, setFeedbackState] = useState2(/* @__PURE__ */ new Map());
884
893
  const feedbackConfig = useMemo(() => resolveFeedbackConfig(config.feedback), [config.feedback]);
@@ -896,43 +905,6 @@ function FAQWidget({ config, runtime: runtime7, instanceId }) {
896
905
  forceUpdate();
897
906
  });
898
907
  }, [runtime7.accumulator]);
899
- useEffect2(() => {
900
- var _a, _b;
901
- if (!config.scope || !((_a = runtime7.accumulator) == null ? void 0 : _a.register))
902
- return;
903
- const { events: eventNames, urlContains, props: propFilters } = config.scope;
904
- const keys = /* @__PURE__ */ new Set();
905
- for (const action of config.actions) {
906
- if (((_b = action.showWhen) == null ? void 0 : _b.type) === "rules") {
907
- for (const rule of action.showWhen.rules) {
908
- for (const cond of rule.conditions) {
909
- if (cond.type === "event_count" && cond.key) {
910
- keys.add(cond.key);
911
- }
912
- }
913
- }
914
- }
915
- }
916
- for (const key of keys) {
917
- runtime7.accumulator.register(key, (event) => {
918
- var _a2, _b2, _c;
919
- if (!eventNames.includes(event.name))
920
- return false;
921
- if (urlContains) {
922
- const pathname = String((_b2 = (_a2 = event.props) == null ? void 0 : _a2.pathname) != null ? _b2 : "");
923
- if (!pathname.includes(urlContains))
924
- return false;
925
- }
926
- if (propFilters) {
927
- for (const [k, v] of Object.entries(propFilters)) {
928
- if (((_c = event.props) == null ? void 0 : _c[k]) !== v)
929
- return false;
930
- }
931
- }
932
- return true;
933
- });
934
- }
935
- }, [config.scope, config.actions, runtime7.accumulator]);
936
908
  useEffect2(() => {
937
909
  if (!runtime7.events.subscribe)
938
910
  return;
@@ -969,10 +941,43 @@ function FAQWidget({ config, runtime: runtime7, instanceId }) {
969
941
  });
970
942
  return unsubscribe;
971
943
  }, [runtime7]);
944
+ useEffect2(() => {
945
+ if (!runtime7.events.subscribe)
946
+ return;
947
+ const handleDeepLink = (event) => {
948
+ var _a, _b;
949
+ const tileId = (_a = event.props) == null ? void 0 : _a.tileId;
950
+ const itemId = (_b = event.props) == null ? void 0 : _b.itemId;
951
+ if (tileId !== instanceId)
952
+ return;
953
+ if (!itemId)
954
+ return;
955
+ setExpandedIds(/* @__PURE__ */ new Set([itemId]));
956
+ setHighlightId(itemId);
957
+ setTimeout(() => setHighlightId(null), 1500);
958
+ requestAnimationFrame(() => {
959
+ const el = document.querySelector(`[data-faq-item-id="${itemId}"]`);
960
+ if (el)
961
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
962
+ });
963
+ };
964
+ if (runtime7.events.getRecent) {
965
+ const recent = runtime7.events.getRecent({ names: ["notification.deep_link"] }, 5);
966
+ const pending = recent.filter((e) => {
967
+ var _a, _b;
968
+ return ((_a = e.props) == null ? void 0 : _a.tileId) === instanceId && ((_b = e.props) == null ? void 0 : _b.itemId);
969
+ }).pop();
970
+ if (pending && Date.now() - pending.ts < 1e4) {
971
+ handleDeepLink(pending);
972
+ }
973
+ }
974
+ const unsubscribe = runtime7.events.subscribe({ names: ["notification.deep_link"] }, handleDeepLink);
975
+ return unsubscribe;
976
+ }, [runtime7, instanceId]);
972
977
  const visibleQuestions = useMemo(() => config.actions.filter((q) => {
973
- if (!q.showWhen)
978
+ if (!q.triggerWhen)
974
979
  return true;
975
- const result = runtime7.evaluateSync(q.showWhen);
980
+ const result = runtime7.evaluateSync(q.triggerWhen);
976
981
  return result.value;
977
982
  }), [config.actions, runtime7, renderTick]);
978
983
  const orderedQuestions = useMemo(() => {
@@ -1066,11 +1071,11 @@ function FAQWidget({ config, runtime: runtime7, instanceId }) {
1066
1071
  ...baseStyles.categoryHeader,
1067
1072
  ...themeStyles[resolvedTheme].categoryHeader
1068
1073
  };
1069
- const renderItems = (items) => items.map((q) => _jsx2(FAQItem, { item: q, isExpanded: expandedIds.has(q.config.id), onToggle: () => handleToggle(q.config.id), theme: resolvedTheme, feedbackConfig, feedbackValue: feedbackState.get(q.config.id), onFeedback: handleFeedback }, q.config.id));
1074
+ const renderItems = (items) => items.map((q, index) => _jsx2(FAQItem, { item: q, isExpanded: expandedIds.has(q.config.id), isHighlighted: highlightId === q.config.id, isLast: index === items.length - 1, onToggle: () => handleToggle(q.config.id), theme: resolvedTheme, feedbackConfig, feedbackValue: feedbackState.get(q.config.id), onFeedback: handleFeedback }, q.config.id));
1070
1075
  if (visibleQuestions.length === 0) {
1071
- return _jsx2("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: _jsx2("div", { style: emptyStateStyle, children: "No FAQ questions available." }) });
1076
+ return _jsx2("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: _jsx2("div", { style: emptyStateStyle, children: "You're all set for now! We'll surface answers here when they're relevant to what you're doing." }) });
1072
1077
  }
1073
- return _jsxs2("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: [config.searchable && _jsx2("div", { style: baseStyles.searchWrapper, children: _jsx2("input", { type: "text", placeholder: "Search questions...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: searchInputStyle }) }), _jsx2("div", { style: baseStyles.accordion, children: hasCategories ? Array.from(categoryGroups.entries()).map(([category, items]) => _jsxs2(React2.Fragment, { children: [category && _jsx2("div", { style: categoryHeaderStyle, "data-category-header": category, children: category }), renderItems(items)] }, category != null ? category : "__ungrouped")) : renderItems(filteredQuestions) }), config.searchable && filteredQuestions.length === 0 && searchQuery && _jsxs2("div", { style: { ...baseStyles.noResults, ...themeStyles[resolvedTheme].emptyState }, children: ['No questions found matching "', searchQuery, '"'] })] });
1078
+ return _jsxs2("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-faq", children: [config.searchable && _jsxs2("div", { style: baseStyles.searchWrapper, children: [_jsx2("style", { children: `[data-adaptive-id="${instanceId}"] input::placeholder { color: var(--sc-content-search-color, inherit); opacity: 0.7; }` }), _jsx2("input", { type: "text", placeholder: "Search questions...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), style: searchInputStyle })] }), _jsx2("div", { style: baseStyles.accordion, children: hasCategories ? Array.from(categoryGroups.entries()).map(([category, items]) => _jsxs2(React2.Fragment, { children: [category && _jsx2("div", { style: categoryHeaderStyle, "data-category-header": category, children: category }), renderItems(items)] }, category != null ? category : "__ungrouped")) : renderItems(filteredQuestions) }), config.searchable && filteredQuestions.length === 0 && searchQuery && _jsxs2("div", { style: { ...baseStyles.noResults, ...themeStyles[resolvedTheme].emptyState }, children: ['No questions found matching "', searchQuery, '"'] })] });
1074
1079
  }
1075
1080
  var FAQMountableWidget = {
1076
1081
  mount(container, config) {
@@ -1128,21 +1133,22 @@ var runtime4 = {
1128
1133
  metadata: {
1129
1134
  name: "FAQ Accordion",
1130
1135
  description: "Collapsible Q&A accordion with search, categories, and feedback",
1131
- icon: "\u2753"
1136
+ icon: "\u2753",
1137
+ subtitle: "Curated just for you."
1132
1138
  }
1133
1139
  }
1134
1140
  ],
1135
1141
  /**
1136
1142
  * Extract notify watcher entries from tile config props.
1137
1143
  * The runtime evaluates these continuously (even with drawer closed)
1138
- * and publishes faq:question_revealed when showWhen transitions false → true.
1144
+ * and publishes faq:question_revealed when triggerWhen transitions false → true.
1139
1145
  */
1140
1146
  notifyWatchers(props) {
1141
1147
  var _a;
1142
1148
  const actions = (_a = props.actions) != null ? _a : [];
1143
- return actions.filter((a) => a.notify && a.showWhen).map((a) => ({
1149
+ return actions.filter((a) => a.notify && a.triggerWhen).map((a) => ({
1144
1150
  id: `faq:${a.config.id}`,
1145
- strategy: a.showWhen,
1151
+ strategy: a.triggerWhen,
1146
1152
  eventName: "faq:question_revealed",
1147
1153
  eventProps: {
1148
1154
  questionId: a.config.id,
@@ -1208,18 +1214,17 @@ function escapeHtml(str) {
1208
1214
  }
1209
1215
  var baseStyles2 = {
1210
1216
  container: {
1211
- fontFamily: "system-ui, -apple-system, sans-serif",
1212
- padding: "8px",
1217
+ fontFamily: "var(--sc-font-family, system-ui, -apple-system, sans-serif)",
1213
1218
  maxWidth: "100%",
1214
1219
  overflow: "hidden"
1215
1220
  },
1216
1221
  accordion: {
1217
1222
  display: "flex",
1218
1223
  flexDirection: "column",
1219
- gap: "4px"
1224
+ gap: "var(--sc-content-item-gap, 6px)"
1220
1225
  },
1221
1226
  item: {
1222
- borderRadius: "8px",
1227
+ borderRadius: "var(--sc-content-border-radius, 8px)",
1223
1228
  overflow: "hidden",
1224
1229
  transition: "box-shadow 0.2s ease"
1225
1230
  },
@@ -1228,20 +1233,21 @@ var baseStyles2 = {
1228
1233
  alignItems: "center",
1229
1234
  gap: "8px",
1230
1235
  width: "100%",
1231
- padding: "12px 16px",
1236
+ padding: "var(--sc-content-item-padding, 12px 16px)",
1232
1237
  border: "none",
1233
1238
  cursor: "pointer",
1234
- fontSize: "14px",
1239
+ fontSize: "var(--sc-content-item-font-size, 15px)",
1235
1240
  fontWeight: 500,
1236
1241
  fontFamily: "inherit",
1237
1242
  textAlign: "left",
1238
1243
  transition: "background-color 0.15s ease"
1239
1244
  },
1240
1245
  chevron: {
1241
- fontSize: "10px",
1246
+ fontSize: "20px",
1242
1247
  transition: "transform 0.2s ease",
1243
1248
  marginLeft: "auto",
1244
- flexShrink: 0
1249
+ flexShrink: 0,
1250
+ color: "var(--sc-content-chevron-color, currentColor)"
1245
1251
  },
1246
1252
  icon: {
1247
1253
  fontSize: "16px",
@@ -1250,10 +1256,10 @@ var baseStyles2 = {
1250
1256
  body: {
1251
1257
  overflow: "hidden",
1252
1258
  transition: "max-height 0.25s ease, padding-bottom 0.25s ease",
1253
- padding: "0 16px"
1259
+ padding: "var(--sc-content-body-padding, 0 16px 12px 16px)"
1254
1260
  },
1255
1261
  description: {
1256
- fontSize: "13px",
1262
+ fontSize: "var(--sc-content-body-font-size, 14px)",
1257
1263
  lineHeight: "1.5",
1258
1264
  margin: 0
1259
1265
  },
@@ -1272,11 +1278,11 @@ var baseStyles2 = {
1272
1278
  transition: "background-color 0.15s ease"
1273
1279
  },
1274
1280
  categoryHeader: {
1275
- fontSize: "11px",
1281
+ fontSize: "var(--sc-content-category-font-size, 12px)",
1276
1282
  fontWeight: 600,
1277
1283
  textTransform: "uppercase",
1278
1284
  letterSpacing: "0.05em",
1279
- padding: "12px 4px 4px"
1285
+ padding: "var(--sc-content-category-padding, 8px 4px 4px 4px)"
1280
1286
  },
1281
1287
  emptyState: {
1282
1288
  fontSize: "13px",
@@ -1287,29 +1293,29 @@ var baseStyles2 = {
1287
1293
  var themeStyles2 = {
1288
1294
  light: {
1289
1295
  container: {
1290
- backgroundColor: base.white,
1291
- color: slateGrey[1]
1296
+ backgroundColor: "transparent",
1297
+ color: "inherit"
1292
1298
  },
1293
1299
  item: {
1294
- backgroundColor: slateGrey[12],
1295
- border: `1px solid ${slateGrey[11]}`
1300
+ backgroundColor: "var(--sc-content-background)",
1301
+ border: "var(--sc-content-border)"
1296
1302
  },
1297
1303
  itemExpanded: {
1298
1304
  boxShadow: "0 4px 12px rgba(0, 0, 0, 0.08)"
1299
1305
  },
1300
1306
  header: {
1301
1307
  backgroundColor: "transparent",
1302
- color: slateGrey[1]
1308
+ color: "var(--sc-content-text-color)"
1303
1309
  },
1304
1310
  headerHover: {
1305
- backgroundColor: slateGrey[12]
1311
+ backgroundColor: "var(--sc-content-background-hover)"
1306
1312
  },
1307
1313
  body: {
1308
- color: slateGrey[6]
1314
+ color: "var(--sc-content-text-secondary-color)"
1309
1315
  },
1310
1316
  linkButton: {
1311
- backgroundColor: purple[8],
1312
- color: purple[2]
1317
+ backgroundColor: "var(--sc-color-primary, #6366f1)",
1318
+ color: "#ffffff"
1313
1319
  },
1314
1320
  categoryHeader: {
1315
1321
  color: slateGrey[7]
@@ -1320,29 +1326,29 @@ var themeStyles2 = {
1320
1326
  },
1321
1327
  dark: {
1322
1328
  container: {
1323
- backgroundColor: slateGrey[1],
1324
- color: slateGrey[12]
1329
+ backgroundColor: "transparent",
1330
+ color: "inherit"
1325
1331
  },
1326
1332
  item: {
1327
- backgroundColor: slateGrey[3],
1328
- border: `1px solid ${slateGrey[5]}`
1333
+ backgroundColor: "var(--sc-content-background)",
1334
+ border: "var(--sc-content-border)"
1329
1335
  },
1330
1336
  itemExpanded: {
1331
- boxShadow: "0 4px 12px rgba(0, 0, 0, 0.3)"
1337
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.15)"
1332
1338
  },
1333
1339
  header: {
1334
1340
  backgroundColor: "transparent",
1335
- color: slateGrey[12]
1341
+ color: "var(--sc-content-text-color)"
1336
1342
  },
1337
1343
  headerHover: {
1338
- backgroundColor: slateGrey[5]
1344
+ backgroundColor: "var(--sc-content-background-hover)"
1339
1345
  },
1340
1346
  body: {
1341
- color: slateGrey[8]
1347
+ color: "var(--sc-content-text-secondary-color)"
1342
1348
  },
1343
1349
  linkButton: {
1344
- backgroundColor: purple[0],
1345
- color: purple[6]
1350
+ backgroundColor: "var(--sc-color-primary, #6366f1)",
1351
+ color: "#ffffff"
1346
1352
  },
1347
1353
  categoryHeader: {
1348
1354
  color: slateGrey[8]
@@ -1352,14 +1358,15 @@ var themeStyles2 = {
1352
1358
  }
1353
1359
  }
1354
1360
  };
1355
- function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
1361
+ function NavTipItem({ item, isExpanded, isLast, onToggle, onNavigate, theme }) {
1356
1362
  const [isHovered, setIsHovered] = useState3(false);
1357
1363
  const colors = themeStyles2[theme];
1358
1364
  const { title, description, href, icon, external } = item.config;
1359
1365
  const itemStyle = {
1360
1366
  ...baseStyles2.item,
1361
1367
  ...colors.item,
1362
- ...isExpanded ? colors.itemExpanded : {}
1368
+ ...isExpanded ? colors.itemExpanded : {},
1369
+ ...!isLast ? { borderBottom: "var(--sc-content-item-divider, none)" } : {}
1363
1370
  };
1364
1371
  const headerStyle = {
1365
1372
  ...baseStyles2.header,
@@ -1368,7 +1375,7 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
1368
1375
  };
1369
1376
  const chevronStyle = {
1370
1377
  ...baseStyles2.chevron,
1371
- transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)"
1378
+ transform: isExpanded ? "rotate(90deg)" : "rotate(0deg)"
1372
1379
  };
1373
1380
  const bodyStyle = {
1374
1381
  ...baseStyles2.body,
@@ -1383,7 +1390,7 @@ function NavTipItem({ item, isExpanded, onToggle, onNavigate, theme }) {
1383
1390
  onNavigate(href, external != null ? external : false);
1384
1391
  }
1385
1392
  };
1386
- return _jsxs3("div", { style: itemStyle, "data-nav-tip-id": item.config.id, children: [_jsxs3("button", { type: "button", style: headerStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [icon && _jsx3("span", { style: baseStyles2.icon, children: icon }), _jsx3("span", { children: title }), _jsx3("span", { style: chevronStyle, children: "\u25BC" })] }), _jsxs3("div", { style: bodyStyle, "aria-hidden": !isExpanded, children: [_jsx3("p", { style: baseStyles2.description, children: description }), href && _jsxs3("a", { href, onClick: handleLinkClick, style: { ...baseStyles2.linkButton, ...colors.linkButton }, target: external ? "_blank" : void 0, rel: external ? "noopener noreferrer" : void 0, children: ["Go ", external ? "\u2197" : "\u2192"] })] })] });
1393
+ return _jsxs3("div", { style: itemStyle, "data-nav-tip-id": item.config.id, children: [_jsxs3("button", { type: "button", style: headerStyle, onClick: onToggle, onMouseEnter: () => setIsHovered(true), onMouseLeave: () => setIsHovered(false), "aria-expanded": isExpanded, children: [icon && _jsx3("span", { style: baseStyles2.icon, children: icon }), _jsx3("span", { children: title }), _jsx3("span", { style: chevronStyle, children: "\u203A" })] }), _jsxs3("div", { style: bodyStyle, "aria-hidden": !isExpanded, children: [_jsx3("p", { style: baseStyles2.description, children: description }), href && _jsxs3("a", { href, onClick: handleLinkClick, style: { ...baseStyles2.linkButton, ...colors.linkButton }, target: external ? "_blank" : void 0, rel: external ? "noopener noreferrer" : void 0, children: ["Go ", external ? "\u2197" : "\u2192"] })] })] });
1387
1394
  }
1388
1395
  function NavWidget({ config, runtime: runtime7, instanceId }) {
1389
1396
  const [renderTick, forceUpdate] = useReducer2((x) => x + 1, 0);
@@ -1402,48 +1409,11 @@ function NavWidget({ config, runtime: runtime7, instanceId }) {
1402
1409
  forceUpdate();
1403
1410
  });
1404
1411
  }, [runtime7.accumulator]);
1405
- useEffect3(() => {
1406
- var _a, _b;
1407
- if (!config.scope || !((_a = runtime7.accumulator) == null ? void 0 : _a.register))
1408
- return;
1409
- const { events: eventNames, urlContains, props: propFilters } = config.scope;
1410
- const keys = /* @__PURE__ */ new Set();
1411
- for (const action of config.actions) {
1412
- if (((_b = action.showWhen) == null ? void 0 : _b.type) === "rules") {
1413
- for (const rule of action.showWhen.rules) {
1414
- for (const cond of rule.conditions) {
1415
- if (cond.type === "event_count" && cond.key) {
1416
- keys.add(cond.key);
1417
- }
1418
- }
1419
- }
1420
- }
1421
- }
1422
- for (const key of keys) {
1423
- runtime7.accumulator.register(key, (event) => {
1424
- var _a2, _b2, _c;
1425
- if (!eventNames.includes(event.name))
1426
- return false;
1427
- if (urlContains) {
1428
- const pathname = String((_b2 = (_a2 = event.props) == null ? void 0 : _a2.pathname) != null ? _b2 : "");
1429
- if (!pathname.includes(urlContains))
1430
- return false;
1431
- }
1432
- if (propFilters) {
1433
- for (const [k, v] of Object.entries(propFilters)) {
1434
- if (((_c = event.props) == null ? void 0 : _c[k]) !== v)
1435
- return false;
1436
- }
1437
- }
1438
- return true;
1439
- });
1440
- }
1441
- }, [config.scope, config.actions, runtime7.accumulator]);
1442
1412
  const visibleTips = useMemo2(() => config.actions.filter((tip) => {
1443
- if (!tip.showWhen)
1413
+ if (!tip.triggerWhen)
1444
1414
  return true;
1445
1415
  try {
1446
- const result = runtime7.evaluateSync(tip.showWhen);
1416
+ const result = runtime7.evaluateSync(tip.triggerWhen);
1447
1417
  return result.value;
1448
1418
  } catch {
1449
1419
  return false;
@@ -1517,7 +1487,9 @@ function NavWidget({ config, runtime: runtime7, instanceId }) {
1517
1487
  if (external) {
1518
1488
  window.open(href, "_blank", "noopener,noreferrer");
1519
1489
  } else {
1520
- window.location.href = href;
1490
+ const url = new URL(href, window.location.origin);
1491
+ url.search = window.location.search;
1492
+ window.location.href = url.toString();
1521
1493
  }
1522
1494
  }, [runtime7.events, instanceId]);
1523
1495
  const containerStyle = {
@@ -1532,9 +1504,9 @@ function NavWidget({ config, runtime: runtime7, instanceId }) {
1532
1504
  ...baseStyles2.emptyState,
1533
1505
  ...themeStyles2[resolvedTheme].emptyState
1534
1506
  };
1535
- const renderItems = (items) => items.map((tip) => _jsx3(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, theme: resolvedTheme }, tip.config.id));
1507
+ const renderItems = (items) => items.map((tip, index) => _jsx3(NavTipItem, { item: tip, isExpanded: expandedIds.has(tip.config.id), isLast: index === items.length - 1, onToggle: () => handleToggle(tip.config.id), onNavigate: handleNavigate, theme: resolvedTheme }, tip.config.id));
1536
1508
  if (visibleTips.length === 0) {
1537
- return _jsx3("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx3("div", { style: emptyStateStyle, children: "No navigation tips available." }) });
1509
+ return _jsx3("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx3("div", { style: emptyStateStyle, children: "You're all set for now! We'll share helpful tips here when they're relevant to what you're doing." }) });
1538
1510
  }
1539
1511
  return _jsx3("div", { style: containerStyle, "data-adaptive-id": instanceId, "data-adaptive-type": "adaptive-nav", children: _jsx3("div", { style: baseStyles2.accordion, children: hasCategories ? Array.from(categoryGroups.entries()).map(([category, items]) => _jsxs3(React3.Fragment, { children: [category && _jsx3("div", { style: categoryHeaderStyle, "data-category-header": category, children: category }), renderItems(items)] }, category != null ? category : "__ungrouped")) : renderItems(visibleTips) }) });
1540
1512
  }
@@ -1579,7 +1551,9 @@ var executeScrollTo = async (action, context) => {
1579
1551
  var _a, _b, _c, _d;
1580
1552
  const anchorEl = context.resolveAnchor(action.anchorId);
1581
1553
  if (!anchorEl) {
1582
- throw new Error(`Anchor not found: ${action.anchorId}`);
1554
+ console.error(`[adaptive-nav] Anchor not found for scrollTo, skipping: ${action.anchorId.selector}`);
1555
+ return { cleanup: () => {
1556
+ } };
1583
1557
  }
1584
1558
  anchorEl.scrollIntoView({
1585
1559
  behavior: (_a = action.behavior) != null ? _a : "smooth",
@@ -1661,14 +1635,14 @@ var runtime6 = {
1661
1635
  /**
1662
1636
  * Extract notify watcher entries from tile config props.
1663
1637
  * The runtime evaluates these continuously (even with drawer closed)
1664
- * and publishes nav:tip_revealed when showWhen transitions false → true.
1638
+ * and publishes nav:tip_revealed when triggerWhen transitions false → true.
1665
1639
  */
1666
1640
  notifyWatchers(props) {
1667
1641
  var _a;
1668
1642
  const actions = (_a = props.actions) != null ? _a : [];
1669
- return actions.filter((a) => a.notify && a.showWhen).map((a) => ({
1643
+ return actions.filter((a) => a.notify && a.triggerWhen).map((a) => ({
1670
1644
  id: `nav:${a.config.id}`,
1671
- strategy: a.showWhen,
1645
+ strategy: a.triggerWhen,
1672
1646
  eventName: "nav:tip_revealed",
1673
1647
  eventProps: {
1674
1648
  tipId: a.config.id,
@@ -1743,8 +1717,8 @@ function TileWheel({ tiles, intervalMs = 7e3, telemetry }) {
1743
1717
  position: "relative",
1744
1718
  overflow: "hidden",
1745
1719
  borderRadius: "1.5rem",
1746
- border: `1px solid ${border.primary}`,
1747
- background: slateGrey[1],
1720
+ border: "var(--sc-tile-border)",
1721
+ background: "var(--sc-tile-background)",
1748
1722
  padding: "1.5rem",
1749
1723
  boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)"
1750
1724
  },
@@ -1770,7 +1744,7 @@ function TileWheel({ tiles, intervalMs = 7e3, telemetry }) {
1770
1744
  height: "0.5rem",
1771
1745
  width: "1.5rem",
1772
1746
  borderRadius: "9999px",
1773
- background: idx === index ? brand[3] : slateGrey[6],
1747
+ background: idx === index ? "var(--sc-color-primary)" : "var(--sc-tile-text-color)",
1774
1748
  border: "none",
1775
1749
  cursor: "pointer",
1776
1750
  padding: 0
@@ -1859,18 +1833,7 @@ function migrateV1ToV2(config) {
1859
1833
  if (config.tiles && Array.isArray(config.tiles)) {
1860
1834
  migrated.tiles = config.tiles.map((tile) => {
1861
1835
  const migratedTile = { ...tile };
1862
- if (tile.experiment && !tile.activation) {
1863
- const exp = tile.experiment;
1864
- if (exp.featureKey) {
1865
- migratedTile.activation = {
1866
- strategy: {
1867
- type: "external",
1868
- provider: "growthbook",
1869
- featureKey: exp.featureKey,
1870
- fallback: false
1871
- }
1872
- };
1873
- }
1836
+ if (tile.experiment) {
1874
1837
  delete migratedTile.experiment;
1875
1838
  }
1876
1839
  return migratedTile;
@@ -2085,6 +2048,7 @@ export {
2085
2048
  ANIMATION_KEYFRAMES,
2086
2049
  ActivationConfigZ,
2087
2050
  AddClassZ,
2051
+ AnchorIdZ,
2088
2052
  AnchorStateZ,
2089
2053
  AnchorVisibleConditionZ,
2090
2054
  AppRegistry,
@@ -2097,6 +2061,7 @@ export {
2097
2061
  ConditionZ,
2098
2062
  ContextManager,
2099
2063
  CooldownActiveConditionZ,
2064
+ CounterDefZ,
2100
2065
  CtaButtonZ,
2101
2066
  DEFAULT_COOLDOWN,
2102
2067
  DEFAULT_TTL,
@@ -2118,6 +2083,7 @@ export {
2118
2083
  InsertHtmlZ,
2119
2084
  InsertPositionZ,
2120
2085
  MAX_VISIBLE_TOASTS,
2086
+ MatchOpZ,
2121
2087
  ModalContentZ,
2122
2088
  ModalZ,
2123
2089
  ModelStrategyZ,
@@ -2175,6 +2141,7 @@ export {
2175
2141
  TooltipZ,
2176
2142
  TourStepForSchemaZ,
2177
2143
  TourZ,
2144
+ TriggerWhenZ,
2178
2145
  ViewportConditionZ,
2179
2146
  ViewportContextZ,
2180
2147
  WaitZ,
@@ -2186,6 +2153,7 @@ export {
2186
2153
  runtime as contentRuntime,
2187
2154
  coreActionStepSchemas,
2188
2155
  createActionEngine,
2156
+ createAnchorResolver,
2189
2157
  createAppContext,
2190
2158
  createAppLoader,
2191
2159
  createCanvasConfigFetcher,