@jsenv/plugin-supervisor 1.8.7 → 1.8.9

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.
@@ -921,14 +921,16 @@ window.__supervisor__ = (() => {
921
921
  } : () => {};
922
922
  }
923
923
  const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay";
924
+ const JSENV_WARNING_OVERLAY_TAGNAME = "jsenv-warning-overlay";
924
925
  let displayJsenvErrorOverlay;
926
+ let displayJsenvWarningOverlay;
925
927
  {
926
928
  displayJsenvErrorOverlay = params => {
927
929
  if (logs) {
928
930
  console.log("display jsenv error overlay", params);
929
931
  }
930
932
  let jsenvErrorOverlay = new JsenvErrorOverlay(params);
931
- document.querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME).forEach(node => {
933
+ document.querySelectorAll("".concat(JSENV_ERROR_OVERLAY_TAGNAME, ",").concat(JSENV_WARNING_OVERLAY_TAGNAME)).forEach(node => {
932
934
  node.parentNode.removeChild(node);
933
935
  });
934
936
  const appendIntoRespectingLineBreaksAndIndentation = (node, parentNode, {
@@ -967,7 +969,8 @@ window.__supervisor__ = (() => {
967
969
  const tips = [];
968
970
  tips.push("Click outside to close.");
969
971
  const tip = tips.join("\n <br />\n ");
970
- root.innerHTML = "\n<style>\n ".concat(overlayCSS, "\n</style>\n<div class=\"backdrop\"></div>\n<div class=\"overlay\" data-theme=\"dark\">\n <h1 class=\"title\">\n An error occured\n </h1>\n <pre class=\"text\"></pre>\n <div class=\"tip\">\n ").concat(tip, "\n </div>\n</div>");
972
+ root.innerHTML = "\n<style>\n ".concat(overlayCSS, "\n</style>\n<div class=\"backdrop\"></div>\n<div class=\"overlay\" data-theme=\"dark\" data-level=\"error\">\n <button class=\"copy\" type=\"button\">Copy</button>\n <h1 class=\"title\">\n An error occured\n </h1>\n <pre class=\"text\"></pre>\n <div class=\"tip\">\n ").concat(tip, "\n </div>\n</div>");
973
+ enableCopyButton(root);
971
974
  root.querySelector(".backdrop").onclick = () => {
972
975
  if (!this.parentNode) {
973
976
  // not in document anymore
@@ -1057,6 +1060,77 @@ window.__supervisor__ = (() => {
1057
1060
  }
1058
1061
  }
1059
1062
  }
1063
+ /*
1064
+ * Same overlay, lower stakes: something the developer should act on but
1065
+ * that does not prevent the page from running. An error always wins over
1066
+ * a warning, never the other way around.
1067
+ */
1068
+ class JsenvWarningOverlay extends HTMLElement {
1069
+ constructor({
1070
+ title,
1071
+ text,
1072
+ details = []
1073
+ }) {
1074
+ super();
1075
+ const root = this.attachShadow({
1076
+ mode: "open"
1077
+ });
1078
+ root.innerHTML = "\n<style>\n ".concat(overlayCSS, "\n</style>\n<div class=\"backdrop\"></div>\n<div class=\"overlay\" data-theme=\"dark\" data-level=\"warning\">\n <button class=\"copy\" type=\"button\">Copy</button>\n <h1 class=\"title\">\n ").concat(escapeHtml(title), "\n </h1>\n <pre class=\"text\">").concat(escapeHtml(text), "</pre>\n <div class=\"details\">\n ").concat(details.map(detail => "<p>".concat(escapeHtml(detail), "</p>")).join("\n "), "\n </div>\n <div class=\"tip\">\n Click outside to close.\n </div>\n</div>");
1079
+ enableCopyButton(root);
1080
+ root.querySelector(".backdrop").onclick = () => {
1081
+ if (!this.parentNode) {
1082
+ // not in document anymore
1083
+ return;
1084
+ }
1085
+ root.querySelector(".backdrop").onclick = null;
1086
+ this.parentNode.removeChild(this);
1087
+ };
1088
+ }
1089
+ }
1090
+
1091
+ // the text is meant to be pasted as-is in a chat: a title line, the
1092
+ // location, then the raw text inside a fenced code block
1093
+ const enableCopyButton = root => {
1094
+ const button = root.querySelector(".copy");
1095
+ const overlay = root.querySelector(".overlay");
1096
+ let resetTimeout;
1097
+ button.onclick = async () => {
1098
+ const detailsNode = overlay.querySelector(".details");
1099
+ const parts = ["[jsenv] ".concat(overlay.querySelector(".title").textContent.trim()), "Page: ".concat(window.location.href), "```", overlay.querySelector(".text").textContent.trim(), "```"];
1100
+ if (detailsNode) {
1101
+ for (const paragraph of detailsNode.querySelectorAll("p")) {
1102
+ parts.push(paragraph.textContent.trim());
1103
+ }
1104
+ }
1105
+ const copied = await writeTextToClipboard(parts.join("\n"));
1106
+ button.textContent = copied ? "Copied" : "Copy failed";
1107
+ clearTimeout(resetTimeout);
1108
+ resetTimeout = setTimeout(() => {
1109
+ button.textContent = "Copy";
1110
+ }, 2000);
1111
+ };
1112
+ };
1113
+ const writeTextToClipboard = async text => {
1114
+ try {
1115
+ await window.navigator.clipboard.writeText(text);
1116
+ return true;
1117
+ } catch (_unused4) {
1118
+ // the clipboard API needs a secure context, fallback for plain http origins
1119
+ }
1120
+ try {
1121
+ const textarea = document.createElement("textarea");
1122
+ textarea.value = text;
1123
+ textarea.style.position = "fixed";
1124
+ textarea.style.opacity = "0";
1125
+ document.body.appendChild(textarea);
1126
+ textarea.select();
1127
+ const copied = document.execCommand("copy");
1128
+ document.body.removeChild(textarea);
1129
+ return copied;
1130
+ } catch (_unused5) {
1131
+ return false;
1132
+ }
1133
+ };
1060
1134
  const generateClickableText = text => {
1061
1135
  const textWithHtmlLinks = makeLinksClickable(text, {
1062
1136
  createLink: ({
@@ -1133,12 +1207,48 @@ window.__supervisor__ = (() => {
1133
1207
  href,
1134
1208
  text = href
1135
1209
  }) => "<a href=\"".concat(href, "\">").concat(text, "</a>");
1210
+ displayJsenvWarningOverlay = params => {
1211
+ if (logs) {
1212
+ console.log("display jsenv warning overlay", params);
1213
+ }
1214
+ if (document.querySelector(JSENV_ERROR_OVERLAY_TAGNAME)) {
1215
+ return () => {};
1216
+ }
1217
+ document.querySelectorAll(JSENV_WARNING_OVERLAY_TAGNAME).forEach(node => {
1218
+ node.parentNode.removeChild(node);
1219
+ });
1220
+ let jsenvWarningOverlay = new JsenvWarningOverlay(params);
1221
+ document.body.appendChild(jsenvWarningOverlay);
1222
+ return () => {
1223
+ if (jsenvWarningOverlay && jsenvWarningOverlay.parentNode) {
1224
+ jsenvWarningOverlay.parentNode.removeChild(jsenvWarningOverlay);
1225
+ jsenvWarningOverlay = null;
1226
+ }
1227
+ };
1228
+ };
1136
1229
  if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
1137
1230
  customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay);
1138
1231
  }
1139
- const overlayCSS = /* css */"\n :host {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 99999;\n width: 100%;\n height: 100%;\n /* overflow-y: scroll; */\n margin: 0;\n background: rgba(0, 0, 0, 0.66);\n }\n\n .backdrop {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n }\n\n .overlay {\n position: relative;\n box-sizing: border-box;\n width: 800px;\n max-width: 100vw;\n margin: 30px auto;\n padding: 25px 40px;\n padding-top: 0;\n font-family: monospace;\n direction: ltr;\n background: rgba(0, 0, 0, 0.95);\n border-radius: 4px 8px;\n box-shadow:\n 0 20px 40px rgb(0 0 0 / 30%),\n 0 15px 12px rgb(0 0 0 / 20%);\n overflow: hidden; /* for h1 margins */\n }\n\n h1 {\n color: red;\n text-align: center;\n }\n\n pre {\n max-width: 100%;\n /* padding is nice + prevents scrollbar from hiding the text behind it */\n /* does not work nicely on firefox though https://bugzilla.mozilla.org/show_bug.cgi?id=748518 */\n padding: 20px;\n overflow: auto;\n }\n\n .tip {\n padding-top: 12px;\n border-top: 1px solid #999;\n }\n\n [data-theme=\"dark\"] {\n color: #999;\n }\n [data-theme=\"dark\"] pre {\n color: #eee;\n background: #111;\n border: 1px solid #333;\n }\n\n [data-theme=\"light\"] {\n color: #eeeeee;\n }\n [data-theme=\"light\"] pre {\n color: #eeeeee;\n background: #1e1e1e;\n border: 1px solid white;\n }\n\n pre a {\n color: inherit;\n }\n ";
1232
+ if (customElements && !customElements.get(JSENV_WARNING_OVERLAY_TAGNAME)) {
1233
+ customElements.define(JSENV_WARNING_OVERLAY_TAGNAME, JsenvWarningOverlay);
1234
+ }
1235
+ const overlayCSS = /* css */"\n :host {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 99999;\n width: 100%;\n height: 100%;\n /* overflow-y: scroll; */\n margin: 0;\n background: rgba(0, 0, 0, 0.66);\n }\n\n .backdrop {\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n }\n\n .overlay {\n position: relative;\n box-sizing: border-box;\n width: 800px;\n /* keep a margin visible on every side, the content scrolls rather\n than the overlay growing edge to edge */\n max-width: 98dvw;\n max-height: calc(100dvh - 60px);\n margin: 30px auto;\n padding: 25px 40px;\n padding-top: 0;\n font-family: monospace;\n direction: ltr;\n background: rgba(0, 0, 0, 0.95);\n border-radius: 4px 8px;\n box-shadow:\n 0 20px 40px rgb(0 0 0 / 30%),\n 0 15px 12px rgb(0 0 0 / 20%);\n overflow: auto; /* creates a block formatting context, for h1 margins */\n }\n\n h1 {\n color: red;\n text-align: center;\n }\n\n [data-level=\"warning\"] h1 {\n color: #ffab40;\n }\n\n .copy {\n position: absolute;\n top: 12px;\n right: 12px;\n padding: 4px 10px;\n color: inherit;\n font-size: 12px;\n font-family: inherit;\n background: transparent;\n border: 1px solid currentColor;\n border-radius: 4px;\n opacity: 0.7;\n cursor: pointer;\n }\n .copy:hover {\n opacity: 1;\n }\n\n .details {\n padding: 0 20px 16px;\n line-height: 1.5;\n }\n .details p {\n margin: 0 0 8px;\n }\n .details p:last-child {\n margin-bottom: 0;\n }\n\n pre {\n max-width: 100%;\n /* padding is nice + prevents scrollbar from hiding the text behind it */\n /* does not work nicely on firefox though https://bugzilla.mozilla.org/show_bug.cgi?id=748518 */\n padding: 20px;\n overflow: auto;\n }\n\n .tip {\n padding-top: 12px;\n border-top: 1px solid #999;\n }\n\n [data-theme=\"dark\"] {\n color: #999;\n }\n [data-theme=\"dark\"] pre {\n color: #eee;\n background: #111;\n border: 1px solid #333;\n }\n\n [data-theme=\"light\"] {\n color: #eeeeee;\n }\n [data-theme=\"light\"] pre {\n color: #eeeeee;\n background: #1e1e1e;\n border: 1px solid white;\n }\n\n pre a {\n color: inherit;\n }\n ";
1140
1236
  }
1141
1237
  supervisor.createException = createException;
1238
+ supervisor.reportWarning = ({
1239
+ title,
1240
+ text,
1241
+ details
1242
+ }) => {
1243
+ if (!errorOverlay) {
1244
+ return () => {};
1245
+ }
1246
+ return displayJsenvWarningOverlay({
1247
+ title,
1248
+ text,
1249
+ details
1250
+ });
1251
+ };
1142
1252
  supervisor.reportException = exception => {
1143
1253
  if (errorOverlay) {
1144
1254
  const removeErrorOverlay = displayJsenvErrorOverlay(exception);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/plugin-supervisor",
3
- "version": "1.8.7",
3
+ "version": "1.8.9",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -27,7 +27,7 @@
27
27
  "prepublishOnly": "npm run build"
28
28
  },
29
29
  "dependencies": {
30
- "@jsenv/ast": "6.8.4",
30
+ "@jsenv/ast": "6.8.5",
31
31
  "@jsenv/sourcemap": "1.4.2"
32
32
  },
33
33
  "devDependencies": {
@@ -900,7 +900,9 @@ window.__supervisor__ = (() => {
900
900
  }
901
901
 
902
902
  const JSENV_ERROR_OVERLAY_TAGNAME = "jsenv-error-overlay";
903
+ const JSENV_WARNING_OVERLAY_TAGNAME = "jsenv-warning-overlay";
903
904
  let displayJsenvErrorOverlay;
905
+ let displayJsenvWarningOverlay;
904
906
  error_overlay: {
905
907
  displayJsenvErrorOverlay = (params) => {
906
908
  if (logs) {
@@ -908,7 +910,9 @@ window.__supervisor__ = (() => {
908
910
  }
909
911
  let jsenvErrorOverlay = new JsenvErrorOverlay(params);
910
912
  document
911
- .querySelectorAll(JSENV_ERROR_OVERLAY_TAGNAME)
913
+ .querySelectorAll(
914
+ `${JSENV_ERROR_OVERLAY_TAGNAME},${JSENV_WARNING_OVERLAY_TAGNAME}`,
915
+ )
912
916
  .forEach((node) => {
913
917
  node.parentNode.removeChild(node);
914
918
  });
@@ -961,7 +965,8 @@ window.__supervisor__ = (() => {
961
965
  ${overlayCSS}
962
966
  </style>
963
967
  <div class="backdrop"></div>
964
- <div class="overlay" data-theme="dark">
968
+ <div class="overlay" data-theme="dark" data-level="error">
969
+ <button class="copy" type="button">Copy</button>
965
970
  <h1 class="title">
966
971
  An error occured
967
972
  </h1>
@@ -970,6 +975,7 @@ window.__supervisor__ = (() => {
970
975
  ${tip}
971
976
  </div>
972
977
  </div>`;
978
+ enableCopyButton(root);
973
979
  root.querySelector(".backdrop").onclick = () => {
974
980
  if (!this.parentNode) {
975
981
  // not in document anymore
@@ -1095,6 +1101,95 @@ window.__supervisor__ = (() => {
1095
1101
  }
1096
1102
  }
1097
1103
  }
1104
+ /*
1105
+ * Same overlay, lower stakes: something the developer should act on but
1106
+ * that does not prevent the page from running. An error always wins over
1107
+ * a warning, never the other way around.
1108
+ */
1109
+ class JsenvWarningOverlay extends HTMLElement {
1110
+ constructor({ title, text, details = [] }) {
1111
+ super();
1112
+ const root = this.attachShadow({ mode: "open" });
1113
+ root.innerHTML = `
1114
+ <style>
1115
+ ${overlayCSS}
1116
+ </style>
1117
+ <div class="backdrop"></div>
1118
+ <div class="overlay" data-theme="dark" data-level="warning">
1119
+ <button class="copy" type="button">Copy</button>
1120
+ <h1 class="title">
1121
+ ${escapeHtml(title)}
1122
+ </h1>
1123
+ <pre class="text">${escapeHtml(text)}</pre>
1124
+ <div class="details">
1125
+ ${details.map((detail) => `<p>${escapeHtml(detail)}</p>`).join("\n ")}
1126
+ </div>
1127
+ <div class="tip">
1128
+ Click outside to close.
1129
+ </div>
1130
+ </div>`;
1131
+ enableCopyButton(root);
1132
+ root.querySelector(".backdrop").onclick = () => {
1133
+ if (!this.parentNode) {
1134
+ // not in document anymore
1135
+ return;
1136
+ }
1137
+ root.querySelector(".backdrop").onclick = null;
1138
+ this.parentNode.removeChild(this);
1139
+ };
1140
+ }
1141
+ }
1142
+
1143
+ // the text is meant to be pasted as-is in a chat: a title line, the
1144
+ // location, then the raw text inside a fenced code block
1145
+ const enableCopyButton = (root) => {
1146
+ const button = root.querySelector(".copy");
1147
+ const overlay = root.querySelector(".overlay");
1148
+ let resetTimeout;
1149
+ button.onclick = async () => {
1150
+ const detailsNode = overlay.querySelector(".details");
1151
+ const parts = [
1152
+ `[jsenv] ${overlay.querySelector(".title").textContent.trim()}`,
1153
+ `Page: ${window.location.href}`,
1154
+ "```",
1155
+ overlay.querySelector(".text").textContent.trim(),
1156
+ "```",
1157
+ ];
1158
+ if (detailsNode) {
1159
+ for (const paragraph of detailsNode.querySelectorAll("p")) {
1160
+ parts.push(paragraph.textContent.trim());
1161
+ }
1162
+ }
1163
+ const copied = await writeTextToClipboard(parts.join("\n"));
1164
+ button.textContent = copied ? "Copied" : "Copy failed";
1165
+ clearTimeout(resetTimeout);
1166
+ resetTimeout = setTimeout(() => {
1167
+ button.textContent = "Copy";
1168
+ }, 2000);
1169
+ };
1170
+ };
1171
+ const writeTextToClipboard = async (text) => {
1172
+ try {
1173
+ await window.navigator.clipboard.writeText(text);
1174
+ return true;
1175
+ } catch {
1176
+ // the clipboard API needs a secure context, fallback for plain http origins
1177
+ }
1178
+ try {
1179
+ const textarea = document.createElement("textarea");
1180
+ textarea.value = text;
1181
+ textarea.style.position = "fixed";
1182
+ textarea.style.opacity = "0";
1183
+ document.body.appendChild(textarea);
1184
+ textarea.select();
1185
+ const copied = document.execCommand("copy");
1186
+ document.body.removeChild(textarea);
1187
+ return copied;
1188
+ } catch {
1189
+ return false;
1190
+ }
1191
+ };
1192
+
1098
1193
  const generateClickableText = (text) => {
1099
1194
  const textWithHtmlLinks = makeLinksClickable(text, {
1100
1195
  createLink: ({ url, line, column }) => {
@@ -1158,9 +1253,40 @@ window.__supervisor__ = (() => {
1158
1253
  };
1159
1254
  const link = ({ href, text = href }) => `<a href="${href}">${text}</a>`;
1160
1255
 
1256
+ displayJsenvWarningOverlay = (params) => {
1257
+ if (logs) {
1258
+ console.log("display jsenv warning overlay", params);
1259
+ }
1260
+ if (document.querySelector(JSENV_ERROR_OVERLAY_TAGNAME)) {
1261
+ return () => {};
1262
+ }
1263
+ document
1264
+ .querySelectorAll(JSENV_WARNING_OVERLAY_TAGNAME)
1265
+ .forEach((node) => {
1266
+ node.parentNode.removeChild(node);
1267
+ });
1268
+ let jsenvWarningOverlay = new JsenvWarningOverlay(params);
1269
+ document.body.appendChild(jsenvWarningOverlay);
1270
+ return () => {
1271
+ if (jsenvWarningOverlay && jsenvWarningOverlay.parentNode) {
1272
+ jsenvWarningOverlay.parentNode.removeChild(jsenvWarningOverlay);
1273
+ jsenvWarningOverlay = null;
1274
+ }
1275
+ };
1276
+ };
1277
+
1161
1278
  if (customElements && !customElements.get(JSENV_ERROR_OVERLAY_TAGNAME)) {
1162
1279
  customElements.define(JSENV_ERROR_OVERLAY_TAGNAME, JsenvErrorOverlay);
1163
1280
  }
1281
+ if (
1282
+ customElements &&
1283
+ !customElements.get(JSENV_WARNING_OVERLAY_TAGNAME)
1284
+ ) {
1285
+ customElements.define(
1286
+ JSENV_WARNING_OVERLAY_TAGNAME,
1287
+ JsenvWarningOverlay,
1288
+ );
1289
+ }
1164
1290
 
1165
1291
  const overlayCSS = /* css */ `
1166
1292
  :host {
@@ -1187,7 +1313,10 @@ window.__supervisor__ = (() => {
1187
1313
  position: relative;
1188
1314
  box-sizing: border-box;
1189
1315
  width: 800px;
1190
- max-width: 100vw;
1316
+ /* keep a margin visible on every side, the content scrolls rather
1317
+ than the overlay growing edge to edge */
1318
+ max-width: 98dvw;
1319
+ max-height: calc(100dvh - 60px);
1191
1320
  margin: 30px auto;
1192
1321
  padding: 25px 40px;
1193
1322
  padding-top: 0;
@@ -1198,7 +1327,7 @@ window.__supervisor__ = (() => {
1198
1327
  box-shadow:
1199
1328
  0 20px 40px rgb(0 0 0 / 30%),
1200
1329
  0 15px 12px rgb(0 0 0 / 20%);
1201
- overflow: hidden; /* for h1 margins */
1330
+ overflow: auto; /* creates a block formatting context, for h1 margins */
1202
1331
  }
1203
1332
 
1204
1333
  h1 {
@@ -1206,6 +1335,39 @@ window.__supervisor__ = (() => {
1206
1335
  text-align: center;
1207
1336
  }
1208
1337
 
1338
+ [data-level="warning"] h1 {
1339
+ color: #ffab40;
1340
+ }
1341
+
1342
+ .copy {
1343
+ position: absolute;
1344
+ top: 12px;
1345
+ right: 12px;
1346
+ padding: 4px 10px;
1347
+ color: inherit;
1348
+ font-size: 12px;
1349
+ font-family: inherit;
1350
+ background: transparent;
1351
+ border: 1px solid currentColor;
1352
+ border-radius: 4px;
1353
+ opacity: 0.7;
1354
+ cursor: pointer;
1355
+ }
1356
+ .copy:hover {
1357
+ opacity: 1;
1358
+ }
1359
+
1360
+ .details {
1361
+ padding: 0 20px 16px;
1362
+ line-height: 1.5;
1363
+ }
1364
+ .details p {
1365
+ margin: 0 0 8px;
1366
+ }
1367
+ .details p:last-child {
1368
+ margin-bottom: 0;
1369
+ }
1370
+
1209
1371
  pre {
1210
1372
  max-width: 100%;
1211
1373
  /* padding is nice + prevents scrollbar from hiding the text behind it */
@@ -1244,6 +1406,12 @@ window.__supervisor__ = (() => {
1244
1406
  }
1245
1407
 
1246
1408
  supervisor.createException = createException;
1409
+ supervisor.reportWarning = ({ title, text, details }) => {
1410
+ if (!errorOverlay) {
1411
+ return () => {};
1412
+ }
1413
+ return displayJsenvWarningOverlay({ title, text, details });
1414
+ };
1247
1415
  supervisor.reportException = (exception) => {
1248
1416
  if (errorOverlay) {
1249
1417
  const removeErrorOverlay = displayJsenvErrorOverlay(exception);