gesso-framework 0.6.16 → 0.6.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { A as output, C as equalityOf, D as input, E as InputCell, M as trackRead, N as withBodyOf, O as into, S as derive, T as internalState, _ as Resource, a as undoable, b as ComputedCell, c as classifyStorageError, d as storageReadValue, f as pick, g as mutate, h as throttled, i as OpfsStorage, j as outputTargetOf, k as isOutputTarget, l as storageErrorMessage, m as debounced, n as persisted, o as UndoStack, p as pickKeys, r as IndexedDbStorage, s as MemoryStorage, t as PersistedState, u as storageReadFailure, v as resource, w as InternalState, x as computed, y as select } from "./persisted-m5v2fOju.js";
2
- import { C as isChannelClientMessage, D as viewKeys, E as defineChannel, O as structurallyEqual, S as diffProjection, T as channel, _ as printPropValue, a as isPortErrorMessage, b as applyPatch, c as servePorts, d as isConsoleEntryMessage, f as describeStream, g as formatStream, h as formatNodeReport, i as isHubMessage, l as workerHandle, m as formatNodePath, n as serveChannels, o as isPortHandshake, p as formatAge, r as APPLICATION_WORKER, s as portHandle, t as serve, u as captureConsole, v as ProvidedChannel, w as isChannelHostMessage, x as applyPatches, y as provide } from "./serveChannels-DN2R2s0A.js";
1
+ import { A as output, C as equalityOf, D as input, E as InputCell, M as trackRead, N as withBodyOf, O as into, S as derive, T as internalState, _ as Resource, a as undoable, b as ComputedCell, c as classifyStorageError, d as storageReadValue, f as pick, g as mutate, h as throttled, i as OpfsStorage, j as outputTargetOf, k as isOutputTarget, l as storageErrorMessage, m as debounced, n as persisted, o as UndoStack, p as pickKeys, r as IndexedDbStorage, s as MemoryStorage, t as PersistedState, u as storageReadFailure, v as resource, w as InternalState, x as computed, y as select } from "./persisted-SkVtTKZG.js";
2
+ import { C as isChannelClientMessage, D as viewKeys, E as defineChannel, O as structurallyEqual, S as diffProjection, T as channel, _ as printPropValue, a as isPortErrorMessage, b as applyPatch, c as servePorts, d as isConsoleEntryMessage, f as describeStream, g as formatStream, h as formatNodeReport, i as isHubMessage, l as workerHandle, m as formatNodePath, n as serveChannels, o as isPortHandshake, p as formatAge, r as APPLICATION_WORKER, s as portHandle, t as serve, u as captureConsole, v as ProvidedChannel, w as isChannelHostMessage, x as applyPatches, y as provide } from "./serveChannels-BfIXVnoE.js";
3
3
  import { a as Each, i as show, n as getComponentMetadata, o as each, r as Show, t as createComponent } from "./createComponent-3n1ZSql8.js";
4
- import { c as findUnplainPath, d as describeChannel, l as requirePlainData, t as AGENT_PORT, u as channelSchema } from "./remote-CSdwpwXz.js";
5
- import { n as serveApplicationAgent } from "./app-NLcQF2Oh.js";
4
+ import { c as findUnplainPath, d as describeChannel, l as requirePlainData, t as AGENT_PORT, u as channelSchema } from "./remote-BoB8zh7z.js";
5
+ import { n as serveApplicationAgent } from "./app-Yig59Ygf.js";
6
6
  import { BehaviorSubject, EMPTY, Observable, Subject, Subscription, distinctUntilChanged, map } from "rxjs";
