gesso-core 0.6.14 → 0.6.16

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # gesso-core
2
2
 
3
+ ## 0.6.16
4
+
5
+ ### Patch Changes
6
+
7
+ - dd5c789: A painted node whose picture changes is repainted without being laid out again, unless the new picture declares a different intrinsic size. Before, every new picture marked layout, which for a painted node sized by its parent (`height: percent(100)`, say) laid the tree out again up to the root on every repaint: a minimap redrawn on each frame of a scroll re-measured thirteen nodes a frame for nothing. Property definitions gain `narrowEffects`, which lets a property mark less than its worst case for a particular change.
8
+ - b681046: A press whose node is taken out of the tree mid-drag goes on to the nearest of that node's ancestors still in it. A drag-select in a virtual list whose row scrolled out of view, and was unmounted, sent the rest of the drag to a node nothing could hear from; the list, and whatever listens above it, now gets it.
9
+ - 8149ae2: What a `textInput` surface draws is left out of the semantics tree: the editing proxy already carries its text as the field's value, so a screen reader heard it twice, and a keystroke in a code editor that redrew hundreds of runs rewrote as many elements of the accessibility mirror on the main thread.
10
+ - 4eed2fd: A node can take text without being a field: `textInput` makes it a surface that keeps and draws its own text, such as a code editor, and receives typed text through `onBeforeInput`, IME composition through the new `onCompositionStart`, `onCompositionUpdate` and `onCompositionEnd`, and the clipboard through `onPaste` and its state's `clipboard`. Keys belonging to an open IME composition (the Enter that commits a candidate, the arrows that choose one) are no longer forwarded as keys, which inserted a line on commit in a field too. A key whose `onKeyDown` called `preventDefault()` now keeps its text out, as in a browser; `UiKeyboardEvent.textFollows` says whether text follows a key at all. `fireEvent` gains `beforeInput`, the composition events, `copy` and `cut`.
11
+
12
+ Key events carry `code`, the physical key (`KeyZ`), so a shortcut can match Option+Z on a Mac, where the key is `Ω`.
13
+
14
+ ## 0.6.15
15
+
16
+ ### Patch Changes
17
+
18
+ - 9872957: Removing a node now drops the layout records of everything under it, not only its own. `UiGraph.removeNode` unlinked every removed node from its parent before telling the removal listener, so the layout engine's walk down from the removed root found no children, and the records of every descendant — sticky and absolutely positioned ones included — stayed for the life of the app. Anything that removes subtrees as it goes, a virtualised list or grid above all, got slower with every scroll, because each frame walked every sticky node ever mounted: gessosheet's frame went from 2.4 ms to 5.7 ms over eight scrolls of an unchanged sheet, and stays at 2.1 ms with this fixed. The listener is now called with the removed root already out of the tree and its subtree still linked under it, and the rest is unlinked afterwards.
19
+
3
20
  ## 0.6.14
4
21
 
5
22
  ## 0.6.13
@@ -187,6 +187,14 @@ let UiEventType = /* @__PURE__ */ function(UiEventType) {
187
187
  * `preventDefault()` says it was taken.
188
188
  */
189
189
  UiEventType["Paste"] = "paste";
190
+ /**
191
+ * An IME composition on a `textInput` surface: opened, its text so
192
+ * far, and committed. A field's composition is the field's own and
193
+ * never reaches here.
194
+ */
195
+ UiEventType["CompositionStart"] = "compositionstart";
196
+ UiEventType["CompositionUpdate"] = "compositionupdate";
197
+ UiEventType["CompositionEnd"] = "compositionend";
190
198
  UiEventType["Click"] = "click";
191
199
  /**
192
200
  * A second Click on the same node, soon enough after the first and
@@ -412,10 +420,14 @@ var UiPinchEvent = class extends UiInputEvent {
412
420
  var UiKeyboardEvent = class extends UiInputEvent {
413
421
  key;
414
422
  modifiers;
415
- constructor(type, key, modifiers = noKeyModifiers()) {
423
+ textFollows;
424
+ code;
425
+ constructor(type, key, modifiers = noKeyModifiers(), textFollows = false, code = "") {
416
426
  super(type);
417
427
  this.key = key;
418
428
  this.modifiers = modifiers;
429
+ this.textFollows = textFollows;
430
+ this.code = code;
419
431
  }
420
432
  };
421
433
  /**
@@ -461,6 +473,23 @@ var UiPasteEvent = class extends UiInputEvent {
461
473
  this.html = html;
462
474
  }
463
475
  };
476
+ /**
477
+ * An IME composition on a focused `textInput` surface.
478
+ *
479
+ * `text` is the composition so far (the committed text at
480
+ * `CompositionEnd`, empty when it was cancelled) and `caret` the
481
+ * IME's caret within it. The surface draws the composition where its
482
+ * carets are and replaces it with the committed text at the end.
483
+ */
484
+ var UiCompositionEvent = class extends UiInputEvent {
485
+ text;
486
+ caret;
487
+ constructor(type, text = "", caret = text.length) {
488
+ super(type);
489
+ this.text = text;
490
+ this.caret = caret;
491
+ }
492
+ };
464
493
  var UiTextChangeEvent = class extends UiInputEvent {
465
494
  value;
466
495
  selectionStart;
@@ -2680,7 +2709,8 @@ function defineProperty(options) {
2680
2709
  compare: options.compare,
2681
2710
  environmentKey: options.environmentKey,
2682
2711
  resolveFromEnvironment: options.resolveFromEnvironment,
2683
- validate: options.validate
2712
+ validate: options.validate,
2713
+ narrowEffects: options.narrowEffects
2684
2714
  };
2685
2715
  }
2686
2716
  /**
@@ -4229,6 +4259,18 @@ const UiProperties = {
4229
4259
  affects: 64
4230
4260
  }),
4231
4261
  /**
4262
+ * Makes the node a text input surface: it keeps and draws its own
4263
+ * text, and takes typed text, IME composition and the clipboard
4264
+ * through the shell's editing proxy while it has focus. The function
4265
+ * says what the proxy mirrors. See `UiTextInput`.
4266
+ */
4267
+ textInput: defineProperty({
4268
+ name: "textInput",
4269
+ defaultValue: void 0,
4270
+ inherited: false,
4271
+ affects: 64
4272
+ }),
4273
+ /**
4232
4274
  * `'none'` takes the node and its whole subtree out of hit testing, to
4233
4275
  * block a region. Unlike CSS, a descendant cannot opt back in: its own
4234
4276
  * `'auto'` is ignored under a `'none'` ancestor. For a container that
@@ -4636,9 +4678,10 @@ const UiProperties = {
4636
4678
  *
4637
4679
  * Layout as well as paint, for the reason `text` carries layout: a
4638
4680
  * painter may declare an intrinsic size, and a node sized by its
4639
- * content has to be measured again when that content changes. A
4640
- * painted node with an explicit width and height is a relayout
4641
- * boundary, so a chart repainting on a stream re-measures one leaf.
4681
+ * content has to be measured again when that content changes. Only
4682
+ * then, though: a new picture that declares the same intrinsic size
4683
+ * as the old one (usually none) is a repaint and re-measures nothing,
4684
+ * however the node is sized (`narrowEffects`).
4642
4685
  *
4643
4686
  * See `rendering/PaintSurface.ts` for the vocabulary.
4644
4687
  */
@@ -4647,7 +4690,8 @@ const UiProperties = {
4647
4690
  defaultValue: void 0,
4648
4691
  inherited: false,
4649
4692
  affects: 6,
4650
- compare: paintValuesEqual
4693
+ compare: paintValuesEqual,
4694
+ narrowEffects: (previous, next, flags) => previous?.intrinsicWidth === next?.intrinsicWidth && previous?.intrinsicHeight === next?.intrinsicHeight ? flags & -5 : flags
4651
4695
  }),
