@oharness/server 0.10.0 → 0.12.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.
Files changed (74) hide show
  1. package/dist/analytics.d.ts +32 -29
  2. package/dist/analytics.js +225 -278
  3. package/dist/analytics.js.map +1 -1
  4. package/dist/i18n.js +242 -12
  5. package/dist/i18n.js.map +1 -1
  6. package/dist/icons.d.ts +3 -0
  7. package/dist/icons.js +69 -3
  8. package/dist/icons.js.map +1 -1
  9. package/dist/model-order.js +1 -0
  10. package/dist/model-order.js.map +1 -1
  11. package/dist/page/client/account.js +140 -4
  12. package/dist/page/client/account.js.map +1 -1
  13. package/dist/page/client/base.js +42 -16
  14. package/dist/page/client/base.js.map +1 -1
  15. package/dist/page/client/browser.js +1 -1
  16. package/dist/page/client/composer.js +641 -47
  17. package/dist/page/client/composer.js.map +1 -1
  18. package/dist/page/client/dialogs.js +75 -0
  19. package/dist/page/client/dialogs.js.map +1 -1
  20. package/dist/page/client/events.js +25 -2
  21. package/dist/page/client/events.js.map +1 -1
  22. package/dist/page/client/index.js +4 -0
  23. package/dist/page/client/index.js.map +1 -1
  24. package/dist/page/client/panels.js +4 -4
  25. package/dist/page/client/ripple.d.ts +13 -0
  26. package/dist/page/client/ripple.js +247 -0
  27. package/dist/page/client/ripple.js.map +1 -0
  28. package/dist/page/client/routines.js +173 -41
  29. package/dist/page/client/routines.js.map +1 -1
  30. package/dist/page/client/shell.js +200 -54
  31. package/dist/page/client/shell.js.map +1 -1
  32. package/dist/page/client/state.js +7 -2
  33. package/dist/page/client/state.js.map +1 -1
  34. package/dist/page/client/terminal.js +34 -7
  35. package/dist/page/client/terminal.js.map +1 -1
  36. package/dist/page/client/theme.d.ts +17 -0
  37. package/dist/page/client/theme.js +365 -0
  38. package/dist/page/client/theme.js.map +1 -0
  39. package/dist/page/client/transcript.js +63 -7
  40. package/dist/page/client/transcript.js.map +1 -1
  41. package/dist/page/style.js +273 -22
  42. package/dist/page/style.js.map +1 -1
  43. package/dist/page.js +5 -5
  44. package/dist/page.js.map +1 -1
  45. package/dist/prefs.d.ts +17 -0
  46. package/dist/prefs.js +5 -0
  47. package/dist/prefs.js.map +1 -1
  48. package/dist/server.js +710 -45
  49. package/dist/server.js.map +1 -1
  50. package/dist/suggest.d.ts +42 -0
  51. package/dist/suggest.js +215 -0
  52. package/dist/suggest.js.map +1 -0
  53. package/dist/terminal.d.ts +35 -2
  54. package/dist/terminal.js +60 -4
  55. package/dist/terminal.js.map +1 -1
  56. package/dist/theme-assets.d.ts +39 -0
  57. package/dist/theme-assets.js +109 -0
  58. package/dist/theme-assets.js.map +1 -0
  59. package/dist/theme-store.d.ts +50 -0
  60. package/dist/theme-store.js +124 -0
  61. package/dist/theme-store.js.map +1 -0
  62. package/dist/theme-tools.d.ts +25 -0
  63. package/dist/theme-tools.js +243 -0
  64. package/dist/theme-tools.js.map +1 -0
  65. package/dist/tool-icons.d.ts +12 -0
  66. package/dist/tool-icons.js +16 -0
  67. package/dist/tool-icons.js.map +1 -0
  68. package/dist/turn-shots.d.ts +18 -2
  69. package/dist/turn-shots.js +10 -1
  70. package/dist/turn-shots.js.map +1 -1
  71. package/dist/user-theme.d.ts +143 -0
  72. package/dist/user-theme.js +510 -0
  73. package/dist/user-theme.js.map +1 -0
  74. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"composer.js","sourceRoot":"","sources":["../../../src/page/client/composer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAozEjC,CAAC"}