7
7
  import { AnimationDriver, Box, Canvas2DRenderer, CanvasPlatformSurface, CanvasTextMeasurer, Constraints, DefaultImageResolver, DefaultVideoResolver, DirtyFlags, EnvironmentNotifier, FocusNotifier, IconRasterizer, LayoutEngine, LayoutInspector, LayoutNotifier, MARK_PREFIX, Stack, TEXT_RUN_ID_SEPARATOR, UI_ROLES, UI_SEMANTIC_STATES, UiAnimationFrameClock, UiEditingController, UiEnvironmentKeys, UiEventType, UiFindController, UiFocusManager, UiGestureRecognizer, UiGraph, UiGraphBuilder, UiHitTester, UiHostFrameClock, UiInputDispatcher, UiInsetRegistry, UiKeyboardController, UiNodeType, UiPlatformAdapter, UiPointerController, UiPointerEvent, UiScheduler, UiSelectionController, UiSharedElements, UiSpring, UiTimerFrameClock, UiTouchScroller, UiVirtualSheet, UiVirtualWindow, UiWheelController, VIRTUAL_INDEX_PROP, VIRTUAL_LEAD_PROP, VIRTUAL_SHEET_PROP, VIRTUAL_WINDOW_PROP, WebGPURenderer, buildSemanticsSubtree, buildSemanticsTree, bumpFontStack, capturePointer, createCanvasSurface, createTween, createWebGPUSurface, defaultMotion, defineModifier, describeOverrides, diffSemantics, dragSessionFor, dropIndexShifts, easings, fade, formatExplanation, insetsEqual, isComponentLikeElement, isImeKey, isObservable, isWebGPUAvailable, linear, linkOf, markInstant, markNow, measure, measureSpan, motion, noInsets, observeViewportInsets, percent, performanceMarksEnabled, pointerDeviceOf, prepareInputSurface, propertyEffects, recordsEqual, registerFontStack, resolveCursor, rewalkSemantics, scrollRange, scrollbarThumb, semanticsInertAbove, semanticsMemory, setPerformanceMarks, setPerformanceMarks as setPerformanceMarks$1, textRunOfRecordId, themeTokens, touchActionFor, wheelDeltaYOf } from "gesso-core";
8
8
  //#region src/Component.ts