4652
4696
  /** A static vector shape: `rendering/PaintSurface.ts`'s `UiPath`. */
4653
4697
  path: defineProperty({
@@ -4703,6 +4747,12 @@ function getPropertyNames() {
4703
4747
  return Array.from(registry.keys());
4704
4748
  }
4705
4749
  /**
4750
+ * The properties that narrow their effects for a particular change,
4751
+ * by name: few, so the graph's write path pays one map lookup and finds
4752
+ * nothing for nearly every property. See `narrowEffects`.
4753
+ */
4754
+ const propertyNarrowers = new Map([...registry.values()].filter((definition) => definition.narrowEffects !== void 0).map((definition) => [definition.name, definition.narrowEffects]));
4755
+ /**
4706
4756
  * Returns the dirty flags caused by changing the named property.
4707
4757
  *
4708
4758
  * Unknown properties are treated as generic property changes so
@@ -5067,19 +5117,22 @@ var UiGraph = class {
5067
5117
  }
5068
5118
  removeNode(node) {
5069
5119
  const parent = node.parent;
5120
+ const removed = [];
5070
5121
  const stack = [node];
5071
5122
  while (stack.length > 0) {
5072
5123
  const current = stack.pop();
5124
+ removed.push(current);
5073
5125
  this.unbindNode(current);
5074
5126
  this.unbindChildren(current);
5075
5127
  this.unbindEvents(current);
5076
- this.detachNode(current);
5077
5128
  this.nodes.delete(current.id);
5078
5129
  this.dirtyNodes.delete(current);
5079
5130
  for (let child = current.firstChild; child !== null; child = child.nextSibling) stack.push(child);
5080
5131
  }
5132
+ this.detachNode(node);
5081
5133
  if (parent !== null) this.markDirty(parent, 16);
5082
5134
  this.nodeRemovedListener?.(node);
5135
+ for (let i = removed.length - 1; i > 0; i--) this.detachNode(removed[i]);
5083
5136
  }
5084
5137
  detachNode(node) {
5085
5138
  const parent = node.parent;
@@ -5270,8 +5323,11 @@ var UiGraph = class {
5270
5323
  /**
5271
5324
  * Subscribes to node removals.
5272
5325
  *
5273
- * The listener is invoked once per removed node. Pass null to
5274
- * clear the subscription.
5326
+ * The listener is invoked once per removal, with the root of the
5327
+ * removed subtree. It is already out of the tree, and everything
5328
+ * that was under it is still linked under it, so a listener that
5329
+ * keeps state per node walks down from it. Pass null to clear the
5330
+ * subscription.
5275
5331
  */
5276
5332
  setNodeRemovedListener(listener) {
5277
5333
  this.nodeRemovedListener = listener;
@@ -5350,7 +5406,9 @@ var UiGraph = class {
5350
5406
  const previousValue = node.getProperty(property);
5351
5407
  if (Object.is(previousValue, value)) return false;
5352
5408
  node.setProperty(property, value);
5353
- this.markDirty(node, this.withProvidedFlags(property, dirtyFlags));
5409
+ const narrow = propertyNarrowers.get(property);
5410
+ const flags = narrow === void 0 ? dirtyFlags : narrow(previousValue, value, dirtyFlags);
5411
+ this.markDirty(node, this.withProvidedFlags(property, flags));
5354
5412
  return true;
5355
5413
  }
5356
5414
  /**
@@ -8969,6 +9027,13 @@ function comparePositions(a, b) {
8969
9027
  return 1;
8970
9028
  }
8971
9029
  //#endregion
9030
+ //#region src/editing/UiTextInput.ts
9031
+ /** The node's text input surface, if it is one. */
9032
+ function textInputOf(node) {
9033
+ const value = node.properties.get("textInput");
9034
+ return typeof value === "function" ? value : void 0;
9035
+ }
9036
+ //#endregion
8972
9037
  //#region src/selection/TextSelectionGeometry.ts
8973
9038
  const NO_RANGES = [];
8974
9039
  /**
@@ -10151,6 +10216,8 @@ var UiPointerController = class {
10151
10216
  return this.lastPosition;
10152
10217
  }
10153
10218
  lastPosition = null;
10219
+ /** The pressed node and its ancestors when the press began, deepest first. */
10220
+ downPath = [];
10154
10221
  get hoveredNode() {
10155
10222
  return this.hoverNode;
10156
10223
  }
@@ -10158,6 +10225,20 @@ var UiPointerController = class {
10158
10225
  get pressedNode() {
10159
10226
  return this.downTarget;
10160
10227
  }
10228
+ /**
10229
+ * Where the press's events go: the node it started on, or, once that
10230
+ * has been taken out of the tree, the nearest of its ancestors at the
10231
+ * time that is still in it. A row of a virtual list that scrolls out
10232
+ * of view in the middle of a drag-select is unmounted, and without
10233
+ * this the rest of the drag went to a node nothing could hear from;
10234
+ * the list it was in, and whoever is listening above it, still are.
10235
+ */
10236
+ captured() {
10237
+ const path = this.downPath;
10238
+ const root = path[path.length - 1];
10239
+ for (const node of path) if (topOf(node) === root) return node;
10240
+ return this.downTarget;
10241
+ }
10161
10242
  /** The scroll container whose thumb is being dragged, or null. */
10162
10243
  get draggingScrollbarOf() {
10163
10244
  return this.scrollbarDrag?.node ?? null;
@@ -10206,6 +10287,7 @@ var UiPointerController = class {
10206
10287
  }
10207
10288
  } else this.selection?.pointerDown(null, x, y, modifiers);
10208
10289
  this.downTarget = target;
10290
+ this.downPath = pathOf(target);
10209
10291
  this.downX = x;
10210
10292
  this.downY = y;
10211
10293
  this.downDefaultPrevented = event.defaultPrevented;
@@ -10231,10 +10313,11 @@ var UiPointerController = class {
10231
10313
  }
10232
10314
  if (this.downTarget !== null) {
10233
10315
  const event = new UiPointerEvent("pointermove", x, y, buttons, modifiers, pointer);
10234
- this.dispatcher.dispatch(event, this.downTarget);
10235
- this.gestures?.pointerMove(event, this.downTarget);
10316
+ const captured = this.captured();
10317
+ this.dispatcher.dispatch(event, captured);
10318
+ this.gestures?.pointerMove(event, captured);
10236
10319
  if (!event.defaultPrevented) {
10237
- if (this.editing !== void 0 && this.editing.isEditable(this.downTarget)) this.editing.pointerMove(this.downTarget, x, y);
10320
+ if (this.editing !== void 0 && this.editing.isEditable(captured)) this.editing.pointerMove(captured, x, y);
10238
10321
  else if (!this.downDefaultPrevented) this.selection?.pointerMove(x, y);
10239
10322
  }
10240
10323
  return event;
@@ -10266,9 +10349,10 @@ var UiPointerController = class {
10266
10349
  this.activePointer = null;
10267
10350
  return null;
10268
10351
  }
10269
- const target = this.downTarget;
10270
- if (target === null) return null;
10352
+ if (this.downTarget === null) return null;
10353
+ const target = this.captured();
10271
10354
  this.downTarget = null;
10355
+ this.downPath = [];
10272
10356
  this.activePointer = null;
10273
10357
  this.editing?.pointerUp();
10274
10358
  this.selection?.pointerUp();
@@ -10325,10 +10409,14 @@ var UiPointerController = class {
10325
10409
  this.scrollbarDrag = null;
10326
10410
  this.editing?.pointerUp();
10327
10411
  this.selection?.pointerUp();
10328
- const target = this.downTarget;
10412
+ if (this.downTarget === null) {
10413
+ this.activePointer = null;
10414
+ return;
10415
+ }
10416
+ const target = this.captured();
10329
10417
  this.downTarget = null;
10418
+ this.downPath = [];
10330
10419
  this.activePointer = null;
10331
- if (target === null) return;
10332
10420
  const event = new UiPointerEvent("pointercancel", this.downX, this.downY, 0, noKeyModifiers(), pointer);
10333
10421
  this.dispatcher.dispatch(event, target);
10334
10422
  this.gestures?.pointerCancel();
@@ -10473,6 +10561,18 @@ const MULTI_CLICK_SLOP$1 = 4;
10473
10561
  function defaultClock$2() {
10474
10562
  return typeof performance !== "undefined" ? performance.now() : Date.now();
10475
10563
  }
10564
+ /** A node and its ancestors, deepest first. */
10565
+ function pathOf(node) {
10566
+ const path = [];
10567
+ for (let at = node; at !== null; at = at.parent) path.push(at);
10568
+ return path;
10569
+ }
10570
+ /** The top of the tree a node is in now. */
10571
+ function topOf(node) {
10572
+ let at = node;
10573
+ while (at.parent !== null) at = at.parent;
10574
+ return at;
10575
+ }
10476
10576
  //#endregion
10477
10577
  //#region src/input/UiKeyboardController.ts
10478
10578
  /**
@@ -10533,12 +10633,14 @@ var UiKeyboardController = class {
10533
10633
  * an editable held the focus — has the key inserted here, whatever the
10534
10634
  * text source; absent leaves it to the editing controller's setting.
10535
10635
  */
10536
- keyDown(key, modifiers = noKeyModifiers(), textFollows) {
10636
+ keyDown(key, modifiers = noKeyModifiers(), textFollows, code = "") {
10537
10637
  if (!MODIFIER_KEYS.has(key)) this.focusManager.noteInput("keyboard");
10538
- const event = new UiKeyboardEvent("keydown", key, modifiers);
10638
+ const follows = textFollows ?? this.editing?.textFromKeys === false;
10639
+ const event = new UiKeyboardEvent("keydown", key, modifiers, follows, code);
10539
10640
  const focused = this.focusManager.focusedNode;
10540
10641
  const target = focused ?? this.root();
10541
10642
  this.dispatcher.dispatch(event, target);
10643
+ this.editing?.noteKey?.(follows && event.defaultPrevented);
10542
10644
  if (!event.defaultPrevented && focused !== null && this.editing !== void 0 && this.editing.handleKey(focused, key, modifiers, textFollows === false ? true : void 0)) {
10543
10645
  event.preventDefault();
10544
10646
  return event;
@@ -10566,8 +10668,9 @@ var UiKeyboardController = class {
10566
10668
  }
10567
10669
  return event;
10568
10670
  }
10569
- keyUp(key, modifiers = noKeyModifiers()) {
10570
- const event = new UiKeyboardEvent("keyup", key, modifiers);
10671
+ keyUp(key, modifiers = noKeyModifiers(), code = "") {
10672
+ this.editing?.noteKey?.(false);
10673
+ const event = new UiKeyboardEvent("keyup", key, modifiers, false, code);
10571
10674
  const target = this.focusManager.focusedNode ?? this.root();
10572
10675
  this.dispatcher.dispatch(event, target);
10573
10676
  return event;
@@ -11727,6 +11830,15 @@ var UiEditingController = class {
11727
11830
  platform;
11728
11831
  paint = createPaintState();
11729
11832
  focusedEditable = null;
11833
+ /**
11834
+ * The focused `textInput` surface, which keeps its own text: input is
11835
+ * dispatched to it as events rather than applied here. See `UiTextInput`.
11836
+ */
11837
+ surface = null;
11838
+ /** The surface's open composition's text, or null while none is open. */
11839
+ surfaceComposition = null;
11840
+ /** The last key was cancelled, so the text that follows it is dropped. See `noteKey`. */
11841
+ textCancelled = false;
11730
11842
  visible = true;
11731
11843
  /** x the current run of vertical moves keeps returning to. */
11732
11844
  verticalGoalX = void 0;
@@ -11773,6 +11885,20 @@ var UiEditingController = class {
11773
11885
  get focused() {
11774
11886
  return this.focusedEditable;
11775
11887
  }
11888
+ /** The `textInput` surface that holds focus, or null. */
11889
+ get focusedSurface() {
11890
+ return this.surface;
11891
+ }
11892
+ /**
11893
+ * Each key down says whether a listener cancelled it, and each key up
11894
+ * that it is over. A cancelled key's text, which the shell's proxy
11895
+ * sends after it whatever happened (it cannot wait for the worker's
11896
+ * answer), is dropped when it arrives, as a browser drops the text of
11897
+ * a keydown whose default was prevented.
11898
+ */
11899
+ noteKey(cancelled) {
11900
+ this.textCancelled = cancelled;
11901
+ }
11776
11902
  /** Whether the pointer controller should hand presses on the node to this controller. */
11777
11903
  isEditable(node) {
11778
11904
  return isEditableNode(node);
@@ -11926,6 +12052,14 @@ var UiEditingController = class {
11926
12052
  * twice.
11927
12053
  */
11928
12054
  beforeInput(inputType, data) {
12055
+ if (this.textCancelled && inputType.startsWith("insert")) {
12056
+ this.textCancelled = false;
12057
+ return true;
12058
+ }
12059
+ if (this.surface !== null) {
12060
+ this.toSurface(new UiBeforeInputEvent(inputType, data));
12061
+ return true;
12062
+ }
11929
12063
  const node = this.focusedEditable;
11930
12064
  if (node === null) return false;
11931
12065
  const model = editorFor(node);
@@ -11973,6 +12107,10 @@ var UiEditingController = class {
11973
12107
  }
11974
12108
  /** Inserts text at the selection of the focused editable, as typing would. */
11975
12109
  insertText(text) {
12110
+ if (this.surface !== null) {
12111
+ this.toSurface(new UiBeforeInputEvent("insertText", text));
12112
+ return true;
12113
+ }
11976
12114
  const node = this.focusedEditable;
11977
12115
  if (node === null || isReadOnly(node)) return false;
11978
12116
  return this.insert(node, editorFor(node), "insertText", text);
@@ -12021,6 +12159,11 @@ var UiEditingController = class {
12021
12159
  return this.applyEdit(node, model, inputType, data, () => model.insertText(data), html);
12022
12160
  }
12023
12161
  compositionStart() {
12162
+ if (this.surface !== null) {
12163
+ this.surfaceComposition = "";
12164
+ this.toSurface(new UiCompositionEvent("compositionstart"));
12165
+ return;
12166
+ }
12024
12167
  if (this.span !== null) this.spanEdit("deleteContent", null);
12025
12168
  const node = this.focusedEditable;
12026
12169
  if (node === null || isReadOnly(node)) return;
@@ -12032,6 +12175,12 @@ var UiEditingController = class {
12032
12175
  }
12033
12176
  /** The composition text so far and the caret within it. */
12034
12177
  compositionUpdate(text, caret = text.length) {
12178
+ if (this.surface !== null) {
12179
+ if (this.surfaceComposition === null) this.toSurface(new UiCompositionEvent("compositionstart"));
12180
+ this.surfaceComposition = text;
12181
+ this.toSurface(new UiCompositionEvent("compositionupdate", text, caret));
12182
+ return;
12183
+ }
12035
12184
  const node = this.focusedEditable;
12036
12185
  if (node === null || isReadOnly(node)) return;
12037
12186
  const model = editorFor(node);
@@ -12041,6 +12190,11 @@ var UiEditingController = class {
12041
12190
  }
12042
12191
  /** The IME committed `text` (possibly empty: the composition was cancelled). */
12043
12192
  compositionEnd(text) {
12193
+ if (this.surface !== null) {
12194
+ this.surfaceComposition = null;
12195
+ this.toSurface(new UiCompositionEvent("compositionend", text));
12196
+ return;
12197
+ }
12044
12198
  const node = this.focusedEditable;
12045
12199
  this.compositionOpen = false;
12046
12200
  if (node === null || isReadOnly(node)) return;
@@ -12181,6 +12335,7 @@ var UiEditingController = class {
12181
12335
  }
12182
12336
  /** The focused editable's state for the shell to mirror, or null. */
12183
12337
  state() {
12338
+ if (this.surface !== null) return this.surfaceState(this.surface);
12184
12339
  const node = this.focusedEditable;
12185
12340
  if (node === null || this.host.recordFor(node) === void 0) return null;
12186
12341
  const model = editorFor(node);
@@ -12245,6 +12400,14 @@ var UiEditingController = class {
12245
12400
  }
12246
12401
  handleFocusChange(node) {
12247
12402
  if (this.span !== null && !this.spanFocusing && node !== this.span.focus.node) this.clearSpan();
12403
+ const surface = node !== null && !isEditableNode(node) && textInputOf(node) !== void 0 ? node : null;
12404
+ if (surface !== this.surface) {
12405
+ const left = this.surface;
12406
+ const open = this.surfaceComposition;
12407
+ this.surface = surface;
12408
+ this.surfaceComposition = null;
12409
+ if (left !== null && open !== null) this.dispatcher.dispatch(new UiCompositionEvent("compositionend", open), left);
12410
+ }
12248
12411
  const previous = this.focusedEditable;
12249
12412
  const next = node !== null && isEditableNode(node) ? node : null;
12250
12413
  if (previous === next) return;
@@ -12266,6 +12429,25 @@ var UiEditingController = class {
12266
12429
  this.host.markDirty(next, 2);
12267
12430
  }
12268
12431
  }
12432
+ /** Hands an input event to the focused surface. */
12433
+ toSurface(event) {
12434
+ if (this.surface !== null) this.dispatcher.dispatch(event, this.surface);
12435
+ }
12436
+ /** What the shell mirrors for a focused surface: what it says, with its caret placed on the canvas. */
12437
+ surfaceState(node) {
12438
+ const source = textInputOf(node);
12439
+ if (source === void 0 || this.host.recordFor(node) === void 0) return null;
12440
+ const surface = source();
12441
+ return {
12442
+ text: surface.text,
12443
+ selectionStart: surface.selectionStart,
12444
+ selectionEnd: surface.selectionEnd,
12445
+ caret: this.drawnBox(node, surface.caret),
12446
+ multiline: surface.multiline ?? true,
12447
+ composing: this.surfaceComposition !== null,
12448
+ ...surface.clipboard === void 0 ? {} : { clipboard: surface.clipboard }
12449
+ };
12450
+ }
12269
12451
  /**
12270
12452
  * Runs an edit unless a `beforeinput` listener cancels it, then
12271
12453
  * reports it. Returns true when the input was consumed either way.
@@ -12679,11 +12861,13 @@ var UiPlatformAdapter = class {
12679
12861
  };
12680
12862
  this.keyDownHandler = (e) => {
12681
12863
  const k = e;
12682
- if (this.keyboard.keyDown(k.key, modifiersFromEvent(k)).defaultPrevented) k.preventDefault();
12864
+ if (isImeKey(k)) return;
12865
+ if (this.keyboard.keyDown(k.key, modifiersFromEvent(k), void 0, k.code ?? "").defaultPrevented) k.preventDefault();
12683
12866
  };
12684
12867
  this.keyUpHandler = (e) => {
12685
12868
  const k = e;
12686
- this.keyboard.keyUp(k.key, modifiersFromEvent(k));
12869
+ if (isImeKey(k)) return;
12870
+ this.keyboard.keyUp(k.key, modifiersFromEvent(k), k.code ?? "");
12687
12871
  };
12688
12872
  surface.pointerTarget.addEventListener("pointerdown", this.pointerDownHandler);
12689
12873
  surface.pointerTarget.addEventListener("pointermove", this.pointerMoveHandler);
@@ -12763,6 +12947,17 @@ function capturePointer(target, pointerId) {
12763
12947
  element.setPointerCapture(pointerId);
12764
12948
  } catch {}
12765
12949
  }
12950
+ /**
12951
+ * Whether a key belongs to an IME rather than to the application: one
12952
+ * pressed while a composition is open (the arrows that move through
12953
+ * candidates, the Enter that commits one, the Backspace that edits the
12954
+ * reading) or the key that opens one, which browsers report with
12955
+ * `keyCode` 229. Its effect arrives as composition events; handled as a
12956
+ * key too, the Enter that commits a candidate would also insert a line.
12957
+ */
12958
+ function isImeKey(event) {
12959
+ return event.isComposing === true || event.keyCode === 229;
12960
+ }
12766
12961
  function modifiersFromEvent(event) {
12767
12962
  return {
12768
12963
  shift: event.shiftKey,
@@ -21222,6 +21417,6 @@ function createCanvasSurface(host) {
21222
21417
  return new CanvasSurface(host);
21223
21418
  }
21224
21419
  //#endregion
21225
- export { pointerDeviceOf as $, UiKeyboardEvent as $a, boxShadow as $i, lineIndexAtY as $n, radialGradient as $r, DEFAULT_FONT_SIZE as $t, withOverrideSource as A, rgb8 as Aa, darkTheme as Ai, linkRunAtPointIn as An, UiProperties as Ar, edgeField as At, FlexDirection as B, writeDeclaredProperty as Ba, withThemeExtension as Bi, isReadOnly as Bn, validateSubgrid as Br, fillTextRects as Bt, SCROLLBAR_LINGER_MS as C, UiBasicColors as Ca, insetsEqual as Ci, isNodeTabStop as Cn, findPropertyDefinition as Cr, offsetAtPointIn as Ct, formatExplanation as D, colorsEqual as Da, bandOf as Di, hasTextLinks as Dn, propertyEffects as Dr, wordRangeIn as Dt, formatConstraints as E, colorValuesEqual as Ea, UiContainerSizeSource as Ei, clearLinkHover as En, inheritedPropertyNames as Er, selectionRectsIn as Et, clampSize as F, UiNode as Fa, defineThemeExtension as Fi, caretVisibleAt as Fn, normalizeStates as Fr, EditableLayout as Ft, CrossAxisAlignment as G, visualState as Ga, densityFactors as Gi, videoFrameSize as Gn, parseTransform as Gr, textMeasureRequest as Gt, placeGridItems as H, UiVisualState as Ha, highContrastColors as Hi, syncEditorValue as Hn, paintValuesEqual as Hr, runCanvasStyleInto as Ht, constraintsEqual as I, DirtyNodeSet as Ia, hasThemeExtension as Ii, editorFor as In, statesEqual as Ir, applyCanvasTextStyle as It, parseCrossAxisAlignment as J, UiBeforeInputEvent as Ja, spacingEqual as Ji, spannedRunsFor as Jn, transformsEqual as Jr, bumpFontStack as Jt, MainAxisAlignment as K, visualStatesEqual as Ka, defaultSpacing as Ki, CharacterCountTextMeasurer as Kn, transform as Kr, textRunBackgrounds as Kt, tightenConstraints as L, clearOverrideProperty as La, noThemeExtensions as Li, editorOf as Ln, validateRole as Lr, buildFontString as Lt, contentOffset as M, createEnvironmentKey as Ma, themesEqual as Mi, CARET_BLINK_MS as Mn, UI_SEMANTIC_STATES as Mr, commandForKey as Mt, LayoutRecord as N, findEnvironmentKey as Na, withContrast as Ni, CARET_WIDTH as Nn, isUiRole as Nr, detectEditingPlatform as Nt, formatNumber as O, normalizeColor as Oa, containerBands as Oi, linkHoverOf as On, propertyIsInherited as Or, adjacentField as Ot, Constraints as P, UiEnvironment as Pa, withDensity as Pi, EDITOR_PROP as Pn, isUiSemanticState as Pr, isPrintable as Pt, capturePointer as Q, UiInputEvent as Qa, BOX_SHADOW_BLUR_REACH as Qi, caretRectFor as Qn, linearGradient as Qr, DEFAULT_FONT_FAMILY as Qt, MAX_MEASURES_PER_CHILD as R, overrideSources as Ra, themeExtension as Ri, isEditableNode as Rn, validateStates as Rr, createCanvasTextStyle as Rt, SCROLLBAR_FADE_MS as S, lightColors as Sa, UiInsetRegistry as Si, isNodeSelectable as Sn, closestPropertyName as Sr, hasDrawnText as St, describeOverrides as T, colorToRgba as Ta, observeViewportInsets as Ti, resolveCursor as Tn, inheritedPropertyFlags as Tr, paragraphGeometryFrom as Tt, sizeGridTracks as U, defaultVisualState as Ua, raiseContrast as Ui, resolveColorValue as Un, pathValuesEqual as Ur, runFontString as Ut, parseFlexDirection as V, writeOverrideProperty as Va, contrastRatio as Vi, nextCaretToggle as Vn, inputsEqual as Vr, layoutTextLines as Vt, AlignContent as W, hasVisualState as Wa, relativeLuminance as Wi, isVideoSurface as Wn, UiTransforms as Wr, spanOf as Wt, CanvasPlatformSurface as X, UiFocusEvent as Xa, defaultShadows as Xi, layoutParagraph as Xn, gradientPaint as Xr, fontStackFor as Xt, parseMainAxisAlignment as Y, UiEventType as Ya, spacingSteps as Yi, ELLIPSIS as Yn, MAX_GRADIENT_STOPS as Yr, clearFontStacks as Yt, UiPlatformAdapter as Z, UiGestureEvent as Za, shadowsEqual as Zi, proportionalFontMetrics as Zn, gradientsEqual as Zr, registerFontStack as Zt, LABEL_PADDING_X as _, textContentOf as _a, normalizeBorderRadius as _i, selectionRangeOf as _n, resolveBoolean as _r, scrollRange as _t, PaintPictureCache as a, isShapeName as aa, isMinMaxTrack as ai, isPaintVisible as an, UiWheelDeltaMode as ao, selectionRects as ar, UiPinchRecognizer as at, snapScroll as b, colorsEqualPalette as ba, propertyValuesEqual as bi, isNodeHitTestable as bn, resolvePropertyByName as br, scrollbarZoneAt as bt, EMPTY_RECORDING as c, isTypographyRole as ca, percent as ci, resolvePaintState as cn, wheelDeltaYOf as co, lineEndAt as cr, UiKeyboardController as ct, tracePathData as d, editableSpansOf as da, UiBorderRadiuses as di, matchRangesOf as dn, UiBinding as do, nextWordEnd as dr, UiHitTester as dt, boxShadowArraysEqual as ea, validateGradient as ei, DEFAULT_FONT_WEIGHT as en, UiPasteEvent as eo, lineIndexForOffset as er, prepareInputSurface as et, ScaledImageCache as f, flattenTextSpans as fa, borderRadius as fi, setMatchRanges as fn, previousGraphemeStart as fr, SCROLLBAR_HOVER_ZONE as ft, paintsAfterChildren as g, spannedTextOf as ga, borderRadiusValuesEqual as gi, selectableTextOf as gn, DEFAULT_LINE_HEIGHT_FACTOR as gr, scrollLeaderOf as gt, hasDecorationPhase as h, spanAtOffset as ha, borderRadiusIsZero as hi, selectableTextNodes as hn, UiGraph as hr, SCROLLBAR_THICKNESS as ht, traceRoundedRect as i, defaultShapes as ia, isFrLength as ii, createPaintState as in, UiTextChangeEvent as io, offsetForVerticalMove as ir, UiGestureRecognizer as it, accumulatedOffsetTo as j, rgba as ja, lightTheme as ji, setLinkHover as jn, UI_ROLES as jr, editingGroupOf as jt, labelNode as k, parseColor as ka, unknownContainerSize as ki, linkOf as kn, propertyValuesEqualByName as kr, comparePositions as kt, PaintRecorder as l, typographyEqual as la, repeat as li, TEXT_MATCHES_PROP as ln, UiNodeType as lo, lineStartAt as lr, UiPointerController as lt, decorationRect as m, resolvedSpansOf as ma, borderRadiusEqual as mi, clearSelectionRange as mn, wordRangeAt as mr, SCROLLBAR_MIN_THUMB as mt, createCanvasSurface as n, boxShadowReach as na, fr as ni, colorToCss as nn, UiPointerEvent as no, offsetAtPoint as nr, UiEditingController as nt, paintPictures as o, shapesEqual as oa, isPercentLength as oi, normalizeTextAlign as on, UiWheelEvent as oo, EditableTextModel as or, UiWheelController as ot, decorationColor as p, hiddenRangesOf as pa, borderRadiusCorners as pi, TEXT_SELECTION_PROP as pn, previousWordStart as pr, SCROLLBAR_INSET as pt, parseAlignContent as q, MOUSE_POINTER as qa, scaleSpacing as qi, ParagraphTextMeasurer as qn, transformIsIdentity as qr, textRunDecorations as qt, Canvas2DRenderer as r, boxShadowsEqual as ra, isAutoLength as ri, computeObjectFitRect as rn, UiSelectionChangeEvent as ro, offsetAtX as rr, UiTouchScroller as rt, PaintTarget as s, defaultTypography as sa, minmax as si, normalizeVerticalAlign as sn, noKeyModifiers as so, graphemeBoundaries as sr, isScrollContainer as st, CanvasSurface as t, boxShadowRadius as ta, auto as ti, DEFAULT_TEXT_COLOR as tn, UiPinchEvent as to, lineLimit as tr, touchActionFor as tt, replayPaint as u, defaultTextStyle as ua, resolveLength as ui, clearMatchRanges as un, DirtyFlags as uo, nextGraphemeEnd as ur, UiFocusManager as ut, drawOverlayShapes as v, textSpansEqual as va, uniformBorderRadius as vi, setSelectionRange as vn, resolveNumber as vr, scrollbarThumb as vt, describeLength as w, colorToHex as wa, noInsets as wi, isPressable as wn, getPropertyNames as wr, paragraphGeometry as wt, LayoutEngine as x, darkColors as xa, UiEnvironmentKeys as xi, isNodeInert as xn, resolveString as xr, UiInputDispatcher as xt, labelOrigin as y, textStylesEqual as ya, defineProperty as yi, isNodeFocusable as yn, resolveProperty as yr, scrollbarThumbs as yt, isLayoutProtocol as z, resetOverrideWarnings as za, themeExtensionsEqual as zi, isMultiline as zn, UI_SUBGRID_AXES as zr, drawText as zt };
21420
+ export { isImeKey as $, UiEventType as $a, defaultShadows as $i, proportionalFontMetrics as $n, gradientPaint as $r, registerFontStack as $t, withOverrideSource as A, colorsEqual as Aa, bandOf as Ai, linkHoverOf as An, propertyIsInherited as Ar, adjacentField as At, FlexDirection as B, clearOverrideProperty as Ba, noThemeExtensions as Bi, isEditableNode as Bn, validateRole as Br, createCanvasTextStyle as Bt, SCROLLBAR_LINGER_MS as C, colorsEqualPalette as Ca, propertyValuesEqual as Ci, isNodeInert as Cn, resolveString as Cr, hasDrawnText as Ct, formatExplanation as D, colorToHex as Da, noInsets as Di, resolveCursor as Dn, inheritedPropertyFlags as Dr, selectionRectsIn as Dt, formatConstraints as E, UiBasicColors as Ea, insetsEqual as Ei, isPressable as En, getPropertyNames as Er, paragraphGeometryFrom as Et, clampSize as F, createEnvironmentKey as Fa, themesEqual as Fi, CARET_WIDTH as Fn, UI_SEMANTIC_STATES as Fr, detectEditingPlatform as Ft, CrossAxisAlignment as G, UiVisualState as Ga, highContrastColors as Gi, resolveColorValue as Gn, paintValuesEqual as Gr, runFontString as Gt, placeGridItems as H, resetOverrideWarnings as Ha, themeExtensionsEqual as Hi, isReadOnly as Hn, UI_SUBGRID_AXES as Hr, fillTextRects as Ht, constraintsEqual as I, findEnvironmentKey as Ia, withContrast as Ii, EDITOR_PROP as In, isUiRole as Ir, isPrintable as It, parseCrossAxisAlignment as J, visualState as Ja, densityFactors as Ji, CharacterCountTextMeasurer as Jn, parseTransform as Jr, textRunBackgrounds as Jt, MainAxisAlignment as K, defaultVisualState as Ka, raiseContrast as Ki, isVideoSurface as Kn, pathValuesEqual as Kr, spanOf as Kt, tightenConstraints as L, UiEnvironment as La, withDensity as Li, caretVisibleAt as Ln, isUiSemanticState as Lr, EditableLayout as Lt, contentOffset as M, parseColor as Ma, unknownContainerSize as Mi, linkRunAtPointIn as Mn, propertyValuesEqualByName as Mr, edgeField as Mt, LayoutRecord as N, rgb8 as Na, darkTheme as Ni, setLinkHover as Nn, UiProperties as Nr, editingGroupOf as Nt, formatNumber as O, colorToRgba as Oa, observeViewportInsets as Oi, clearLinkHover as On, inheritedPropertyNames as Or, wordRangeIn as Ot, Constraints as P, rgba as Pa, lightTheme as Pi, CARET_BLINK_MS as Pn, UI_ROLES as Pr, commandForKey as Pt, capturePointer as Q, UiCompositionEvent as Qa, spacingSteps as Qi, layoutParagraph as Qn, MAX_GRADIENT_STOPS as Qr, fontStackFor as Qt, MAX_MEASURES_PER_CHILD as R, UiNode as Ra, defineThemeExtension as Ri, editorFor as Rn, normalizeStates as Rr, applyCanvasTextStyle as Rt, SCROLLBAR_FADE_MS as S, textStylesEqual as Sa, defineProperty as Si, isNodeHitTestable as Sn, resolvePropertyByName as Sr, UiInputDispatcher as St, describeOverrides as T, lightColors as Ta, UiInsetRegistry as Ti, isNodeTabStop as Tn, findPropertyDefinition as Tr, paragraphGeometry as Tt, sizeGridTracks as U, writeDeclaredProperty as Ua, withThemeExtension as Ui, nextCaretToggle as Un, validateSubgrid as Ur, layoutTextLines as Ut, parseFlexDirection as V, overrideSources as Va, themeExtension as Vi, isMultiline as Vn, validateStates as Vr, drawText as Vt, AlignContent as W, writeOverrideProperty as Wa, contrastRatio as Wi, syncEditorValue as Wn, inputsEqual as Wr, runCanvasStyleInto as Wt, CanvasPlatformSurface as X, MOUSE_POINTER as Xa, scaleSpacing as Xi, spannedRunsFor as Xn, transformIsIdentity as Xr, bumpFontStack as Xt, parseMainAxisAlignment as Y, visualStatesEqual as Ya, defaultSpacing as Yi, ParagraphTextMeasurer as Yn, transform as Yr, textRunDecorations as Yt, UiPlatformAdapter as Z, UiBeforeInputEvent as Za, spacingEqual as Zi, ELLIPSIS as Zn, transformsEqual as Zr, clearFontStacks as Zt, LABEL_PADDING_X as _, resolvedSpansOf as _a, borderRadiusEqual as _i, selectableTextNodes as _n, UiGraph as _r, scrollLeaderOf as _t, PaintPictureCache as a, boxShadowReach as aa, fr as ai, computeObjectFitRect as an, UiPinchEvent as ao, offsetAtX as ar, UiGestureRecognizer as at, snapScroll as b, textContentOf as ba, normalizeBorderRadius as bi, setSelectionRange as bn, resolveNumber as br, scrollbarThumbs as bt, EMPTY_RECORDING as c, isShapeName as ca, isMinMaxTrack as ci, normalizeTextAlign as cn, UiTextChangeEvent as co, EditableTextModel as cr, isScrollContainer as ct, tracePathData as d, isTypographyRole as da, percent as di, TEXT_MATCHES_PROP as dn, noKeyModifiers as do, lineStartAt as dr, UiFocusManager as dt, shadowsEqual as ea, gradientsEqual as ei, DEFAULT_FONT_FAMILY as en, UiFocusEvent as eo, caretRectFor as er, pointerDeviceOf as et, ScaledImageCache as f, typographyEqual as fa, repeat as fi, clearMatchRanges as fn, wheelDeltaYOf as fo, nextGraphemeEnd as fr, UiHitTester as ft, paintsAfterChildren as g, hiddenRangesOf as ga, borderRadiusCorners as gi, clearSelectionRange as gn, wordRangeAt as gr, SCROLLBAR_THICKNESS as gt, hasDecorationPhase as h, flattenTextSpans as ha, borderRadius as hi, TEXT_SELECTION_PROP as hn, UiBinding as ho, previousWordStart as hr, SCROLLBAR_MIN_THUMB as ht, traceRoundedRect as i, boxShadowRadius as ia, auto as ii, colorToCss as in, UiPasteEvent as io, offsetAtPoint as ir, UiTouchScroller as it, accumulatedOffsetTo as j, normalizeColor as ja, containerBands as ji, linkOf as jn, propertyNarrowers as jr, comparePositions as jt, labelNode as k, colorValuesEqual as ka, UiContainerSizeSource as ki, hasTextLinks as kn, propertyEffects as kr, textInputOf as kt, PaintRecorder as l, shapesEqual as la, isPercentLength as li, normalizeVerticalAlign as ln, UiWheelDeltaMode as lo, graphemeBoundaries as lr, UiKeyboardController as lt, decorationRect as m, editableSpansOf as ma, UiBorderRadiuses as mi, setMatchRanges as mn, DirtyFlags as mo, previousGraphemeStart as mr, SCROLLBAR_INSET as mt, createCanvasSurface as n, boxShadow as na, radialGradient as ni, DEFAULT_FONT_WEIGHT as nn, UiInputEvent as no, lineIndexForOffset as nr, touchActionFor as nt, paintPictures as o, boxShadowsEqual as oa, isAutoLength as oi, createPaintState as on, UiPointerEvent as oo, offsetForVerticalMove as or, UiPinchRecognizer as ot, decorationColor as p, defaultTextStyle as pa, resolveLength as pi, matchRangesOf as pn, UiNodeType as po, nextWordEnd as pr, SCROLLBAR_HOVER_ZONE as pt, parseAlignContent as q, hasVisualState as qa, relativeLuminance as qi, videoFrameSize as qn, UiTransforms as qr, textMeasureRequest as qt, Canvas2DRenderer as r, boxShadowArraysEqual as ra, validateGradient as ri, DEFAULT_TEXT_COLOR as rn, UiKeyboardEvent as ro, lineLimit as rr, UiEditingController as rt, PaintTarget as s, defaultShapes as sa, isFrLength as si, isPaintVisible as sn, UiSelectionChangeEvent as so, selectionRects as sr, UiWheelController as st, CanvasSurface as t, BOX_SHADOW_BLUR_REACH as ta, linearGradient as ti, DEFAULT_FONT_SIZE as tn, UiGestureEvent as to, lineIndexAtY as tr, prepareInputSurface as tt, replayPaint as u, defaultTypography as ua, minmax as ui, resolvePaintState as un, UiWheelEvent as uo, lineEndAt as ur, UiPointerController as ut, drawOverlayShapes as v, spanAtOffset as va, borderRadiusIsZero as vi, selectableTextOf as vn, DEFAULT_LINE_HEIGHT_FACTOR as vr, scrollRange as vt, describeLength as w, darkColors as wa, UiEnvironmentKeys as wi, isNodeSelectable as wn, closestPropertyName as wr, offsetAtPointIn as wt, LayoutEngine as x, textSpansEqual as xa, uniformBorderRadius as xi, isNodeFocusable as xn, resolveProperty as xr, scrollbarZoneAt as xt, labelOrigin as y, spannedTextOf as ya, borderRadiusValuesEqual as yi, selectionRangeOf as yn, resolveBoolean as yr, scrollbarThumb as yt, isLayoutProtocol as z, DirtyNodeSet as za, hasThemeExtension as zi, editorOf as zn, statesEqual as zr, buildFontString as zt };
21226
21421
 
21227
- //# sourceMappingURL=CanvasSurface-CLDORkZo.js.map
21422
+ //# sourceMappingURL=CanvasSurface-Dz-AXwa4.js.map