veo-sdk 0.5.32 → 0.5.34
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/builder-ZKWVTSTP.mjs +5 -0
- package/dist/{builder-MNYSDJFT.mjs.map → builder-ZKWVTSTP.mjs.map} +1 -1
- package/dist/builder.cjs +1035 -164
- package/dist/builder.cjs.map +1 -1
- package/dist/builder.d.cts +93 -3
- package/dist/builder.d.ts +93 -3
- package/dist/builder.mjs +4 -4
- package/dist/{chunk-JPANNXIK.mjs → chunk-HAAT6XVN.mjs} +213 -20
- package/dist/chunk-HAAT6XVN.mjs.map +1 -0
- package/dist/{chunk-AFU5ZG6Z.mjs → chunk-IZXJA55N.mjs} +714 -74
- package/dist/chunk-IZXJA55N.mjs.map +1 -0
- package/dist/{chunk-X27GD7IT.mjs → chunk-W4R4XCWL.mjs} +247 -16
- package/dist/chunk-W4R4XCWL.mjs.map +1 -0
- package/dist/{guide-preview-XUnTmzUO.d.cts → guide-preview-DbEZdGBV.d.cts} +43 -1
- package/dist/{guide-preview-XUnTmzUO.d.ts → guide-preview-DbEZdGBV.d.ts} +43 -1
- package/dist/index.cjs +1278 -183
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +5 -5
- package/dist/veo-builder.js +162 -33
- package/dist/veo-builder.js.map +1 -1
- package/dist/veo-guides.js +6 -6
- package/dist/veo-guides.js.map +1 -1
- package/package.json +16 -16
- package/dist/builder-MNYSDJFT.mjs +0 -5
- package/dist/chunk-AFU5ZG6Z.mjs.map +0 -1
- package/dist/chunk-JPANNXIK.mjs.map +0 -1
- package/dist/chunk-X27GD7IT.mjs.map +0 -1
package/dist/builder.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PreviewGuideInput } from './guide-preview-
|
|
2
|
-
export { a as PreviewHandle, b as PreviewResult, c as closeGuidePreview, p as previewGuide } from './guide-preview-
|
|
1
|
+
import { P as PreviewGuideInput } from './guide-preview-DbEZdGBV.cjs';
|
|
2
|
+
export { a as PreviewHandle, b as PreviewResult, c as closeGuidePreview, p as previewGuide } from './guide-preview-DbEZdGBV.cjs';
|
|
3
3
|
|
|
4
4
|
/** Dónde soltó el autor un bloque nuevo. */
|
|
5
5
|
interface BlockDropPayload {
|
|
@@ -81,8 +81,10 @@ interface ElementPickerOptions {
|
|
|
81
81
|
onCancel?: () => void;
|
|
82
82
|
/** Niveles de ancestros del selector. Default 5 (igual que autocapture). */
|
|
83
83
|
maxAncestors?: number;
|
|
84
|
-
/**
|
|
84
|
+
/** Título del banner superior. Default en español. */
|
|
85
85
|
hint?: string;
|
|
86
|
+
/** Subtítulo del banner (default: cómo abrir menús con Alt + clic). */
|
|
87
|
+
subhint?: string;
|
|
86
88
|
}
|
|
87
89
|
interface ElementPickerHandle {
|
|
88
90
|
/** Cierra el picker sin elegir nada. Idempotente. */
|
|
@@ -119,9 +121,87 @@ declare function stopElementPicker(): void;
|
|
|
119
121
|
* 4. La app responde con eventos (`picked`, `pick-cancelled`, …).
|
|
120
122
|
*/
|
|
121
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Estado que el panel (editor) publica para que el SDK pinte el "chrome" del
|
|
126
|
+
* editor (topbar + fila de pasos) ENCIMA de la app. El panel es la única fuente
|
|
127
|
+
* de verdad; el chrome solo lo refleja y devuelve `chrome-action`s.
|
|
128
|
+
*/
|
|
129
|
+
interface EditorChromeState {
|
|
130
|
+
guideName: string;
|
|
131
|
+
/** ISO de creación (null = guía nueva sin guardar). */
|
|
132
|
+
createdAt: string | null;
|
|
133
|
+
steps: Array<{
|
|
134
|
+
id: string;
|
|
135
|
+
label: string;
|
|
136
|
+
presentation: 'floating' | 'inline';
|
|
137
|
+
}>;
|
|
138
|
+
activeStep: number;
|
|
139
|
+
/** Qué muestra el panel: ajustes de activación o el paso activo. */
|
|
140
|
+
section: 'activation' | 'step' | 'badge' | 'target';
|
|
141
|
+
saving: boolean;
|
|
142
|
+
dirty: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Estado de publicación de la guía, para que el autor vea en la barra si lo
|
|
145
|
+
* que edita está Activa/Borrador/etc. (sin esto, guardar una guía en borrador
|
|
146
|
+
* y no verla en la app era un misterio). `tone` elige el color del chip.
|
|
147
|
+
*/
|
|
148
|
+
status?: {
|
|
149
|
+
label: string;
|
|
150
|
+
tone: 'live' | 'testing' | 'review' | 'draft' | 'paused';
|
|
151
|
+
} | null;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Qué está haciendo la preview en la app. El SDK lo publica cada vez que
|
|
155
|
+
* evalúa un comando `preview` con `honorTrigger`, para que el panel explique
|
|
156
|
+
* por qué la guía no se ve (retraso, elemento objetivo, otra página, otro
|
|
157
|
+
* dispositivo) en vez de parecer rota.
|
|
158
|
+
*/
|
|
159
|
+
type PreviewStatus = {
|
|
160
|
+
state: 'shown';
|
|
161
|
+
} | {
|
|
162
|
+
state: 'waiting-delay';
|
|
163
|
+
delayMs: number;
|
|
164
|
+
} | {
|
|
165
|
+
state: 'waiting-trigger';
|
|
166
|
+
trigger: 'on_click' | 'on_hover';
|
|
167
|
+
selector: string;
|
|
168
|
+
} | {
|
|
169
|
+
state: 'page-mismatch';
|
|
170
|
+
pattern: string;
|
|
171
|
+
} | {
|
|
172
|
+
state: 'device-mismatch';
|
|
173
|
+
devices: 'desktop' | 'mobile';
|
|
174
|
+
};
|
|
175
|
+
/** Acciones que el usuario dispara en el chrome (barras) y el panel ejecuta. */
|
|
176
|
+
type ChromeAction = {
|
|
177
|
+
action: 'select-activation';
|
|
178
|
+
} | {
|
|
179
|
+
action: 'select-step';
|
|
180
|
+
index: number;
|
|
181
|
+
} | {
|
|
182
|
+
action: 'add-step';
|
|
183
|
+
} | {
|
|
184
|
+
action: 'save';
|
|
185
|
+
exit: boolean;
|
|
186
|
+
} | {
|
|
187
|
+
action: 'exit';
|
|
188
|
+
} | {
|
|
189
|
+
action: 'dock';
|
|
190
|
+
position: 'top' | 'bottom';
|
|
191
|
+
} | {
|
|
192
|
+
action: 'show-panel';
|
|
193
|
+
} | {
|
|
194
|
+
action: 'preview';
|
|
195
|
+
} | {
|
|
196
|
+
action: 'exit-preview';
|
|
197
|
+
};
|
|
122
198
|
/** Eventos que la app del cliente envía al dashboard. */
|
|
123
199
|
type BuilderEvent = {
|
|
124
200
|
type: 'ready';
|
|
201
|
+
chrome?: boolean;
|
|
202
|
+
} | {
|
|
203
|
+
type: 'chrome-action';
|
|
204
|
+
payload: ChromeAction;
|
|
125
205
|
} | {
|
|
126
206
|
type: 'picked';
|
|
127
207
|
payload: PickedElement;
|
|
@@ -133,6 +213,9 @@ type BuilderEvent = {
|
|
|
133
213
|
} | {
|
|
134
214
|
type: 'block-dropped';
|
|
135
215
|
payload: BlockDropPayload;
|
|
216
|
+
} | {
|
|
217
|
+
type: 'preview-status';
|
|
218
|
+
payload: PreviewStatus;
|
|
136
219
|
} | {
|
|
137
220
|
type: 'closed';
|
|
138
221
|
};
|
|
@@ -147,6 +230,7 @@ type BuilderCommand = {
|
|
|
147
230
|
type: 'preview';
|
|
148
231
|
guide: PreviewGuideInput;
|
|
149
232
|
editable?: boolean;
|
|
233
|
+
honorTrigger?: boolean;
|
|
150
234
|
} | {
|
|
151
235
|
type: 'block-drag';
|
|
152
236
|
phase: 'move' | 'drop' | 'cancel';
|
|
@@ -158,6 +242,12 @@ type BuilderCommand = {
|
|
|
158
242
|
type: 'close-preview';
|
|
159
243
|
} | {
|
|
160
244
|
type: 'teardown';
|
|
245
|
+
} | {
|
|
246
|
+
type: 'editor-state';
|
|
247
|
+
state: EditorChromeState;
|
|
248
|
+
} | {
|
|
249
|
+
type: 'panel-visibility';
|
|
250
|
+
visible: boolean;
|
|
161
251
|
} | {
|
|
162
252
|
type: 'panel-ready';
|
|
163
253
|
};
|
package/dist/builder.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { P as PreviewGuideInput } from './guide-preview-
|
|
2
|
-
export { a as PreviewHandle, b as PreviewResult, c as closeGuidePreview, p as previewGuide } from './guide-preview-
|
|
1
|
+
import { P as PreviewGuideInput } from './guide-preview-DbEZdGBV.js';
|
|
2
|
+
export { a as PreviewHandle, b as PreviewResult, c as closeGuidePreview, p as previewGuide } from './guide-preview-DbEZdGBV.js';
|
|
3
3
|
|
|
4
4
|
/** Dónde soltó el autor un bloque nuevo. */
|
|
5
5
|
interface BlockDropPayload {
|
|
@@ -81,8 +81,10 @@ interface ElementPickerOptions {
|
|
|
81
81
|
onCancel?: () => void;
|
|
82
82
|
/** Niveles de ancestros del selector. Default 5 (igual que autocapture). */
|
|
83
83
|
maxAncestors?: number;
|
|
84
|
-
/**
|
|
84
|
+
/** Título del banner superior. Default en español. */
|
|
85
85
|
hint?: string;
|
|
86
|
+
/** Subtítulo del banner (default: cómo abrir menús con Alt + clic). */
|
|
87
|
+
subhint?: string;
|
|
86
88
|
}
|
|
87
89
|
interface ElementPickerHandle {
|
|
88
90
|
/** Cierra el picker sin elegir nada. Idempotente. */
|
|
@@ -119,9 +121,87 @@ declare function stopElementPicker(): void;
|
|
|
119
121
|
* 4. La app responde con eventos (`picked`, `pick-cancelled`, …).
|
|
120
122
|
*/
|
|
121
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Estado que el panel (editor) publica para que el SDK pinte el "chrome" del
|
|
126
|
+
* editor (topbar + fila de pasos) ENCIMA de la app. El panel es la única fuente
|
|
127
|
+
* de verdad; el chrome solo lo refleja y devuelve `chrome-action`s.
|
|
128
|
+
*/
|
|
129
|
+
interface EditorChromeState {
|
|
130
|
+
guideName: string;
|
|
131
|
+
/** ISO de creación (null = guía nueva sin guardar). */
|
|
132
|
+
createdAt: string | null;
|
|
133
|
+
steps: Array<{
|
|
134
|
+
id: string;
|
|
135
|
+
label: string;
|
|
136
|
+
presentation: 'floating' | 'inline';
|
|
137
|
+
}>;
|
|
138
|
+
activeStep: number;
|
|
139
|
+
/** Qué muestra el panel: ajustes de activación o el paso activo. */
|
|
140
|
+
section: 'activation' | 'step' | 'badge' | 'target';
|
|
141
|
+
saving: boolean;
|
|
142
|
+
dirty: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Estado de publicación de la guía, para que el autor vea en la barra si lo
|
|
145
|
+
* que edita está Activa/Borrador/etc. (sin esto, guardar una guía en borrador
|
|
146
|
+
* y no verla en la app era un misterio). `tone` elige el color del chip.
|
|
147
|
+
*/
|
|
148
|
+
status?: {
|
|
149
|
+
label: string;
|
|
150
|
+
tone: 'live' | 'testing' | 'review' | 'draft' | 'paused';
|
|
151
|
+
} | null;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Qué está haciendo la preview en la app. El SDK lo publica cada vez que
|
|
155
|
+
* evalúa un comando `preview` con `honorTrigger`, para que el panel explique
|
|
156
|
+
* por qué la guía no se ve (retraso, elemento objetivo, otra página, otro
|
|
157
|
+
* dispositivo) en vez de parecer rota.
|
|
158
|
+
*/
|
|
159
|
+
type PreviewStatus = {
|
|
160
|
+
state: 'shown';
|
|
161
|
+
} | {
|
|
162
|
+
state: 'waiting-delay';
|
|
163
|
+
delayMs: number;
|
|
164
|
+
} | {
|
|
165
|
+
state: 'waiting-trigger';
|
|
166
|
+
trigger: 'on_click' | 'on_hover';
|
|
167
|
+
selector: string;
|
|
168
|
+
} | {
|
|
169
|
+
state: 'page-mismatch';
|
|
170
|
+
pattern: string;
|
|
171
|
+
} | {
|
|
172
|
+
state: 'device-mismatch';
|
|
173
|
+
devices: 'desktop' | 'mobile';
|
|
174
|
+
};
|
|
175
|
+
/** Acciones que el usuario dispara en el chrome (barras) y el panel ejecuta. */
|
|
176
|
+
type ChromeAction = {
|
|
177
|
+
action: 'select-activation';
|
|
178
|
+
} | {
|
|
179
|
+
action: 'select-step';
|
|
180
|
+
index: number;
|
|
181
|
+
} | {
|
|
182
|
+
action: 'add-step';
|
|
183
|
+
} | {
|
|
184
|
+
action: 'save';
|
|
185
|
+
exit: boolean;
|
|
186
|
+
} | {
|
|
187
|
+
action: 'exit';
|
|
188
|
+
} | {
|
|
189
|
+
action: 'dock';
|
|
190
|
+
position: 'top' | 'bottom';
|
|
191
|
+
} | {
|
|
192
|
+
action: 'show-panel';
|
|
193
|
+
} | {
|
|
194
|
+
action: 'preview';
|
|
195
|
+
} | {
|
|
196
|
+
action: 'exit-preview';
|
|
197
|
+
};
|
|
122
198
|
/** Eventos que la app del cliente envía al dashboard. */
|
|
123
199
|
type BuilderEvent = {
|
|
124
200
|
type: 'ready';
|
|
201
|
+
chrome?: boolean;
|
|
202
|
+
} | {
|
|
203
|
+
type: 'chrome-action';
|
|
204
|
+
payload: ChromeAction;
|
|
125
205
|
} | {
|
|
126
206
|
type: 'picked';
|
|
127
207
|
payload: PickedElement;
|
|
@@ -133,6 +213,9 @@ type BuilderEvent = {
|
|
|
133
213
|
} | {
|
|
134
214
|
type: 'block-dropped';
|
|
135
215
|
payload: BlockDropPayload;
|
|
216
|
+
} | {
|
|
217
|
+
type: 'preview-status';
|
|
218
|
+
payload: PreviewStatus;
|
|
136
219
|
} | {
|
|
137
220
|
type: 'closed';
|
|
138
221
|
};
|
|
@@ -147,6 +230,7 @@ type BuilderCommand = {
|
|
|
147
230
|
type: 'preview';
|
|
148
231
|
guide: PreviewGuideInput;
|
|
149
232
|
editable?: boolean;
|
|
233
|
+
honorTrigger?: boolean;
|
|
150
234
|
} | {
|
|
151
235
|
type: 'block-drag';
|
|
152
236
|
phase: 'move' | 'drop' | 'cancel';
|
|
@@ -158,6 +242,12 @@ type BuilderCommand = {
|
|
|
158
242
|
type: 'close-preview';
|
|
159
243
|
} | {
|
|
160
244
|
type: 'teardown';
|
|
245
|
+
} | {
|
|
246
|
+
type: 'editor-state';
|
|
247
|
+
state: EditorChromeState;
|
|
248
|
+
} | {
|
|
249
|
+
type: 'panel-visibility';
|
|
250
|
+
visible: boolean;
|
|
161
251
|
} | {
|
|
162
252
|
type: 'panel-ready';
|
|
163
253
|
};
|
package/dist/builder.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import './chunk-
|
|
2
|
-
import { initBuilderMode } from './chunk-
|
|
3
|
-
export { createBuilderSession, initBuilderMode, startElementPicker, stopElementPicker } from './chunk-
|
|
4
|
-
export { closeGuidePreview, previewGuide } from './chunk-
|
|
1
|
+
import './chunk-HAAT6XVN.mjs';
|
|
2
|
+
import { initBuilderMode } from './chunk-IZXJA55N.mjs';
|
|
3
|
+
export { createBuilderSession, initBuilderMode, startElementPicker, stopElementPicker } from './chunk-IZXJA55N.mjs';
|
|
4
|
+
export { closeGuidePreview, previewGuide } from './chunk-W4R4XCWL.mjs';
|
|
5
5
|
import './chunk-PTG3BTJE.mjs';
|
|
6
6
|
|
|
7
7
|
// src/builder.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { hasDocument, hasWindow,
|
|
1
|
+
import { hasDocument, hasWindow, isAutomaticActivation, activationDelayMs, clearPreviewToken, DEFAULT_TRACKER_MAX_RETRIES, DEFAULT_TRACKER_BATCH_SIZE, DEFAULT_TRACKER_FLUSH_INTERVAL_MS, isDeviceAllowed, stepMatchesPage, CompositeRenderer, ModalRenderer, BannerRenderer, WalkthroughRenderer, BadgeRenderer, InlineFormRenderer, FormRenderer, InlineCustomRenderer, InlineRenderer, CustomRenderer, TooltipRenderer, previewGuide, FREQUENCY_CACHE_KEY_PREFIX, FREQUENCY_CACHE_TTL_MS, FREQUENCY_CACHE_MAX_ENTRIES, WALKTHROUGH_STATE_KEY_PREFIX, WALKTHROUGH_ABANDONMENT_TIMEOUT_MS } from './chunk-W4R4XCWL.mjs';
|
|
2
2
|
|
|
3
3
|
// src/plugins/plugin.ts
|
|
4
4
|
function definePlugin(plugin) {
|
|
@@ -43,26 +43,87 @@ async function runPreviewMode(opts) {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
let preview = null;
|
|
46
|
-
|
|
46
|
+
let delayTimer = null;
|
|
47
|
+
let disarmTrigger = null;
|
|
48
|
+
const baseLabel = guide ? `Vista previa: ${guide.guideName}` : "";
|
|
49
|
+
const clearWaits = () => {
|
|
50
|
+
if (delayTimer) clearTimeout(delayTimer);
|
|
51
|
+
delayTimer = null;
|
|
52
|
+
disarmTrigger?.();
|
|
53
|
+
disarmTrigger = null;
|
|
54
|
+
};
|
|
55
|
+
const paint = () => {
|
|
47
56
|
if (!guide) return;
|
|
48
57
|
preview?.close();
|
|
58
|
+
let startStepIndex;
|
|
59
|
+
if (guide.guideType === "composite") {
|
|
60
|
+
const steps = guide.guideSteps;
|
|
61
|
+
const idx = steps.findIndex((st) => stepMatchesPage(st.pageLocation, window.location.href));
|
|
62
|
+
if (idx === -1) {
|
|
63
|
+
chip.setLabel(`${baseLabel} \xB7 ning\xFAn paso aplica a esta p\xE1gina`);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
startStepIndex = idx;
|
|
67
|
+
}
|
|
68
|
+
chip.setLabel(baseLabel);
|
|
49
69
|
preview = previewGuide({
|
|
50
70
|
guideType: guide.guideType,
|
|
51
71
|
guideSteps: guide.guideSteps,
|
|
52
72
|
activationRules: guide.activationRules,
|
|
53
73
|
guideName: guide.guideName,
|
|
74
|
+
...startStepIndex !== void 0 ? { startStepIndex } : {},
|
|
54
75
|
formSubmit: submitToBackend,
|
|
55
76
|
formSubmitNote: "Vista previa: guardado en TU usuario para probar el flujo."
|
|
56
77
|
});
|
|
57
78
|
};
|
|
79
|
+
const show = () => {
|
|
80
|
+
if (!guide) return;
|
|
81
|
+
clearWaits();
|
|
82
|
+
const rules = guide.activationRules;
|
|
83
|
+
const selector = rules.selector?.trim();
|
|
84
|
+
if ((rules.trigger === "on_click" || rules.trigger === "on_hover") && selector && !isAutomaticActivation(rules)) {
|
|
85
|
+
const verb = rules.trigger === "on_hover" ? "pasa el mouse por" : "haz clic en";
|
|
86
|
+
chip.setLabel(`${baseLabel} \xB7 ${verb} el elemento objetivo`);
|
|
87
|
+
const onFire = (e) => {
|
|
88
|
+
const target = e.target;
|
|
89
|
+
if (!(target instanceof Element)) return;
|
|
90
|
+
if (target.closest("[data-veo-preview-chip],[data-veo-guide]")) return;
|
|
91
|
+
try {
|
|
92
|
+
if (!target.closest(selector)) return;
|
|
93
|
+
} catch {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
clearWaits();
|
|
97
|
+
paint();
|
|
98
|
+
};
|
|
99
|
+
document.addEventListener("click", onFire, true);
|
|
100
|
+
if (rules.trigger === "on_hover") document.addEventListener("mouseover", onFire, true);
|
|
101
|
+
disarmTrigger = () => {
|
|
102
|
+
document.removeEventListener("click", onFire, true);
|
|
103
|
+
document.removeEventListener("mouseover", onFire, true);
|
|
104
|
+
};
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const delayMs = activationDelayMs(rules);
|
|
108
|
+
if (delayMs > 0) {
|
|
109
|
+
chip.setLabel(`${baseLabel} \xB7 aparece en ${Math.round(delayMs / 1e3)} s`);
|
|
110
|
+
delayTimer = setTimeout(() => {
|
|
111
|
+
delayTimer = null;
|
|
112
|
+
paint();
|
|
113
|
+
}, delayMs);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
paint();
|
|
117
|
+
};
|
|
58
118
|
const chip = mountChip({
|
|
59
|
-
label: guide ?
|
|
119
|
+
label: guide ? baseLabel : "Link de preview inv\xE1lido o vencido",
|
|
60
120
|
error: !guide,
|
|
61
121
|
onReshow: guide ? show : null,
|
|
62
122
|
onClose: () => handle.teardown()
|
|
63
123
|
});
|
|
64
124
|
const handle = {
|
|
65
125
|
teardown() {
|
|
126
|
+
clearWaits();
|
|
66
127
|
preview?.close();
|
|
67
128
|
preview = null;
|
|
68
129
|
chip.remove();
|
|
@@ -85,7 +146,7 @@ function mountChip(opts) {
|
|
|
85
146
|
background: #111827; color: #f9fafb; border-radius: 9999px;
|
|
86
147
|
font: 500 12px/1.2 system-ui, sans-serif; box-shadow: 0 4px 12px rgba(0,0,0,.25); }
|
|
87
148
|
.chip.error { background: #7f1d1d; }
|
|
88
|
-
.label { max-width:
|
|
149
|
+
.label { max-width: 360px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
89
150
|
button { all: unset; cursor: pointer; padding: 2px 6px; border-radius: 9999px;
|
|
90
151
|
font-size: 12px; line-height: 1; }
|
|
91
152
|
button:hover { background: rgba(255,255,255,.15); }
|
|
@@ -111,7 +172,19 @@ function mountChip(opts) {
|
|
|
111
172
|
chip.appendChild(close);
|
|
112
173
|
root.appendChild(chip);
|
|
113
174
|
document.body.appendChild(host);
|
|
114
|
-
return {
|
|
175
|
+
return {
|
|
176
|
+
remove: () => host.remove(),
|
|
177
|
+
setLabel: (text) => {
|
|
178
|
+
label.textContent = text;
|
|
179
|
+
label.title = text;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/plugins/guides/campaign-weekdays.ts
|
|
185
|
+
function isWeekdayAllowed(campaignWeekdays, now = /* @__PURE__ */ new Date()) {
|
|
186
|
+
if (!campaignWeekdays || campaignWeekdays.length === 0) return true;
|
|
187
|
+
return campaignWeekdays.includes(now.getDay());
|
|
115
188
|
}
|
|
116
189
|
|
|
117
190
|
// src/plugins/guides/guide-frequency-cache.ts
|
|
@@ -613,6 +686,15 @@ var STATUS_BY_ACTION = {
|
|
|
613
686
|
step_back: null,
|
|
614
687
|
completed: "completed"
|
|
615
688
|
};
|
|
689
|
+
var REPEAT_UNIT_MS = {
|
|
690
|
+
minute: 6e4,
|
|
691
|
+
hour: 36e5,
|
|
692
|
+
day: 864e5,
|
|
693
|
+
week: 6048e5
|
|
694
|
+
};
|
|
695
|
+
function repeatIntervalMs(repeat) {
|
|
696
|
+
return Math.max(0, repeat.every) * REPEAT_UNIT_MS[repeat.unit];
|
|
697
|
+
}
|
|
616
698
|
var GuidesController = class {
|
|
617
699
|
constructor(client, config) {
|
|
618
700
|
this.client = client;
|
|
@@ -647,6 +729,20 @@ var GuidesController = class {
|
|
|
647
729
|
* a esa página (tours multi-página estilo Pendo).
|
|
648
730
|
*/
|
|
649
731
|
this.pendingComposite = null;
|
|
732
|
+
/**
|
|
733
|
+
* Guías composite con «retrasar la aparición» esperando su `delayMs`. Se
|
|
734
|
+
* cancelan si el usuario navega a una página donde el paso ya no aplica y en
|
|
735
|
+
* `destroy()`. Evita el doble arranque por pageviews rápidos durante la espera.
|
|
736
|
+
*/
|
|
737
|
+
this.compositeDelays = /* @__PURE__ */ new Map();
|
|
738
|
+
/**
|
|
739
|
+
* Estado LOCAL de «Repetir la aparición» (por sesión del SDK). El backend es
|
|
740
|
+
* la fuente de verdad (interacciones persistidas), pero entre el descarte y el
|
|
741
|
+
* siguiente resolve la cola de interacciones puede no haber salido todavía:
|
|
742
|
+
* sin esto la guía reaparecía al instante en la próxima navegación.
|
|
743
|
+
*/
|
|
744
|
+
this.repeatHoldUntil = /* @__PURE__ */ new Map();
|
|
745
|
+
this.localDismissals = /* @__PURE__ */ new Map();
|
|
650
746
|
/**
|
|
651
747
|
* Guías composite EN PANTALLA con el paso mostrado, por `guideId`. En una SPA,
|
|
652
748
|
* al navegar a una página que ya NO matchea la `pageLocation` del paso, hay que
|
|
@@ -712,15 +808,21 @@ var GuidesController = class {
|
|
|
712
808
|
if (this.debug) console.error("[veo] guides resolver threw:", err);
|
|
713
809
|
return;
|
|
714
810
|
}
|
|
811
|
+
guides = guides.filter(
|
|
812
|
+
(g) => isWeekdayAllowed(g.campaignWeekdays) && isDeviceAllowed(g.activationRules.devices)
|
|
813
|
+
);
|
|
715
814
|
if (this.debug) console.log(`[veo] guides: ${guides.length} eligible at ${pageUrl}`);
|
|
716
815
|
this.disarmTriggers();
|
|
717
816
|
for (const guide of guides) {
|
|
718
|
-
const
|
|
719
|
-
|
|
817
|
+
const rules = guide.activationRules;
|
|
818
|
+
const trigger = rules.trigger;
|
|
819
|
+
if ((trigger === "on_click" || trigger === "on_hover") && rules.selector) {
|
|
720
820
|
this.armInteractionTrigger(guide, endUserId, sessionId, pageUrl);
|
|
721
|
-
continue;
|
|
821
|
+
if (!isAutomaticActivation(rules)) continue;
|
|
722
822
|
}
|
|
723
|
-
await this.showGuide(guide, endUserId, sessionId, pageUrl
|
|
823
|
+
await this.showGuide(guide, endUserId, sessionId, pageUrl, {
|
|
824
|
+
badgeOpen: trigger !== "manual"
|
|
825
|
+
});
|
|
724
826
|
}
|
|
725
827
|
}
|
|
726
828
|
/**
|
|
@@ -728,11 +830,16 @@ var GuidesController = class {
|
|
|
728
830
|
* tipo). Cuerpo original del loop de `checkGuides`, extraído para que los
|
|
729
831
|
* disparadores por interacción pasen por LOS MISMOS chequeos al disparar.
|
|
730
832
|
*/
|
|
731
|
-
async showGuide(guide, endUserId, sessionId, pageUrl) {
|
|
833
|
+
async showGuide(guide, endUserId, sessionId, pageUrl, opts = {}) {
|
|
834
|
+
const badgeOpen = opts.badgeOpen !== false;
|
|
732
835
|
if (this.frequencyCache.shouldFilter(guide.guideId, guide.displayFrequency)) {
|
|
733
836
|
if (this.debug) console.log(`[veo] guides: filtered locally guideId=${guide.guideId}`);
|
|
734
837
|
return;
|
|
735
838
|
}
|
|
839
|
+
if (!opts.byUser && this.isHeldByRepeat(guide)) {
|
|
840
|
+
if (this.debug) console.log(`[veo] guides: held by repeat guideId=${guide.guideId}`);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
736
843
|
if (this.activeByGuideId.has(guide.guideId)) {
|
|
737
844
|
if (this.debug) console.log(`[veo] guides: already shown guideId=${guide.guideId}`);
|
|
738
845
|
return;
|
|
@@ -747,7 +854,7 @@ var GuidesController = class {
|
|
|
747
854
|
return;
|
|
748
855
|
}
|
|
749
856
|
if (guide.guideType === "composite") {
|
|
750
|
-
this.startComposite(guide, sessionId, pageUrl);
|
|
857
|
+
this.startComposite(guide, sessionId, pageUrl, badgeOpen);
|
|
751
858
|
return;
|
|
752
859
|
}
|
|
753
860
|
this.runSingleStepRender(guide, sessionId, pageUrl);
|
|
@@ -776,7 +883,7 @@ var GuidesController = class {
|
|
|
776
883
|
return;
|
|
777
884
|
}
|
|
778
885
|
disarm();
|
|
779
|
-
void this.showGuide(guide, endUserId, sessionId, pageUrl);
|
|
886
|
+
void this.showGuide(guide, endUserId, sessionId, pageUrl, { byUser: true });
|
|
780
887
|
};
|
|
781
888
|
const disarm = () => {
|
|
782
889
|
document.removeEventListener(eventName, onFire, true);
|
|
@@ -843,6 +950,10 @@ ${pageUrl}`;
|
|
|
843
950
|
this.activeWalkthroughGuide = null;
|
|
844
951
|
}
|
|
845
952
|
this.disarmTriggers();
|
|
953
|
+
for (const pending of this.compositeDelays.values()) clearTimeout(pending.timer);
|
|
954
|
+
this.compositeDelays.clear();
|
|
955
|
+
this.repeatHoldUntil.clear();
|
|
956
|
+
this.localDismissals.clear();
|
|
846
957
|
this.dispatched.clear();
|
|
847
958
|
this.resolveCache = null;
|
|
848
959
|
this.inflightResolve = null;
|
|
@@ -852,7 +963,7 @@ ${pageUrl}`;
|
|
|
852
963
|
const renderer = this.createSingleStepRenderer(guide.guideType);
|
|
853
964
|
if (!renderer) return;
|
|
854
965
|
this.activeByGuideId.set(guide.guideId, renderer);
|
|
855
|
-
const delayMs = guide.activationRules
|
|
966
|
+
const delayMs = activationDelayMs(guide.activationRules);
|
|
856
967
|
window.setTimeout(
|
|
857
968
|
() => this.mountSingleStepRenderer(guide, renderer, sessionId, pageUrl),
|
|
858
969
|
delayMs
|
|
@@ -863,12 +974,79 @@ ${pageUrl}`;
|
|
|
863
974
|
* `pageLocation` matchea la URL actual (targeting por-paso) y lo pinta con el
|
|
864
975
|
* `CompositeRenderer`. Si ningún paso aplica a esta página, no muestra nada.
|
|
865
976
|
*/
|
|
866
|
-
startComposite(guide, sessionId, pageUrl) {
|
|
977
|
+
startComposite(guide, sessionId, pageUrl, badgeOpen = true) {
|
|
867
978
|
if (this.pendingComposite?.guide.guideId === guide.guideId) return;
|
|
868
979
|
const steps = guide.guideSteps;
|
|
869
|
-
const
|
|
980
|
+
const remembered = guide.activationRules.repeat?.onLoad === "resume" ? this.recallStep(guide.guideId) : null;
|
|
981
|
+
const start = remembered !== null && stepMatchesPage(steps[remembered]?.pageLocation, pageUrl) ? remembered : steps.findIndex((s) => stepMatchesPage(s.pageLocation, pageUrl));
|
|
870
982
|
if (start === -1) return;
|
|
871
|
-
|
|
983
|
+
if (guide.activationRules.trigger === "manual" && !steps[start]?.badge?.enabled) return;
|
|
984
|
+
if (this.compositeDelays.has(guide.guideId)) return;
|
|
985
|
+
this.resetDispatched(guide.guideId);
|
|
986
|
+
const delayMs = activationDelayMs(guide.activationRules);
|
|
987
|
+
if (delayMs <= 0) {
|
|
988
|
+
this.renderCompositeStep(guide, start, sessionId, pageUrl, badgeOpen);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const timer = setTimeout(() => {
|
|
992
|
+
this.compositeDelays.delete(guide.guideId);
|
|
993
|
+
if (this.activeByGuideId.has(guide.guideId)) return;
|
|
994
|
+
this.renderCompositeStep(guide, start, sessionId, pageUrl, badgeOpen);
|
|
995
|
+
}, delayMs);
|
|
996
|
+
this.compositeDelays.set(guide.guideId, { guide, stepIndex: start, timer });
|
|
997
|
+
}
|
|
998
|
+
/** Olvida el dedupe de interacciones de una guía (nueva aparición). */
|
|
999
|
+
resetDispatched(guideId) {
|
|
1000
|
+
const prefix = `${guideId}:`;
|
|
1001
|
+
for (const key of this.dispatched) {
|
|
1002
|
+
if (key.startsWith(prefix)) this.dispatched.delete(key);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
/**
|
|
1006
|
+
* «Repetir la aparición» evaluada en local: ya se mostró hace menos del
|
|
1007
|
+
* intervalo, o el usuario ya la descartó N veces en esta sesión del SDK.
|
|
1008
|
+
*/
|
|
1009
|
+
isHeldByRepeat(guide) {
|
|
1010
|
+
const repeat = guide.activationRules.repeat;
|
|
1011
|
+
if (!repeat) return false;
|
|
1012
|
+
const until = this.repeatHoldUntil.get(guide.guideId);
|
|
1013
|
+
if (until !== void 0 && Date.now() < until) return true;
|
|
1014
|
+
if (repeat.stopAfter.kind === "dismissals") {
|
|
1015
|
+
return (this.localDismissals.get(guide.guideId) ?? 0) >= repeat.stopAfter.count;
|
|
1016
|
+
}
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
/** Registra en local el efecto de una interacción sobre «repetir». */
|
|
1020
|
+
recordRepeatLocal(guide, action) {
|
|
1021
|
+
const repeat = guide.activationRules.repeat;
|
|
1022
|
+
if (!repeat) return;
|
|
1023
|
+
if (action === "shown") {
|
|
1024
|
+
this.repeatHoldUntil.set(guide.guideId, Date.now() + repeatIntervalMs(repeat));
|
|
1025
|
+
} else if (action === "dismissed") {
|
|
1026
|
+
this.localDismissals.set(guide.guideId, (this.localDismissals.get(guide.guideId) ?? 0) + 1);
|
|
1027
|
+
} else if (action === "completed" && repeat.stopAfter.kind === "completed") {
|
|
1028
|
+
this.repeatHoldUntil.set(guide.guideId, Number.POSITIVE_INFINITY);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
/** Último paso visto por guía (localStorage) para `repeat.onLoad = 'resume'`. */
|
|
1032
|
+
stepKey(guideId) {
|
|
1033
|
+
return `veo:last_step:${guideId}`;
|
|
1034
|
+
}
|
|
1035
|
+
rememberStep(guideId, idx) {
|
|
1036
|
+
try {
|
|
1037
|
+
localStorage.setItem(this.stepKey(guideId), String(idx));
|
|
1038
|
+
} catch {
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
recallStep(guideId) {
|
|
1042
|
+
try {
|
|
1043
|
+
const raw = localStorage.getItem(this.stepKey(guideId));
|
|
1044
|
+
if (raw === null) return null;
|
|
1045
|
+
const n = Number(raw);
|
|
1046
|
+
return Number.isInteger(n) && n >= 0 ? n : null;
|
|
1047
|
+
} catch {
|
|
1048
|
+
return null;
|
|
1049
|
+
}
|
|
872
1050
|
}
|
|
873
1051
|
/**
|
|
874
1052
|
* Destruye las guías composite en pantalla cuyo paso mostrado ya no matchea la
|
|
@@ -876,6 +1054,14 @@ ${pageUrl}`;
|
|
|
876
1054
|
* esto la guía queda montada en el DOM y aparece en páginas que no debía.
|
|
877
1055
|
*/
|
|
878
1056
|
pruneComposite(pageUrl) {
|
|
1057
|
+
for (const [guideId, pending] of this.compositeDelays) {
|
|
1058
|
+
const steps = pending.guide.guideSteps;
|
|
1059
|
+
const step = steps[pending.stepIndex];
|
|
1060
|
+
if (!step || !stepMatchesPage(step.pageLocation, pageUrl)) {
|
|
1061
|
+
clearTimeout(pending.timer);
|
|
1062
|
+
this.compositeDelays.delete(guideId);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
879
1065
|
for (const entry of this.activeComposite.values()) {
|
|
880
1066
|
const steps = entry.guide.guideSteps;
|
|
881
1067
|
const step = steps[entry.stepIndex];
|
|
@@ -907,8 +1093,9 @@ ${pageUrl}`;
|
|
|
907
1093
|
* que los botones next/prev avancen (misma página); en single-step los
|
|
908
1094
|
* botones cierran. Cada avance destruye el paso anterior y monta el siguiente.
|
|
909
1095
|
*/
|
|
910
|
-
renderCompositeStep(guide, idx, sessionId, pageUrl) {
|
|
1096
|
+
renderCompositeStep(guide, idx, sessionId, pageUrl, badgeOpen = true) {
|
|
911
1097
|
const steps = guide.guideSteps;
|
|
1098
|
+
this.rememberStep(guide.guideId, idx);
|
|
912
1099
|
const renderer = new CompositeRenderer();
|
|
913
1100
|
this.activeRenderers.add(renderer);
|
|
914
1101
|
this.activeByGuideId.set(guide.guideId, renderer);
|
|
@@ -926,7 +1113,7 @@ ${pageUrl}`;
|
|
|
926
1113
|
if (target < 0 || target >= steps.length) return;
|
|
927
1114
|
const step = steps[target];
|
|
928
1115
|
if (step && stepMatchesPage(step.pageLocation, pageUrl)) {
|
|
929
|
-
this.renderCompositeStep(guide, target, sessionId, pageUrl);
|
|
1116
|
+
this.renderCompositeStep(guide, target, sessionId, pageUrl, badgeOpen);
|
|
930
1117
|
} else {
|
|
931
1118
|
this.pendingComposite = { guide, stepIndex: target };
|
|
932
1119
|
}
|
|
@@ -952,6 +1139,10 @@ ${pageUrl}`;
|
|
|
952
1139
|
onOpenGuide: (targetGuideId) => {
|
|
953
1140
|
void this.openGuideById(targetGuideId, sessionId, pageUrl);
|
|
954
1141
|
},
|
|
1142
|
+
// Badge: cuando el MOTOR muestra la guía (automático, elemento objetivo
|
|
1143
|
+
// o encadenada), el contenido se abre solo; el marcador queda para
|
|
1144
|
+
// volver a verla. Solo `manual` deja el tooltip cerrado.
|
|
1145
|
+
badgeOpen,
|
|
955
1146
|
...nav ? { nav } : {}
|
|
956
1147
|
});
|
|
957
1148
|
} catch (err) {
|
|
@@ -1205,6 +1396,8 @@ ${pageUrl}`;
|
|
|
1205
1396
|
this.dispatched.add(dedupKey);
|
|
1206
1397
|
const cacheStatus = STATUS_BY_ACTION[event.action];
|
|
1207
1398
|
if (cacheStatus) this.frequencyCache.record(guide.guideId, cacheStatus);
|
|
1399
|
+
this.recordRepeatLocal(guide, event.action);
|
|
1400
|
+
if (event.action === "dismissed" || event.action === "completed") this.resolveCache = null;
|
|
1208
1401
|
const endUserId = this.client.getEndUserId();
|
|
1209
1402
|
if (!endUserId) {
|
|
1210
1403
|
if (this.debug)
|
|
@@ -1336,5 +1529,5 @@ function guidesPlugin(config) {
|
|
|
1336
1529
|
}
|
|
1337
1530
|
|
|
1338
1531
|
export { definePlugin, guidesPlugin, runPreviewMode };
|
|
1339
|
-
//# sourceMappingURL=chunk-
|
|
1340
|
-
//# sourceMappingURL=chunk-
|
|
1532
|
+
//# sourceMappingURL=chunk-HAAT6XVN.mjs.map
|
|
1533
|
+
//# sourceMappingURL=chunk-HAAT6XVN.mjs.map
|