@metricinsights/pp-dev 1.5.0-beta.15 → 1.5.0-beta.17
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 +3 -2
- package/dist/README.md +3 -2
- package/dist/cjs/auth.provider-CejYwjcG.js +2 -0
- package/dist/cjs/auth.provider-CejYwjcG.js.map +1 -0
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/index-Cev20Ede.js +2 -0
- package/dist/cjs/index-Cev20Ede.js.map +1 -0
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/{migrate-C2YzTFrM.js → migrate-MxrkBSEh.js} +2 -2
- package/dist/cjs/{migrate-C2YzTFrM.js.map → migrate-MxrkBSEh.js.map} +1 -1
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/plugin.js +1 -1
- package/dist/cjs/plugin.js.map +1 -1
- package/dist/cjs/variables-editor-DW2WJZHw.js +106 -0
- package/dist/cjs/variables-editor-DW2WJZHw.js.map +1 -0
- package/dist/cjs/version-manifest-DjLJQWWa.js +2 -0
- package/dist/cjs/version-manifest-DjLJQWWa.js.map +1 -0
- package/dist/cjs/version-plugin-sUHrN2Jo.js +2 -0
- package/dist/cjs/version-plugin-sUHrN2Jo.js.map +1 -0
- package/dist/client/client.js +160 -61
- package/dist/client/client.js.map +1 -1
- package/dist/esm/auth.provider-BijrVDxp.js +2 -0
- package/dist/esm/auth.provider-BijrVDxp.js.map +1 -0
- package/dist/esm/cli.js +1 -1
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/index-CL8_j3ER.js +2 -0
- package/dist/esm/index-CL8_j3ER.js.map +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/{migrate-SXsPvdfV.js → migrate-B0hgQCl_.js} +2 -2
- package/dist/esm/{migrate-SXsPvdfV.js.map → migrate-B0hgQCl_.js.map} +1 -1
- package/dist/esm/package.json +1 -1
- package/dist/esm/plugin.js +1 -1
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/{variables-editor-CWbcJAdI.js → variables-editor-w314pia8.js} +3 -3
- package/dist/esm/variables-editor-w314pia8.js.map +1 -0
- package/dist/esm/version-manifest-1KdgXTW2.js +2 -0
- package/dist/esm/version-manifest-1KdgXTW2.js.map +1 -0
- package/dist/esm/version-plugin-BExwn1e0.js +2 -0
- package/dist/esm/version-plugin-BExwn1e0.js.map +1 -0
- package/dist/types/index.d.ts +7 -6
- package/package.json +1 -1
- package/dist/cjs/auth.provider-DaLYQg0T.js +0 -2
- package/dist/cjs/auth.provider-DaLYQg0T.js.map +0 -1
- package/dist/cjs/index-BcjhRmxz.js +0 -2
- package/dist/cjs/index-BcjhRmxz.js.map +0 -1
- package/dist/cjs/variables-editor-BHiWb4-B.js +0 -106
- package/dist/cjs/variables-editor-BHiWb4-B.js.map +0 -1
- package/dist/cjs/version-manifest-Dq5x7D15.js +0 -2
- package/dist/cjs/version-manifest-Dq5x7D15.js.map +0 -1
- package/dist/cjs/version-plugin-C-8273qp.js +0 -2
- package/dist/cjs/version-plugin-C-8273qp.js.map +0 -1
- package/dist/esm/auth.provider-CaBF6-T3.js +0 -2
- package/dist/esm/auth.provider-CaBF6-T3.js.map +0 -1
- package/dist/esm/index-DewOudJ0.js +0 -2
- package/dist/esm/index-DewOudJ0.js.map +0 -1
- package/dist/esm/variables-editor-CWbcJAdI.js.map +0 -1
- package/dist/esm/version-manifest-C1RlO_jm.js +0 -2
- package/dist/esm/version-manifest-C1RlO_jm.js.map +0 -1
- package/dist/esm/version-plugin-aWKAiJZe.js +0 -2
- package/dist/esm/version-plugin-aWKAiJZe.js.map +0 -1
package/dist/client/client.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* (
|
|
4
|
-
*
|
|
5
|
-
* custom-event wire shape Vite uses: `{ type: 'custom', event, data }`.
|
|
6
|
-
*
|
|
7
|
-
* Used only as a fallback: when `import.meta.hot` exists (Vite), that is used
|
|
8
|
-
* instead and this code never runs.
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* WebSocket path the pp-dev Next.js server listens on for dev-panel messages.
|
|
12
|
-
* Kept in sync with `PP_DEV_HMR_WS_PATH` in `src/constants.ts` (server side).
|
|
2
|
+
* The WebSocket messages between the dev server and the dev panel, for both sides: the server
|
|
3
|
+
* (`src/lib/client.service.ts`, `src/lib/pp-ws-server.ts`) and the panel (`src/client/**`) import
|
|
4
|
+
* them from here, so they can't drift apart. No Node or DOM imports: the panel bundle includes it.
|
|
13
5
|
*/
|
|
6
|
+
/** WebSocket path the `pp-dev next` server listens on for dev-panel messages. */
|
|
14
7
|
const PP_DEV_HMR_WS_PATH = '/@pp-dev-hmr';
|
|
8
|
+
/** `template:sync:response` code for a sync requested while another one runs. */
|
|
9
|
+
const SYNC_IN_PROGRESS_CODE = 'SYNC_IN_PROGRESS';
|
|
10
|
+
|
|
15
11
|
const MAX_OUTBOX_SIZE = 50;
|
|
16
12
|
function resolveWebSocketUrl() {
|
|
17
13
|
const { protocol, host } = window.location;
|
|
@@ -23,6 +19,8 @@ function createPPDevHotContext() {
|
|
|
23
19
|
/** Outgoing messages queued while the socket is not OPEN; flushed on connect. */
|
|
24
20
|
const outbox = [];
|
|
25
21
|
let socket = null;
|
|
22
|
+
/** The connection state last reported to the panel, so each change is reported once. */
|
|
23
|
+
let reported = null;
|
|
26
24
|
let reconnectTimer = null;
|
|
27
25
|
let reconnectDelay = 1_000;
|
|
28
26
|
const MAX_RECONNECT_DELAY = 10_000;
|
|
@@ -58,10 +56,9 @@ function createPPDevHotContext() {
|
|
|
58
56
|
scheduleReconnect();
|
|
59
57
|
return;
|
|
60
58
|
}
|
|
61
|
-
let opened = false;
|
|
62
59
|
socket.addEventListener('open', () => {
|
|
63
|
-
opened = true;
|
|
64
60
|
reconnectDelay = 1_000;
|
|
61
|
+
reported = 'connected';
|
|
65
62
|
dispatch('vite:ws:connect', {});
|
|
66
63
|
flushOutbox();
|
|
67
64
|
});
|
|
@@ -80,9 +77,11 @@ function createPPDevHotContext() {
|
|
|
80
77
|
socket.addEventListener('close', () => {
|
|
81
78
|
socket = null;
|
|
82
79
|
// Same events as Vite's own HMR client, so the panel can tell a lost connection apart
|
|
83
|
-
// (e.g. a sync whose response will never arrive
|
|
84
|
-
// failed reconnect attempt is not a new
|
|
85
|
-
|
|
80
|
+
// (e.g. a sync whose response will never arrive, or a click that can't be sent). Once per
|
|
81
|
+
// loss: each failed reconnect attempt is not a new one, but a first connection that never
|
|
82
|
+
// opened is.
|
|
83
|
+
if (reported !== 'disconnected') {
|
|
84
|
+
reported = 'disconnected';
|
|
86
85
|
dispatch('vite:ws:disconnect', {});
|
|
87
86
|
}
|
|
88
87
|
scheduleReconnect();
|
|
@@ -128,6 +127,9 @@ function createPPDevHotContext() {
|
|
|
128
127
|
outbox.push(payload);
|
|
129
128
|
}
|
|
130
129
|
},
|
|
130
|
+
// Not part of Vite's API: lets the panel tell an open connection from one still connecting, whose
|
|
131
|
+
// queued messages would go out whenever it opens (see `src/client/sync-button.ts`).
|
|
132
|
+
isConnected: () => socket?.readyState === WebSocket.OPEN,
|
|
131
133
|
// Unused by the dev-panel client; provided as no-ops to satisfy the shape.
|
|
132
134
|
accept: noop,
|
|
133
135
|
acceptExports: noop,
|
|
@@ -653,6 +655,37 @@ function initPanelSettings($panel, controller, hooks) {
|
|
|
653
655
|
});
|
|
654
656
|
}
|
|
655
657
|
|
|
658
|
+
/**
|
|
659
|
+
* The rules for a saved user's name, which is also its host (`<name>.localhost`), for both sides:
|
|
660
|
+
* the server checks them (`src/lib/user-registry.ts`) and the dev panel's Users popover
|
|
661
|
+
* (`src/client/users-panel.ts`) checks them as you type. No Node or DOM imports: the panel bundle
|
|
662
|
+
* includes it.
|
|
663
|
+
*/
|
|
664
|
+
/** Longest user name; well under the 63 characters a DNS label allows. */
|
|
665
|
+
const MAX_USER_NAME_LENGTH = 32;
|
|
666
|
+
/**
|
|
667
|
+
* A DNS label: lowercase latin letters, digits and `-`, starting with a letter and not ending with
|
|
668
|
+
* `-`. Also used as an input's `pattern`, which browsers compile with the `v` flag: there an
|
|
669
|
+
* unescaped `-` in a class is a syntax error, and the whole pattern was silently ignored.
|
|
670
|
+
*/
|
|
671
|
+
const USER_NAME_PATTERN = String.raw `[a-z](?:[a-z0-9\-]*[a-z0-9])?`;
|
|
672
|
+
const USER_NAME_REGEXP = new RegExp(`^(?:${USER_NAME_PATTERN})$`);
|
|
673
|
+
/** The suffix of a user's host: `viewer` works on `viewer.localhost`. */
|
|
674
|
+
const USER_HOST_SUFFIX = '.localhost';
|
|
675
|
+
/** Why `name` can't be a user name, or `undefined` if it can. */
|
|
676
|
+
function getUserNameError(name) {
|
|
677
|
+
if (!name) {
|
|
678
|
+
return 'The name is required';
|
|
679
|
+
}
|
|
680
|
+
if (name.length > MAX_USER_NAME_LENGTH) {
|
|
681
|
+
return `The name must be at most ${MAX_USER_NAME_LENGTH} characters long`;
|
|
682
|
+
}
|
|
683
|
+
if (!USER_NAME_REGEXP.test(name)) {
|
|
684
|
+
return 'The name may contain only lowercase latin letters, digits and "-", must start with a letter and must not end with "-"';
|
|
685
|
+
}
|
|
686
|
+
return undefined;
|
|
687
|
+
}
|
|
688
|
+
|
|
656
689
|
/**
|
|
657
690
|
* The dev panel's "Users" popover (PP-4597): the user of this tab, the saved users (open as, log
|
|
658
691
|
* in, make default, remove) and adding a user by API token or by username + password. Each user
|
|
@@ -660,7 +693,8 @@ function initPanelSettings($panel, controller, hooks) {
|
|
|
660
693
|
*/
|
|
661
694
|
const USERS_API = '/@api/users';
|
|
662
695
|
const REFRESH_INTERVAL = 3000;
|
|
663
|
-
|
|
696
|
+
// The server's name rules (src/shared/user-names.ts), not a copy of them.
|
|
697
|
+
const USER_HOST_REGEXP = new RegExp(`^(${USER_NAME_PATTERN})${USER_HOST_SUFFIX.replace('.', '\\.')}$`);
|
|
664
698
|
/** Marks the user this tab works as, apart from the radio, which picks localhost's user. */
|
|
665
699
|
const THIS_TAB_BADGE = '<span class="pp-dev-info__users-badge">this tab</span>';
|
|
666
700
|
const STATE_LABELS = {
|
|
@@ -832,7 +866,7 @@ function initUsersPanel($panel, hooks) {
|
|
|
832
866
|
const name = escapeHtml(user.name);
|
|
833
867
|
if (user.kind === 'token' && user.name === editing) {
|
|
834
868
|
return `<form class="pp-dev-info__users-row pp-dev-info__users-edit" data-user="${name}">
|
|
835
|
-
<input class="pp-dev-info__users-input pp-dev-info__users-edit-name" value="${name}" maxlength="
|
|
869
|
+
<input class="pp-dev-info__users-input pp-dev-info__users-edit-name" value="${name}" maxlength="${MAX_USER_NAME_LENGTH}" pattern="${USER_NAME_PATTERN}"
|
|
836
870
|
autocomplete="off" spellcheck="false" aria-label="Name" data-focus-key="${name}:edit-name"
|
|
837
871
|
title="Lowercase latin letters, digits and -, starting with a letter. The user opens on <name>.localhost." />
|
|
838
872
|
<input class="pp-dev-info__users-input pp-dev-info__users-edit-token" type="password" autocomplete="off"
|
|
@@ -877,6 +911,9 @@ function initUsersPanel($panel, hooks) {
|
|
|
877
911
|
$open.addEventListener('click', (ev) => {
|
|
878
912
|
ev.preventDefault();
|
|
879
913
|
const target = $open.dataset.open;
|
|
914
|
+
if (!target) {
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
880
917
|
window.open($open.dataset.raw ? target : samePageOn(target), '_blank');
|
|
881
918
|
});
|
|
882
919
|
});
|
|
@@ -889,7 +926,7 @@ function initUsersPanel($panel, hooks) {
|
|
|
889
926
|
$list.querySelectorAll('[data-edit]').forEach(($edit) => {
|
|
890
927
|
$edit.addEventListener('click', (ev) => {
|
|
891
928
|
ev.preventDefault();
|
|
892
|
-
startEditing($edit.dataset.edit);
|
|
929
|
+
startEditing($edit.dataset.edit ?? null);
|
|
893
930
|
});
|
|
894
931
|
});
|
|
895
932
|
$list.querySelector('[data-cancel-edit]')?.addEventListener('click', (ev) => {
|
|
@@ -901,8 +938,11 @@ function initUsersPanel($panel, hooks) {
|
|
|
901
938
|
ev.preventDefault();
|
|
902
939
|
const name = editing;
|
|
903
940
|
const $form = ev.currentTarget;
|
|
904
|
-
|
|
905
|
-
|
|
941
|
+
if (!name) {
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
const newName = $form.querySelector('.pp-dev-info__users-edit-name')?.value.trim() ?? '';
|
|
945
|
+
const token = $form.querySelector('.pp-dev-info__users-edit-token')?.value.trim() ?? '';
|
|
906
946
|
const response = await request('PATCH', `${USERS_API}/${encodeURIComponent(name)}`, {
|
|
907
947
|
name: newName,
|
|
908
948
|
...(token ? { token } : {}),
|
|
@@ -923,7 +963,8 @@ function initUsersPanel($panel, hooks) {
|
|
|
923
963
|
$list.querySelectorAll('[data-remove]').forEach(($remove) => {
|
|
924
964
|
$remove.addEventListener('click', async (ev) => {
|
|
925
965
|
ev.preventDefault();
|
|
926
|
-
|
|
966
|
+
const name = $remove.dataset.remove;
|
|
967
|
+
if (name && (await request('DELETE', `${USERS_API}/${encodeURIComponent(name)}`))) {
|
|
927
968
|
await refresh();
|
|
928
969
|
}
|
|
929
970
|
});
|
|
@@ -1004,20 +1045,34 @@ function initUsersPanel($panel, hooks) {
|
|
|
1004
1045
|
const $name = $popover.querySelector('.pp-dev-info__users-name-input');
|
|
1005
1046
|
const $token = $popover.querySelector('.pp-dev-info__users-token');
|
|
1006
1047
|
const $submit = $popover.querySelector('.pp-dev-info__users-submit');
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1048
|
+
if (!$name || !$token || !$submit) {
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
const name = $name.value.trim();
|
|
1052
|
+
const nameError = getUserNameError(name);
|
|
1053
|
+
if (nameError) {
|
|
1054
|
+
setError(nameError);
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
const body = addKind === 'token' ? { name, token: $token.value } : { name, kind: 'session' };
|
|
1058
|
+
// A session user logs in on a tab of its own. Opened here, while the click still counts as the
|
|
1059
|
+
// user's: opened after the request, Safari and Chrome blocked it as a popup.
|
|
1060
|
+
const loginTab = addKind === 'session' ? window.open('', '_blank') : null;
|
|
1010
1061
|
$submit.disabled = true;
|
|
1011
1062
|
const response = await request('POST', USERS_API, body);
|
|
1012
1063
|
$submit.disabled = false;
|
|
1013
1064
|
if (!response) {
|
|
1065
|
+
loginTab?.close();
|
|
1014
1066
|
return;
|
|
1015
1067
|
}
|
|
1016
1068
|
const { user } = (await response.json());
|
|
1017
1069
|
$name.value = '';
|
|
1018
1070
|
$token.value = '';
|
|
1019
|
-
if (user.kind === 'session' && user.loginUrl) {
|
|
1020
|
-
|
|
1071
|
+
if (user.kind === 'session' && user.loginUrl && loginTab) {
|
|
1072
|
+
loginTab.location.href = user.loginUrl;
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
loginTab?.close();
|
|
1021
1076
|
}
|
|
1022
1077
|
await refresh();
|
|
1023
1078
|
};
|
|
@@ -1042,8 +1097,8 @@ function initUsersPanel($panel, hooks) {
|
|
|
1042
1097
|
<button type="button" class="pp-dev-info__theme-btn pp-dev-info__users-kind" data-kind="token">API token</button>
|
|
1043
1098
|
<button type="button" class="pp-dev-info__theme-btn pp-dev-info__users-kind" data-kind="session">Username + password</button>
|
|
1044
1099
|
</div>
|
|
1045
|
-
<input class="pp-dev-info__users-input pp-dev-info__users-name-input" placeholder="Name, e.g. viewer" maxlength="
|
|
1046
|
-
autocomplete="off" spellcheck="false" pattern="
|
|
1100
|
+
<input class="pp-dev-info__users-input pp-dev-info__users-name-input" placeholder="Name, e.g. viewer" maxlength="${MAX_USER_NAME_LENGTH}"
|
|
1101
|
+
autocomplete="off" spellcheck="false" pattern="${USER_NAME_PATTERN}"
|
|
1047
1102
|
title="Lowercase latin letters, digits and -, starting with a letter. The user opens on <name>.localhost." />
|
|
1048
1103
|
<input class="pp-dev-info__users-input pp-dev-info__users-token" type="password" placeholder="API token" autocomplete="off" />
|
|
1049
1104
|
<button type="submit" class="pp-dev-info__vars-btn pp-dev-info__users-submit">Add user</button>
|
|
@@ -1059,7 +1114,7 @@ function initUsersPanel($panel, hooks) {
|
|
|
1059
1114
|
renderKind();
|
|
1060
1115
|
});
|
|
1061
1116
|
});
|
|
1062
|
-
$popover.querySelector('.pp-dev-info__users-add')
|
|
1117
|
+
$popover.querySelector('.pp-dev-info__users-add')?.addEventListener('submit', (ev) => {
|
|
1063
1118
|
ev.preventDefault();
|
|
1064
1119
|
submit();
|
|
1065
1120
|
});
|
|
@@ -1148,12 +1203,50 @@ function createPopupElement(opts) {
|
|
|
1148
1203
|
$popup.appendChild($popupContent);
|
|
1149
1204
|
return $popup;
|
|
1150
1205
|
}
|
|
1151
|
-
|
|
1152
1206
|
/**
|
|
1153
|
-
* `
|
|
1154
|
-
*
|
|
1207
|
+
* Calls `onElapsed` once the page has been visible for `duration` ms in all (time in a hidden tab
|
|
1208
|
+
* doesn't count). Returns a function that stops waiting, which also removes the page's
|
|
1209
|
+
* `visibilitychange` listener: one used to stay behind for every popup ever shown.
|
|
1155
1210
|
*/
|
|
1156
|
-
|
|
1211
|
+
function startAutoDismiss(duration, onElapsed) {
|
|
1212
|
+
let remaining = duration;
|
|
1213
|
+
let lastUpdate = Date.now();
|
|
1214
|
+
let visible = true;
|
|
1215
|
+
let frame;
|
|
1216
|
+
const tick = () => {
|
|
1217
|
+
frame = undefined;
|
|
1218
|
+
if (!visible) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
const now = Date.now();
|
|
1222
|
+
remaining -= now - lastUpdate;
|
|
1223
|
+
lastUpdate = now;
|
|
1224
|
+
if (remaining <= 0) {
|
|
1225
|
+
stop();
|
|
1226
|
+
onElapsed();
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
frame = requestAnimationFrame(tick);
|
|
1230
|
+
};
|
|
1231
|
+
const onVisibilityChange = () => {
|
|
1232
|
+
visible = !document.hidden;
|
|
1233
|
+
if (visible) {
|
|
1234
|
+
lastUpdate = Date.now();
|
|
1235
|
+
frame ??= requestAnimationFrame(tick);
|
|
1236
|
+
}
|
|
1237
|
+
};
|
|
1238
|
+
const stop = () => {
|
|
1239
|
+
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
1240
|
+
if (frame !== undefined) {
|
|
1241
|
+
cancelAnimationFrame(frame);
|
|
1242
|
+
frame = undefined;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
1246
|
+
frame = requestAnimationFrame(tick);
|
|
1247
|
+
return stop;
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1157
1250
|
const TITLE_AVAILABLE = 'Sync template';
|
|
1158
1251
|
const TITLE_UNAVAILABLE = 'Sync is unavailable on this instance';
|
|
1159
1252
|
/**
|
|
@@ -1168,6 +1261,11 @@ function initSyncButton(hot, $button, deps) {
|
|
|
1168
1261
|
const reload = deps.reload ?? (() => window.location.reload());
|
|
1169
1262
|
let available = !$button.disabled && !$button.classList.contains('disabled');
|
|
1170
1263
|
let syncing = false;
|
|
1264
|
+
// Vite's connection is taken as open until reported lost: it may have opened before these handlers
|
|
1265
|
+
// were registered, and its client reloads the page once a lost one is back. The `pp-dev next`
|
|
1266
|
+
// transport says itself whether its socket is open, also while the first one is still connecting.
|
|
1267
|
+
let connected = true;
|
|
1268
|
+
const isConnected = () => connected && (hot.isConnected?.() ?? true);
|
|
1171
1269
|
const applyState = () => {
|
|
1172
1270
|
$button.disabled = syncing || !available;
|
|
1173
1271
|
$button.classList.toggle('syncing', syncing);
|
|
@@ -1202,7 +1300,10 @@ function initSyncButton(hot, $button, deps) {
|
|
|
1202
1300
|
confirmText: payload.confirmText,
|
|
1203
1301
|
cancelText: payload.cancelText,
|
|
1204
1302
|
});
|
|
1205
|
-
hot.send('template:sync:action-response', {
|
|
1303
|
+
hot.send('template:sync:action-response', {
|
|
1304
|
+
requestId: payload.requestId,
|
|
1305
|
+
approved,
|
|
1306
|
+
});
|
|
1206
1307
|
});
|
|
1207
1308
|
hot.on('template:sync:response', (payload) => {
|
|
1208
1309
|
deps.closeAllConfirmModals();
|
|
@@ -1235,7 +1336,11 @@ function initSyncButton(hot, $button, deps) {
|
|
|
1235
1336
|
});
|
|
1236
1337
|
// A response sent to a socket that has closed never arrives. Without this, the button would
|
|
1237
1338
|
// spin, disabled, until the page is reloaded.
|
|
1339
|
+
hot.on('vite:ws:connect', () => {
|
|
1340
|
+
connected = true;
|
|
1341
|
+
});
|
|
1238
1342
|
hot.on('vite:ws:disconnect', () => {
|
|
1343
|
+
connected = false;
|
|
1239
1344
|
if (!syncing) {
|
|
1240
1345
|
return;
|
|
1241
1346
|
}
|
|
@@ -1252,6 +1357,16 @@ function initSyncButton(hot, $button, deps) {
|
|
|
1252
1357
|
if (syncing || !available) {
|
|
1253
1358
|
return;
|
|
1254
1359
|
}
|
|
1360
|
+
// Sent now, the request would wait for a reconnect with the button spinning, and start a sync
|
|
1361
|
+
// whenever that happens. Say so instead, and let the click be repeated once connected.
|
|
1362
|
+
if (!isConnected()) {
|
|
1363
|
+
deps.infoPopup({
|
|
1364
|
+
title: 'No connection',
|
|
1365
|
+
content: 'The dev server is not connected. Check that it is running, then try again.',
|
|
1366
|
+
type: 'warning',
|
|
1367
|
+
});
|
|
1368
|
+
return;
|
|
1369
|
+
}
|
|
1255
1370
|
syncing = true;
|
|
1256
1371
|
applyState();
|
|
1257
1372
|
hot.send('template:sync', {});
|
|
@@ -1329,7 +1444,16 @@ function animatePopup($popup, type) {
|
|
|
1329
1444
|
function infoPopup(opts) {
|
|
1330
1445
|
const $popup = createPopupElement(opts);
|
|
1331
1446
|
const $closeButton = $popup.querySelector('.pp-dev-info__popup-title-close');
|
|
1447
|
+
let removed = false;
|
|
1448
|
+
let stopAutoDismiss;
|
|
1449
|
+
// Once only: closing it by hand used to leave its timer running, which removed it again and
|
|
1450
|
+
// counted it out of `activePopups` twice.
|
|
1332
1451
|
const removePopup = async () => {
|
|
1452
|
+
if (removed) {
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
removed = true;
|
|
1456
|
+
stopAutoDismiss?.();
|
|
1333
1457
|
await animatePopup($popup, 'exit');
|
|
1334
1458
|
$popup.remove();
|
|
1335
1459
|
updatePopupPositions();
|
|
@@ -1342,32 +1466,7 @@ function infoPopup(opts) {
|
|
|
1342
1466
|
animatePopup($popup, 'enter');
|
|
1343
1467
|
const duration = opts.duration ?? 10000;
|
|
1344
1468
|
if (duration > 0) {
|
|
1345
|
-
|
|
1346
|
-
let lastUpdate = Date.now();
|
|
1347
|
-
let isVisible = true;
|
|
1348
|
-
const scheduleDismiss = () => {
|
|
1349
|
-
if (!isVisible) {
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
const now = Date.now();
|
|
1353
|
-
const elapsed = now - lastUpdate;
|
|
1354
|
-
remainingTime -= elapsed;
|
|
1355
|
-
lastUpdate = now;
|
|
1356
|
-
if (remainingTime <= 0) {
|
|
1357
|
-
removePopup();
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
requestAnimationFrame(scheduleDismiss);
|
|
1361
|
-
};
|
|
1362
|
-
// Handle visibility change
|
|
1363
|
-
document.addEventListener('visibilitychange', () => {
|
|
1364
|
-
isVisible = !document.hidden;
|
|
1365
|
-
if (isVisible) {
|
|
1366
|
-
lastUpdate = Date.now();
|
|
1367
|
-
requestAnimationFrame(scheduleDismiss);
|
|
1368
|
-
}
|
|
1369
|
-
});
|
|
1370
|
-
requestAnimationFrame(scheduleDismiss);
|
|
1469
|
+
stopAutoDismiss = startAutoDismiss(duration, removePopup);
|
|
1371
1470
|
}
|
|
1372
1471
|
return { close: removePopup };
|
|
1373
1472
|
}
|