@magicfeedback/popup-sdk 1.8.4 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/dist/assets/assets/style.css +113 -60
- package/dist/{chunk-EBJZDVC6.mjs → chunk-AKVMUQXO.mjs} +114 -61
- package/dist/chunk-AKVMUQXO.mjs.map +1 -0
- package/dist/{chunk-AV2TTQXB.mjs → chunk-MTV7T4OW.mjs} +114 -61
- package/dist/chunk-MTV7T4OW.mjs.map +1 -0
- package/dist/index.d.mts +42 -1
- package/dist/index.d.ts +42 -1
- package/dist/index.js +173 -67
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +64 -11
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.d.mts +35 -0
- package/dist/react-native.d.ts +35 -0
- package/dist/react-native.js +173 -67
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +64 -11
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-3CZ6WTXF.mjs → renderPopup-LEGTSBLD.mjs} +2 -2
- package/dist/{renderPopup-E2OLIXIQ.mjs → renderPopup-LEM2MLWT.mjs} +2 -2
- package/package.json +1 -1
- package/dist/chunk-AV2TTQXB.mjs.map +0 -1
- package/dist/chunk-EBJZDVC6.mjs.map +0 -1
- /package/dist/{renderPopup-3CZ6WTXF.mjs.map → renderPopup-LEGTSBLD.mjs.map} +0 -0
- /package/dist/{renderPopup-E2OLIXIQ.mjs.map → renderPopup-LEM2MLWT.mjs.map} +0 -0
package/dist/react-native.d.mts
CHANGED
|
@@ -122,6 +122,19 @@ interface DeepdotsLogger {
|
|
|
122
122
|
error?: (...args: unknown[]) => void;
|
|
123
123
|
info?: (...args: unknown[]) => void;
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* A session of the analytics channel as the Deepdots API sees it. Every session becomes ONE
|
|
127
|
+
* feedback, and `sessionId` is what the API stores as `sdkSessionId` on it: the value to look
|
|
128
|
+
* that feedback up with (`GET /feedbacks?filter={"where":{"sdkSessionId":"<sessionId>"}}`).
|
|
129
|
+
* A session that closes again keeps that one feedback: the API updates it.
|
|
130
|
+
* - `open`: the API acknowledged the session's first batch; its events keep accumulating.
|
|
131
|
+
* - `closed`: the API acknowledged the session's closing batch (`completed: true`); the
|
|
132
|
+
* feedback is created from it within seconds.
|
|
133
|
+
*/
|
|
134
|
+
interface FeedbackSession {
|
|
135
|
+
sessionId: string;
|
|
136
|
+
status: 'open' | 'closed';
|
|
137
|
+
}
|
|
125
138
|
interface DeepdotsInitParams {
|
|
126
139
|
/** API key for authentication */
|
|
127
140
|
apiKey?: string;
|
|
@@ -157,6 +170,17 @@ interface DeepdotsInitParams {
|
|
|
157
170
|
* logged when `debug` is true.
|
|
158
171
|
*/
|
|
159
172
|
analytics?: AnalyticsKeys;
|
|
173
|
+
/**
|
|
174
|
+
* Called when the analytics channel opens or closes a session (needs `analytics`), with the
|
|
175
|
+
* id the Deepdots API stores as `sdkSessionId` on the feedback the session becomes: send it
|
|
176
|
+
* to your backend to attach data to that feedback later. A session whose only batch is the
|
|
177
|
+
* closing one reports just `closed`. `closed` does not come when the page unloads before the
|
|
178
|
+
* closing request returns, for a session dropped after 30 min hidden, nor when the closing
|
|
179
|
+
* request fails (the API closes those itself, later): their id already came with `open`. Key
|
|
180
|
+
* by `sessionId`, not by order: the next session's `open` can arrive before the previous
|
|
181
|
+
* one's `closed`. Same value as `getFeedbackSessionId()` while the session is open.
|
|
182
|
+
*/
|
|
183
|
+
onFeedbackSession?: (session: FeedbackSession) => void;
|
|
160
184
|
/**
|
|
161
185
|
* IP geolocation (country/city) added to analytics. Default `true`. The lookup calls
|
|
162
186
|
* third-party services (ipapi.co, then ipwho.is, then ipinfo.io as fallbacks, 3 s timeout
|
|
@@ -392,6 +416,8 @@ declare class DeepdotsPopups {
|
|
|
392
416
|
private analytics;
|
|
393
417
|
/** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */
|
|
394
418
|
private analyticsFeedbackSessionId;
|
|
419
|
+
/** `init({ onFeedbackSession })`: avisa al host al abrir y cerrar cada registro. */
|
|
420
|
+
private onFeedbackSession;
|
|
395
421
|
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
396
422
|
private analyticsFlushTimer;
|
|
397
423
|
/** Crash & error reporting (#14–17). Null hasta init(). */
|
|
@@ -435,6 +461,15 @@ declare class DeepdotsPopups {
|
|
|
435
461
|
getUserId(): string | null;
|
|
436
462
|
/** Session id de navegación actual. Null si tracking off. */
|
|
437
463
|
getSessionId(): string | null;
|
|
464
|
+
/**
|
|
465
|
+
* Id del registro de analytics abierto: el `sessionId` que devolvió `POST /sdk/feedback`,
|
|
466
|
+
* que la API guarda como `sdkSessionId` en el Feedback en que se convierte la sesión. No es
|
|
467
|
+
* `getSessionId()`. Null hasta que el backend acepta el primer lote y de nuevo al cerrar la
|
|
468
|
+
* sesión: para saber también qué registro se cerró, usa `init({ onFeedbackSession })`.
|
|
469
|
+
*/
|
|
470
|
+
getFeedbackSessionId(): string | null;
|
|
471
|
+
/** Avisa al host del registro abierto o cerrado; un fallo del host no corta el envío. */
|
|
472
|
+
private notifyFeedbackSession;
|
|
438
473
|
/** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */
|
|
439
474
|
setTrackingEnabled(enabled: boolean): void;
|
|
440
475
|
/** Registra un evento de analítica (modelo GA: nombre + params). No-op si tracking off. */
|
package/dist/react-native.d.ts
CHANGED
|
@@ -122,6 +122,19 @@ interface DeepdotsLogger {
|
|
|
122
122
|
error?: (...args: unknown[]) => void;
|
|
123
123
|
info?: (...args: unknown[]) => void;
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* A session of the analytics channel as the Deepdots API sees it. Every session becomes ONE
|
|
127
|
+
* feedback, and `sessionId` is what the API stores as `sdkSessionId` on it: the value to look
|
|
128
|
+
* that feedback up with (`GET /feedbacks?filter={"where":{"sdkSessionId":"<sessionId>"}}`).
|
|
129
|
+
* A session that closes again keeps that one feedback: the API updates it.
|
|
130
|
+
* - `open`: the API acknowledged the session's first batch; its events keep accumulating.
|
|
131
|
+
* - `closed`: the API acknowledged the session's closing batch (`completed: true`); the
|
|
132
|
+
* feedback is created from it within seconds.
|
|
133
|
+
*/
|
|
134
|
+
interface FeedbackSession {
|
|
135
|
+
sessionId: string;
|
|
136
|
+
status: 'open' | 'closed';
|
|
137
|
+
}
|
|
125
138
|
interface DeepdotsInitParams {
|
|
126
139
|
/** API key for authentication */
|
|
127
140
|
apiKey?: string;
|
|
@@ -157,6 +170,17 @@ interface DeepdotsInitParams {
|
|
|
157
170
|
* logged when `debug` is true.
|
|
158
171
|
*/
|
|
159
172
|
analytics?: AnalyticsKeys;
|
|
173
|
+
/**
|
|
174
|
+
* Called when the analytics channel opens or closes a session (needs `analytics`), with the
|
|
175
|
+
* id the Deepdots API stores as `sdkSessionId` on the feedback the session becomes: send it
|
|
176
|
+
* to your backend to attach data to that feedback later. A session whose only batch is the
|
|
177
|
+
* closing one reports just `closed`. `closed` does not come when the page unloads before the
|
|
178
|
+
* closing request returns, for a session dropped after 30 min hidden, nor when the closing
|
|
179
|
+
* request fails (the API closes those itself, later): their id already came with `open`. Key
|
|
180
|
+
* by `sessionId`, not by order: the next session's `open` can arrive before the previous
|
|
181
|
+
* one's `closed`. Same value as `getFeedbackSessionId()` while the session is open.
|
|
182
|
+
*/
|
|
183
|
+
onFeedbackSession?: (session: FeedbackSession) => void;
|
|
160
184
|
/**
|
|
161
185
|
* IP geolocation (country/city) added to analytics. Default `true`. The lookup calls
|
|
162
186
|
* third-party services (ipapi.co, then ipwho.is, then ipinfo.io as fallbacks, 3 s timeout
|
|
@@ -392,6 +416,8 @@ declare class DeepdotsPopups {
|
|
|
392
416
|
private analytics;
|
|
393
417
|
/** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */
|
|
394
418
|
private analyticsFeedbackSessionId;
|
|
419
|
+
/** `init({ onFeedbackSession })`: avisa al host al abrir y cerrar cada registro. */
|
|
420
|
+
private onFeedbackSession;
|
|
395
421
|
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
396
422
|
private analyticsFlushTimer;
|
|
397
423
|
/** Crash & error reporting (#14–17). Null hasta init(). */
|
|
@@ -435,6 +461,15 @@ declare class DeepdotsPopups {
|
|
|
435
461
|
getUserId(): string | null;
|
|
436
462
|
/** Session id de navegación actual. Null si tracking off. */
|
|
437
463
|
getSessionId(): string | null;
|
|
464
|
+
/**
|
|
465
|
+
* Id del registro de analytics abierto: el `sessionId` que devolvió `POST /sdk/feedback`,
|
|
466
|
+
* que la API guarda como `sdkSessionId` en el Feedback en que se convierte la sesión. No es
|
|
467
|
+
* `getSessionId()`. Null hasta que el backend acepta el primer lote y de nuevo al cerrar la
|
|
468
|
+
* sesión: para saber también qué registro se cerró, usa `init({ onFeedbackSession })`.
|
|
469
|
+
*/
|
|
470
|
+
getFeedbackSessionId(): string | null;
|
|
471
|
+
/** Avisa al host del registro abierto o cerrado; un fallo del host no corta el envío. */
|
|
472
|
+
private notifyFeedbackSession;
|
|
438
473
|
/** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */
|
|
439
474
|
setTrackingEnabled(enabled: boolean): void;
|
|
440
475
|
/** Registra un evento de analítica (modelo GA: nombre + params). No-op si tracking off. */
|
package/dist/react-native.js
CHANGED
|
@@ -563,7 +563,9 @@ var init_style = __esm({
|
|
|
563
563
|
* repintar\xEDa su web.
|
|
564
564
|
* 4. El grupo .magicfeedback-radio se cualifica como div.magicfeedback-radio (la clase acaba
|
|
565
565
|
* tambi\xE9n en el <input> del boolean).
|
|
566
|
-
* 5.
|
|
566
|
+
* 5. Todo :hover va dentro de @media (hover: hover): en t\xE1ctil WebKit deja el hover pegado
|
|
567
|
+
* en el \xFAltimo punto tocado y la opci\xF3n de la p\xE1gina siguiente parec\xEDa seleccionada.
|
|
568
|
+
* 6. Al final, un bloque de reglas propias del popup (fuente en los controles y padding del
|
|
567
569
|
* radio nativo).
|
|
568
570
|
*
|
|
569
571
|
* Las variables --mf-* se dejan en :root a prop\xF3sito: el modal del priority-list se portea a
|
|
@@ -801,9 +803,11 @@ var init_style = __esm({
|
|
|
801
803
|
text-underline-offset: 2px;
|
|
802
804
|
}
|
|
803
805
|
|
|
804
|
-
|
|
805
|
-
.magicfeedback-
|
|
806
|
-
|
|
806
|
+
@media (hover: hover) {
|
|
807
|
+
.magicfeedback-info-message a:hover,
|
|
808
|
+
.magicfeedback-start-message a:hover {
|
|
809
|
+
color: var(--mf-primary-hover);
|
|
810
|
+
}
|
|
807
811
|
}
|
|
808
812
|
|
|
809
813
|
.magicfeedback-info-message ul,
|
|
@@ -1029,10 +1033,12 @@ div.magicfeedback-radio {
|
|
|
1029
1033
|
transition: var(--mf-transition-fast);
|
|
1030
1034
|
}
|
|
1031
1035
|
|
|
1032
|
-
|
|
1033
|
-
.magicfeedback-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
+
@media (hover: hover) {
|
|
1037
|
+
.magicfeedback-checkbox-container:hover,
|
|
1038
|
+
.magicfeedback-radio-container:hover {
|
|
1039
|
+
background-color: var(--mf-bg-hover);
|
|
1040
|
+
border-color: var(--mf-primary);
|
|
1041
|
+
}
|
|
1036
1042
|
}
|
|
1037
1043
|
|
|
1038
1044
|
.magicfeedback-checkbox-container:has(input:checked),
|
|
@@ -1099,9 +1105,11 @@ div.magicfeedback-radio {
|
|
|
1099
1105
|
height: inherit;
|
|
1100
1106
|
}
|
|
1101
1107
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1108
|
+
@media (hover: hover) {
|
|
1109
|
+
.magicfeedback-boolean-option label:hover {
|
|
1110
|
+
background-color: var(--mf-bg-hover);
|
|
1111
|
+
border-color: var(--mf-primary);
|
|
1112
|
+
}
|
|
1105
1113
|
}
|
|
1106
1114
|
|
|
1107
1115
|
.magicfeedback-boolean-option input[type="radio"]:checked + label {
|
|
@@ -1172,9 +1180,11 @@ div.magicfeedback-radio {
|
|
|
1172
1180
|
border: 2px solid transparent;
|
|
1173
1181
|
}
|
|
1174
1182
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1183
|
+
@media (hover: hover) {
|
|
1184
|
+
.magicfeedback-rating-option-label-container img:hover {
|
|
1185
|
+
transform: scale(1.1);
|
|
1186
|
+
border-color: var(--mf-primary);
|
|
1187
|
+
}
|
|
1178
1188
|
}
|
|
1179
1189
|
|
|
1180
1190
|
.magicfeedback-rating-option-label-container input[type="radio"]:checked + img {
|
|
@@ -1326,9 +1336,11 @@ div.magicfeedback-radio {
|
|
|
1326
1336
|
font-size: 0.8rem;
|
|
1327
1337
|
}
|
|
1328
1338
|
|
|
1329
|
-
|
|
1330
|
-
.magicfeedback-rating-number-option-row
|
|
1331
|
-
|
|
1339
|
+
@media (hover: hover) {
|
|
1340
|
+
.magicfeedback-rating-number-option-row .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
|
|
1341
|
+
.magicfeedback-rating-number-option-row-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
|
|
1342
|
+
background: var(--mf-primary-light);
|
|
1343
|
+
}
|
|
1332
1344
|
}
|
|
1333
1345
|
|
|
1334
1346
|
.magicfeedback-rating-number-option-row .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value,
|
|
@@ -1365,8 +1377,10 @@ div.magicfeedback-radio {
|
|
|
1365
1377
|
font-size: var(--mf-font-size-sm);
|
|
1366
1378
|
}
|
|
1367
1379
|
|
|
1368
|
-
|
|
1369
|
-
|
|
1380
|
+
@media (hover: hover) {
|
|
1381
|
+
.magicfeedback-rating-number-option--extra .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
|
|
1382
|
+
background: var(--mf-primary-light);
|
|
1383
|
+
}
|
|
1370
1384
|
}
|
|
1371
1385
|
|
|
1372
1386
|
.magicfeedback-rating-number-option--extra .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value {
|
|
@@ -1399,9 +1413,11 @@ div.magicfeedback-radio {
|
|
|
1399
1413
|
color: var(--mf-primary);
|
|
1400
1414
|
}
|
|
1401
1415
|
|
|
1402
|
-
|
|
1403
|
-
.magicfeedback-rating-number-option-column
|
|
1404
|
-
|
|
1416
|
+
@media (hover: hover) {
|
|
1417
|
+
.magicfeedback-rating-number-option-column .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
|
|
1418
|
+
.magicfeedback-rating-number-option-column-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
|
|
1419
|
+
background: var(--mf-primary-light);
|
|
1420
|
+
}
|
|
1405
1421
|
}
|
|
1406
1422
|
|
|
1407
1423
|
.magicfeedback-rating-number-option-column .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value,
|
|
@@ -1455,10 +1471,12 @@ div.magicfeedback-radio {
|
|
|
1455
1471
|
transition: var(--mf-transition-fast);
|
|
1456
1472
|
}
|
|
1457
1473
|
|
|
1458
|
-
|
|
1459
|
-
.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover
|
|
1460
|
-
|
|
1461
|
-
|
|
1474
|
+
@media (hover: hover) {
|
|
1475
|
+
.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,
|
|
1476
|
+
.magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {
|
|
1477
|
+
color: #fbbf24;
|
|
1478
|
+
transform: scale(1.1);
|
|
1479
|
+
}
|
|
1462
1480
|
}
|
|
1463
1481
|
|
|
1464
1482
|
.magicfeedback-rating-star-selected {
|
|
@@ -1499,9 +1517,11 @@ div.magicfeedback-radio {
|
|
|
1499
1517
|
transition: var(--mf-transition-fast);
|
|
1500
1518
|
}
|
|
1501
1519
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1520
|
+
@media (hover: hover) {
|
|
1521
|
+
.magicfeedback-image-option-label-container:hover img {
|
|
1522
|
+
border-color: var(--mf-primary);
|
|
1523
|
+
box-shadow: var(--mf-shadow-md);
|
|
1524
|
+
}
|
|
1505
1525
|
}
|
|
1506
1526
|
|
|
1507
1527
|
.magicfeedback-image-option-label-container input:checked + img {
|
|
@@ -1542,9 +1562,11 @@ div.magicfeedback-radio {
|
|
|
1542
1562
|
background-color: var(--mf-bg-secondary);
|
|
1543
1563
|
}
|
|
1544
1564
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1565
|
+
@media (hover: hover) {
|
|
1566
|
+
.magicfeedback-multi-question-matrix-row-tr:hover {
|
|
1567
|
+
background-color: var(--mf-bg-hover);
|
|
1568
|
+
box-shadow: var(--mf-shadow-sm);
|
|
1569
|
+
}
|
|
1548
1570
|
}
|
|
1549
1571
|
|
|
1550
1572
|
.magicfeedback-multi-question-matrix-table td {
|
|
@@ -1580,9 +1602,11 @@ div.magicfeedback-radio {
|
|
|
1580
1602
|
transition: var(--mf-transition-fast);
|
|
1581
1603
|
}
|
|
1582
1604
|
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1605
|
+
@media (hover: hover) {
|
|
1606
|
+
.magicfeedback-priority-list-item:hover {
|
|
1607
|
+
box-shadow: var(--mf-shadow-sm);
|
|
1608
|
+
border-color: var(--mf-primary);
|
|
1609
|
+
}
|
|
1586
1610
|
}
|
|
1587
1611
|
|
|
1588
1612
|
.magicfeedback-priority-list-item-label {
|
|
@@ -1603,10 +1627,12 @@ div.magicfeedback-radio {
|
|
|
1603
1627
|
user-select: none;
|
|
1604
1628
|
}
|
|
1605
1629
|
|
|
1606
|
-
|
|
1607
|
-
.magicfeedback-priority-list-arrow-
|
|
1608
|
-
|
|
1609
|
-
|
|
1630
|
+
@media (hover: hover) {
|
|
1631
|
+
.magicfeedback-priority-list-arrow-up:hover,
|
|
1632
|
+
.magicfeedback-priority-list-arrow-down:hover {
|
|
1633
|
+
color: var(--mf-primary);
|
|
1634
|
+
transform: scale(1.2);
|
|
1635
|
+
}
|
|
1610
1636
|
}
|
|
1611
1637
|
|
|
1612
1638
|
/* ========================================
|
|
@@ -1713,10 +1739,12 @@ div.magicfeedback-radio {
|
|
|
1713
1739
|
box-shadow: var(--mf-shadow-md);
|
|
1714
1740
|
}
|
|
1715
1741
|
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1742
|
+
@media (hover: hover) {
|
|
1743
|
+
.magicfeedback-submit:hover {
|
|
1744
|
+
background-color: var(--mf-primary-hover);
|
|
1745
|
+
box-shadow: var(--mf-shadow-lg);
|
|
1746
|
+
transform: translateY(-2px);
|
|
1747
|
+
}
|
|
1720
1748
|
}
|
|
1721
1749
|
|
|
1722
1750
|
.magicfeedback-submit:active {
|
|
@@ -1742,9 +1770,11 @@ div.magicfeedback-radio {
|
|
|
1742
1770
|
transition: var(--mf-transition-fast);
|
|
1743
1771
|
}
|
|
1744
1772
|
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1773
|
+
@media (hover: hover) {
|
|
1774
|
+
.magicfeedback-back:hover {
|
|
1775
|
+
border-color: var(--mf-primary);
|
|
1776
|
+
background-color: var(--mf-bg-hover);
|
|
1777
|
+
}
|
|
1748
1778
|
}
|
|
1749
1779
|
|
|
1750
1780
|
/* ========================================
|
|
@@ -1764,8 +1794,10 @@ div.magicfeedback-radio {
|
|
|
1764
1794
|
transition: var(--mf-transition-fast);
|
|
1765
1795
|
}
|
|
1766
1796
|
|
|
1767
|
-
|
|
1768
|
-
|
|
1797
|
+
@media (hover: hover) {
|
|
1798
|
+
.magicfeedback-skip-container:hover {
|
|
1799
|
+
color: var(--mf-primary);
|
|
1800
|
+
}
|
|
1769
1801
|
}
|
|
1770
1802
|
|
|
1771
1803
|
.magicfeedback-skip-container input[type="checkbox"] {
|
|
@@ -1906,9 +1938,11 @@ div.magicfeedback-radio {
|
|
|
1906
1938
|
box-shadow: var(--mf-shadow-sm);
|
|
1907
1939
|
}
|
|
1908
1940
|
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1941
|
+
@media (hover: hover) {
|
|
1942
|
+
.magicfeedback-button:hover {
|
|
1943
|
+
background: var(--mf-bg-hover);
|
|
1944
|
+
border-color: var(--mf-primary);
|
|
1945
|
+
}
|
|
1912
1946
|
}
|
|
1913
1947
|
|
|
1914
1948
|
.magicfeedback-button:active {
|
|
@@ -1922,8 +1956,10 @@ div.magicfeedback-radio {
|
|
|
1922
1956
|
box-shadow: var(--mf-shadow-md);
|
|
1923
1957
|
}
|
|
1924
1958
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1959
|
+
@media (hover: hover) {
|
|
1960
|
+
.magicfeedback-button-primary:hover {
|
|
1961
|
+
background: var(--mf-primary-hover);
|
|
1962
|
+
}
|
|
1927
1963
|
}
|
|
1928
1964
|
|
|
1929
1965
|
.magicfeedback-button-primary:active {
|
|
@@ -1949,8 +1985,10 @@ div.magicfeedback-radio {
|
|
|
1949
1985
|
box-shadow: var(--mf-shadow-md);
|
|
1950
1986
|
}
|
|
1951
1987
|
|
|
1952
|
-
|
|
1953
|
-
|
|
1988
|
+
@media (hover: hover) {
|
|
1989
|
+
.magicfeedback-start-message-button:hover {
|
|
1990
|
+
background: var(--mf-primary-hover);
|
|
1991
|
+
}
|
|
1954
1992
|
}
|
|
1955
1993
|
|
|
1956
1994
|
/* ========================================
|
|
@@ -2086,12 +2124,18 @@ div.magicfeedback-radio {
|
|
|
2086
2124
|
color: #666;
|
|
2087
2125
|
}
|
|
2088
2126
|
|
|
2089
|
-
.magicfeedback-modal-close:hover,
|
|
2090
2127
|
.magicfeedback-modal-close:focus {
|
|
2091
2128
|
color: #000;
|
|
2092
2129
|
outline: none;
|
|
2093
2130
|
}
|
|
2094
2131
|
|
|
2132
|
+
@media (hover: hover) {
|
|
2133
|
+
.magicfeedback-modal-close:hover {
|
|
2134
|
+
color: #000;
|
|
2135
|
+
outline: none;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2095
2139
|
/* ========================================
|
|
2096
2140
|
FILE / IMAGE UPLOAD
|
|
2097
2141
|
======================================== */
|
|
@@ -2135,9 +2179,11 @@ div.magicfeedback-radio {
|
|
|
2135
2179
|
transition: var(--mf-transition);
|
|
2136
2180
|
}
|
|
2137
2181
|
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2182
|
+
@media (hover: hover) {
|
|
2183
|
+
.magicfeedback-upload-dropzone:hover {
|
|
2184
|
+
border-color: var(--mf-primary);
|
|
2185
|
+
background-color: var(--mf-bg-hover);
|
|
2186
|
+
}
|
|
2141
2187
|
}
|
|
2142
2188
|
|
|
2143
2189
|
.magicfeedback-upload-dropzone:focus-within {
|
|
@@ -2275,13 +2321,20 @@ div.magicfeedback-radio {
|
|
|
2275
2321
|
transition: var(--mf-transition-fast);
|
|
2276
2322
|
}
|
|
2277
2323
|
|
|
2278
|
-
.magicfeedback-upload-remove:hover,
|
|
2279
2324
|
.magicfeedback-upload-remove:focus-visible {
|
|
2280
2325
|
color: var(--mf-error);
|
|
2281
2326
|
background-color: var(--mf-primary-light);
|
|
2282
2327
|
outline: none;
|
|
2283
2328
|
}
|
|
2284
2329
|
|
|
2330
|
+
@media (hover: hover) {
|
|
2331
|
+
.magicfeedback-upload-remove:hover {
|
|
2332
|
+
color: var(--mf-error);
|
|
2333
|
+
background-color: var(--mf-primary-light);
|
|
2334
|
+
outline: none;
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2285
2338
|
.magicfeedback-upload-remove svg {
|
|
2286
2339
|
width: 16px;
|
|
2287
2340
|
height: 16px;
|
|
@@ -4376,8 +4429,10 @@ async function safeBodyText(res) {
|
|
|
4376
4429
|
function createFeedbackSink(options) {
|
|
4377
4430
|
let feedbackSessionId;
|
|
4378
4431
|
let firstPostInFlight = null;
|
|
4432
|
+
let generation = 0;
|
|
4379
4433
|
const url = `${options.baseUrl}/sdk/feedback`;
|
|
4380
4434
|
const post = async (body, final, sessionEnd = false) => {
|
|
4435
|
+
const postGeneration = generation;
|
|
4381
4436
|
const json = JSON.stringify(body);
|
|
4382
4437
|
const small = json.length <= KEEPALIVE_MAX_BYTES;
|
|
4383
4438
|
if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
|
|
@@ -4407,9 +4462,25 @@ function createFeedbackSink(options) {
|
|
|
4407
4462
|
);
|
|
4408
4463
|
return;
|
|
4409
4464
|
}
|
|
4410
|
-
if (sessionEnd)
|
|
4465
|
+
if (sessionEnd) {
|
|
4466
|
+
let closedId = body.sessionId;
|
|
4467
|
+
try {
|
|
4468
|
+
const data = await res.json();
|
|
4469
|
+
closedId = data?.sessionId ?? closedId;
|
|
4470
|
+
} catch {
|
|
4471
|
+
}
|
|
4472
|
+
try {
|
|
4473
|
+
if (closedId) options.onSessionClosed?.(closedId);
|
|
4474
|
+
} catch {
|
|
4475
|
+
}
|
|
4476
|
+
return;
|
|
4477
|
+
}
|
|
4411
4478
|
try {
|
|
4412
4479
|
const data = await res.json();
|
|
4480
|
+
if (postGeneration !== generation) {
|
|
4481
|
+
options.log?.("[DeepdotsAnalytics] sessionId of a closed session ignored:", data?.sessionId);
|
|
4482
|
+
return;
|
|
4483
|
+
}
|
|
4413
4484
|
if (data?.sessionId && data.sessionId !== feedbackSessionId) {
|
|
4414
4485
|
feedbackSessionId = data.sessionId;
|
|
4415
4486
|
options.log?.("[DeepdotsAnalytics] feedbackSessionId cached:", feedbackSessionId);
|
|
@@ -4422,6 +4493,7 @@ function createFeedbackSink(options) {
|
|
|
4422
4493
|
if (meta?.resetSession) {
|
|
4423
4494
|
feedbackSessionId = void 0;
|
|
4424
4495
|
firstPostInFlight = null;
|
|
4496
|
+
generation++;
|
|
4425
4497
|
options.onSessionReset?.();
|
|
4426
4498
|
return;
|
|
4427
4499
|
}
|
|
@@ -4436,6 +4508,7 @@ function createFeedbackSink(options) {
|
|
|
4436
4508
|
if (sessionEnd) {
|
|
4437
4509
|
feedbackSessionId = void 0;
|
|
4438
4510
|
firstPostInFlight = null;
|
|
4511
|
+
generation++;
|
|
4439
4512
|
options.onSessionReset?.();
|
|
4440
4513
|
} else if (!feedbackSessionId) {
|
|
4441
4514
|
firstPostInFlight = sent.then(
|
|
@@ -4449,7 +4522,7 @@ function createFeedbackSink(options) {
|
|
|
4449
4522
|
|
|
4450
4523
|
// src/tracking/navigation-observer.ts
|
|
4451
4524
|
var UUID_PREFIX = /^[0-9a-f]{8}-[0-9a-f]{4}-/i;
|
|
4452
|
-
function
|
|
4525
|
+
function screenPath(href) {
|
|
4453
4526
|
let pathAndHash;
|
|
4454
4527
|
try {
|
|
4455
4528
|
const u = new URL(href, "http://deepdots.local");
|
|
@@ -4457,7 +4530,10 @@ function normalizeScreen(href) {
|
|
|
4457
4530
|
} catch {
|
|
4458
4531
|
pathAndHash = href;
|
|
4459
4532
|
}
|
|
4460
|
-
|
|
4533
|
+
return pathAndHash.replace(/\?[^#]*/g, "") || "/";
|
|
4534
|
+
}
|
|
4535
|
+
function normalizeScreen(href) {
|
|
4536
|
+
const pathAndHash = screenPath(href);
|
|
4461
4537
|
const collapsed = pathAndHash.split("/").map((seg) => /^[0-9]+$/.test(seg) || UUID_PREFIX.test(seg) ? ":id" : seg).join("/");
|
|
4462
4538
|
return collapsed || "/";
|
|
4463
4539
|
}
|
|
@@ -4465,6 +4541,7 @@ var NavigationObserver = class {
|
|
|
4465
4541
|
constructor(options = {}) {
|
|
4466
4542
|
this.listeners = /* @__PURE__ */ new Set();
|
|
4467
4543
|
this.currentScreen = null;
|
|
4544
|
+
this.currentPath = null;
|
|
4468
4545
|
this.entryAt = 0;
|
|
4469
4546
|
this.installed = false;
|
|
4470
4547
|
this.domHandlers = [];
|
|
@@ -4476,20 +4553,23 @@ var NavigationObserver = class {
|
|
|
4476
4553
|
/** Fija la pantalla inicial (sin emitir visita; su duración se cierra al salir). */
|
|
4477
4554
|
begin(href) {
|
|
4478
4555
|
this.currentScreen = normalizeScreen(href);
|
|
4556
|
+
this.currentPath = screenPath(href);
|
|
4479
4557
|
this.entryAt = this.now();
|
|
4480
4558
|
}
|
|
4481
4559
|
/** Navega a una pantalla nueva: cierra la anterior (emite visita) y abre la nueva. */
|
|
4482
4560
|
visit(href) {
|
|
4483
|
-
const
|
|
4484
|
-
if (
|
|
4561
|
+
const nextPath = screenPath(href);
|
|
4562
|
+
if (this.currentScreen != null && nextPath === this.currentPath) return;
|
|
4485
4563
|
this.closeCurrent();
|
|
4486
|
-
this.currentScreen =
|
|
4564
|
+
this.currentScreen = normalizeScreen(href);
|
|
4565
|
+
this.currentPath = nextPath;
|
|
4487
4566
|
this.entryAt = this.now();
|
|
4488
4567
|
}
|
|
4489
4568
|
/** Cierra la pantalla actual emitiendo su visita (p. ej. al cerrar la app/pestaña). */
|
|
4490
4569
|
stop() {
|
|
4491
4570
|
this.closeCurrent();
|
|
4492
4571
|
this.currentScreen = null;
|
|
4572
|
+
this.currentPath = null;
|
|
4493
4573
|
}
|
|
4494
4574
|
/**
|
|
4495
4575
|
* Olvida la pantalla actual SIN emitir su visita. Para una sesión que el backend ya dio
|
|
@@ -4498,6 +4578,7 @@ var NavigationObserver = class {
|
|
|
4498
4578
|
*/
|
|
4499
4579
|
discard() {
|
|
4500
4580
|
this.currentScreen = null;
|
|
4581
|
+
this.currentPath = null;
|
|
4501
4582
|
}
|
|
4502
4583
|
/**
|
|
4503
4584
|
* Retoma el seguimiento en la pantalla actual tras un `stop()`, cuando se abre una sesión
|
|
@@ -4513,6 +4594,7 @@ var NavigationObserver = class {
|
|
|
4513
4594
|
const exit = this.now();
|
|
4514
4595
|
const visit = {
|
|
4515
4596
|
screen: this.currentScreen,
|
|
4597
|
+
path: this.currentPath ?? this.currentScreen,
|
|
4516
4598
|
entry: this.entryAt,
|
|
4517
4599
|
exit,
|
|
4518
4600
|
durationSeconds: Math.max(0, Math.round((exit - this.entryAt) / 1e3))
|
|
@@ -5077,6 +5159,8 @@ var DeepdotsPopups = class {
|
|
|
5077
5159
|
this.analytics = null;
|
|
5078
5160
|
/** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */
|
|
5079
5161
|
this.analyticsFeedbackSessionId = void 0;
|
|
5162
|
+
/** `init({ onFeedbackSession })`: avisa al host al abrir y cerrar cada registro. */
|
|
5163
|
+
this.onFeedbackSession = void 0;
|
|
5080
5164
|
/** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
|
|
5081
5165
|
this.analyticsFlushTimer = void 0;
|
|
5082
5166
|
/** Crash & error reporting (#14–17). Null hasta init(). */
|
|
@@ -5134,6 +5218,7 @@ var DeepdotsPopups = class {
|
|
|
5134
5218
|
this.renderChrome = config.renderChrome ?? true;
|
|
5135
5219
|
this.showProgressBar = config.showProgressBar;
|
|
5136
5220
|
this.surveyCss = config.surveyCss;
|
|
5221
|
+
this.onFeedbackSession = config.onFeedbackSession;
|
|
5137
5222
|
this.config = {
|
|
5138
5223
|
apiKey: config.apiKey || void 0,
|
|
5139
5224
|
debug: config.debug || false,
|
|
@@ -5167,7 +5252,9 @@ var DeepdotsPopups = class {
|
|
|
5167
5252
|
log: (...a) => this.log(...a),
|
|
5168
5253
|
onSessionId: (id) => {
|
|
5169
5254
|
this.analyticsFeedbackSessionId = id;
|
|
5255
|
+
this.notifyFeedbackSession(id, "open");
|
|
5170
5256
|
},
|
|
5257
|
+
onSessionClosed: (id) => this.notifyFeedbackSession(id, "closed"),
|
|
5171
5258
|
onSessionReset: () => {
|
|
5172
5259
|
this.analyticsFeedbackSessionId = void 0;
|
|
5173
5260
|
}
|
|
@@ -5205,7 +5292,9 @@ var DeepdotsPopups = class {
|
|
|
5205
5292
|
this.geoWanted = this.geoEnabled && !!config.analytics;
|
|
5206
5293
|
this.resolveGeo();
|
|
5207
5294
|
this.navObserver = new NavigationObserver();
|
|
5208
|
-
this.navObserver.onVisit(
|
|
5295
|
+
this.navObserver.onVisit(
|
|
5296
|
+
(v) => this.track("deepdots_page_view", { screen: v.screen, path: v.path, duration_seconds: v.durationSeconds })
|
|
5297
|
+
);
|
|
5209
5298
|
this.navObserver.install();
|
|
5210
5299
|
this.engagement = new EngagementTracker();
|
|
5211
5300
|
this.setupAnalyticsFlush();
|
|
@@ -5240,6 +5329,23 @@ var DeepdotsPopups = class {
|
|
|
5240
5329
|
getSessionId() {
|
|
5241
5330
|
return this.tracking?.getSessionId() ?? null;
|
|
5242
5331
|
}
|
|
5332
|
+
/**
|
|
5333
|
+
* Id del registro de analytics abierto: el `sessionId` que devolvió `POST /sdk/feedback`,
|
|
5334
|
+
* que la API guarda como `sdkSessionId` en el Feedback en que se convierte la sesión. No es
|
|
5335
|
+
* `getSessionId()`. Null hasta que el backend acepta el primer lote y de nuevo al cerrar la
|
|
5336
|
+
* sesión: para saber también qué registro se cerró, usa `init({ onFeedbackSession })`.
|
|
5337
|
+
*/
|
|
5338
|
+
getFeedbackSessionId() {
|
|
5339
|
+
return this.analyticsFeedbackSessionId ?? null;
|
|
5340
|
+
}
|
|
5341
|
+
/** Avisa al host del registro abierto o cerrado; un fallo del host no corta el envío. */
|
|
5342
|
+
notifyFeedbackSession(sessionId, status) {
|
|
5343
|
+
try {
|
|
5344
|
+
this.onFeedbackSession?.({ sessionId, status });
|
|
5345
|
+
} catch (error) {
|
|
5346
|
+
this.log("analytics \xB7 onFeedbackSession threw:", error);
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5243
5349
|
/** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */
|
|
5244
5350
|
setTrackingEnabled(enabled) {
|
|
5245
5351
|
if (!enabled) this.closeSession("tracking_disabled");
|