1
+ {"version":3,"file":"composer.js","sourceRoot":"","sources":["../../../src/page/client/composer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAs4FjC,CAAC"}
@@ -807,5 +807,80 @@ function closePairing(id) {
807
807
  dialog.remove();
808
808
  }
809
809
 
810
+ /**
811
+ * The app's own confirm and prompt, in place of the browser's. Those carry the
812
+ * page's address as a title ("127.0.0.1:4314 says"), take the OS's look, and
813
+ * block the whole page; these match every other dialog here and resolve a
814
+ * promise. Escape and Cancel are the same answer: false, or null for text.
815
+ */
816
+ function askDialog(opts, withField) {
817
+ return new Promise((resolve) => {
818
+ const dialog = el("dialog", "ask-dialog");
819
+ if (opts.title) dialog.appendChild(el("div", "ask-dialog-title", opts.title));
820
+ if (opts.body) dialog.appendChild(el("div", "ask-dialog-body", opts.body));
821
+ let field = null;
822
+ if (withField) {
823
+ field = el("input", "ask-dialog-field");
824
+ field.type = "text";
825
+ field.value = opts.value || "";
826
+ if (opts.placeholder) field.placeholder = opts.placeholder;
827
+ field.setAttribute("aria-label", opts.title || opts.body || "");
828
+ dialog.appendChild(field);
829
+ }
830
+ const actions = el("div", "dlg-actions");
831
+ const cancel = el("button", "quiet", opts.cancel || t("dialog.cancel"));
832
+ const ok = el("button", opts.danger ? "approve danger" : "approve", opts.ok || t("dialog.ok"));
833
+ cancel.type = ok.type = "button";
834
+ actions.append(cancel, ok);
835
+ dialog.appendChild(actions);
836
+
837
+ let settled = false;
838
+ const finish = (value) => {
839
+ if (settled) return;
840
+ settled = true;
841
+ if (dialog.open) dialog.close();
842
+ dialog.remove();
843
+ resolve(value);
844
+ };
845
+ const accept = () => {
846
+ if (!field) return finish(true);
847
+ const text = field.value.trim();
848
+ if (text) finish(text);
849
+ };
850
+ cancel.onclick = () => finish(field ? null : false);
851
+ ok.onclick = accept;
852
+ if (field) {
853
+ const sync = () => { ok.disabled = !field.value.trim(); };
854
+ field.oninput = sync;
855
+ sync();
856
+ field.onkeydown = (e) => {
857
+ if (e.key !== "Enter" || e.isComposing) return;
858
+ e.preventDefault();
859
+ accept();
860
+ };
861
+ }
862
+ dialog.addEventListener("cancel", (e) => {
863
+ e.preventDefault();
864
+ finish(field ? null : false);
865
+ });
866
+ document.body.appendChild(dialog);
867
+ dialog.showModal();
868
+ if (field) {
869
+ field.focus();
870
+ field.select();
871
+ } else (opts.danger ? cancel : ok).focus();
872
+ });
873
+ }
874
+
875
+ /** Yes or no. Resolves true only for the confirming button. */
876
+ function askConfirm(opts) {
877
+ return askDialog(typeof opts === "string" ? { body: opts } : opts, false);
878
+ }
879
+
880
+ /** One line of text, trimmed. Resolves null when cancelled. */
881
+ function askText(opts) {
882
+ return askDialog(opts, true);
883
+ }
884
+
810
885
  `;
811
886
  //# sourceMappingURL=dialogs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../../../src/page/client/dialogs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmyBhC,CAAC"}
1
+ {"version":3,"file":"dialogs.js","sourceRoot":"","sources":["../../../src/page/client/dialogs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA82BhC,CAAC"}
@@ -74,6 +74,8 @@ events.onmessage = (message) => {
74
74
  // been written yet, so there is nothing at /attachment to ask for.
75
75
  const sent = sentPreviews.get(event.session) || [];
76
76
  sentPreviews.delete(event.session);
77
+ P.suggestion = null;
78
+ if (event.session === activeId) paintSuggestion();
77
79
  addUser(event.text || "", sent);
78
80
  startWorking(t("work.thinking"));
79
81
  const unshown = (event.attachments || []).filter(
@@ -144,7 +146,15 @@ events.onmessage = (message) => {
144
146
  renderQueued();
145
147
  break;
146
148
 
147
- case "assistant_message": P.streaming = null; break;
149
+ case "assistant_message": {
150
+ P.streaming = null;
151
+ const meta = event.message && event.message.meta;
152
+ if (meta && typeof meta.agent === "string") {
153
+ const session = meta.acpSession || {};
154
+ agentReply(P.turn, { name: meta.agent, session: session.id, cwd: session.cwd });
155
+ }
156
+ break;
157
+ }
148
158
  // After the turn ended: under its last reply, not a new turn of its own.
149
159
  case "turn_shots": {
150
160
  const replies = P.node.querySelectorAll(":scope > .turn.assistant");
@@ -228,6 +238,11 @@ events.onmessage = (message) => {
228
238
  case "remote_pair_request": showPairing(event); break;
229
239
  case "remote_pair_closed": closePairing(event.id); break;
230
240
  case "question_request": questionCard(event); break;
241
+ // A theme proposed by set_theme, and what became of it; then the theme in
242
+ // use, from this window or another.
243
+ case "theme_preview": themePreviewCard(event.preview); break;
244
+ case "theme_preview_closed": closeThemePreview(event.id, event.outcome); break;
245
+ case "theme": setThemeState(event.state); break;
231
246
  case "question_result": settleQuestion(event); break;
232
247
  // Sent once per (re)connect, after the replay: a card this page still
233
248
  // shows as open but the server no longer holds closed while we were away.
@@ -329,13 +344,21 @@ events.onmessage = (message) => {
329
344
  void loadSessions();
330
345
  break;
331
346
  case "state": applyState(event.state); break;
347
+ case "suggestion":
348
+ P.suggestion = event.text;
349
+ if (event.session === activeId) paintSuggestion();
350
+ break;
351
+ case "suggestions":
352
+ if (!event.enabled) for (const entry of panes.values()) entry.suggestion = null;
353
+ paintSuggestion();
354
+ break;
332
355
  // What a conversation is doing on the computer, for the live view.
333
356
  case "computer": computerEvent(event.session, event.event); break;
334
357
  // The project's starters, written in the background after the fixed three.
335
358
  case "starters": startersArrived(event); break;
336
359
  case "connectors_changed":
337
360
  if (authorizing === event.connector) authorizing = null;
338
- offer = { connectors: event.connectors, plugins: event.plugins || [], skills: event.skills || [] };
361
+ offer = { connectors: event.connectors, plugins: event.plugins || [], skills: event.skills || [], agents: event.agents || offer.agents || [] };
339
362
  // The failure already said what went wrong; this only announces the
340
363
  // half that is worth celebrating.
341
364
  if (event.authorized) addNotice(t("market.authorized", { name: event.connector }));
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/page/client/events.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+W/B,CAAC"}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../../src/page/client/events.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsY/B,CAAC"}
@@ -16,6 +16,8 @@ import { COMPUTER } from "./computer.js";
16
16
  import { FILES } from "./files.js";
17
17
  import { TERMINAL } from "./terminal.js";
18
18
  import { SHELL } from "./shell.js";
19
+ import { RIPPLE } from "./ripple.js";
20
+ import { THEME } from "./theme.js";
19
21
  /**
20
22
  * The client script, assembled.
21
23
  *
@@ -50,5 +52,7 @@ export const APP_SCRIPT = [
50
52
  BROWSER,
51
53
  COMPUTER,
52
54
  SHELL,
55
+ RIPPLE,
56
+ THEME,
53
57
  ].join("") + "\n";
54
58
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/page/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,UAAU,GACrB;IACA,IAAI;IACJ,OAAO;IACP,UAAU;IACV,KAAK;IACL,MAAM;IACN,OAAO;IACP,QAAQ;IACR,KAAK;IACL,SAAS;IACT,OAAO;IACP,QAAQ;IACR,KAAK;IACL,MAAM;IACN,KAAK;IACL,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,KAAK;CACJ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/page/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,MAAM,UAAU,GACrB;IACA,IAAI;IACJ,OAAO;IACP,UAAU;IACV,KAAK;IACL,MAAM;IACN,OAAO;IACP,QAAQ;IACR,KAAK;IACL,SAAS;IACT,OAAO;IACP,QAAQ;IACR,KAAK;IACL,MAAM;IACN,KAAK;IACL,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,KAAK;IACL,MAAM;IACN,KAAK;CACJ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC"}
@@ -384,7 +384,7 @@ async function showMemoryPage(id) {
384
384
  form.appendChild(mergeActions);
385
385
  form.onsubmit = async (event) => {
386
386
  event.preventDefault();
387
- if (!confirm(t("memory.mergeConfirm"))) return;
387
+ if (!(await askConfirm({ body: t("memory.mergeConfirm"), ok: t("memory.mergeApply") }))) return;
388
388
  apply.disabled = true;
389
389
  await act({ action: "merge", note: { summary: summary.value.trim(), body: body.value.trim(), tags: tags.value.split(/[,,\s]+/).filter(Boolean), type: proposal.draft.type || "" } });
390
390
  apply.disabled = false;
@@ -563,9 +563,9 @@ async function showMemoryPage(id) {
563
563
  row.appendChild(line);
564
564
  }
565
565
  const remove = el("button", "ghost", t("memory.delete")); remove.type = "button";
566
- remove.onclick = () => {
566
+ remove.onclick = async () => {
567
567
  const linked = links.in.length ? t("memory.confirmDeleteLinked", { count: links.in.length }) : "";
568
- if (confirm(linked + t("memory.confirmDelete"))) void act({ action: "delete", scope: note.scope, id: note.id, expectedHash: note.hash });
568
+ if (await askConfirm({ body: linked + t("memory.confirmDelete"), ok: t("memory.delete"), danger: true })) void act({ action: "delete", scope: note.scope, id: note.id, expectedHash: note.hash });
569
569
  };
570
570
  row.appendChild(editor(note, remove));
571
571
  list.appendChild(row);
@@ -588,7 +588,7 @@ async function showMemoryPage(id) {
588
588
  text.appendChild(el("div", "muted small", [new Date(backup.backedUpAt).toLocaleString(LOCALE), backup.reason ? t("memory.backupReason." + backup.reason) : "", scopeLabel, backup.id].filter(Boolean).join(" · ")));
589
589
  row.appendChild(text);
590
590
  const restore = el("button", "ghost", t("memory.restore")); restore.type = "button";
591
- restore.onclick = () => { if (confirm(t("memory.confirmRestore"))) void act({ action: "restore", file: backup.file }); };
591
+ restore.onclick = async () => { if (await askConfirm({ body: t("memory.confirmRestore"), ok: t("memory.restore") })) void act({ action: "restore", file: backup.file }); };
592
592
  row.appendChild(restore);
593
593
  backups.appendChild(row);
594
594
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The field behind a new session: a grid of dots a click sends a shockwave
3
+ * through.
4
+ *
5
+ * Only while the empty state is on screen. The canvas sits under everything
6
+ * in #main and takes no pointer, so it can never eat a click meant for the
7
+ * composer or a starter; it only hears them go by. Nothing animates at rest —
8
+ * a frame is drawn when a wave is travelling or the pointer moves, and the
9
+ * loop stops once the last wave has died out, so an idle new tab costs nothing.
10
+ *
11
+ * One part of the client script; see ./index.ts for how the parts join.
12
+ */
13
+ export declare const RIPPLE: string;
@@ -0,0 +1,247 @@
1
+ /**
2
+ * The field behind a new session: a grid of dots a click sends a shockwave
3
+ * through.
4
+ *
5
+ * Only while the empty state is on screen. The canvas sits under everything
6
+ * in #main and takes no pointer, so it can never eat a click meant for the
7
+ * composer or a starter; it only hears them go by. Nothing animates at rest —
8
+ * a frame is drawn when a wave is travelling or the pointer moves, and the
9
+ * loop stops once the last wave has died out, so an idle new tab costs nothing.
10
+ *
11
+ * One part of the client script; see ./index.ts for how the parts join.
12
+ */
13
+ export const RIPPLE = String.raw `
14
+ // --- ripple field ------------------------------------------------------------
15
+
16
+ (function rippleField() {
17
+ const host = document.getElementById("main");
18
+ if (!host || !emptyEl) return;
19
+ const still = window.matchMedia("(prefers-reduced-motion: reduce)");
20
+
21
+ const canvas = document.createElement("canvas");
22
+ canvas.id = "ripple";
23
+ canvas.setAttribute("aria-hidden", "true");
24
+ host.prepend(canvas);
25
+ const ctx = canvas.getContext("2d");
26
+
27
+ const GAP = 24; // grid pitch, css px
28
+ const SPEED = 0.38; // wavefront, px per ms
29
+ const LIFE = 3200; // ms a wave travels before it is gone
30
+ const BAND = 90; // width of the wavefront's swell
31
+ const LENS = 120; // radius the pointer magnifies
32
+ const TINT = "fg"; // what a wave lights up in: "fg" plain, "brand" lime
33
+
34
+ let w = 0, h = 0, dpr = 1;
35
+ let waves = [];
36
+ let pointer = null; // {x, y} inside the canvas, or null
37
+ let raf = 0;
38
+ let shown = false;
39
+ let ink = { fg: "#111", brand: "#5e8c00" };
40
+
41
+ function readInk() {
42
+ const css = getComputedStyle(document.documentElement);
43
+ ink = {
44
+ fg: css.getPropertyValue("--fg").trim() || ink.fg,
45
+ brand: css.getPropertyValue("--brand").trim() || ink.brand,
46
+ };
47
+ }
48
+
49
+ function fit() {
50
+ const box = host.getBoundingClientRect();
51
+ dpr = Math.min(window.devicePixelRatio || 1, 2);
52
+ w = Math.max(1, Math.round(box.width));
53
+ h = Math.max(1, Math.round(box.height));
54
+ canvas.width = w * dpr;
55
+ canvas.height = h * dpr;
56
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
57
+ }
58
+
59
+ /** Is the invitation actually in front of someone? */
60
+ const visible = () => emptyEl.isConnected && emptyEl.offsetParent !== null;
61
+
62
+ function kick() {
63
+ if (!raf) raf = requestAnimationFrame(frame);
64
+ }
65
+
66
+ function frame(now) {
67
+ raf = 0;
68
+ waves = waves.filter((wave) => now - wave.t < LIFE);
69
+ draw(now);
70
+ if (waves.length) kick();
71
+ }
72
+
73
+ function draw(at) {
74
+ ctx.clearRect(0, 0, w, h);
75
+ if (!shown) return;
76
+ // rAF's stamp is when the frame began, which can trail a wave pushed by a
77
+ // timer since; time is read here so no wave is ever younger than zero.
78
+ const now = Math.max(at, performance.now());
79
+
80
+ // The dot field: each dot pushed outward by every wave passing it, and lit
81
+ // by how hard it is being pushed.
82
+ const ox = (w % GAP) / 2 + GAP / 2;
83
+ const oy = (h % GAP) / 2 + GAP / 2;
84
+ ctx.fillStyle = ink.fg;
85
+ const lit = [];
86
+ for (let gy = oy; gy < h; gy += GAP) {
87
+ for (let gx = ox; gx < w; gx += GAP) {
88
+ let dx = 0, dy = 0, energy = 0;
89
+ for (const wave of waves) {
90
+ const age = now - wave.t;
91
+ const front = age * SPEED;
92
+ const vx = gx - wave.x, vy = gy - wave.y;
93
+ const dist = Math.hypot(vx, vy) || 1;
94
+ const off = dist - front;
95
+ if (off > BAND || off < -BAND * 3) continue;
96
+ const fade = 1 - age / LIFE;
97
+ // One long, low swell at the front — a breath through the grid, not a shake.
98
+ const env = Math.exp(-(off * off) / (BAND * BAND * 0.35)) * fade * fade;
99
+ const push = Math.sin(off / 26) * 4.5 * env * wave.power;
100
+ dx += (vx / dist) * push;
101
+ dy += (vy / dist) * push;
102
+ energy += env * wave.power;
103
+ }
104
+ let r = 1.05, alpha = 0.13;
105
+ if (pointer) {
106
+ const near = Math.hypot(gx - pointer.x, gy - pointer.y);
107
+ if (near < LENS) {
108
+ const k = 1 - near / LENS;
109
+ r += k * k * 1.3;
110
+ alpha += k * k * 0.22;
111
+ }
112
+ }
113
+ if (energy > 0.06) {
114
+ lit.push(gx + dx, gy + dy, Math.min(1, energy), r);
115
+ continue;
116
+ }
117
+ ctx.globalAlpha = alpha;
118
+ ctx.beginPath();
119
+ ctx.arc(gx + dx, gy + dy, r, 0, 6.2832);
120
+ ctx.fill();
121
+ }
122
+ }
123
+ // Dots on a wavefront take the brand's lime and swell with it.
124
+ ctx.fillStyle = ink[TINT];
125
+ for (let i = 0; i < lit.length; i += 4) {
126
+ const e = lit[i + 2];
127
+ ctx.globalAlpha = 0.18 + e * 0.5;
128
+ ctx.beginPath();
129
+ ctx.arc(lit[i], lit[i + 1], lit[i + 3] + e * 0.9, 0, 6.2832);
130
+ ctx.fill();
131
+ }
132
+
133
+ // The fronts themselves: a hairline lime ring with a faint dotted ghost of
134
+ // the ink riding just outside it, and a soft bloom where the click landed.
135
+ ctx.lineCap = "round";
136
+ for (const wave of waves) {
137
+ const age = now - wave.t;
138
+ const fade = 1 - age / LIFE;
139
+ const front = age * SPEED;
140
+ ctx.globalAlpha = 0.32 * fade * fade * fade * wave.power;
141
+ ctx.strokeStyle = ink[TINT];
142
+ ctx.lineWidth = 1;
143
+ ctx.beginPath();
144
+ ctx.arc(wave.x, wave.y, front, 0, 6.2832);
145
+ ctx.stroke();
146
+ ctx.globalAlpha = 0.08 * fade * fade * fade * wave.power;
147
+ ctx.strokeStyle = ink.fg;
148
+ ctx.lineWidth = 1;
149
+ ctx.setLineDash([2, 9]);
150
+ ctx.lineDashOffset = -age / 40;
151
+ ctx.beginPath();
152
+ ctx.arc(wave.x, wave.y, front * 1.03 + 4, 0, 6.2832);
153
+ ctx.stroke();
154
+ ctx.setLineDash([]);
155
+ if (age < 420) {
156
+ const k = 1 - age / 420;
157
+ const glow = ctx.createRadialGradient(wave.x, wave.y, 0, wave.x, wave.y, 46);
158
+ glow.addColorStop(0, ink[TINT]);
159
+ glow.addColorStop(1, "transparent");
160
+ ctx.globalAlpha = 0.22 * k * k * wave.power;
161
+ ctx.fillStyle = glow;
162
+ ctx.beginPath();
163
+ ctx.arc(wave.x, wave.y, 46, 0, 6.2832);
164
+ ctx.fill();
165
+ }
166
+ }
167
+ ctx.globalAlpha = 1;
168
+ }
169
+
170
+ function wave(x, y, power) {
171
+ if (still.matches) return;
172
+ waves.push({ x, y, t: performance.now(), power });
173
+ if (waves.length > 28) waves.shift();
174
+ kick();
175
+ }
176
+
177
+ function local(event) {
178
+ const box = canvas.getBoundingClientRect();
179
+ return { x: event.clientX - box.left, y: event.clientY - box.top };
180
+ }
181
+
182
+ // Held down and dragged, the pointer leaves a wake: a small wave every
183
+ // TRAIL px it travels, so a stroke draws a line of rings behind it.
184
+ const TRAIL = 40;
185
+ let drag = null; // where the last wake wave went out, while held
186
+
187
+ host.addEventListener("pointerdown", (event) => {
188
+ if (!shown || event.button > 0) return;
189
+ const at = local(event);
190
+ drag = at;
191
+ // A stroke begun on bare background is drawing, not selecting: without
192
+ // this it would sweep the headline and starters into a text selection.
193
+ const bare = event.target === host || event.target.id === "log" || event.target === emptyEl;
194
+ if (bare) event.preventDefault();
195
+ wave(at.x, at.y, 1);
196
+ });
197
+ host.addEventListener("pointermove", (event) => {
198
+ if (!shown) return;
199
+ const at = local(event);
200
+ if (event.pointerType === "mouse") pointer = at;
201
+ if (drag && event.buttons & 1) {
202
+ const gone = Math.hypot(at.x - drag.x, at.y - drag.y);
203
+ if (gone >= TRAIL) {
204
+ drag = at;
205
+ wave(at.x, at.y, Math.min(0.45, 0.2 + gone / 240));
206
+ }
207
+ }
208
+ kick();
209
+ });
210
+ const release = () => { drag = null; };
211
+ window.addEventListener("pointerup", release);
212
+ window.addEventListener("pointercancel", release);
213
+ host.addEventListener("pointerleave", () => {
214
+ pointer = null;
215
+ if (shown) kick();
216
+ });
217
+
218
+ // The empty state comes and goes with tab switches and first messages, and
219
+ // nothing announces it, so look twice a second; on arrival, the brand mark
220
+ // sends out one wave of its own.
221
+ function check() {
222
+ const now = visible();
223
+ if (now === shown) return;
224
+ shown = now;
225
+ canvas.classList.toggle("on", shown);
226
+ if (!shown) { waves = []; pointer = null; kick(); return; }
227
+ readInk();
228
+ fit();
229
+ const mark = emptyEl.querySelector(".brand-mark");
230
+ if (mark) {
231
+ const at = mark.getBoundingClientRect();
232
+ const box = canvas.getBoundingClientRect();
233
+ wave(at.left + at.width / 2 - box.left, at.top + at.height / 2 - box.top, 0.7);
234
+ }
235
+ kick();
236
+ }
237
+ setInterval(check, 500);
238
+ check();
239
+
240
+ new ResizeObserver(() => { if (shown) { fit(); kick(); } }).observe(host);
241
+ // Theme flips (system or the in-app toggle) change the ink.
242
+ window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => { readInk(); kick(); });
243
+ new MutationObserver(() => { readInk(); kick(); })
244
+ .observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
245
+ })();
246
+ `;
247
+ //# sourceMappingURL=ripple.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ripple.js","sourceRoot":"","sources":["../../../src/page/client/ripple.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyO/B,CAAC"}