@modelnex/sdk 0.5.26 → 0.5.27
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.d.mts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +120 -31
- package/dist/index.mjs +120 -31
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -133,7 +133,7 @@ interface TourStep {
|
|
|
133
133
|
/** Optional onboarding-specific step metadata */
|
|
134
134
|
onboarding?: OnboardingStepMetadata;
|
|
135
135
|
}
|
|
136
|
-
type TourTrigger = 'first_visit' | 'feature_launch' | 'feature_unlocked' | 'feature_unlock' | 'new_feature' | 'manual';
|
|
136
|
+
type TourTrigger = 'first_visit' | 'return_visit' | 'feature_launch' | 'feature_unlocked' | 'feature_unlock' | 'new_feature' | 'manual';
|
|
137
137
|
type TourStartPolicy = 'immediate_start' | 'prompt_only' | 'manual_only';
|
|
138
138
|
type TourNotificationType = 'bubble_card' | 'modal';
|
|
139
139
|
interface TourVoiceConfig {
|
|
@@ -200,6 +200,12 @@ interface UserProfile {
|
|
|
200
200
|
type: string;
|
|
201
201
|
/** Whether this is a new user — triggers first_visit tours */
|
|
202
202
|
isNewUser?: boolean;
|
|
203
|
+
/** Optional feature facts used for feature-triggered tours */
|
|
204
|
+
features?: string[];
|
|
205
|
+
/** Backward-compatible facts bag for feature-triggered tours */
|
|
206
|
+
tourFacts?: {
|
|
207
|
+
features?: string[];
|
|
208
|
+
};
|
|
203
209
|
/** User ID for per-user completion state */
|
|
204
210
|
userId?: string;
|
|
205
211
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -133,7 +133,7 @@ interface TourStep {
|
|
|
133
133
|
/** Optional onboarding-specific step metadata */
|
|
134
134
|
onboarding?: OnboardingStepMetadata;
|
|
135
135
|
}
|
|
136
|
-
type TourTrigger = 'first_visit' | 'feature_launch' | 'feature_unlocked' | 'feature_unlock' | 'new_feature' | 'manual';
|
|
136
|
+
type TourTrigger = 'first_visit' | 'return_visit' | 'feature_launch' | 'feature_unlocked' | 'feature_unlock' | 'new_feature' | 'manual';
|
|
137
137
|
type TourStartPolicy = 'immediate_start' | 'prompt_only' | 'manual_only';
|
|
138
138
|
type TourNotificationType = 'bubble_card' | 'modal';
|
|
139
139
|
interface TourVoiceConfig {
|
|
@@ -200,6 +200,12 @@ interface UserProfile {
|
|
|
200
200
|
type: string;
|
|
201
201
|
/** Whether this is a new user — triggers first_visit tours */
|
|
202
202
|
isNewUser?: boolean;
|
|
203
|
+
/** Optional feature facts used for feature-triggered tours */
|
|
204
|
+
features?: string[];
|
|
205
|
+
/** Backward-compatible facts bag for feature-triggered tours */
|
|
206
|
+
tourFacts?: {
|
|
207
|
+
features?: string[];
|
|
208
|
+
};
|
|
203
209
|
/** User ID for per-user completion state */
|
|
204
210
|
userId?: string;
|
|
205
211
|
}
|
package/dist/index.js
CHANGED
|
@@ -2667,6 +2667,9 @@ function normalizeTrigger(trigger) {
|
|
|
2667
2667
|
if (trigger === "feature_launch" || trigger === "feature_unlocked" || trigger === "feature_unlock" || trigger === "new_feature") {
|
|
2668
2668
|
return "feature_launch";
|
|
2669
2669
|
}
|
|
2670
|
+
if (trigger === "return_visit") {
|
|
2671
|
+
return "return_visit";
|
|
2672
|
+
}
|
|
2670
2673
|
return trigger ?? "first_visit";
|
|
2671
2674
|
}
|
|
2672
2675
|
function getStartPolicy(tour) {
|
|
@@ -2675,10 +2678,22 @@ function getStartPolicy(tour) {
|
|
|
2675
2678
|
function getNotificationType(tour) {
|
|
2676
2679
|
return tour.notificationType ?? "bubble_card";
|
|
2677
2680
|
}
|
|
2681
|
+
function getUserProfileFeatures(userProfile) {
|
|
2682
|
+
const directFeatures = Array.isArray(userProfile.features) ? userProfile.features : [];
|
|
2683
|
+
const nestedFeatures = Array.isArray(userProfile.tourFacts?.features) ? userProfile.tourFacts.features : [];
|
|
2684
|
+
return [...new Set([...directFeatures, ...nestedFeatures].filter((value) => Boolean(value)))];
|
|
2685
|
+
}
|
|
2678
2686
|
function isTourEligible(tour, userProfile) {
|
|
2679
2687
|
switch (normalizeTrigger(tour.trigger)) {
|
|
2680
2688
|
case "first_visit":
|
|
2681
2689
|
return !!userProfile.isNewUser;
|
|
2690
|
+
case "return_visit":
|
|
2691
|
+
return userProfile.isNewUser === false;
|
|
2692
|
+
case "feature_launch": {
|
|
2693
|
+
const featureKey = tour.featureKey?.trim();
|
|
2694
|
+
if (!featureKey) return false;
|
|
2695
|
+
return getUserProfileFeatures(userProfile).includes(featureKey);
|
|
2696
|
+
}
|
|
2682
2697
|
case "manual":
|
|
2683
2698
|
return false;
|
|
2684
2699
|
default:
|
|
@@ -8196,6 +8211,58 @@ function buildTranscriptPreviewLines(transcript, options = {}) {
|
|
|
8196
8211
|
return lines.slice(-maxLines);
|
|
8197
8212
|
}
|
|
8198
8213
|
|
|
8214
|
+
// src/utils/pendingPromptUi.ts
|
|
8215
|
+
function isPendingReviewPrompt(pendingPrompt) {
|
|
8216
|
+
return Boolean(pendingPrompt?.options?.reviewMode);
|
|
8217
|
+
}
|
|
8218
|
+
function shouldRenderPendingPromptInline({
|
|
8219
|
+
pendingPrompt,
|
|
8220
|
+
isPlaybackActive,
|
|
8221
|
+
recordingMode
|
|
8222
|
+
}) {
|
|
8223
|
+
return Boolean(
|
|
8224
|
+
pendingPrompt && !isPlaybackActive && !recordingMode && !isPendingReviewPrompt(pendingPrompt)
|
|
8225
|
+
);
|
|
8226
|
+
}
|
|
8227
|
+
function shouldRenderPendingPromptModal({
|
|
8228
|
+
pendingPrompt,
|
|
8229
|
+
isPlaybackActive,
|
|
8230
|
+
recordingMode,
|
|
8231
|
+
pendingNotificationType
|
|
8232
|
+
}) {
|
|
8233
|
+
return Boolean(
|
|
8234
|
+
pendingPrompt && !isPlaybackActive && !recordingMode && isPendingReviewPrompt(pendingPrompt) && pendingNotificationType === "modal"
|
|
8235
|
+
);
|
|
8236
|
+
}
|
|
8237
|
+
function shouldAutoExpandForPendingPrompt({
|
|
8238
|
+
pendingPrompt,
|
|
8239
|
+
isPlaybackActive,
|
|
8240
|
+
recordingMode,
|
|
8241
|
+
pendingNotificationType
|
|
8242
|
+
}) {
|
|
8243
|
+
if (!pendingPrompt || isPlaybackActive || recordingMode) return false;
|
|
8244
|
+
return pendingNotificationType === "bubble_card" || shouldRenderPendingPromptInline({
|
|
8245
|
+
pendingPrompt,
|
|
8246
|
+
isPlaybackActive,
|
|
8247
|
+
recordingMode
|
|
8248
|
+
});
|
|
8249
|
+
}
|
|
8250
|
+
function getPendingPromptTitle(pendingPrompt) {
|
|
8251
|
+
return `I found a ${pendingPrompt.experienceType === "onboarding" ? "workflow" : "tour"} called "${pendingPrompt.tour.name}". Would you like to start it now?`;
|
|
8252
|
+
}
|
|
8253
|
+
function getPendingPromptReason(pendingPrompt) {
|
|
8254
|
+
if (pendingPrompt.tour.trigger === "first_visit") {
|
|
8255
|
+
return "This was surfaced because the current user matches the configured first-visit trigger.";
|
|
8256
|
+
}
|
|
8257
|
+
if (pendingPrompt.tour.trigger === "return_visit") {
|
|
8258
|
+
return "This was surfaced because the current user matches the configured return-visit trigger.";
|
|
8259
|
+
}
|
|
8260
|
+
if (pendingPrompt.tour.featureKey) {
|
|
8261
|
+
return `This was surfaced because the "${pendingPrompt.tour.featureKey}" trigger condition matched.`;
|
|
8262
|
+
}
|
|
8263
|
+
return "This was surfaced because the configured trigger condition matched.";
|
|
8264
|
+
}
|
|
8265
|
+
|
|
8199
8266
|
// src/utils/floatingLiveTranscript.ts
|
|
8200
8267
|
var floatingTranscriptEl = null;
|
|
8201
8268
|
var liveTranscriptSuppressed = false;
|
|
@@ -8915,7 +8982,20 @@ function ModelNexChatBubble({
|
|
|
8915
8982
|
}
|
|
8916
8983
|
}, [devMode, handleAutoTag, ctx?.extractedElements.length, window.location.pathname]);
|
|
8917
8984
|
const onboardingReviewToggle = getReviewModeToggleConfig(onboardingPlayback.playbackState);
|
|
8985
|
+
const pendingPrompt = playbackController.pendingPrompt;
|
|
8918
8986
|
const pendingNotificationType = (onboardingPlayback.pendingTour || tourPlayback.pendingTour)?.notificationType ?? "bubble_card";
|
|
8987
|
+
const isPlaybackActive = tourPlayback.isActive || onboardingPlayback.isActive;
|
|
8988
|
+
const renderPendingPromptInline = shouldRenderPendingPromptInline({
|
|
8989
|
+
pendingPrompt,
|
|
8990
|
+
isPlaybackActive,
|
|
8991
|
+
recordingMode
|
|
8992
|
+
});
|
|
8993
|
+
const renderPendingPromptModal = shouldRenderPendingPromptModal({
|
|
8994
|
+
pendingPrompt,
|
|
8995
|
+
isPlaybackActive,
|
|
8996
|
+
recordingMode,
|
|
8997
|
+
pendingNotificationType
|
|
8998
|
+
});
|
|
8919
8999
|
(0, import_react18.useEffect)(() => {
|
|
8920
9000
|
setHydrated(true);
|
|
8921
9001
|
try {
|
|
@@ -8982,10 +9062,15 @@ function ModelNexChatBubble({
|
|
|
8982
9062
|
}
|
|
8983
9063
|
}, [tourPlayback.isActive, onboardingPlayback.isActive]);
|
|
8984
9064
|
(0, import_react18.useEffect)(() => {
|
|
8985
|
-
if ((
|
|
9065
|
+
if (shouldAutoExpandForPendingPrompt({
|
|
9066
|
+
pendingPrompt,
|
|
9067
|
+
isPlaybackActive,
|
|
9068
|
+
recordingMode,
|
|
9069
|
+
pendingNotificationType
|
|
9070
|
+
})) {
|
|
8986
9071
|
setExpandedState(true);
|
|
8987
9072
|
}
|
|
8988
|
-
}, [
|
|
9073
|
+
}, [isPlaybackActive, pendingNotificationType, pendingPrompt, recordingMode, setExpandedState]);
|
|
8989
9074
|
const preferredListeningExperienceRef = (0, import_react18.useRef)(null);
|
|
8990
9075
|
const playbackVoiceRoutingRef = (0, import_react18.useRef)({
|
|
8991
9076
|
activeExperienceType,
|
|
@@ -9086,13 +9171,13 @@ function ModelNexChatBubble({
|
|
|
9086
9171
|
updateTourSttError
|
|
9087
9172
|
]);
|
|
9088
9173
|
(0, import_react18.useEffect)(() => {
|
|
9089
|
-
const
|
|
9174
|
+
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
9090
9175
|
isTourActive: tourPlayback.isActive,
|
|
9091
9176
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
9092
9177
|
startingExperienceType
|
|
9093
9178
|
});
|
|
9094
|
-
const becameActive =
|
|
9095
|
-
previousTourActiveRef.current =
|
|
9179
|
+
const becameActive = isPlaybackActive2 && !previousTourActiveRef.current;
|
|
9180
|
+
previousTourActiveRef.current = isPlaybackActive2;
|
|
9096
9181
|
if (becameActive) {
|
|
9097
9182
|
try {
|
|
9098
9183
|
sessionStorage.setItem(TOUR_MINIMIZED_STORAGE_KEY, "true");
|
|
@@ -9101,7 +9186,7 @@ function ModelNexChatBubble({
|
|
|
9101
9186
|
setExpandedState(false, { rememberTourMinimize: true });
|
|
9102
9187
|
updateTourSttError(null);
|
|
9103
9188
|
}
|
|
9104
|
-
if (!
|
|
9189
|
+
if (!isPlaybackActive2) {
|
|
9105
9190
|
try {
|
|
9106
9191
|
sessionStorage.removeItem(TOUR_MINIMIZED_STORAGE_KEY);
|
|
9107
9192
|
} catch {
|
|
@@ -9128,12 +9213,12 @@ function ModelNexChatBubble({
|
|
|
9128
9213
|
}
|
|
9129
9214
|
}, [recordingMode, setExpandedState]);
|
|
9130
9215
|
(0, import_react18.useEffect)(() => {
|
|
9131
|
-
const
|
|
9216
|
+
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
9132
9217
|
isTourActive: tourPlayback.isActive,
|
|
9133
9218
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
9134
9219
|
startingExperienceType
|
|
9135
9220
|
});
|
|
9136
|
-
if (!
|
|
9221
|
+
if (!isPlaybackActive2) {
|
|
9137
9222
|
updateTourListenReady(false);
|
|
9138
9223
|
setTourLiveTranscript("");
|
|
9139
9224
|
hideFloatingLiveTranscript();
|
|
@@ -9142,12 +9227,12 @@ function ModelNexChatBubble({
|
|
|
9142
9227
|
updateTourListenReady(Boolean(voice.isListening && sttActiveRef.current));
|
|
9143
9228
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, voice.isListening, startingExperienceType, updateTourListenReady]);
|
|
9144
9229
|
(0, import_react18.useEffect)(() => {
|
|
9145
|
-
const
|
|
9230
|
+
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
9146
9231
|
isTourActive: tourPlayback.isActive,
|
|
9147
9232
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
9148
9233
|
startingExperienceType
|
|
9149
9234
|
});
|
|
9150
|
-
if (!
|
|
9235
|
+
if (!isPlaybackActive2 && sttActiveRef.current) {
|
|
9151
9236
|
sttActiveRef.current = false;
|
|
9152
9237
|
updateTourListenReady(false);
|
|
9153
9238
|
updateTourSttError(null);
|
|
@@ -9157,12 +9242,12 @@ function ModelNexChatBubble({
|
|
|
9157
9242
|
}
|
|
9158
9243
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, voice, startingExperienceType, updateTourListenReady, updateTourSttError]);
|
|
9159
9244
|
(0, import_react18.useEffect)(() => {
|
|
9160
|
-
const
|
|
9245
|
+
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
9161
9246
|
isTourActive: tourPlayback.isActive,
|
|
9162
9247
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
9163
9248
|
startingExperienceType
|
|
9164
9249
|
});
|
|
9165
|
-
if (!
|
|
9250
|
+
if (!isPlaybackActive2 || tourListenReady || !voice.sttSupported || tourSttError === "not-allowed") {
|
|
9166
9251
|
return;
|
|
9167
9252
|
}
|
|
9168
9253
|
const enableTourListeningFromGesture = (event) => {
|
|
@@ -9369,8 +9454,8 @@ function ModelNexChatBubble({
|
|
|
9369
9454
|
return (0, import_react_dom.createPortal)(
|
|
9370
9455
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: containerStyle, className, "data-modelnex-internal": "true", onMouseDown: stopEventPropagation, onClick: stopEventPropagation, children: [
|
|
9371
9456
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("style", { children: GLOBAL_STYLES }),
|
|
9372
|
-
|
|
9373
|
-
const pt =
|
|
9457
|
+
renderPendingPromptModal && (() => {
|
|
9458
|
+
const pt = pendingPrompt?.tour;
|
|
9374
9459
|
const mc = pt?.presentation?.modalConfig || {};
|
|
9375
9460
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
9376
9461
|
"div",
|
|
@@ -9432,8 +9517,8 @@ function ModelNexChatBubble({
|
|
|
9432
9517
|
"button",
|
|
9433
9518
|
{
|
|
9434
9519
|
onClick: () => {
|
|
9435
|
-
const preferredExperience =
|
|
9436
|
-
if (
|
|
9520
|
+
const preferredExperience = pendingPrompt?.experienceType === "onboarding" ? "onboarding" : "tour";
|
|
9521
|
+
if (preferredExperience === "onboarding") {
|
|
9437
9522
|
onboardingPlayback.acceptPendingTour();
|
|
9438
9523
|
} else {
|
|
9439
9524
|
tourPlayback.acceptPendingTour();
|
|
@@ -9596,24 +9681,28 @@ function ModelNexChatBubble({
|
|
|
9596
9681
|
children: "Microphone access is blocked. Use the input box below, or allow microphone permission and tap the mic again."
|
|
9597
9682
|
}
|
|
9598
9683
|
),
|
|
9599
|
-
|
|
9684
|
+
renderPendingPromptInline && pendingPrompt && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", justifyContent: "flex-start" }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
9600
9685
|
"div",
|
|
9601
9686
|
{
|
|
9602
9687
|
style: {
|
|
9603
|
-
|
|
9604
|
-
padding: "
|
|
9605
|
-
borderRadius: "
|
|
9606
|
-
|
|
9607
|
-
|
|
9688
|
+
maxWidth: "85%",
|
|
9689
|
+
padding: "12px 16px",
|
|
9690
|
+
borderRadius: "var(--modelnex-radius-inner, 16px)",
|
|
9691
|
+
borderBottomLeftRadius: 4,
|
|
9692
|
+
fontSize: "14px",
|
|
9693
|
+
lineHeight: 1.55,
|
|
9694
|
+
background: "var(--modelnex-bg, #fff)",
|
|
9695
|
+
color: "var(--modelnex-fg, #18181b)",
|
|
9696
|
+
border: "1px solid var(--modelnex-border, #e4e4e7)",
|
|
9697
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.02)"
|
|
9608
9698
|
},
|
|
9609
9699
|
children: [
|
|
9610
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: {
|
|
9611
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "
|
|
9612
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "12px", color: "#52525b", lineHeight: 1.45, marginBottom: "12px" }, children: (onboardingPlayback.pendingTour || tourPlayback.pendingTour)?.trigger === "first_visit" ? "Start a short walkthrough for this user now?" : (onboardingPlayback.pendingTour || tourPlayback.pendingTour)?.featureKey ? `A walkthrough is available for ${(onboardingPlayback.pendingTour || tourPlayback.pendingTour)?.featureKey}.` : "A walkthrough is available for this user." }),
|
|
9700
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontWeight: 500, marginBottom: "8px" }, children: getPendingPromptTitle(pendingPrompt) }),
|
|
9701
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { fontSize: "12px", color: "#52525b", marginBottom: "12px" }, children: getPendingPromptReason(pendingPrompt) }),
|
|
9613
9702
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: "8px" }, children: [
|
|
9614
9703
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { className: "btn btn-primary btn-sm", onClick: () => {
|
|
9615
|
-
const preferredExperience =
|
|
9616
|
-
if (
|
|
9704
|
+
const preferredExperience = pendingPrompt.experienceType === "onboarding" ? "onboarding" : "tour";
|
|
9705
|
+
if (preferredExperience === "onboarding") {
|
|
9617
9706
|
onboardingPlayback.acceptPendingTour();
|
|
9618
9707
|
} else {
|
|
9619
9708
|
tourPlayback.acceptPendingTour();
|
|
@@ -9621,13 +9710,13 @@ function ModelNexChatBubble({
|
|
|
9621
9710
|
startTourListening(preferredExperience);
|
|
9622
9711
|
}, children: [
|
|
9623
9712
|
"Start ",
|
|
9624
|
-
|
|
9713
|
+
pendingPrompt.experienceType === "onboarding" ? "workflow" : "tour"
|
|
9625
9714
|
] }),
|
|
9626
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "btn btn-secondary btn-sm", onClick:
|
|
9715
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "btn btn-secondary btn-sm", onClick: pendingPrompt.experienceType === "onboarding" ? onboardingPlayback.dismissPendingTour : tourPlayback.dismissPendingTour, children: "Not now" })
|
|
9627
9716
|
] })
|
|
9628
9717
|
]
|
|
9629
9718
|
}
|
|
9630
|
-
),
|
|
9719
|
+
) }),
|
|
9631
9720
|
tourPlayback.isActive && tourPlayback.isReviewMode && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
9632
9721
|
"div",
|
|
9633
9722
|
{
|
|
@@ -10206,7 +10295,7 @@ function ModelNexChatBubble({
|
|
|
10206
10295
|
)
|
|
10207
10296
|
] })
|
|
10208
10297
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
|
|
10209
|
-
messages.length === 0 && !loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
10298
|
+
messages.length === 0 && !loading && !renderPendingPromptInline && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
10210
10299
|
"div",
|
|
10211
10300
|
{
|
|
10212
10301
|
style: {
|
package/dist/index.mjs
CHANGED
|
@@ -2457,6 +2457,9 @@ function normalizeTrigger(trigger) {
|
|
|
2457
2457
|
if (trigger === "feature_launch" || trigger === "feature_unlocked" || trigger === "feature_unlock" || trigger === "new_feature") {
|
|
2458
2458
|
return "feature_launch";
|
|
2459
2459
|
}
|
|
2460
|
+
if (trigger === "return_visit") {
|
|
2461
|
+
return "return_visit";
|
|
2462
|
+
}
|
|
2460
2463
|
return trigger ?? "first_visit";
|
|
2461
2464
|
}
|
|
2462
2465
|
function getStartPolicy(tour) {
|
|
@@ -2465,10 +2468,22 @@ function getStartPolicy(tour) {
|
|
|
2465
2468
|
function getNotificationType(tour) {
|
|
2466
2469
|
return tour.notificationType ?? "bubble_card";
|
|
2467
2470
|
}
|
|
2471
|
+
function getUserProfileFeatures(userProfile) {
|
|
2472
|
+
const directFeatures = Array.isArray(userProfile.features) ? userProfile.features : [];
|
|
2473
|
+
const nestedFeatures = Array.isArray(userProfile.tourFacts?.features) ? userProfile.tourFacts.features : [];
|
|
2474
|
+
return [...new Set([...directFeatures, ...nestedFeatures].filter((value) => Boolean(value)))];
|
|
2475
|
+
}
|
|
2468
2476
|
function isTourEligible(tour, userProfile) {
|
|
2469
2477
|
switch (normalizeTrigger(tour.trigger)) {
|
|
2470
2478
|
case "first_visit":
|
|
2471
2479
|
return !!userProfile.isNewUser;
|
|
2480
|
+
case "return_visit":
|
|
2481
|
+
return userProfile.isNewUser === false;
|
|
2482
|
+
case "feature_launch": {
|
|
2483
|
+
const featureKey = tour.featureKey?.trim();
|
|
2484
|
+
if (!featureKey) return false;
|
|
2485
|
+
return getUserProfileFeatures(userProfile).includes(featureKey);
|
|
2486
|
+
}
|
|
2472
2487
|
case "manual":
|
|
2473
2488
|
return false;
|
|
2474
2489
|
default:
|
|
@@ -7985,6 +8000,58 @@ function buildTranscriptPreviewLines(transcript, options = {}) {
|
|
|
7985
8000
|
return lines.slice(-maxLines);
|
|
7986
8001
|
}
|
|
7987
8002
|
|
|
8003
|
+
// src/utils/pendingPromptUi.ts
|
|
8004
|
+
function isPendingReviewPrompt(pendingPrompt) {
|
|
8005
|
+
return Boolean(pendingPrompt?.options?.reviewMode);
|
|
8006
|
+
}
|
|
8007
|
+
function shouldRenderPendingPromptInline({
|
|
8008
|
+
pendingPrompt,
|
|
8009
|
+
isPlaybackActive,
|
|
8010
|
+
recordingMode
|
|
8011
|
+
}) {
|
|
8012
|
+
return Boolean(
|
|
8013
|
+
pendingPrompt && !isPlaybackActive && !recordingMode && !isPendingReviewPrompt(pendingPrompt)
|
|
8014
|
+
);
|
|
8015
|
+
}
|
|
8016
|
+
function shouldRenderPendingPromptModal({
|
|
8017
|
+
pendingPrompt,
|
|
8018
|
+
isPlaybackActive,
|
|
8019
|
+
recordingMode,
|
|
8020
|
+
pendingNotificationType
|
|
8021
|
+
}) {
|
|
8022
|
+
return Boolean(
|
|
8023
|
+
pendingPrompt && !isPlaybackActive && !recordingMode && isPendingReviewPrompt(pendingPrompt) && pendingNotificationType === "modal"
|
|
8024
|
+
);
|
|
8025
|
+
}
|
|
8026
|
+
function shouldAutoExpandForPendingPrompt({
|
|
8027
|
+
pendingPrompt,
|
|
8028
|
+
isPlaybackActive,
|
|
8029
|
+
recordingMode,
|
|
8030
|
+
pendingNotificationType
|
|
8031
|
+
}) {
|
|
8032
|
+
if (!pendingPrompt || isPlaybackActive || recordingMode) return false;
|
|
8033
|
+
return pendingNotificationType === "bubble_card" || shouldRenderPendingPromptInline({
|
|
8034
|
+
pendingPrompt,
|
|
8035
|
+
isPlaybackActive,
|
|
8036
|
+
recordingMode
|
|
8037
|
+
});
|
|
8038
|
+
}
|
|
8039
|
+
function getPendingPromptTitle(pendingPrompt) {
|
|
8040
|
+
return `I found a ${pendingPrompt.experienceType === "onboarding" ? "workflow" : "tour"} called "${pendingPrompt.tour.name}". Would you like to start it now?`;
|
|
8041
|
+
}
|
|
8042
|
+
function getPendingPromptReason(pendingPrompt) {
|
|
8043
|
+
if (pendingPrompt.tour.trigger === "first_visit") {
|
|
8044
|
+
return "This was surfaced because the current user matches the configured first-visit trigger.";
|
|
8045
|
+
}
|
|
8046
|
+
if (pendingPrompt.tour.trigger === "return_visit") {
|
|
8047
|
+
return "This was surfaced because the current user matches the configured return-visit trigger.";
|
|
8048
|
+
}
|
|
8049
|
+
if (pendingPrompt.tour.featureKey) {
|
|
8050
|
+
return `This was surfaced because the "${pendingPrompt.tour.featureKey}" trigger condition matched.`;
|
|
8051
|
+
}
|
|
8052
|
+
return "This was surfaced because the configured trigger condition matched.";
|
|
8053
|
+
}
|
|
8054
|
+
|
|
7988
8055
|
// src/utils/floatingLiveTranscript.ts
|
|
7989
8056
|
var floatingTranscriptEl = null;
|
|
7990
8057
|
var liveTranscriptSuppressed = false;
|
|
@@ -8704,7 +8771,20 @@ function ModelNexChatBubble({
|
|
|
8704
8771
|
}
|
|
8705
8772
|
}, [devMode, handleAutoTag, ctx?.extractedElements.length, window.location.pathname]);
|
|
8706
8773
|
const onboardingReviewToggle = getReviewModeToggleConfig(onboardingPlayback.playbackState);
|
|
8774
|
+
const pendingPrompt = playbackController.pendingPrompt;
|
|
8707
8775
|
const pendingNotificationType = (onboardingPlayback.pendingTour || tourPlayback.pendingTour)?.notificationType ?? "bubble_card";
|
|
8776
|
+
const isPlaybackActive = tourPlayback.isActive || onboardingPlayback.isActive;
|
|
8777
|
+
const renderPendingPromptInline = shouldRenderPendingPromptInline({
|
|
8778
|
+
pendingPrompt,
|
|
8779
|
+
isPlaybackActive,
|
|
8780
|
+
recordingMode
|
|
8781
|
+
});
|
|
8782
|
+
const renderPendingPromptModal = shouldRenderPendingPromptModal({
|
|
8783
|
+
pendingPrompt,
|
|
8784
|
+
isPlaybackActive,
|
|
8785
|
+
recordingMode,
|
|
8786
|
+
pendingNotificationType
|
|
8787
|
+
});
|
|
8708
8788
|
useEffect17(() => {
|
|
8709
8789
|
setHydrated(true);
|
|
8710
8790
|
try {
|
|
@@ -8771,10 +8851,15 @@ function ModelNexChatBubble({
|
|
|
8771
8851
|
}
|
|
8772
8852
|
}, [tourPlayback.isActive, onboardingPlayback.isActive]);
|
|
8773
8853
|
useEffect17(() => {
|
|
8774
|
-
if ((
|
|
8854
|
+
if (shouldAutoExpandForPendingPrompt({
|
|
8855
|
+
pendingPrompt,
|
|
8856
|
+
isPlaybackActive,
|
|
8857
|
+
recordingMode,
|
|
8858
|
+
pendingNotificationType
|
|
8859
|
+
})) {
|
|
8775
8860
|
setExpandedState(true);
|
|
8776
8861
|
}
|
|
8777
|
-
}, [
|
|
8862
|
+
}, [isPlaybackActive, pendingNotificationType, pendingPrompt, recordingMode, setExpandedState]);
|
|
8778
8863
|
const preferredListeningExperienceRef = useRef13(null);
|
|
8779
8864
|
const playbackVoiceRoutingRef = useRef13({
|
|
8780
8865
|
activeExperienceType,
|
|
@@ -8875,13 +8960,13 @@ function ModelNexChatBubble({
|
|
|
8875
8960
|
updateTourSttError
|
|
8876
8961
|
]);
|
|
8877
8962
|
useEffect17(() => {
|
|
8878
|
-
const
|
|
8963
|
+
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
8879
8964
|
isTourActive: tourPlayback.isActive,
|
|
8880
8965
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
8881
8966
|
startingExperienceType
|
|
8882
8967
|
});
|
|
8883
|
-
const becameActive =
|
|
8884
|
-
previousTourActiveRef.current =
|
|
8968
|
+
const becameActive = isPlaybackActive2 && !previousTourActiveRef.current;
|
|
8969
|
+
previousTourActiveRef.current = isPlaybackActive2;
|
|
8885
8970
|
if (becameActive) {
|
|
8886
8971
|
try {
|
|
8887
8972
|
sessionStorage.setItem(TOUR_MINIMIZED_STORAGE_KEY, "true");
|
|
@@ -8890,7 +8975,7 @@ function ModelNexChatBubble({
|
|
|
8890
8975
|
setExpandedState(false, { rememberTourMinimize: true });
|
|
8891
8976
|
updateTourSttError(null);
|
|
8892
8977
|
}
|
|
8893
|
-
if (!
|
|
8978
|
+
if (!isPlaybackActive2) {
|
|
8894
8979
|
try {
|
|
8895
8980
|
sessionStorage.removeItem(TOUR_MINIMIZED_STORAGE_KEY);
|
|
8896
8981
|
} catch {
|
|
@@ -8917,12 +9002,12 @@ function ModelNexChatBubble({
|
|
|
8917
9002
|
}
|
|
8918
9003
|
}, [recordingMode, setExpandedState]);
|
|
8919
9004
|
useEffect17(() => {
|
|
8920
|
-
const
|
|
9005
|
+
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
8921
9006
|
isTourActive: tourPlayback.isActive,
|
|
8922
9007
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
8923
9008
|
startingExperienceType
|
|
8924
9009
|
});
|
|
8925
|
-
if (!
|
|
9010
|
+
if (!isPlaybackActive2) {
|
|
8926
9011
|
updateTourListenReady(false);
|
|
8927
9012
|
setTourLiveTranscript("");
|
|
8928
9013
|
hideFloatingLiveTranscript();
|
|
@@ -8931,12 +9016,12 @@ function ModelNexChatBubble({
|
|
|
8931
9016
|
updateTourListenReady(Boolean(voice.isListening && sttActiveRef.current));
|
|
8932
9017
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, voice.isListening, startingExperienceType, updateTourListenReady]);
|
|
8933
9018
|
useEffect17(() => {
|
|
8934
|
-
const
|
|
9019
|
+
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
8935
9020
|
isTourActive: tourPlayback.isActive,
|
|
8936
9021
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
8937
9022
|
startingExperienceType
|
|
8938
9023
|
});
|
|
8939
|
-
if (!
|
|
9024
|
+
if (!isPlaybackActive2 && sttActiveRef.current) {
|
|
8940
9025
|
sttActiveRef.current = false;
|
|
8941
9026
|
updateTourListenReady(false);
|
|
8942
9027
|
updateTourSttError(null);
|
|
@@ -8946,12 +9031,12 @@ function ModelNexChatBubble({
|
|
|
8946
9031
|
}
|
|
8947
9032
|
}, [tourPlayback.isActive, onboardingPlayback.isActive, voice, startingExperienceType, updateTourListenReady, updateTourSttError]);
|
|
8948
9033
|
useEffect17(() => {
|
|
8949
|
-
const
|
|
9034
|
+
const isPlaybackActive2 = isTourListeningSessionActive({
|
|
8950
9035
|
isTourActive: tourPlayback.isActive,
|
|
8951
9036
|
isOnboardingActive: onboardingPlayback.isActive,
|
|
8952
9037
|
startingExperienceType
|
|
8953
9038
|
});
|
|
8954
|
-
if (!
|
|
9039
|
+
if (!isPlaybackActive2 || tourListenReady || !voice.sttSupported || tourSttError === "not-allowed") {
|
|
8955
9040
|
return;
|
|
8956
9041
|
}
|
|
8957
9042
|
const enableTourListeningFromGesture = (event) => {
|
|
@@ -9158,8 +9243,8 @@ function ModelNexChatBubble({
|
|
|
9158
9243
|
return createPortal(
|
|
9159
9244
|
/* @__PURE__ */ jsxs3("div", { style: containerStyle, className, "data-modelnex-internal": "true", onMouseDown: stopEventPropagation, onClick: stopEventPropagation, children: [
|
|
9160
9245
|
/* @__PURE__ */ jsx4("style", { children: GLOBAL_STYLES }),
|
|
9161
|
-
|
|
9162
|
-
const pt =
|
|
9246
|
+
renderPendingPromptModal && (() => {
|
|
9247
|
+
const pt = pendingPrompt?.tour;
|
|
9163
9248
|
const mc = pt?.presentation?.modalConfig || {};
|
|
9164
9249
|
return /* @__PURE__ */ jsx4(
|
|
9165
9250
|
"div",
|
|
@@ -9221,8 +9306,8 @@ function ModelNexChatBubble({
|
|
|
9221
9306
|
"button",
|
|
9222
9307
|
{
|
|
9223
9308
|
onClick: () => {
|
|
9224
|
-
const preferredExperience =
|
|
9225
|
-
if (
|
|
9309
|
+
const preferredExperience = pendingPrompt?.experienceType === "onboarding" ? "onboarding" : "tour";
|
|
9310
|
+
if (preferredExperience === "onboarding") {
|
|
9226
9311
|
onboardingPlayback.acceptPendingTour();
|
|
9227
9312
|
} else {
|
|
9228
9313
|
tourPlayback.acceptPendingTour();
|
|
@@ -9385,24 +9470,28 @@ function ModelNexChatBubble({
|
|
|
9385
9470
|
children: "Microphone access is blocked. Use the input box below, or allow microphone permission and tap the mic again."
|
|
9386
9471
|
}
|
|
9387
9472
|
),
|
|
9388
|
-
|
|
9473
|
+
renderPendingPromptInline && pendingPrompt && /* @__PURE__ */ jsx4("div", { style: { display: "flex", justifyContent: "flex-start" }, children: /* @__PURE__ */ jsxs3(
|
|
9389
9474
|
"div",
|
|
9390
9475
|
{
|
|
9391
9476
|
style: {
|
|
9392
|
-
|
|
9393
|
-
padding: "
|
|
9394
|
-
borderRadius: "
|
|
9395
|
-
|
|
9396
|
-
|
|
9477
|
+
maxWidth: "85%",
|
|
9478
|
+
padding: "12px 16px",
|
|
9479
|
+
borderRadius: "var(--modelnex-radius-inner, 16px)",
|
|
9480
|
+
borderBottomLeftRadius: 4,
|
|
9481
|
+
fontSize: "14px",
|
|
9482
|
+
lineHeight: 1.55,
|
|
9483
|
+
background: "var(--modelnex-bg, #fff)",
|
|
9484
|
+
color: "var(--modelnex-fg, #18181b)",
|
|
9485
|
+
border: "1px solid var(--modelnex-border, #e4e4e7)",
|
|
9486
|
+
boxShadow: "0 2px 4px rgba(0,0,0,0.02)"
|
|
9397
9487
|
},
|
|
9398
9488
|
children: [
|
|
9399
|
-
/* @__PURE__ */ jsx4("div", { style: {
|
|
9400
|
-
/* @__PURE__ */ jsx4("div", { style: { fontSize: "
|
|
9401
|
-
/* @__PURE__ */ jsx4("div", { style: { fontSize: "12px", color: "#52525b", lineHeight: 1.45, marginBottom: "12px" }, children: (onboardingPlayback.pendingTour || tourPlayback.pendingTour)?.trigger === "first_visit" ? "Start a short walkthrough for this user now?" : (onboardingPlayback.pendingTour || tourPlayback.pendingTour)?.featureKey ? `A walkthrough is available for ${(onboardingPlayback.pendingTour || tourPlayback.pendingTour)?.featureKey}.` : "A walkthrough is available for this user." }),
|
|
9489
|
+
/* @__PURE__ */ jsx4("div", { style: { fontWeight: 500, marginBottom: "8px" }, children: getPendingPromptTitle(pendingPrompt) }),
|
|
9490
|
+
/* @__PURE__ */ jsx4("div", { style: { fontSize: "12px", color: "#52525b", marginBottom: "12px" }, children: getPendingPromptReason(pendingPrompt) }),
|
|
9402
9491
|
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: "8px" }, children: [
|
|
9403
9492
|
/* @__PURE__ */ jsxs3("button", { className: "btn btn-primary btn-sm", onClick: () => {
|
|
9404
|
-
const preferredExperience =
|
|
9405
|
-
if (
|
|
9493
|
+
const preferredExperience = pendingPrompt.experienceType === "onboarding" ? "onboarding" : "tour";
|
|
9494
|
+
if (preferredExperience === "onboarding") {
|
|
9406
9495
|
onboardingPlayback.acceptPendingTour();
|
|
9407
9496
|
} else {
|
|
9408
9497
|
tourPlayback.acceptPendingTour();
|
|
@@ -9410,13 +9499,13 @@ function ModelNexChatBubble({
|
|
|
9410
9499
|
startTourListening(preferredExperience);
|
|
9411
9500
|
}, children: [
|
|
9412
9501
|
"Start ",
|
|
9413
|
-
|
|
9502
|
+
pendingPrompt.experienceType === "onboarding" ? "workflow" : "tour"
|
|
9414
9503
|
] }),
|
|
9415
|
-
/* @__PURE__ */ jsx4("button", { className: "btn btn-secondary btn-sm", onClick:
|
|
9504
|
+
/* @__PURE__ */ jsx4("button", { className: "btn btn-secondary btn-sm", onClick: pendingPrompt.experienceType === "onboarding" ? onboardingPlayback.dismissPendingTour : tourPlayback.dismissPendingTour, children: "Not now" })
|
|
9416
9505
|
] })
|
|
9417
9506
|
]
|
|
9418
9507
|
}
|
|
9419
|
-
),
|
|
9508
|
+
) }),
|
|
9420
9509
|
tourPlayback.isActive && tourPlayback.isReviewMode && /* @__PURE__ */ jsxs3(
|
|
9421
9510
|
"div",
|
|
9422
9511
|
{
|
|
@@ -9995,7 +10084,7 @@ function ModelNexChatBubble({
|
|
|
9995
10084
|
)
|
|
9996
10085
|
] })
|
|
9997
10086
|
] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
9998
|
-
messages.length === 0 && !loading && /* @__PURE__ */ jsx4(
|
|
10087
|
+
messages.length === 0 && !loading && !renderPendingPromptInline && /* @__PURE__ */ jsx4(
|
|
9999
10088
|
"div",
|
|
10000
10089
|
{
|
|
10001
10090
|
style: {
|