gesso-core 0.6.20 → 0.6.21

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.
@@ -881,9 +881,24 @@ var UiNode = class {
881
881
  * opacity → 0.5
882
882
  * visible → true
883
883
  * width → 200
884
+ *
885
+ * Read it directly; write it through `setProperty` and
886
+ * `removeProperty`, which count the change in `propertyVersion`.
884
887
  */
885
888
  properties = /* @__PURE__ */ new Map();
886
889
  /**
890
+ * Counts writes to `properties`.
891
+ *
892
+ * Lets something derived from a node's own properties tell that it
893
+ * is still right without reading them again. The layout engine
894
+ * resolves forty of them into each record, and on a full pass over a
895
+ * tree where nothing changed that reading was most of a sixth of the
896
+ * pass. A counter is exact where a dirty flag is not: flags are
897
+ * cleared by whichever pass runs next, and a record is resolved by
898
+ * whichever pass reaches it.
899
+ */
900
+ propertyVersion = 0;
901
+ /**
887
902
  * Values a modifier has written over the element's own.
888
903
  *
889
904
  * Null until the first modifier writes, so a tree without modifiers
@@ -929,6 +944,13 @@ var UiNode = class {
929
944
  }
930
945
  setProperty(property, value) {
931
946
  this.properties.set(property, value);
947
+ this.propertyVersion++;
948
+ }
949
+ /** Removes a property; whether it was there. */
950
+ removeProperty(property) {
951
+ if (!this.properties.delete(property)) return false;
952
+ this.propertyVersion++;
953
+ return true;
932
954
  }
933
955
  };
934
956
  //#endregion