@@ -1793,6 +1793,8 @@ function filesDuringDrag(event) {
1793
1793
  */
1794
1794
  var ShellService = class {
1795
1795
  handler = null;
1796
+ /** What `warnOnLeave` last said. */
1797
+ leaving = false;
1796
1798
  scheme = internalState("light");
1797
1799
  contrastState = internalState("standard");
1798
1800
  activeState = internalState(true);
@@ -1888,9 +1890,17 @@ var ShellService = class {
1888
1890
  get currentViewportInsets() {
1889
1891
  return this.insets.value;
1890
1892
  }
1891
- /** Installed by the runtime; a request with no handler is dropped. */
1893
+ /**
1894
+ * Installed by the runtime; a request with no handler is dropped,
1895
+ * except the leave warning, which is a state rather than an event: a
1896
+ * shell that arrives after it was turned on is told.
1897
+ */
1892
1898
  setHandler(handler) {
1893
1899
  this.handler = handler;
1900
+ if (handler !== null && this.leaving) handler({
1901
+ type: "leaveWarning",
1902
+ on: true
1903
+ });
1894
1904
  }
1895
1905
  /**
1896
1906
  * Called by the runtime when the shell reports the appearance.
@@ -2051,6 +2061,27 @@ var ShellService = class {
2051
2061
  enter
2052
2062
  });
2053
2063
  }
2064
+ /**
2065
+ * Whether leaving the page now would lose work: edits not yet saved,
2066
+ * an upload half done. While it is on, the shell has the browser ask
2067
+ * before the page is closed, reloaded or left (`beforeunload`), in the
2068
+ * browser's own words, which a page cannot set.
2069
+ *
2070
+ * A request rather than a call because `beforeunload` is the window's,
2071
+ * and the thread this runs on may not have one. Turn it off as soon as
2072
+ * the work is safe: the browser will not keep a page that has a
2073
+ * `beforeunload` listener in its back/forward cache, so the shell
2074
+ * listens only while this is on. Saying the same thing twice sends
2075
+ * nothing.
2076
+ */
2077
+ warnOnLeave(on) {
2078
+ if (on === this.leaving) return;
2079
+ this.leaving = on;
2080
+ this.handler?.({
2081
+ type: "leaveWarning",
2082
+ on
2083
+ });
2084
+ }
2054
2085
  /** Called by the runtime when the shell reports the state. Not for applications. */
2055
2086
  applyFullscreen(active) {
2056
2087
  if (this.isFullscreen.value !== active) this.isFullscreen.value = active;
@@ -2226,6 +2257,33 @@ var ShellService = class {
2226
2257
  recentFiles() {
2227
2258
  return this.requestFile({ op: "recent" });
2228
2259
  }
2260
+ /**
2261
+ * Shows a folder picker and remembers the folder chosen, under a
2262
+ * number, as `openFiles` does a file. Nothing in it is read: a folder
2263
+ * can hold a hundred thousand files, and the worker that wants them
2264
+ * takes the handle itself with `rememberedDirectory` and reads what
2265
+ * it needs, rather than every byte crossing to the render worker.
2266
+ * `outcome` is `unsupported` in a browser with no folder picker. Call
2267
+ * it from a click handler, as `openFiles`.
2268
+ */
2269
+ openDirectory(options = {}) {
2270
+ return this.requestFile({
2271
+ op: "openDirectory",
2272
+ mode: options.mode ?? "read"
2273
+ });
2274
+ }
2275
+ /**
2276
+ * Asks again for a folder remembered from an earlier session, which a
2277
+ * browser lets lapse across a reload. Asking needs a gesture: a click
2278
+ * handler.
2279
+ */
2280
+ reopenDirectory(handle, options = {}) {
2281
+ return this.requestFile({
2282
+ op: "reopen",
2283
+ handle,
2284
+ mode: options.mode ?? "read"
2285
+ });
2286
+ }
2229
2287
  /** Stops remembering a file. Its handle means nothing afterwards. */
2230
2288
  forgetFile(handle) {
2231
2289
  return this.requestFile({
@@ -2234,7 +2292,7 @@ var ShellService = class {
2234
2292
  });
2235
2293
  }
2236
2294
  /**
2237
- * The wire under the five above. With no shell installed the answer
2295
+ * The wire under the methods above. With no shell installed the answer
2238
2296
  * is `unsupported`, for the reason a storage request's is `denied`:
2239
2297
  * a promise left unsettled would hang whatever was waiting on it.
2240
2298
  */
@@ -2268,6 +2326,7 @@ function shellFilesUnsupported(error = "There is no shell to reach files through
2268
2326
  return {
2269
2327
  outcome: "unsupported",
2270
2328
  files: [],
2329
+ directory: null,
2271
2330
  saved: null,
2272
2331
  recent: [],
2273
2332
  error
@@ -2289,7 +2348,8 @@ var ShellFiles = class {
2289
2348
  try {
2290
2349
  switch (request.op) {
2291
2350
  case "open": return await this.open(request.accept, request.multiple);
2292
- case "reopen": return await this.reopen(request.handle);
2351
+ case "reopen": return await this.reopen(request.handle, request.mode ?? "read");
2352
+ case "openDirectory": return await this.openDirectory(request.mode);
2293
2353
  case "save": return await this.save(request);
2294
2354
  case "recent": return ok({ recent: await this.recent() });
2295
2355
  case "forget":
@@ -2308,7 +2368,7 @@ var ShellFiles = class {
2308
2368
  });
2309
2369
  const files = [];
2310
2370
  for (const handle of handles) {
2311
- const id = await this.remember(handle);
2371
+ const id = await this.rememberIfPossible(handle);
2312
2372
  files.push(await read(await handle.getFile(), id));
2313
2373
  }
2314
2374
  return ok({ files });
@@ -2320,18 +2380,31 @@ var ShellFiles = class {
2320
2380
  }
2321
2381
  return shellFilesUnsupported("This browser cannot open files.");
2322
2382
  }
2323
- async reopen(id) {
2383
+ async openDirectory(mode) {
2384
+ if (this.host.showDirectoryPicker === void 0) return shellFilesUnsupported("This browser cannot open folders.");
2385
+ const handle = await this.host.showDirectoryPicker({ mode });
2386
+ const id = await this.remember(handle);
2387
+ return ok({ directory: {
2388
+ name: handle.name,
2389
+ handle: id
2390
+ } });
2391
+ }
2392
+ async reopen(id, mode) {
2324
2393
  const entry = await this.find(id);
2325
2394
  if (entry === null) return {
2326
2395
  ...failure(null),
2327
2396
  error: "The shell has no file under that handle."
2328
2397
  };
2329
- if (!await permitted(entry.handle, "read")) return denied();
2398
+ if (!await permitted(entry.handle, entry.handle.kind === "directory" ? mode : "read")) return denied();
2399
+ if (entry.handle.kind === "directory") {
2400
+ await this.touch(entry);
2401
+ return ok({ directory: {
2402
+ name: entry.handle.name,
2403
+ handle: id
2404
+ } });
2405
+ }
2330
2406
  const file = await entry.handle.getFile();
2331
- await this.host.store.put({
2332
- ...entry,
2333
- used: this.host.now()
2334
- });
2407
+ await this.touch(entry);
2335
2408
  return ok({ files: [await read(file, id)] });
2336
2409
  }
2337
2410
  async save(request) {
@@ -2341,12 +2414,13 @@ var ShellFiles = class {
2341
2414
  ...failure(null),
2342
2415
  error: "The shell has no file under that handle."
2343
2416
  };
2417
+ if (entry.handle.kind === "directory") return {
2418
+ ...failure(null),
2419
+ error: "That handle is a folder, which cannot be written to as a file."
2420
+ };
2344
2421
  if (!await permitted(entry.handle, "readwrite")) return denied();
2345
2422
  await write(entry.handle, request.bytes ?? request.text);
2346
- await this.host.store.put({
2347
- ...entry,
2348
- used: this.host.now()
2349
- });
2423
+ await this.touch(entry);
2350
2424
  return ok({ saved: {
2351
2425
  name: entry.handle.name,
2352
2426
  handle: entry.id,
@@ -2359,7 +2433,7 @@ var ShellFiles = class {
2359
2433
  suggestedName: request.name
2360
2434
  });
2361
2435
  await write(handle, request.bytes ?? request.text);
2362
- const id = request.remember === false ? null : await this.remember(handle);
2436
+ const id = request.remember === false ? null : await this.rememberIfPossible(handle);
2363
2437
  return ok({ saved: {
2364
2438
  name: handle.name,
2365
2439
  handle: id,
@@ -2380,6 +2454,7 @@ var ShellFiles = class {
2380
2454
  return (await this.host.store.all()).sort((a, b) => b.used - a.used).map((entry) => ({
2381
2455
  handle: entry.id,
2382
2456
  name: entry.name,
2457
+ kind: entry.handle.kind === "directory" ? "directory" : "file",
2383
2458
  used: entry.used
2384
2459
  }));
2385
2460
  }
@@ -2387,6 +2462,31 @@ var ShellFiles = class {
2387
2462
  return (await this.host.store.all()).find((entry) => entry.id === id) ?? null;
2388
2463
  }
2389
2464
  /**
2465
+ * `remember`, for a file that has already been read or written.
2466
+ *
2467
+ * Remembering is what the recent files and a later Save are made of,
2468
+ * and a store can refuse it for reasons that have nothing to do with
2469
+ * the file: storage blocked for the site, a spent quota, a private
2470
+ * window. The file was opened or saved all the same, so that is the
2471
+ * answer, with no handle, exactly as a file input's file has none.
2472
+ */
2473
+ async rememberIfPossible(handle) {
2474
+ try {
2475
+ return await this.remember(handle);
2476
+ } catch {
2477
+ return null;
2478
+ }
2479
+ }
2480
+ /** Notes that a remembered file was used, for the recent list's order. Best effort, as above. */
2481
+ async touch(entry) {
2482
+ try {
2483
+ await this.host.store.put({
2484
+ ...entry,
2485
+ used: this.host.now()
2486
+ });
2487
+ } catch {}
2488
+ }
2489
+ /**
2390
2490
  * Keeps a handle and answers its number.
2391
2491
  *
2392
2492
  * The same file picked twice is one entry, not two: a picker hands
@@ -2458,6 +2558,7 @@ function ok(fields) {
2458
2558
  return {
2459
2559
  outcome: "ok",
2460
2560
  files: [],
2561
+ directory: null,
2461
2562
  saved: null,
2462
2563
  recent: [],
2463
2564
  error: null,
@@ -2468,6 +2569,7 @@ function cancelled() {
2468
2569
  return {
2469
2570
  outcome: "cancelled",
2470
2571
  files: [],
2572
+ directory: null,
2471
2573
  saved: null,
2472
2574
  recent: [],
2473
2575
  error: null
@@ -2477,6 +2579,7 @@ function denied() {
2477
2579
  return {
2478
2580
  outcome: "denied",
2479
2581
  files: [],
2582
+ directory: null,
2480
2583
  saved: null,
2481
2584
  recent: [],
2482
2585
  error: "Permission to use the file was refused."
@@ -2501,6 +2604,7 @@ function failure(error) {
2501
2604
  return {
2502
2605
  outcome: "failed",
2503
2606
  files: [],
2607
+ directory: null,
2504
2608
  saved: null,
2505
2609
  recent: [],
2506
2610
  error: message
@@ -2587,6 +2691,7 @@ function browserFilesHost(view) {
2587
2691
  now: () => Date.now(),
2588
2692
  ...pickers.showOpenFilePicker === void 0 ? {} : { showOpenFilePicker: (options) => pickers.showOpenFilePicker.call(view, options) },
2589
2693
  ...pickers.showSaveFilePicker === void 0 ? {} : { showSaveFilePicker: (options) => pickers.showSaveFilePicker.call(view, options) },
2694
+ ...pickers.showDirectoryPicker === void 0 ? {} : { showDirectoryPicker: (options) => pickers.showDirectoryPicker.call(view, options) },
2590
2695
  pickWithInput: (accept, multiple) => new Promise((resolve) => {
2591
2696
  const input = document.createElement("input");
2592
2697
  input.type = "file";
@@ -2606,6 +2711,42 @@ function browserFilesHost(view) {
2606
2711
  }
2607
2712
  };
2608
2713
  }
2714
+ /**
2715
+ * A folder the shell remembers, by its number, for the thread that reads
2716
+ * it: null when there is no such folder, or IndexedDB is not here.
2717
+ *
2718
+ * The shell keeps handles on the page's own thread and hands out
2719
+ * numbers, which suits a file read once. A folder is different: an
2720
+ * editor or a photo library reads it a file at a time, a hundred
2721
+ * thousand times, and each read crossing to the page and back would be
2722
+ * the whole cost. IndexedDB is shared by every thread of an origin, so
2723
+ * the worker that owns the work takes the handle from the shell's store
2724
+ * and reads the folder itself, with the permission the picker (or
2725
+ * `reopenDirectory`) granted.
2726
+ */
2727
+ async function rememberedDirectory(handle, factory = globalThis.indexedDB) {
2728
+ if (factory === void 0) return null;
2729
+ const entry = (await new IndexedDbHandleStore(factory).all()).find((stored) => stored.id === handle);
2730
+ return entry !== void 0 && entry.handle.kind === "directory" ? entry.handle : null;
2731
+ }
2732
+ //#endregion
2733
+ //#region src/app/shellLeaveWarning.ts
2734
+ const listening = /* @__PURE__ */ new WeakMap();
2735
+ function setLeaveWarning(view, on) {
2736
+ if (view === null || view === void 0) return;
2737
+ const listener = listening.get(view);
2738
+ if (on && listener === void 0) {
2739
+ const warn = (event) => {
2740
+ event.preventDefault();
2741
+ event.returnValue = "";
2742
+ };
2743
+ listening.set(view, warn);
2744
+ view.addEventListener("beforeunload", warn);
2745
+ } else if (!on && listener !== void 0) {
2746
+ listening.delete(view);
2747
+ view.removeEventListener("beforeunload", listener);
2748
+ }
2749
+ }
2609
2750
  //#endregion
2610
2751
  //#region src/app/shellRedirect.ts
2611
2752
  /**
@@ -2953,8 +3094,9 @@ var EditingProxy = class {
2953
3094
  if (this.composing) return;
2954
3095
  const textarea = this.textarea;
2955
3096
  if (textarea.value !== state.text) textarea.value = state.text;
2956
- if (textarea.selectionStart !== state.selectionStart || textarea.selectionEnd !== state.selectionEnd) try {
2957
- textarea.setSelectionRange(state.selectionStart, state.selectionEnd);
3097
+ const direction = state.selectionDirection ?? "forward";
3098
+ if (textarea.selectionStart !== state.selectionStart || textarea.selectionEnd !== state.selectionEnd || state.selectionEnd > state.selectionStart && textarea.selectionDirection !== direction) try {
3099
+ textarea.setSelectionRange(state.selectionStart, state.selectionEnd, direction);
2958
3100
  } catch {}
2959
3101
  }
2960
3102
  listen() {
@@ -3168,7 +3310,8 @@ const RECORD_ATTRIBUTES = [
3168
3310
  "aria-current",
3169
3311
  "aria-activedescendant",
3170
3312
  "aria-controls",
3171
- "aria-autocomplete"
3313
+ "aria-autocomplete",
3314
+ "aria-orientation"
3172
3315
  ];
3173
3316
  let mirrors = 0;
3174
3317
  /**
@@ -3383,6 +3526,7 @@ var SemanticsMirror = class {
3383
3526
  if (record.valueText !== void 0) element.setAttribute("aria-valuetext", record.valueText);
3384
3527
  if (record.activeDescendant !== void 0) element.setAttribute("aria-activedescendant", this.idPrefix + record.activeDescendant);
3385
3528
  if (record.controls !== void 0) element.setAttribute("aria-controls", this.idPrefix + record.controls);
3529
+ if (record.orientation !== void 0) element.setAttribute("aria-orientation", record.orientation);
3386
3530
  if (record.autocomplete !== void 0) element.setAttribute("aria-autocomplete", record.autocomplete);
3387
3531
  }
3388
3532
  /**
@@ -3605,9 +3749,15 @@ function setNumber(element, attribute, value) {
3605
3749
  * `aria-label` that was just written.
3606
3750
  */
3607
3751
  function setText(element, text) {
3608
- if (element.children.length > 0) return;
3752
+ if (element.children.length > 0) {
3753
+ const own = element.firstChild;
3754
+ if (own !== null && own.nodeType === TEXT_NODE && own.nodeValue !== text) own.nodeValue = text;
3755
+ return;
3756
+ }
3609
3757
  if (element.textContent !== text) element.textContent = text;
3610
3758
  }
3759
+ /** `Node.TEXT_NODE`, which a worker-free page has but a spec's fake document does not. */
3760
+ const TEXT_NODE = 3;
3611
3761
  //#endregion
3612
3762
  //#region src/app/shellStorage.ts
3613
3763
  const DENIED = {
@@ -4492,7 +4642,7 @@ var WorkerApp = class {
4492
4642
  })
4493
4643
  }, this.proxy);
4494
4644
  const webmcp = this.options.webmcp;
4495
- if (webmcp !== void 0 && webmcp !== false) import("./webmcp-BvQBxNjX.js").then((n) => n.a).then((agent) => agent.connectWebMcp(this, webmcp === true ? {} : webmcp)).then((disconnect) => {
4645
+ if (webmcp !== void 0 && webmcp !== false) import("./webmcp-CdDq2nzS.js").then((n) => n.a).then((agent) => agent.connectWebMcp(this, webmcp === true ? {} : webmcp)).then((disconnect) => {
4496
4646
  if (this.renderWorker === worker) this.disconnectWebMcp = disconnect;
4497
4647
  else disconnect();
4498
4648
  }, (error) => console.warn("[gesso] WebMCP could not register the channels:", error));
@@ -4684,6 +4834,7 @@ var WorkerApp = class {
4684
4834
  dispose() {
4685
4835
  this.disconnectWebMcp?.();
4686
4836
  this.disconnectWebMcp = null;
4837
+ if (typeof window !== "undefined") setLeaveWarning(window, false);
4687
4838
  this.ready = false;
4688
4839
  this.setFrameLoop(false);
4689
4840
  this.detachColorScheme?.();
@@ -4823,6 +4974,10 @@ var WorkerApp = class {
4823
4974
  redirectWith(this.options.onRedirect, message.url, window.location);
4824
4975
  return;
4825
4976
  }
4977
+ if (message.type === "leaveWarning") {
4978
+ setLeaveWarning(window, message.on);
4979
+ return;
4980
+ }
4826
4981
  if (message.type === "fullscreen") {
4827
4982
  if (this.canvas !== void 0) setElementFullscreen(this.canvas, message.enter);
4828
4983
  return;
@@ -9849,7 +10004,7 @@ function timeOrigin() {
9849
10004
  }
9850
10005
  function editingStatesEqual(a, b) {
9851
10006
  if (a === null || b === null) return a === b;
9852
- return a.text === b.text && a.selectionStart === b.selectionStart && a.selectionEnd === b.selectionEnd && a.multiline === b.multiline && a.composing === b.composing && a.html === b.html && a.clipboard === b.clipboard && a.caret.x === b.caret.x && a.caret.y === b.caret.y && a.caret.width === b.caret.width && a.caret.height === b.caret.height;
10007
+ return a.text === b.text && a.selectionStart === b.selectionStart && a.selectionEnd === b.selectionEnd && a.selectionDirection === b.selectionDirection && a.multiline === b.multiline && a.composing === b.composing && a.html === b.html && a.clipboard === b.clipboard && a.caret.x === b.caret.x && a.caret.y === b.caret.y && a.caret.width === b.caret.width && a.caret.height === b.caret.height;
9853
10008
  }
9854
10009
  /** A canvas for the text measurer when the draw canvas is not a 2D one. */
9855
10010
  function createMeasureCanvas() {
@@ -10211,6 +10366,7 @@ var GessoApp = class {
10211
10366
  */
10212
10367
  dispose() {
10213
10368
  this.running = false;
10369
+ if (isCanvasElement(this.canvas)) setLeaveWarning(this.canvas.ownerDocument.defaultView, false);
10214
10370
  this.audio?.dispose();
10215
10371
  this.audio = null;
10216
10372
  this.runtime.onAudioRequest(null);
@@ -10397,6 +10553,10 @@ var GessoApp = class {
10397
10553
  this.runtime.settlePopup(request.id, opened);
10398
10554
  return;
10399
10555
  }
10556
+ if (request.type === "leaveWarning") {
10557
+ setLeaveWarning(view, request.on);
10558
+ return;
10559
+ }
10400
10560
  if (request.type === "redirect") {
10401
10561
  if (view !== null) redirectWith(void 0, request.url, view.location);
10402
10562
  return;
@@ -10683,7 +10843,7 @@ var GessoAppBuilder = class {
10683
10843
  if (app === void 0 || key !== "gesso:agent") return;
10684
10844
  const pair = new MessageChannel();
10685
10845
  const registrations = this.channelRegistrations;
10686
- import("./app-NLcQF2Oh.js").then((n) => n.t).then((agent) => agent.serveApplicationAgent(pair.port2, {
10846
+ import("./app-Yig59Ygf.js").then((n) => n.t).then((agent) => agent.serveApplicationAgent(pair.port2, {
10687
10847
  channels: () => registrations.filter((registration) => registration.source !== void 0).map((registration) => ({
10688
10848
  token: registration.token,
10689
10849
  source: registration.source
@@ -10696,7 +10856,7 @@ var GessoAppBuilder = class {
10696
10856
  connectWebMcp(app) {
10697
10857
  const choice = this.webmcpChoice;
10698
10858
  if (choice === false) return;
10699
- import("./webmcp-BvQBxNjX.js").then((n) => n.a).then((agent) => agent.connectWebMcp(this, choice === true ? {} : choice)).then((disconnect) => {
10859
+ import("./webmcp-CdDq2nzS.js").then((n) => n.a).then((agent) => agent.connectWebMcp(this, choice === true ? {} : choice)).then((disconnect) => {
10700
10860
  if (this.app === app) this.disconnectWebMcp = disconnect;
10701
10861
  else disconnect();
10702
10862
  }, (error) => console.warn("[gesso] WebMCP could not register the channels:", error));
@@ -11734,6 +11894,10 @@ var RenderWorkerApp = class {
11734
11894
  type: "fullscreen",
11735
11895
  enter: request.enter
11736
11896
  });
11897
+ else if (request.type === "leaveWarning") this.host.postMessage({
11898
+ type: "leaveWarning",
11899
+ on: request.on
11900
+ });
11737
11901
  else if (request.type === "popup") this.host.postMessage({
11738
11902
  type: "popup",
11739
11903
  id: request.id,
@@ -11822,6 +11986,6 @@ function locationOf(event) {
11822
11986
  return `${event.filename}:${line}:${column}`;
11823
11987
  }
11824
11988
  //#endregion
11825
- export { APPLICATION_WORKER, AnimationService, AudioService, AudioSink, BoundsCell, Channel, ChannelRegistry, ChannelReplica, Component, ComponentHost, ComponentHostResolver, ComputedCell, Define, Each, EditingProxy, EditingService, FanCell, FanOut, FindService, FocusService, FontService, FrameService, GessoApp, GessoAppBuilder, GessoRuntime, IndexedDbStorage, Inject, Input, InputCell, InternalState, MARK_PREFIX, MediaService, MemoryStorage, OpfsStorage, Output, OverlayLayer, OverlayService, PersistedState, Presence, ProvidedChannel, RenderWorkerApp, Resource, RouteState, RouterOutlet, RouterService, ScrollService, SemanticsMirror, ServiceRegistry, ShellService, ShellStorage, Show, TextService, ThemeTokenCell, UI_FRAME_PHASES, UI_ROLES, UI_SEMANTIC_STATES, UndoStack, WorkerApp, applyPatch, applyPatches, audioClock, bind, bounds, buildPath, channel, channelSchema, classifyStorageError, computed, controlled, createApp, createChannelRegistry, createComponent, createShellHistory, createSyncApp, debounced, defineChannel, derive, describeChannel, describeStream, diffProjection, each, fanOut, findUnplainPath, formatAge, formatNodePath, formatNodeReport, formatStream, formatUrl, input, internalState, into, isChannelClientMessage, isChannelHostMessage, isClassComponent, isComponentElement, isHubMessage, isOutputTarget, isPortErrorMessage, isPortHandshake, markInstant, markNow, measureSpan, mutate, observeColorScheme, observeMediaQuery, observeReducedMotion, output, parseUrl, performShellStorage, performanceMarksEnabled, persisted, pick, pickKeys, portHandle, printPropValue, provide, registerUndoShortcuts, renderRoot, requirePlainData, resource, route, select, serve, serveChannels, servePorts, setPerformanceMarks, shellStorageDenied, show, storageErrorMessage, storageReadFailure, storageReadValue, structurallyEqual, themeTokenCell, throttled, to, treeText, undoable, viewKeys, workerHandle, writeClipboard };
11989
+ export { APPLICATION_WORKER, AnimationService, AudioService, AudioSink, BoundsCell, Channel, ChannelRegistry, ChannelReplica, Component, ComponentHost, ComponentHostResolver, ComputedCell, Define, Each, EditingProxy, EditingService, FanCell, FanOut, FindService, FocusService, FontService, FrameService, GessoApp, GessoAppBuilder, GessoRuntime, IndexedDbStorage, Inject, Input, InputCell, InternalState, MARK_PREFIX, MediaService, MemoryStorage, OpfsStorage, Output, OverlayLayer, OverlayService, PersistedState, Presence, ProvidedChannel, RenderWorkerApp, Resource, RouteState, RouterOutlet, RouterService, ScrollService, SemanticsMirror, ServiceRegistry, ShellService, ShellStorage, Show, TextService, ThemeTokenCell, UI_FRAME_PHASES, UI_ROLES, UI_SEMANTIC_STATES, UndoStack, WorkerApp, applyPatch, applyPatches, audioClock, bind, bounds, buildPath, channel, channelSchema, classifyStorageError, computed, controlled, createApp, createChannelRegistry, createComponent, createShellHistory, createSyncApp, debounced, defineChannel, derive, describeChannel, describeStream, diffProjection, each, fanOut, findUnplainPath, formatAge, formatNodePath, formatNodeReport, formatStream, formatUrl, input, internalState, into, isChannelClientMessage, isChannelHostMessage, isClassComponent, isComponentElement, isHubMessage, isOutputTarget, isPortErrorMessage, isPortHandshake, markInstant, markNow, measureSpan, mutate, observeColorScheme, observeMediaQuery, observeReducedMotion, output, parseUrl, performShellStorage, performanceMarksEnabled, persisted, pick, pickKeys, portHandle, printPropValue, provide, registerUndoShortcuts, rememberedDirectory, renderRoot, requirePlainData, resource, route, select, serve, serveChannels, servePorts, setPerformanceMarks, shellStorageDenied, show, storageErrorMessage, storageReadFailure, storageReadValue, structurallyEqual, themeTokenCell, throttled, to, treeText, undoable, viewKeys, workerHandle, writeClipboard };
11826
11990
 
11827
11991
  //# sourceMappingURL=index.js.map