@@ -1613,6 +1635,7 @@ function resolvedSpansOf(node) {
1613
1635
  * as soon as the application catches up.
1614
1636
  */
1615
1637
  function editableSpansOf(node, text, composition) {
1638
+ if (node.properties.get("secret") === true) return EMPTY_SPANS;
1616
1639
  const spanned = spannedTextOf(node);
1617
1640
  if (spanned === void 0) return EMPTY_SPANS;
1618
1641
  if (spanned.text === text) return spanned.spans;
@@ -4219,6 +4242,17 @@ const UiProperties = {
4219
4242
  inherited: false,
4220
4243
  affects: 64
4221
4244
  }),
4245
+ /**
4246
+ * A password field: every character is drawn as a bullet, copy and
4247
+ * cut take nothing, and the shell edits it through a password input.
4248
+ * The value itself is unchanged. Single-line only.
4249
+ */
4250
+ secret: defineProperty({
4251
+ name: "secret",
4252
+ defaultValue: void 0,
4253
+ inherited: false,
4254
+ affects: 262
4255
+ }),
4222
4256
  /** Colour of the caret; defaults to the text colour. */
4223
4257
  caretColor: defineProperty({
4224
4258
  name: "caretColor",
@@ -4966,39 +5000,6 @@ function createFallbackKey(definition) {
4966
5000
  * no line height is set. Shared by ParagraphLayout and PaintState.
4967
5001
  */
4968
5002
  const DEFAULT_LINE_HEIGHT_FACTOR = 1.2;
4969
- /**
4970
- * Resolves a node's font the one way layout and paint both use.
4971
- *
4972
- * Font size, family, weight and letter spacing inherit through the
4973
- * environment's text style. Line height is the subtle one: a text
4974
- * style carries a line height that belongs with its font size, so a
4975
- * node that inherits the style takes it whole. A node that sets its
4976
- * own `fontSize` without a `lineHeight` gets a normal line height for
4977
- * that size, `fontSize × 1.2`, rather than the inherited pixel value
4978
- * sized for a different font — the inherited 16.8px of the default
4979
- * style is right for 14px text and wrong for 22px. An explicit
4980
- * `lineHeight` always wins.
4981
- *
4982
- * Before this, layout read the raw props (unset line height → ×1.2)
4983
- * and paint read the inherited ones (→ 16.8px), so any text whose size
4984
- * differed from the style's was measured in one line box and drawn in
4985
- * another, and sat high or low inside it.
4986
- */
4987
- function resolveFont(node) {
4988
- const out = {
4989
- fontSize: 0,
4990
- fontFamily: "",
4991
- fontWeight: "normal",
4992
- lineHeight: 0,
4993
- letterSpacing: 0,
4994
- fontStyle: "normal",
4995
- fontStretch: "normal",
4996
- fontVariant: "normal",
4997
- fontKerning: "auto"
4998
- };
4999
- resolveFontInto(node, out);
5000
- return out;
5001
- }
5002
5003
  /** `resolveFont`, writing into a target the caller owns. */
5003
5004
  function resolveFontInto(node, out) {
5004
5005
  const fontSize = positive$1(resolveProperty(node, UiProperties.fontSize)) ?? UiProperties.fontSize.defaultValue;
@@ -5473,7 +5474,7 @@ var UiGraph = class {
5473
5474
  applyResolvedProperty(node, property, present, value, dirtyFlags) {
5474
5475
  if (!present) {
5475
5476
  if (!node.properties.has(property)) return false;
5476
- node.properties.delete(property);
5477
+ node.removeProperty(property);
5477
5478
  this.markDirty(node, this.withProvidedFlags(property, dirtyFlags));
5478
5479
  return true;
5479
5480
  }
@@ -6848,7 +6849,10 @@ const FIT_EPSILON = 1 / 64;
6848
6849
  * narrower than the widest segment. When a segment is wider than the
6849
6850
  * available width the paragraph grows to it, and every line wraps
6850
6851
  * against that grown width rather than the one asked for, because that
6851
- * is the box the lines will be painted in.
6852
+ * is the box the lines will be painted in. When the width is the box's
6853
+ * own (`exactWidth`: a width set from outside, or the box being
6854
+ * painted), it does not grow: the lines wrap against it and the wide
6855
+ * segment overflows alone, as CSS wraps text in a box of a fixed width.
6852
6856
  *
6853
6857
  * The first baseline sits half the leading below the line top, plus the
6854
6858
  * ascent, with the half-leading floored to a whole pixel. That floor is
@@ -6914,7 +6918,7 @@ function layoutParagraph(request, runs) {
6914
6918
  if (paragraphEnd >= text.length) break;
6915
6919
  paragraphStart = paragraphEnd + 1;
6916
6920
  }
6917
- const available = Math.max(maxWidth, minContentWidth);
6921
+ const available = request.exactWidth === true ? maxWidth : Math.max(maxWidth, minContentWidth);
6918
6922
  const unwrapped = wrap === "none" || !isFinite(available);
6919
6923
  const lines = [];
6920
6924
  for (const paragraph of paragraphs) {
@@ -6923,7 +6927,7 @@ function layoutParagraph(request, runs) {
6923
6927
  const key = unwrapped ? -1 : available;
6924
6928
  let relative = paragraph.cached.lines.get(key);
6925
6929
  if (relative === void 0) {
6926
- relative = breakHardLine(paragraph.text, paragraph.segments, width, unwrapped, measure);
6930
+ relative = breakHardLine(paragraph.cached.text, paragraph.segments, width, unwrapped, measure);
6927
6931
  if (paragraph.cached.lines.size >= WIDTHS_PER_LINE) paragraph.cached.lines.clear();
6928
6932
  paragraph.cached.lines.set(key, relative);
6929
6933
  }
@@ -6997,12 +7001,14 @@ function hardLine(memo, style, text, wrap, measure) {
6997
7001
  memo.set(key, cached);
6998
7002
  return cached;
6999
7003
  }
7000
- const segments = segmentParagraph(text, wrap);
7004
+ const own = ownCopy(text);
7005
+ const segments = segmentParagraph(own, wrap);
7001
7006
  let minContentWidth = 0;
7002
- for (const segment of segments) minContentWidth = Math.max(minContentWidth, measure(text.slice(segment.start, segment.end), segment.start, segment.end));
7007
+ for (const segment of segments) minContentWidth = Math.max(minContentWidth, measure(own.slice(segment.start, segment.end), segment.start, segment.end));
7003
7008
  const entry = {
7009
+ text: own,
7004
7010
  segments,
7005
- maxContentWidth: measure(text, 0, text.length),
7011
+ maxContentWidth: measure(own, 0, own.length),
7006
7012
  minContentWidth,
7007
7013
  lines: /* @__PURE__ */ new Map()
7008
7014
  };
@@ -7013,6 +7019,23 @@ function hardLine(memo, style, text, wrap, measure) {
7013
7019
  memo.set(key, entry);
7014
7020
  return entry;
7015
7021
  }
7022
+ /**
7023
+ * `text` in a string of its own.
7024
+ *
7025
+ * A hard line arrives as a slice of the whole paragraph, and a slice
7026
+ * in V8 is a view that keeps the string it was cut from alive. The
7027
+ * memo outlives the paragraph — it is what a later edit finds the
7028
+ * unchanged lines in — so each line it remembered, and every line
7029
+ * broken from it, held on to the whole text it came from. Typing into
7030
+ * a 115 KB field kept every version of it: 100 MB over 4,096 edits.
7031
+ * Prefixing a character makes a new string that has to be flattened
7032
+ * before it can be sliced, and the slice is then of that copy. A short
7033
+ * line is copied by `slice` anyway, and an engine that copies every
7034
+ * slice loses nothing.
7035
+ */
7036
+ function ownCopy(text) {
7037
+ return text.length < 16 ? text : (" " + text).slice(1);
7038
+ }
7016
7039
  /** Breaks one hard line on its own, as if it were the whole text. */
7017
7040
  function breakHardLine(text, segments, available, unwrapped, measure) {
7018
7041
  const lines = [];
@@ -7313,8 +7336,58 @@ function spannedRunsFor(request, runs) {
7313
7336
  }
7314
7337
  };
7315
7338
  }
7316
- /** Paragraphs remembered before the least recently asked for is forgotten. */
7317
- const PARAGRAPH_CACHE_ENTRIES = 4096;
7339
+ /**
7340
+ * How much the paragraph cache holds before the least recently asked
7341
+ * for is forgotten: a number of paragraphs, and their text counted in
7342
+ * UTF-16 code units, whichever runs out first.
7343
+ *
7344
+ * A count alone treated a two-word label and a whole document as the
7345
+ * same size. Every keystroke in a long field is a new request, its key
7346
+ * the whole text, so 4,096 edits to a 115 KB field held 4,096 copies of
7347
+ * it, keys and lines: 1.1 GB of heap, for paragraphs nobody would ask
7348
+ * for again. The text budget keeps a handful of those; the count is
7349
+ * what bounds a screen of labels, and is larger than it was, since a
7350
+ * label costs a few hundred bytes and a list that remounts its rows
7351
+ * should find them still here.
7352
+ */
7353
+ const PARAGRAPH_CACHE_ENTRIES = 16384;
7354
+ const PARAGRAPH_CACHE_TEXT = 1 << 20;
7355
+ /** A least-recently-used map of paragraphs, bounded by count and by text; see `PARAGRAPH_CACHE_ENTRIES`. */
7356
+ var ParagraphCache = class {
7357
+ entries = /* @__PURE__ */ new Map();
7358
+ text = 0;
7359
+ get size() {
7360
+ return this.entries.size;
7361
+ }
7362
+ get(key) {
7363
+ const entry = this.entries.get(key);
7364
+ if (entry === void 0) return;
7365
+ this.entries.delete(key);
7366
+ this.entries.set(key, entry);
7367
+ return entry.paragraph;
7368
+ }
7369
+ set(key, paragraph, text) {
7370
+ const previous = this.entries.get(key);
7371
+ if (previous !== void 0) {
7372
+ this.entries.delete(key);
7373
+ this.text -= previous.text;
7374
+ }
7375
+ while (this.entries.size > 0 && (this.entries.size >= PARAGRAPH_CACHE_ENTRIES || this.text + text > PARAGRAPH_CACHE_TEXT)) {
7376
+ const [oldest, entry] = this.entries.entries().next().value;
7377
+ this.entries.delete(oldest);
7378
+ this.text -= entry.text;
7379
+ }
7380
+ this.entries.set(key, {
7381
+ paragraph,
7382
+ text
7383
+ });
7384
+ this.text += text;
7385
+ }
7386
+ clear() {
7387
+ this.entries.clear();
7388
+ this.text = 0;
7389
+ }
7390
+ };
7318
7391
  /**
7319
7392
  * Base class for measurers: implement the two primitives and the
7320
7393
  * paragraph algorithm does the rest.
@@ -7336,21 +7409,31 @@ const PARAGRAPH_CACHE_ENTRIES = 4096;
7336
7409
  * The result is shared between callers and must not be mutated.
7337
7410
  */
7338
7411
  var ParagraphTextMeasurer = class {
7339
- paragraphs = /* @__PURE__ */ new Map();
7412
+ paragraphs = new ParagraphCache();
7413
+ /**
7414
+ * Paragraphs that came out the same at every width they fit in, by
7415
+ * their key without the width; see `layoutFitsAnyWidth`.
7416
+ *
7417
+ * A resize asks every paragraph again at a width a few pixels from
7418
+ * the last, and a title, a caption or a label that fitted before
7419
+ * fits still: its lines, its box and everything else are what they
7420
+ * were. Keyed by its width as well, each of those missed on every
7421
+ * step of a dragged window edge and was laid out again, and the
7422
+ * box it was then painted in (its own width, exact) missed once
7423
+ * more.
7424
+ */
7425
+ fitting = new ParagraphCache();
7426
+ generations = 0;
7340
7427
  layout(request) {
7341
- const key = paragraphKey(request);
7428
+ const style = paragraphStyleKey(request);
7429
+ const key = `${request.maxWidth ?? ""}\0${request.exactWidth === true ? 1 : ""}\0${style}`;
7342
7430
  const cached = this.paragraphs.get(key);
7343
- if (cached !== void 0) {
7344
- this.paragraphs.delete(key);
7345
- this.paragraphs.set(key, cached);
7346
- return cached;
7347
- }
7431
+ if (cached !== void 0) return cached;
7432
+ const fitted = this.fitting.get(style);
7433
+ if (fitted !== void 0 && fitsWidth(fitted, request.maxWidth)) return fitted;
7348
7434
  const paragraph = layoutParagraph(request, this);
7349
- if (this.paragraphs.size >= PARAGRAPH_CACHE_ENTRIES) {
7350
- const oldest = this.paragraphs.keys().next().value;
7351
- if (oldest !== void 0) this.paragraphs.delete(oldest);
7352
- }
7353
- this.paragraphs.set(key, paragraph);
7435
+ this.paragraphs.set(key, paragraph, request.text.length);
7436
+ if (layoutFitsAnyWidth(request, paragraph)) this.fitting.set(style, paragraph, request.text.length);
7354
7437
  return paragraph;
7355
7438
  }
7356
7439
  measure(request) {
@@ -7364,6 +7447,9 @@ var ParagraphTextMeasurer = class {
7364
7447
  get cachedParagraphs() {
7365
7448
  return this.paragraphs.size;
7366
7449
  }
7450
+ get generation() {
7451
+ return this.generations;
7452
+ }
7367
7453
  /**
7368
7454
  * Forgets every laid-out paragraph. A subclass that caches its own
7369
7455
  * primitives overrides this and calls it, so a font that finished
@@ -7371,13 +7457,47 @@ var ParagraphTextMeasurer = class {
7371
7457
  * built from.
7372
7458
  */
7373
7459
  invalidate() {
7460
+ this.generations++;
7374
7461
  this.paragraphs.clear();
7462
+ this.fitting.clear();
7375
7463
  forgetHardLines(this);
7376
7464
  }
7377
7465
  };
7378
- /** Every field of a request, in a fixed order; two requests that lay out alike share a key. */
7379
- function paragraphKey(request) {
7380
- return `${request.fontSize}\0${request.maxWidth ?? ""}\0${request.fontFamily ?? ""}\0${request.fontWeight ?? ""}\0${request.lineHeight ?? ""}\0${request.letterSpacing ?? ""}\0${request.wrap ?? ""}\0${request.maxLines ?? ""}\0${request.overflow ?? ""}\0${request.fontStyle ?? ""}\0${request.fontStretch ?? ""}\0${request.fontVariant ?? ""}\0${request.fontKerning ?? ""}\0${spansKey(request.spans)}\0${request.text}`;
7466
+ /**
7467
+ * Whether `paragraph`, laid out for `request`, is what a request
7468
+ * differing only in its width would get at any width it fits in.
7469
+ *
7470
+ * A paragraph's width enters its layout in four places, and none of
7471
+ * them can tell two widths apart once every hard line fits whole:
7472
+ * nothing breaks (a line that fits is not broken, `exactWidth` or
7473
+ * not); the box is fit-content, which is the natural width; no line is
7474
+ * wider than the box, so none is ellipsised in place; and the one
7475
+ * ellipsis that is fitted to the width — the last line kept by
7476
+ * `maxLines` — is excluded here, because it is only drawn when there
7477
+ * were more hard lines than that. An unbounded request is left out:
7478
+ * it is laid out unwrapped, which is a different path, and it has a
7479
+ * key of its own that never changes anyway.
7480
+ */
7481
+ function layoutFitsAnyWidth(request, paragraph) {
7482
+ if (!fitsWidth(paragraph, request.maxWidth)) return false;
7483
+ const maxLines = request.maxLines;
7484
+ return request.overflow !== "ellipsis" || maxLines === void 0 || maxLines < 1 || !hasMoreHardLines(request.text, maxLines);
7485
+ }
7486
+ /** Whether a paragraph with nothing in it that depends on the width fits at `maxWidth`. */
7487
+ function fitsWidth(paragraph, maxWidth) {
7488
+ return maxWidth !== void 0 && maxWidth >= 0 && paragraph.maxContentWidth <= maxWidth;
7489
+ }
7490
+ function hasMoreHardLines(text, count) {
7491
+ let at = -1;
7492
+ for (let i = 0; i < count; i++) {
7493
+ at = text.indexOf("\n", at + 1);
7494
+ if (at < 0) return false;
7495
+ }
7496
+ return true;
7497
+ }
7498
+ /** Every field of a request but its width, in a fixed order; two requests that lay out alike at one width share a key. */
7499
+ function paragraphStyleKey(request) {
7500
+ return `${request.fontSize}\0${request.fontFamily ?? ""}\0${request.fontWeight ?? ""}\0${request.lineHeight ?? ""}\0${request.letterSpacing ?? ""}\0${request.wrap ?? ""}\0${request.maxLines ?? ""}\0${request.overflow ?? ""}\0${request.fontStyle ?? ""}\0${request.fontStretch ?? ""}\0${request.fontVariant ?? ""}\0${request.fontKerning ?? ""}\0${spansKey(request.spans)}\0${request.text}`;
7381
7501
  }
7382
7502
  /**
7383
7503
  * The runs' part of a paragraph key, remembered by the identity of the
@@ -7629,7 +7749,7 @@ function editorFor(node) {
7629
7749
  let model = node.properties.get(EDITOR_PROP);
7630
7750
  if (!(model instanceof EditableTextModel)) {
7631
7751
  model = new EditableTextModel();
7632
- node.properties.set(EDITOR_PROP, model);
7752
+ node.setProperty(EDITOR_PROP, model);
7633
7753
  }
7634
7754
  syncEditorValue(node, model);
7635
7755
  syncEditorHidden(node, model);
@@ -7659,12 +7779,33 @@ function syncEditorHidden(node, model) {
7659
7779
  function isEditableNode(node) {
7660
7780
  return node.type === "editable-text";
7661
7781
  }
7782
+ /** Whether Enter inserts a newline. Never in a secret field, which is one line, as a password input is. */
7662
7783
  function isMultiline(node) {
7663
- return node.properties.get("multiline") === true;
7784
+ return node.properties.get("multiline") === true && !isSecret(node);
7664
7785
  }
7665
7786
  function isReadOnly(node) {
7666
7787
  return node.properties.get("readOnly") === true;
7667
7788
  }
7789
+ function isSecret(node) {
7790
+ return node.properties.get("secret") === true;
7791
+ }
7792
+ /**
7793
+ * A secret field's text as it is drawn: one bullet per UTF-16 code
7794
+ * unit, not per character. Every offset the editor works in (caret,
7795
+ * selection, hit-testing, the shell's mirror) is a code unit offset
7796
+ * into the model's text, and a mask of the same length keeps each of
7797
+ * them meaning the same position in both strings. A character outside
7798
+ * the BMP draws as two bullets, which says a little about it; counting
7799
+ * graphemes instead would need a map between the two strings' offsets
7800
+ * in every one of those places.
7801
+ */
7802
+ function maskText(text) {
7803
+ return "•".repeat(text.length);
7804
+ }
7805
+ /** The text an editable draws and measures: its model's, masked when it is secret. */
7806
+ function shownTextOf(node, model) {
7807
+ return isSecret(node) ? maskText(model.text) : model.text;
7808
+ }
7668
7809
  /**
7669
7810
  * Whether the caret is in the visible half of its blink at `now`. It
7670
7811
  * is always visible while a composition is open — the IME caret must
@@ -7702,8 +7843,8 @@ function linkHoverOf(node) {
7702
7843
  /** Records the hovered run. Returns whether it changed. */
7703
7844
  function setLinkHover(node, index) {
7704
7845
  if (linkHoverOf(node) === index) return false;
7705
- if (index < 0) node.properties.delete(TEXT_LINK_HOVER_PROP);
7706
- else node.properties.set(TEXT_LINK_HOVER_PROP, index);
7846
+ if (index < 0) node.removeProperty(TEXT_LINK_HOVER_PROP);
7847
+ else node.setProperty(TEXT_LINK_HOVER_PROP, index);
7707
7848
  return true;
7708
7849
  }
7709
7850
  /** Forgets the hovered run. Returns whether there was one. */
@@ -7904,7 +8045,7 @@ function selectionRangeOf(node) {
7904
8045
  function setSelectionRange(node, start, end) {
7905
8046
  const current = selectionRangeOf(node);
7906
8047
  if (current !== void 0 && current.start === start && current.end === end) return false;
7907
- node.properties.set(TEXT_SELECTION_PROP, {
8048
+ node.setProperty(TEXT_SELECTION_PROP, {
7908
8049
  start,
7909
8050
  end
7910
8051
  });
@@ -7912,7 +8053,7 @@ function setSelectionRange(node, start, end) {
7912
8053
  }
7913
8054
  /** Clears a node's selected range. Returns whether it had one. */
7914
8055
  function clearSelectionRange(node) {
7915
- return node.properties.delete(TEXT_SELECTION_PROP);
8056
+ return node.removeProperty(TEXT_SELECTION_PROP);
7916
8057
  }
7917
8058
  /**
7918
8059
  * The text a `Text` node draws, when the user may select it.
@@ -7982,12 +8123,12 @@ function setMatchRanges(node, ranges) {
7982
8123
  const current = matchRangesOf(node);
7983
8124
  if (current !== void 0 && rangesEqual(current, ranges)) return false;
7984
8125
  if (ranges.length === 0) return clearMatchRanges(node);
7985
- node.properties.set(TEXT_MATCHES_PROP, ranges);
8126
+ node.setProperty(TEXT_MATCHES_PROP, ranges);
7986
8127
  return true;
7987
8128
  }
7988
8129
  /** Clears a node's matched ranges. Returns whether it had any. */
7989
8130
  function clearMatchRanges(node) {
7990
- return node.properties.delete(TEXT_MATCHES_PROP);
8131
+ return node.removeProperty(TEXT_MATCHES_PROP);
7991
8132
  }
7992
8133
  function rangesEqual(a, b) {
7993
8134
  if (a.length !== b.length) return false;
@@ -8092,7 +8233,7 @@ function resolvePaintState(node, out) {
8092
8233
  if (isEditableNode(node)) {
8093
8234
  const model = editorFor(node);
8094
8235
  out.editor = model;
8095
- out.text = model.text;
8236
+ out.text = shownTextOf(node, model);
8096
8237
  out.placeholder = resolveString(node, "placeholder");
8097
8238
  out.placeholderColor = resolveColor(node, UiProperties.placeholderColor) ?? themeColor(node, "textMuted") ?? DEFAULT_PLACEHOLDER_COLOR;
8098
8239
  out.selectionColor = resolveColor(node, UiProperties.selectionColor) ?? defaultSelectionColor(node);
@@ -8554,6 +8695,7 @@ function textMeasureRequest(text, state, box) {
8554
8695
  fontVariant: state.fontVariant,
8555
8696
  fontKerning: state.fontKerning,
8556
8697
  maxWidth: box.width > 0 ? box.width : void 0,
8698
+ exactWidth: box.width > 0,
8557
8699
  wrap: state.textWrap,
8558
8700
  maxLines: state.maxLines,
8559
8701
  overflow: state.textOverflow,
@@ -8881,23 +9023,32 @@ var EditableLayout = class {
8881
9023
  placeholderLines;
8882
9024
  measure;
8883
9025
  rtl;
9026
+ /**
9027
+ * The text as drawn: the model's, or a secret field's mask of it
9028
+ * (see `maskText`). The geometry is the drawing's, so it is measured
9029
+ * from this; the two are the same length, so its offsets are the
9030
+ * model's.
9031
+ */
9032
+ text;
8884
9033
  constructor(model, box, state, measurer) {
8885
9034
  this.model = model;
8886
9035
  this.box = box;
8887
- const request = textMeasureRequest(model.text, state, box);
9036
+ const text = state.text ?? model.text;
9037
+ this.text = text;
9038
+ const request = textMeasureRequest(text, state, box);
8888
9039
  request.maxLines = void 0;
8889
9040
  request.overflow = "clip";
8890
9041
  this.lines = placeLines(box, state, measurer.layout(request));
8891
9042
  const spanned = spannedRunsFor(request, measurer);
8892
- this.measure = (text, start) => text.length === 0 ? 0 : spanned === void 0 || start === void 0 ? measurer.measureRunWidth(text, request) : spanned.width(model.text, start, start + text.length);
9043
+ this.measure = (text, start) => text.length === 0 ? 0 : spanned === void 0 || start === void 0 ? measurer.measureRunWidth(text, request) : spanned.width(this.text, start, start + text.length);
8893
9044
  this.rtl = state.rtl;
8894
- this.placeholderLines = model.text.length === 0 && state.placeholder !== void 0 && state.placeholder.length > 0 ? placeLines(box, state, measurer.layout({
9045
+ this.placeholderLines = text.length === 0 && state.placeholder !== void 0 && state.placeholder.length > 0 ? placeLines(box, state, measurer.layout({
8895
9046
  ...request,
8896
9047
  text: state.placeholder
8897
9048
  })) : [];
8898
9049
  }
8899
9050
  caretRect(offset = this.model.focus) {
8900
- return caretRectFor(this.lines, this.model.text, offset, this.measure, this.rtl);
9051
+ return caretRectFor(this.lines, this.text, offset, this.measure, this.rtl);
8901
9052
  }
8902
9053
  /**
8903
9054
  * The caret position nearest a point. Where hidden text sits at the
@@ -8908,10 +9059,10 @@ var EditableLayout = class {
8908
9059
  * apart. Never inside hidden text.
8909
9060
  */
8910
9061
  offsetAt(x, y) {
8911
- return this.model.caretOffsetNear(offsetAtPoint(this.lines, this.model.text, x, y, this.measure, this.rtl));
9062
+ return this.model.caretOffsetNear(offsetAtPoint(this.lines, this.text, x, y, this.measure, this.rtl));
8912
9063
  }
8913
9064
  verticalMove(offset, direction, goalX) {
8914
- const move = offsetForVerticalMove(this.lines, this.model.text, offset, direction, this.measure, this.rtl, goalX);
9065
+ const move = offsetForVerticalMove(this.lines, this.text, offset, direction, this.measure, this.rtl, goalX);
8915
9066
  return move === null ? null : {
8916
9067
  offset: this.model.caretOffsetNear(move.offset),
8917
9068
  x: move.x
@@ -8924,13 +9075,13 @@ var EditableLayout = class {
8924
9075
  selectionBoxes(range) {
8925
9076
  const start = range?.start ?? this.model.start;
8926
9077
  const end = range?.end ?? this.model.end;
8927
- return selectionRects(this.lines, this.model.text, start, end, this.measure, this.rtl);
9078
+ return selectionRects(this.lines, this.text, start, end, this.measure, this.rtl);
8928
9079
  }
8929
9080
  /** Boxes under the text the IME is composing, for its underline. */
8930
9081
  compositionBoxes() {
8931
9082
  const range = this.model.composition;
8932
9083
  if (range === null) return [];
8933
- return selectionRects(this.lines, this.model.text, range.start, range.end, this.measure, this.rtl);
9084
+ return selectionRects(this.lines, this.text, range.start, range.end, this.measure, this.rtl);
8934
9085
  }
8935
9086
  };
8936
9087
  //#endregion
@@ -10157,8 +10308,21 @@ var UiFocusManager = class {
10157
10308
  * when that scope was pushed, when that node is still in the tree
10158
10309
  * and still focusable. Otherwise focus is dropped rather than left
10159
10310
  * on a node the scope was hiding.
10160
- */
10161
- popScope() {
10311
+ *
10312
+ * Given its root, only that scope ends: none, when it has gone already
10313
+ * (its subtree removed, which pops it), so a trap released after its
10314
+ * content was taken away cannot end the one under it, a submenu's
10315
+ * menu's; and one under another is taken out where it is.
10316
+ */
10317
+ popScope(root) {
10318
+ if (root !== void 0 && this.scopes.at(-1)?.root !== root) {
10319
+ const at = this.scopes.findIndex((each) => each.root === root);
10320
+ if (at >= 0) {
10321
+ this.scopes.splice(at, 1);
10322
+ this.notifyScope();
10323
+ }
10324
+ return;
10325
+ }
10162
10326
  const scope = this.scopes.pop();
10163
10327
  if (scope === void 0) return;
10164
10328
  const restore = scope.restore;
@@ -12099,9 +12263,13 @@ var UiEditingController = class {
12099
12263
  if (command === null) return false;
12100
12264
  return this.execute(node, command);
12101
12265
  }
12102
- execute(node, command) {
12266
+ execute(node, keyed) {
12103
12267
  const model = editorFor(node);
12104
12268
  const readOnly = isReadOnly(node);
12269
+ const command = (keyed.kind === "move" || keyed.kind === "delete") && keyed.unit === "word" ? {
12270
+ ...keyed,
12271
+ unit: secretWordUnit(node)
12272
+ } : keyed;
12105
12273
  switch (command.kind) {
12106
12274
  case "move":
12107
12275
  if (this.span !== null && !command.extend) {
@@ -12272,7 +12440,8 @@ var UiEditingController = class {
12272
12440
  case "deleteByCut":
12273
12441
  case "deleteByDrag": {
12274
12442
  if (readOnly) return true;
12275
- const unit = /Word/.test(inputType) ? "word" : /Line/.test(inputType) ? "line" : "grapheme";
12443
+ if (inputType === "deleteByCut" && isSecret(node)) return true;
12444
+ const unit = /Word/.test(inputType) ? secretWordUnit(node) : /Line/.test(inputType) ? "line" : "grapheme";
12276
12445
  const forward = /Forward/.test(inputType);
12277
12446
  return this.applyEdit(node, model, inputType, null, () => {
12278
12447
  if (inputType === "deleteContent" || inputType === "deleteByCut" || inputType === "deleteByDrag") model.replaceRange(model.start, model.end, "");
@@ -12477,6 +12646,7 @@ var UiEditingController = class {
12477
12646
  };
12478
12647
  this.dragging = node;
12479
12648
  if (modifiers.shift) model.moveTo(offset, true);
12649
+ else if (count === 2 && isSecret(node)) model.selectAll();
12480
12650
  else if (count === 2) {
12481
12651
  const word = visibleWordRange(model.text, model.hidden, offset);
12482
12652
  model.select(word.start, word.end);
@@ -12542,6 +12712,16 @@ var UiEditingController = class {
12542
12712
  ...html === void 0 ? {} : { html }
12543
12713
  };
12544
12714
  }
12715
+ if (isSecret(node)) return {
12716
+ text: maskText(model.text),
12717
+ selectionStart: model.start,
12718
+ selectionEnd: model.end,
12719
+ caret,
12720
+ multiline: false,
12721
+ composing: model.composing,
12722
+ clipboard: "",
12723
+ secret: true
12724
+ };
12545
12725
  const html = model.collapsed || model.composing ? void 0 : this.fieldHtml(node, model.start, model.end);
12546
12726
  return {
12547
12727
  text: model.text,
@@ -12812,10 +12992,11 @@ var UiEditingController = class {
12812
12992
  const end = forward ? span.focus : span.anchor;
12813
12993
  const key = [span];
12814
12994
  for (const node of span.lit) key.push(editorFor(node).text);
12995
+ const textOf = (node) => isSecret(node) ? maskText(editorFor(node).text) : editorFor(node).text;
12815
12996
  return this.cachedCopy(key, () => {
12816
12997
  return {
12817
12998
  text: span.group.copyText !== void 0 ? span.group.copyText(start, end) : span.lit.map((node) => {
12818
- const text = editorFor(node).text;
12999
+ const text = textOf(node);
12819
13000
  return text.slice(node === start.node ? start.offset : 0, node === end.node ? end.offset : text.length);
12820
13001
  }).join("\n"),
12821
13002
  html: span.group.copyHtml?.(start, end) ?? void 0
@@ -12972,6 +13153,13 @@ var UiEditingController = class {
12972
13153
  return new EditableLayout(state.editor, box, state, this.host.measurer);
12973
13154
  }
12974
13155
  };
13156
+ /**
13157
+ * The unit a word move or delete takes in a field: the whole text in a
13158
+ * secret one, whose word boundaries the caret stopping at would show.
13159
+ */
13160
+ function secretWordUnit(node) {
13161
+ return isSecret(node) ? "document" : "word";
13162
+ }
12975
13163
  function deleteInputType(unit, direction) {
12976
13164
  const side = direction < 0 ? "Backward" : "Forward";
12977
13165
  switch (unit) {
@@ -14142,6 +14330,23 @@ var LayoutRecord = class {
14142
14330
  /** Constraints this record was last measured under. */
14143
14331
  lastConstraints = NEVER_MEASURED;
14144
14332
  /**
14333
+ * The paragraphs a text leaf was last measured as, one asked with a
14334
+ * width and one without; see `LayoutEngine.measureLeaf`. Flex asks
14335
+ * an item both questions every pass (its max-content basis, then its
14336
+ * final width), so one slot would be overwritten by the other every
14337
+ * time. Null until the node is measured as text that way.
14338
+ *
14339
+ * **Not reset.** These are the one thing a record carries from one
14340
+ * full pass into the next, on purpose: a full pass re-asks every
14341
+ * text leaf, and a memo that every pass cleared would answer
14342
+ * nothing a resize asks. They are safe to keep because they check
14343
+ * themselves — a memo is used only when every field of the request
14344
+ * it was laid out for matches and the measurer has not been
14345
+ * invalidated since — rather than trusting that nothing changed.
14346
+ */
14347
+ textBounded = null;
14348
+ textUnbounded = null;
14349
+ /**
14145
14350
  * The percentage base the current measurement resolved against, and
14146
14351
  * whether the node has a length that resolves against one.
14147
14352
  *
@@ -14257,6 +14462,20 @@ var LayoutRecord = class {
14257
14462
  propsBaseWidth = void 0;
14258
14463
  propsBaseHeight = void 0;
14259
14464
  /**
14465
+ * The node's `propertyVersion` when its layout properties were last
14466
+ * resolved, and whether that resolution read the node and nothing
14467
+ * else (no inherited direction, no scroll leader). Together with the
14468
+ * percentage base they let a later pass, a full one included, keep
14469
+ * what the last one resolved instead of reading forty properties
14470
+ * again; see `LayoutEngine.resolveLayoutProps`.
14471
+ *
14472
+ * **Not reset,** with the fields they vouch for: `reset()` leaves
14473
+ * every field `resolveLayoutProps` writes as it was, because these
14474
+ * say whether it is still right.
14475
+ */
14476
+ propsVersion = -1;
14477
+ propsFromNodeAlone = false;
14478
+ /**
14260
14479
  * Returns the record to the state a freshly constructed one is in,
14261
14480
  * so a full layout can reuse the object instead of replacing it.
14262
14481
  *
@@ -14270,7 +14489,9 @@ var LayoutRecord = class {
14270
14489
  * never reused across.
14271
14490
  *
14272
14491
  * **Every field above has to appear here, with the value it is
14273
- * declared with.** A field that is added and not reset would carry
14492
+ * declared with,** but the two text memos and the resolved layout
14493
+ * properties, which check themselves and are kept across passes on
14494
+ * purpose. A field that is added and not reset would carry
14274
14495
  * one pass's answer into the next, which is the kind of fault that
14275
14496
  * shows as a stale box on a screen and as nothing at all in a test
14276
14497
  * that lays out once. `LayoutRecord.spec.ts` compares a reset record
@@ -14285,28 +14506,6 @@ var LayoutRecord = class {
14285
14506
  this.measuredHeight = 0;
14286
14507
  this.outerWidth = 0;
14287
14508
  this.outerHeight = 0;
14288
- this.paddingLeft = 0;
14289
- this.paddingRight = 0;
14290
- this.paddingTop = 0;
14291
- this.paddingBottom = 0;
14292
- this.marginLeft = 0;
14293
- this.marginRight = 0;
14294
- this.marginTop = 0;
14295
- this.marginBottom = 0;
14296
- this.minWidth = 0;
14297
- this.maxWidth = Infinity;
14298
- this.minHeight = 0;
14299
- this.maxHeight = Infinity;
14300
- this.flexGrow = 0;
14301
- this.flexShrink = 1;
14302
- this.flexBasisZero = false;
14303
- this.minWidthAuto = true;
14304
- this.minHeightAuto = true;
14305
- this.marginLeftAuto = false;
14306
- this.marginRightAuto = false;
14307
- this.marginTopAuto = false;
14308
- this.marginBottomAuto = false;
14309
- this.aspectRatio = void 0;
14310
14509
  this.hasBaseline = false;
14311
14510
  this.baseline = 0;
14312
14511
  this.minContentWidth = 0;
@@ -14319,16 +14518,6 @@ var LayoutRecord = class {
14319
14518
  this.flexMin = 0;
14320
14519
  this.flexMax = Infinity;
14321
14520
  this.flexMinAuto = false;
14322
- this.positioned = false;
14323
- this.absolute = false;
14324
- this.top = void 0;
14325
- this.right = void 0;
14326
- this.bottom = void 0;
14327
- this.left = void 0;
14328
- this.zIndex = 0;
14329
- this.lifted = false;
14330
- this.liftBoundary = false;
14331
- this.clips = false;
14332
14521
  this.extentMinX = 0;
14333
14522
  this.extentMinY = 0;
14334
14523
  this.extentMaxX = 0;
@@ -14337,12 +14526,6 @@ var LayoutRecord = class {
14337
14526
  this.paintReachTop = 0;
14338
14527
  this.paintReachRight = 0;
14339
14528
  this.paintReachBottom = 0;
14340
- this.scrollable = false;
14341
- this.scrollsText = false;
14342
- this.hidesScrollbars = false;
14343
- this.textScrollbars = false;
14344
- this.mirrored = false;
14345
- this.sticky = false;
14346
14529
  this.stickyOffsetX = 0;
14347
14530
  this.stickyOffsetY = 0;
14348
14531
  this.scrollbarVisibleUntil = 0;
@@ -14361,8 +14544,6 @@ var LayoutRecord = class {
14361
14544
  this.lastConstraints = NEVER_MEASURED;
14362
14545
  this.lastBaseWidth = void 0;
14363
14546
  this.lastBaseHeight = void 0;
14364
- this.percentWidth = false;
14365
- this.percentHeight = false;
14366
14547
  this.alternates = null;
14367
14548
  this.contentMatters = true;
14368
14549
  this.relayoutBoundary = false;
@@ -14371,8 +14552,6 @@ var LayoutRecord = class {
14371
14552
  this.placeDirty = true;
14372
14553
  this.transformDirty = false;
14373
14554
  this.propsPass = 0;
14374
- this.propsBaseWidth = void 0;
14375
- this.propsBaseHeight = void 0;
14376
14555
  }
14377
14556
  };
14378
14557
  /** How many earlier measurements a record remembers besides the current one. */
@@ -15267,6 +15446,18 @@ var LayoutEngine = class {
15267
15446
  */
15268
15447
  stickyShifted = /* @__PURE__ */ new Set();
15269
15448
  textMeasurer;
15449
+ /** Where `measureLeaf` resolves a text node's font; read before anything else can resolve one. */
15450
+ fontScratch = {
15451
+ fontSize: 0,
15452
+ fontFamily: "",
15453
+ fontWeight: "normal",
15454
+ lineHeight: 0,
15455
+ letterSpacing: 0,
15456
+ fontStyle: "normal",
15457
+ fontStretch: "normal",
15458
+ fontVariant: "normal",
15459
+ fontKerning: "auto"
15460
+ };
15270
15461
  /**
15271
15462
  * The definite size percentages resolve against while a container's
15272
15463
  * children are measured: its content box. Undefined on an axis whose
@@ -17105,37 +17296,79 @@ var LayoutEngine = class {
17105
17296
  const paddingV = rec.paddingTop + rec.paddingBottom;
17106
17297
  const editable = node.type === "editable-text";
17107
17298
  if (node.type === "text" || node.type === "button" || editable) {
17108
- const typed = editable ? editorFor(node).text : "";
17299
+ const typed = editable ? shownTextOf(node, editorFor(node)) : "";
17109
17300
  const spans = editable ? editableSpansOf(node, typed, editorFor(node).composition) : resolvedSpansOf(node);
17110
17301
  const text = editable ? typed : spans.length > 0 ? textContentOf(node) : String(node.properties.get("text") ?? "");
17111
17302
  const placeholderProp = node.properties.get("placeholder");
17112
17303
  const placeholder = editable && text.length === 0 && typeof placeholderProp === "string" ? placeholderProp : void 0;
17113
- const font = resolveFont(node);
17304
+ const font = this.fontScratch;
17305
+ resolveFontInto(node, font);
17114
17306
  const maxLines = editable ? void 0 : this.numberProp(node, "maxLines");
17115
- const request = {
17116
- text,
17117
- fontSize: font.fontSize,
17118
- fontFamily: font.fontFamily,
17119
- fontWeight: font.fontWeight,
17120
- lineHeight: font.lineHeight,
17121
- letterSpacing: font.letterSpacing,
17122
- fontStyle: font.fontStyle,
17123
- fontStretch: font.fontStretch,
17124
- fontVariant: font.fontVariant,
17125
- fontKerning: font.fontKerning,
17126
- maxWidth: isFinite(effective.maxWidth) ? Math.max(0, effective.maxWidth - paddingH) : void 0,
17127
- wrap: this.textWrapProp(node),
17128
- maxLines: maxLines !== void 0 && maxLines >= 1 ? Math.floor(maxLines) : void 0,
17129
- overflow: editable ? "clip" : this.textOverflowProp(node),
17130
- spans: spans.length > 0 ? spans : void 0
17131
- };
17132
- const paragraph = this.textMeasurer.layout(request);
17307
+ const maxWidth = isFinite(effective.maxWidth) ? Math.max(0, effective.maxWidth - paddingH) : void 0;
17308
+ const exactWidth = isFinite(effective.maxWidth) && effective.minWidth === effective.maxWidth;
17309
+ const wrap = this.textWrapProp(node);
17310
+ const lines = maxLines !== void 0 && maxLines >= 1 ? Math.floor(maxLines) : void 0;
17311
+ const overflow = editable ? "clip" : this.textOverflowProp(node);
17312
+ const runs = spans.length > 0 ? spans : void 0;
17313
+ /**
17314
+ * The node's own last paragraph, when nothing it was laid out
17315
+ * from has changed.
17316
+ *
17317
+ * Flex asks a text leaf three or four questions a pass (its
17318
+ * basis, its min- and max-content sizes, its final size), and
17319
+ * each one went to the measurer as a new request, whose cache
17320
+ * key spells out every field and the whole text before it can
17321
+ * look anything up. That was a fifth of a full layout pass over
17322
+ * the benchmark list with every answer already cached. Compared
17323
+ * field by field here it is a handful of identity checks, and a
17324
+ * paragraph that fits at the width it was laid out at also
17325
+ * answers any wider question, which is most of the questions a
17326
+ * row of labels is asked.
17327
+ */
17328
+ const generation = this.textMeasurer.generation;
17329
+ const memo = maxWidth === void 0 ? rec.textUnbounded : rec.textBounded;
17330
+ const last = memo?.request;
17331
+ let paragraph;
17332
+ let asked;
17333
+ if (memo !== null && last !== void 0 && generation !== void 0 && memo.generation === generation && last.text === text && last.fontSize === font.fontSize && last.fontFamily === font.fontFamily && last.fontWeight === font.fontWeight && last.lineHeight === font.lineHeight && last.letterSpacing === font.letterSpacing && last.fontStyle === font.fontStyle && last.fontStretch === font.fontStretch && last.fontVariant === font.fontVariant && last.fontKerning === font.fontKerning && last.wrap === wrap && last.maxLines === lines && last.overflow === overflow && last.spans === runs && (last.maxWidth === maxWidth && last.exactWidth === exactWidth || memo.fitsAnyWidth && fitsWidth(memo.paragraph, maxWidth))) {
17334
+ paragraph = memo.paragraph;
17335
+ asked = last;
17336
+ } else {
17337
+ const request = {
17338
+ text,
17339
+ fontSize: font.fontSize,
17340
+ fontFamily: font.fontFamily,
17341
+ fontWeight: font.fontWeight,
17342
+ lineHeight: font.lineHeight,
17343
+ letterSpacing: font.letterSpacing,
17344
+ fontStyle: font.fontStyle,
17345
+ fontStretch: font.fontStretch,
17346
+ fontVariant: font.fontVariant,
17347
+ fontKerning: font.fontKerning,
17348
+ maxWidth,
17349
+ exactWidth,
17350
+ wrap,
17351
+ maxLines: lines,
17352
+ overflow,
17353
+ spans: runs
17354
+ };
17355
+ paragraph = this.textMeasurer.layout(request);
17356
+ asked = request;
17357
+ const fresh = {
17358
+ request,
17359
+ paragraph,
17360
+ generation: generation ?? -1,
17361
+ fitsAnyWidth: layoutFitsAnyWidth(request, paragraph)
17362
+ };
17363
+ if (maxWidth === void 0) rec.textUnbounded = fresh;
17364
+ else rec.textBounded = fresh;
17365
+ }
17133
17366
  let width = paragraph.width;
17134
17367
  let minContentWidth = paragraph.minContentWidth;
17135
17368
  let maxContentWidth = paragraph.maxContentWidth;
17136
17369
  if (placeholder !== void 0 && placeholder.length > 0) {
17137
17370
  const hint = this.textMeasurer.layout({
17138
- ...request,
17371
+ ...asked,
17139
17372
  text: placeholder
17140
17373
  });
17141
17374
  width = Math.max(width, hint.width);
@@ -18318,7 +18551,28 @@ var LayoutEngine = class {
18318
18551
  const props = node.properties;
18319
18552
  const base = this.percentBase;
18320
18553
  if (rec.propsPass === this.layoutPass && rec.propsBaseWidth === base.width && rec.propsBaseHeight === base.height) return;
18554
+ /**
18555
+ * What an earlier pass resolved, when none of what it read has
18556
+ * changed: the node's own properties (`propertyVersion`), and the
18557
+ * percentage base only on an axis that has a percentage.
18558
+ *
18559
+ * Each pass used to read every property below again for every node
18560
+ * it reached, and a full pass reaches all of them: a sixth of a full
18561
+ * pass over the benchmark list, where nothing had changed since the
18562
+ * last. A node whose resolution read anything inherited (a logical
18563
+ * edge or a scroll container's direction) or another node (a scroll
18564
+ * leader) is resolved again every pass, as it always was; they are
18565
+ * few. The record keeps these fields across `reset()` for this.
18566
+ */
18567
+ if (rec.propsFromNodeAlone && rec.propsVersion === node.propertyVersion && (!rec.percentWidth || rec.propsBaseWidth === base.width) && (!rec.percentHeight || rec.propsBaseHeight === base.height)) {
18568
+ rec.propsPass = this.layoutPass;
18569
+ rec.propsBaseWidth = base.width;
18570
+ rec.propsBaseHeight = base.height;
18571
+ if (rec.lifted) this.liftedNodes.add(node);
18572
+ return;
18573
+ }
18321
18574
  rec.propsPass = this.layoutPass;
18575
+ rec.propsVersion = node.propertyVersion;
18322
18576
  rec.propsBaseWidth = base.width;
18323
18577
  rec.propsBaseHeight = base.height;
18324
18578
  rec.percentWidth = hasPercentLength(props, WIDTH_RELATIVE);
@@ -18371,9 +18625,11 @@ var LayoutEngine = class {
18371
18625
  rec.left = this.lengthProp(node, "left", base.width) ?? insetH;
18372
18626
  rec.zIndex = this.numberProp(node, "zIndex") ?? 0;
18373
18627
  this.setLifted(node, rec, node.properties.get("lift") === true);
18628
+ if (rec.lifted) this.liftedNodes.add(node);
18374
18629
  rec.liftBoundary = node.properties.get("liftBoundary") === true;
18375
18630
  const ratio = this.numberProp(node, "aspectRatio");
18376
18631
  rec.aspectRatio = ratio !== void 0 && ratio > 0 ? ratio : void 0;
18632
+ rec.propsFromNodeAlone = !hasLogicalSpacing(props) && !rec.scrollable && !rec.textScrollbars && overflow !== "hidden";
18377
18633
  }
18378
18634
  /**
18379
18635
  * Sizes with a fixed width:height ratio (CSS `aspect-ratio`). The
@@ -22077,6 +22333,6 @@ function createCanvasSurface(host) {
22077
22333
  return new CanvasSurface(host);
22078
22334
  }
22079
22335
  //#endregion
22080
- export { isImeKey as $, MOUSE_POINTER as $a, spacingEqual as $i, ELLIPSIS as $n, transformsEqual as $r, registerFontStack as $t, withOverrideSource as A, colorToHex as Aa, observeViewportInsets as Ai, clearLinkHover as An, inheritedPropertyNames as Ar, adjacentField as At, FlexDirection as B, UiEnvironment as Ba, defineThemeExtension as Bi, editorFor as Bn, normalizeStates as Br, createCanvasTextStyle as Bt, SCROLLBAR_LINGER_MS as C, textContentOf as Ca, uniformBorderRadius as Ci, isNodeInert as Cn, resolveProperty as Cr, hasDrawnText as Ct, formatExplanation as D, darkColors as Da, UiInsetRegistry as Di, isSubtreeInert as Dn, findPropertyDefinition as Dr, selectionRectsIn as Dt, formatConstraints as E, colorsEqualPalette as Ea, UiEnvironmentKeys as Ei, isPressable as En, closestPropertyName as Er, paragraphGeometryFrom as Et, clampSize as F, parseColor as Fa, darkTheme as Fi, setLinkHover as Fn, UiProperties as Fr, detectEditingPlatform as Ft, CrossAxisAlignment as G, resetOverrideWarnings as Ga, withThemeExtension as Gi, nextCaretToggle as Gn, validateSubgrid as Gr, runFontString as Gt, placeGridItems as H, DirtyNodeSet as Ha, noThemeExtensions as Hi, isEditableNode as Hn, validateRole as Hr, fillTextRects as Ht, constraintsEqual as I, rgb8 as Ia, lightTheme as Ii, CARET_BLINK_MS as In, UI_ROLES as Ir, isPrintable as It, parseCrossAxisAlignment as J, UiVisualState as Ja, raiseContrast as Ji, isVideoSurface as Jn, pathValuesEqual as Jr, textRunBackgrounds as Jt, MainAxisAlignment as K, writeDeclaredProperty as Ka, contrastRatio as Ki, syncEditorValue as Kn, inputsEqual as Kr, spanOf as Kt, tightenConstraints as L, rgba as La, themesEqual as Li, CARET_WIDTH as Ln, UI_SEMANTIC_STATES as Lr, EditableLayout as Lt, contentOffset as M, colorValuesEqual as Ma, bandOf as Mi, linkHoverOf as Mn, propertyIsInherited as Mr, edgeField as Mt, LayoutRecord as N, colorsEqual as Na, containerBands as Ni, linkOf as Nn, propertyNarrowers as Nr, editingGroupOf as Nt, formatNumber as O, lightColors as Oa, insetsEqual as Oi, pointerEventsOf as On, getPropertyNames as Or, wordRangeIn as Ot, Constraints as P, normalizeColor as Pa, unknownContainerSize as Pi, linkRunAtPointIn as Pn, propertyValuesEqualByName as Pr, commandForKey as Pt, capturePointer as Q, visualStatesEqual as Qa, scaleSpacing as Qi, spannedRunsFor as Qn, transformIsIdentity as Qr, fontStackFor as Qt, MAX_MEASURES_PER_CHILD as R, createEnvironmentKey as Ra, withContrast as Ri, EDITOR_PROP as Rn, isUiRole as Rr, applyCanvasTextStyle as Rt, SCROLLBAR_FADE_MS as S, spansDifferOnlyInPaint as Sa, normalizeBorderRadius as Si, isNodeHitTestable as Sn, resolveNumber as Sr, UiInputDispatcher as St, describeOverrides as T, textStylesEqual as Ta, propertyValuesEqual as Ti, isNodeTabStop as Tn, resolveString as Tr, paragraphGeometry as Tt, sizeGridTracks as U, clearOverrideProperty as Ua, themeExtension as Ui, isMultiline as Un, validateStates as Ur, layoutTextLines as Ut, parseFlexDirection as V, UiNode as Va, hasThemeExtension as Vi, editorOf as Vn, statesEqual as Vr, drawText as Vt, AlignContent as W, overrideSources as Wa, themeExtensionsEqual as Wi, isReadOnly as Wn, UI_SUBGRID_AXES as Wr, runCanvasStyleInto as Wt, CanvasPlatformSurface as X, hasVisualState as Xa, densityFactors as Xi, CharacterCountTextMeasurer as Xn, parseTransform as Xr, bumpFontStack as Xt, parseMainAxisAlignment as Y, defaultVisualState as Ya, relativeLuminance as Yi, videoFrameSize as Yn, UiTransforms as Yr, textRunDecorations as Yt, UiPlatformAdapter as Z, visualState as Za, defaultSpacing as Zi, ParagraphTextMeasurer as Zn, transform as Zr, clearFontStacks as Zt, LABEL_PADDING_X as _, flattenTextSpans as _a, borderRadius as _i, selectableTextNodes as _n, DirtyFlags as _o, previousWordStart as _r, scrollLeaderOf as _t, PaintPictureCache as a, boxShadowArraysEqual as aa, validateGradient as ai, computeObjectFitRect as an, UiInputEvent as ao, lineLimit as ar, UiGestureRecognizer as at, snapScroll as b, spanAtOffset as ba, borderRadiusIsZero as bi, setSelectionRange as bn, DEFAULT_LINE_HEIGHT_FACTOR as br, scrollbarThumbs as bt, EMPTY_RECORDING as c, boxShadowsEqual as ca, isAutoLength as ci, normalizeTextAlign as cn, UiPinchEvent as co, offsetForVerticalMove as cr, isScrollContainer as ct, tracePathData as d, shapesEqual as da, isPercentLength as di, TEXT_MATCHES_PROP as dn, UiTextChangeEvent as do, graphemeBoundaries as dr, UiFocusManager as dt, spacingSteps as ea, MAX_GRADIENT_STOPS as ei, DEFAULT_FONT_FAMILY as en, UiBeforeInputEvent as eo, layoutParagraph as er, pointerDeviceOf as et, ScaledImageCache as f, defaultTypography as fa, minmax as fi, clearMatchRanges as fn, UiWheelDeltaMode as fo, lineEndAt as fr, UiHitTester as ft, paintsAfterChildren as g, editableSpansOf as ga, UiBorderRadiuses as gi, clearSelectionRange as gn, UiNodeType as go, previousGraphemeStart as gr, SCROLLBAR_THICKNESS as gt, hasDecorationPhase as h, defaultTextStyle as ha, resolveLength as hi, TEXT_SELECTION_PROP as hn, wheelDeltaYOf as ho, nextWordEnd as hr, SCROLLBAR_MIN_THUMB as ht, traceRoundedRect as i, boxShadow as ia, radialGradient as ii, colorToCss as in, UiGestureEvent as io, lineIndexForOffset as ir, UiTouchScroller as it, accumulatedOffsetTo as j, colorToRgba as ja, UiContainerSizeSource as ji, hasTextLinks as jn, propertyEffects as jr, comparePositions as jt, labelNode as k, UiBasicColors as ka, noInsets as ki, resolveCursor as kn, inheritedPropertyFlags as kr, textInputOf as kt, PaintRecorder as l, defaultShapes as la, isFrLength as li, normalizeVerticalAlign as ln, UiPointerEvent as lo, selectionRects as lr, UiKeyboardController as lt, decorationRect as m, typographyEqual as ma, repeat as mi, setMatchRanges as mn, noKeyModifiers as mo, nextGraphemeEnd as mr, SCROLLBAR_INSET as mt, createCanvasSurface as n, shadowsEqual as na, gradientsEqual as ni, DEFAULT_FONT_WEIGHT as nn, UiEventType as no, caretRectFor as nr, touchActionFor as nt, paintPictures as o, boxShadowRadius as oa, auto as oi, createPaintState as on, UiKeyboardEvent as oo, offsetAtPoint as or, UiPinchRecognizer as ot, decorationColor as p, isTypographyRole as pa, percent as pi, matchRangesOf as pn, UiWheelEvent as po, lineStartAt as pr, SCROLLBAR_HOVER_ZONE as pt, parseAlignContent as q, writeOverrideProperty as qa, highContrastColors as qi, resolveColorValue as qn, paintValuesEqual as qr, textMeasureRequest as qt, Canvas2DRenderer as r, BOX_SHADOW_BLUR_REACH as ra, linearGradient as ri, DEFAULT_TEXT_COLOR as rn, UiFocusEvent as ro, lineIndexAtY as rr, UiEditingController as rt, PaintTarget as s, boxShadowReach as sa, fr as si, isPaintVisible as sn, UiPasteEvent as so, offsetAtX as sr, UiWheelController as st, CanvasSurface as t, defaultShadows as ta, gradientPaint as ti, DEFAULT_FONT_SIZE as tn, UiCompositionEvent as to, proportionalFontMetrics as tr, prepareInputSurface as tt, replayPaint as u, isShapeName as ua, isMinMaxTrack as ui, resolvePaintState as un, UiSelectionChangeEvent as uo, EditableTextModel as ur, UiPointerController as ut, drawOverlayShapes as v, hiddenRangesOf as va, borderRadiusCorners as vi, selectableTextOf as vn, UiBinding as vo, wordRangeAt as vr, scrollRange as vt, describeLength as w, textSpansEqual as wa, defineProperty as wi, isNodeSelectable as wn, resolvePropertyByName as wr, offsetAtPointIn as wt, LayoutEngine as x, spannedTextOf as xa, borderRadiusValuesEqual as xi, isNodeFocusable as xn, resolveBoolean as xr, scrollbarZoneAt as xt, labelOrigin as y, resolvedSpansOf as ya, borderRadiusEqual as yi, selectionRangeOf as yn, UiGraph as yr, scrollbarThumb as yt, isLayoutProtocol as z, findEnvironmentKey as za, withDensity as zi, caretVisibleAt as zn, isUiSemanticState as zr, buildFontString as zt };
22336
+ export { isImeKey as $, visualStatesEqual as $a, scaleSpacing as $i, spannedRunsFor as $n, transformIsIdentity as $r, registerFontStack as $t, withOverrideSource as A, UiBasicColors as Aa, noInsets as Ai, clearLinkHover as An, inheritedPropertyFlags as Ar, adjacentField as At, FlexDirection as B, findEnvironmentKey as Ba, withDensity as Bi, editorFor as Bn, isUiSemanticState as Br, createCanvasTextStyle as Bt, SCROLLBAR_LINGER_MS as C, spansDifferOnlyInPaint as Ca, normalizeBorderRadius as Ci, isNodeInert as Cn, resolveNumber as Cr, hasDrawnText as Ct, formatExplanation as D, colorsEqualPalette as Da, UiEnvironmentKeys as Di, isSubtreeInert as Dn, closestPropertyName as Dr, selectionRectsIn as Dt, formatConstraints as E, textStylesEqual as Ea, propertyValuesEqual as Ei, isPressable as En, resolveString as Er, paragraphGeometryFrom as Et, clampSize as F, normalizeColor as Fa, unknownContainerSize as Fi, setLinkHover as Fn, propertyValuesEqualByName as Fr, detectEditingPlatform as Ft, CrossAxisAlignment as G, overrideSources as Ga, themeExtensionsEqual as Gi, maskText as Gn, UI_SUBGRID_AXES as Gr, runFontString as Gt, placeGridItems as H, UiNode as Ha, hasThemeExtension as Hi, isEditableNode as Hn, statesEqual as Hr, fillTextRects as Ht, constraintsEqual as I, parseColor as Ia, darkTheme as Ii, CARET_BLINK_MS as In, UiProperties as Ir, isPrintable as It, parseCrossAxisAlignment as J, writeOverrideProperty as Ja, highContrastColors as Ji, resolveColorValue as Jn, paintValuesEqual as Jr, textRunBackgrounds as Jt, MainAxisAlignment as K, resetOverrideWarnings as Ka, withThemeExtension as Ki, nextCaretToggle as Kn, validateSubgrid as Kr, spanOf as Kt, tightenConstraints as L, rgb8 as La, lightTheme as Li, CARET_WIDTH as Ln, UI_ROLES as Lr, EditableLayout as Lt, contentOffset as M, colorToRgba as Ma, UiContainerSizeSource as Mi, linkHoverOf as Mn, propertyEffects as Mr, edgeField as Mt, LayoutRecord as N, colorValuesEqual as Na, bandOf as Ni, linkOf as Nn, propertyIsInherited as Nr, editingGroupOf as Nt, formatNumber as O, darkColors as Oa, UiInsetRegistry as Oi, pointerEventsOf as On, findPropertyDefinition as Or, wordRangeIn as Ot, Constraints as P, colorsEqual as Pa, containerBands as Pi, linkRunAtPointIn as Pn, propertyNarrowers as Pr, commandForKey as Pt, capturePointer as Q, visualState as Qa, defaultSpacing as Qi, ParagraphTextMeasurer as Qn, transform as Qr, fontStackFor as Qt, MAX_MEASURES_PER_CHILD as R, rgba as Ra, themesEqual as Ri, EDITOR_PROP as Rn, UI_SEMANTIC_STATES as Rr, applyCanvasTextStyle as Rt, SCROLLBAR_FADE_MS as S, spannedTextOf as Sa, borderRadiusValuesEqual as Si, isNodeHitTestable as Sn, resolveBoolean as Sr, UiInputDispatcher as St, describeOverrides as T, textSpansEqual as Ta, defineProperty as Ti, isNodeTabStop as Tn, resolvePropertyByName as Tr, paragraphGeometry as Tt, sizeGridTracks as U, DirtyNodeSet as Ua, noThemeExtensions as Ui, isMultiline as Un, validateRole as Ur, layoutTextLines as Ut, parseFlexDirection as V, UiEnvironment as Va, defineThemeExtension as Vi, editorOf as Vn, normalizeStates as Vr, drawText as Vt, AlignContent as W, clearOverrideProperty as Wa, themeExtension as Wi, isReadOnly as Wn, validateStates as Wr, runCanvasStyleInto as Wt, CanvasPlatformSurface as X, defaultVisualState as Xa, relativeLuminance as Xi, videoFrameSize as Xn, UiTransforms as Xr, bumpFontStack as Xt, parseMainAxisAlignment as Y, UiVisualState as Ya, raiseContrast as Yi, isVideoSurface as Yn, pathValuesEqual as Yr, textRunDecorations as Yt, UiPlatformAdapter as Z, hasVisualState as Za, densityFactors as Zi, CharacterCountTextMeasurer as Zn, parseTransform as Zr, clearFontStacks as Zt, LABEL_PADDING_X as _, editableSpansOf as _a, UiBorderRadiuses as _i, selectableTextNodes as _n, UiNodeType as _o, previousGraphemeStart as _r, scrollLeaderOf as _t, PaintPictureCache as a, boxShadow as aa, radialGradient as ai, computeObjectFitRect as an, UiGestureEvent as ao, lineIndexForOffset as ar, UiGestureRecognizer as at, snapScroll as b, resolvedSpansOf as ba, borderRadiusEqual as bi, setSelectionRange as bn, UiGraph as br, scrollbarThumbs as bt, EMPTY_RECORDING as c, boxShadowReach as ca, fr as ci, normalizeTextAlign as cn, UiPasteEvent as co, offsetAtX as cr, isScrollContainer as ct, tracePathData as d, isShapeName as da, isMinMaxTrack as di, TEXT_MATCHES_PROP as dn, UiSelectionChangeEvent as do, EditableTextModel as dr, UiFocusManager as dt, spacingEqual as ea, transformsEqual as ei, DEFAULT_FONT_FAMILY as en, MOUSE_POINTER as eo, ELLIPSIS as er, pointerDeviceOf as et, ScaledImageCache as f, shapesEqual as fa, isPercentLength as fi, clearMatchRanges as fn, UiTextChangeEvent as fo, graphemeBoundaries as fr, UiHitTester as ft, paintsAfterChildren as g, defaultTextStyle as ga, resolveLength as gi, clearSelectionRange as gn, wheelDeltaYOf as go, nextWordEnd as gr, SCROLLBAR_THICKNESS as gt, hasDecorationPhase as h, typographyEqual as ha, repeat as hi, TEXT_SELECTION_PROP as hn, noKeyModifiers as ho, nextGraphemeEnd as hr, SCROLLBAR_MIN_THUMB as ht, traceRoundedRect as i, BOX_SHADOW_BLUR_REACH as ia, linearGradient as ii, colorToCss as in, UiFocusEvent as io, lineIndexAtY as ir, UiTouchScroller as it, accumulatedOffsetTo as j, colorToHex as ja, observeViewportInsets as ji, hasTextLinks as jn, inheritedPropertyNames as jr, comparePositions as jt, labelNode as k, lightColors as ka, insetsEqual as ki, resolveCursor as kn, getPropertyNames as kr, textInputOf as kt, PaintRecorder as l, boxShadowsEqual as la, isAutoLength as li, normalizeVerticalAlign as ln, UiPinchEvent as lo, offsetForVerticalMove as lr, UiKeyboardController as lt, decorationRect as m, isTypographyRole as ma, percent as mi, setMatchRanges as mn, UiWheelEvent as mo, lineStartAt as mr, SCROLLBAR_INSET as mt, createCanvasSurface as n, defaultShadows as na, gradientPaint as ni, DEFAULT_FONT_WEIGHT as nn, UiCompositionEvent as no, proportionalFontMetrics as nr, touchActionFor as nt, paintPictures as o, boxShadowArraysEqual as oa, validateGradient as oi, createPaintState as on, UiInputEvent as oo, lineLimit as or, UiPinchRecognizer as ot, decorationColor as p, defaultTypography as pa, minmax as pi, matchRangesOf as pn, UiWheelDeltaMode as po, lineEndAt as pr, SCROLLBAR_HOVER_ZONE as pt, parseAlignContent as q, writeDeclaredProperty as qa, contrastRatio as qi, syncEditorValue as qn, inputsEqual as qr, textMeasureRequest as qt, Canvas2DRenderer as r, shadowsEqual as ra, gradientsEqual as ri, DEFAULT_TEXT_COLOR as rn, UiEventType as ro, caretRectFor as rr, UiEditingController as rt, PaintTarget as s, boxShadowRadius as sa, auto as si, isPaintVisible as sn, UiKeyboardEvent as so, offsetAtPoint as sr, UiWheelController as st, CanvasSurface as t, spacingSteps as ta, MAX_GRADIENT_STOPS as ti, DEFAULT_FONT_SIZE as tn, UiBeforeInputEvent as to, layoutParagraph as tr, prepareInputSurface as tt, replayPaint as u, defaultShapes as ua, isFrLength as ui, resolvePaintState as un, UiPointerEvent as uo, selectionRects as ur, UiPointerController as ut, drawOverlayShapes as v, flattenTextSpans as va, borderRadius as vi, selectableTextOf as vn, DirtyFlags as vo, previousWordStart as vr, scrollRange as vt, describeLength as w, textContentOf as wa, uniformBorderRadius as wi, isNodeSelectable as wn, resolveProperty as wr, offsetAtPointIn as wt, LayoutEngine as x, spanAtOffset as xa, borderRadiusIsZero as xi, isNodeFocusable as xn, DEFAULT_LINE_HEIGHT_FACTOR as xr, scrollbarZoneAt as xt, labelOrigin as y, hiddenRangesOf as ya, borderRadiusCorners as yi, selectionRangeOf as yn, UiBinding as yo, wordRangeAt as yr, scrollbarThumb as yt, isLayoutProtocol as z, createEnvironmentKey as za, withContrast as zi, caretVisibleAt as zn, isUiRole as zr, buildFontString as zt };
22081
22337
 
22082
- //# sourceMappingURL=CanvasSurface-VUJRrcXl.js.map
22338
+ //# sourceMappingURL=CanvasSurface-BAhcLj2K.js.map