uicore-ts 1.1.252 → 1.1.255

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.
@@ -17,6 +17,9 @@
17
17
  * including:
18
18
  * - Which view was re-queued
19
19
  * - Its full superview chain
20
+ * - A state snapshot (isVirtualLayouting, frame size, bounds size, class name)
21
+ * - The full history of all previous cycle events for the same view this pass,
22
+ * so the entire oscillation pattern is visible in one place
20
23
  * - The call stack at the point of the re-queue (setNeedsLayout call)
21
24
  * - How many times the view has been laid out in this pass
22
25
  *
@@ -26,14 +29,28 @@
26
29
  * view is laid out, and UILayoutCycleTracer.viewDidCallSetNeedsLayout(view)
27
30
  * from setNeedsLayout() while a pass is active.
28
31
  */
32
+ interface UILayoutCycleViewSnapshot {
33
+ isVirtualLayouting: boolean;
34
+ frameWidth: number;
35
+ frameHeight: number;
36
+ boundsWidth: number;
37
+ boundsHeight: number;
38
+ className: string;
39
+ elementID: string;
40
+ }
41
+ interface UILayoutCycleEvent {
42
+ occurrenceIndex: number;
43
+ iteration: number;
44
+ layoutCountAtTime: number;
45
+ callerFunction: string;
46
+ snapshot: UILayoutCycleViewSnapshot;
47
+ cleanStack: string;
48
+ }
29
49
  export declare class UILayoutCycleTracer {
30
50
  static _isEnabled: boolean;
31
51
  static _isPassActive: boolean;
32
52
  static _layoutCountsThisPass: Map<any, number>;
33
- static _setNeedsLayoutCallsThisPass: Map<any, {
34
- count: number;
35
- stacks: string[];
36
- }>;
53
+ static _eventsThisPass: Map<any, UILayoutCycleEvent[]>;
37
54
  static _currentIteration: number;
38
55
  static _totalReportsThisPass: number;
39
56
  static reportThreshold: number;
@@ -69,12 +86,42 @@ export declare class UILayoutCycleTracer {
69
86
  * the application code that triggered the re-queue.
70
87
  */
71
88
  static _cleanStack(rawStack: string): string;
89
+ /**
90
+ * Extracts the name of the first application function from a cleaned stack string.
91
+ * Returns something like "UIButton.layoutSubviews" or "CellView.layoutSubviews".
92
+ */
93
+ static _extractCallerFunctionName(cleanStack: string): string;
94
+ /**
95
+ * Captures a diagnostic snapshot of a view's current layout state.
96
+ * Safe to call at any point — gracefully handles nil/missing properties.
97
+ */
98
+ static _snapshotView(view: any): UILayoutCycleViewSnapshot;
99
+ static _formatSnapshotInline(snapshot: UILayoutCycleViewSnapshot): string;
72
100
  static _viewIdentifier(view: any): string;
73
101
  static _superviewChain(view: any): string;
74
- static _reportCycle(view: any, layoutCountThisPass: number, cleanStack: string): void;
102
+ /**
103
+ * Prints a single event row inside the history section.
104
+ * Past events are collapsed sub-groups (stack accessible but not noisy).
105
+ * The current event is expanded so it's immediately visible.
106
+ */
107
+ static _printHistoryEvent(event: UILayoutCycleEvent, isCurrent: boolean): void;
108
+ static _reportCycle(view: any, history: UILayoutCycleEvent[]): void;
109
+ /**
110
+ * Manually prints a full diagnostic report for any view on demand.
111
+ * Call from the browser console: layoutReport(someView)
112
+ *
113
+ * Shows:
114
+ * - Current state snapshot (isVirtualLayouting, frame, bounds)
115
+ * - Superview chain with state at each level
116
+ * - Full cycle history for this view from the most recent pass, if any
117
+ * - The raw view object for live inspection
118
+ */
119
+ static printViewReport(view: any): void;
75
120
  }
76
121
  declare global {
77
122
  interface Window {
78
123
  UILayoutCycleTracer?: typeof UILayoutCycleTracer;
124
+ layoutReport: (view: any) => void;
79
125
  }
80
126
  }
127
+ export {};
@@ -50,7 +50,7 @@ const _UILayoutCycleTracer = class {
50
50
  }
51
51
  _UILayoutCycleTracer._isPassActive = true;
52
52
  _UILayoutCycleTracer._layoutCountsThisPass = /* @__PURE__ */ new Map();
53
- _UILayoutCycleTracer._setNeedsLayoutCallsThisPass = /* @__PURE__ */ new Map();
53
+ _UILayoutCycleTracer._eventsThisPass = /* @__PURE__ */ new Map();
54
54
  _UILayoutCycleTracer._currentIteration = 0;
55
55
  _UILayoutCycleTracer._totalReportsThisPass = 0;
56
56
  }
@@ -75,7 +75,7 @@ const _UILayoutCycleTracer = class {
75
75
  _UILayoutCycleTracer._layoutCountsThisPass.set(view, previous + 1);
76
76
  }
77
77
  static viewDidCallSetNeedsLayout(view) {
78
- var _a, _b;
78
+ var _a, _b, _c;
79
79
  if (!_UILayoutCycleTracer._isEnabled || !_UILayoutCycleTracer._isPassActive) {
80
80
  return;
81
81
  }
@@ -96,14 +96,20 @@ const _UILayoutCycleTracer = class {
96
96
  _UILayoutCycleTracer._totalReportsThisPass++;
97
97
  const rawStack = (_b = new Error().stack) != null ? _b : "(stack unavailable)";
98
98
  const cleanStack = _UILayoutCycleTracer._cleanStack(rawStack);
99
- const existing = _UILayoutCycleTracer._setNeedsLayoutCallsThisPass.get(view);
100
- if (existing) {
101
- existing.count++;
102
- existing.stacks.push(cleanStack);
103
- } else {
104
- _UILayoutCycleTracer._setNeedsLayoutCallsThisPass.set(view, { count: 1, stacks: [cleanStack] });
105
- }
106
- _UILayoutCycleTracer._reportCycle(view, layoutCount, cleanStack);
99
+ const snapshot = _UILayoutCycleTracer._snapshotView(view);
100
+ const callerFunction = _UILayoutCycleTracer._extractCallerFunctionName(cleanStack);
101
+ const existingHistory = (_c = _UILayoutCycleTracer._eventsThisPass.get(view)) != null ? _c : [];
102
+ const newEvent = {
103
+ occurrenceIndex: existingHistory.length + 1,
104
+ iteration: _UILayoutCycleTracer._currentIteration,
105
+ layoutCountAtTime: layoutCount,
106
+ callerFunction,
107
+ snapshot,
108
+ cleanStack
109
+ };
110
+ existingHistory.push(newEvent);
111
+ _UILayoutCycleTracer._eventsThisPass.set(view, existingHistory);
112
+ _UILayoutCycleTracer._reportCycle(view, existingHistory);
107
113
  }
108
114
  static didFinishLayoutPass(iterationCount) {
109
115
  if (!_UILayoutCycleTracer._isEnabled) {
@@ -132,6 +138,33 @@ const _UILayoutCycleTracer = class {
132
138
  }
133
139
  return lines.slice(firstAppFrameIndex).join("\n");
134
140
  }
141
+ static _extractCallerFunctionName(cleanStack) {
142
+ var _a, _b;
143
+ const firstLine = (_b = (_a = cleanStack.split("\n")[0]) == null ? void 0 : _a.trim()) != null ? _b : "";
144
+ const match = firstLine.match(/at\s+([\w.<>$]+)\s+\(/);
145
+ if (match) {
146
+ return match[1];
147
+ }
148
+ return firstLine.substring(0, 80) || "(unknown)";
149
+ }
150
+ static _snapshotView(view) {
151
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
152
+ const frame = view == null ? void 0 : view.frame;
153
+ const bounds = view == null ? void 0 : view.bounds;
154
+ return {
155
+ isVirtualLayouting: (_a = view == null ? void 0 : view.isVirtualLayouting) != null ? _a : false,
156
+ frameWidth: (_b = frame == null ? void 0 : frame.width) != null ? _b : -1,
157
+ frameHeight: (_c = frame == null ? void 0 : frame.height) != null ? _c : -1,
158
+ boundsWidth: (_d = bounds == null ? void 0 : bounds.width) != null ? _d : -1,
159
+ boundsHeight: (_e = bounds == null ? void 0 : bounds.height) != null ? _e : -1,
160
+ className: (_g = (_f = view == null ? void 0 : view.constructor) == null ? void 0 : _f.name) != null ? _g : "UnknownView",
161
+ elementID: (_i = (_h = view == null ? void 0 : view.elementID) != null ? _h : view == null ? void 0 : view._UIViewIndex) != null ? _i : "?"
162
+ };
163
+ }
164
+ static _formatSnapshotInline(snapshot) {
165
+ const virtualTag = snapshot.isVirtualLayouting ? "\u{1F52E} VIRTUAL" : "\u{1F4D0} real";
166
+ return `${virtualTag} frame ${snapshot.frameWidth.toFixed(1)}\xD7${snapshot.frameHeight.toFixed(1)} bounds ${snapshot.boundsWidth.toFixed(1)}\xD7${snapshot.boundsHeight.toFixed(1)}`;
167
+ }
135
168
  static _viewIdentifier(view) {
136
169
  var _a, _b, _c, _d;
137
170
  const className = (_b = (_a = view == null ? void 0 : view.constructor) == null ? void 0 : _a.name) != null ? _b : "UnknownView";
@@ -143,25 +176,93 @@ const _UILayoutCycleTracer = class {
143
176
  let current = view;
144
177
  let depth = 0;
145
178
  while (current && depth < 20) {
146
- parts.push(_UILayoutCycleTracer._viewIdentifier(current));
179
+ const snapshot = _UILayoutCycleTracer._snapshotView(current);
180
+ const virtualTag = snapshot.isVirtualLayouting ? "[VIRTUAL]" : "[real]";
181
+ parts.push(
182
+ `${_UILayoutCycleTracer._viewIdentifier(current)} ${virtualTag} frame=${snapshot.frameWidth.toFixed(0)}\xD7${snapshot.frameHeight.toFixed(0)} bounds=${snapshot.boundsWidth.toFixed(0)}\xD7${snapshot.boundsHeight.toFixed(0)}`
183
+ );
147
184
  current = current.superview;
148
185
  depth++;
149
186
  }
150
- return parts.join(" \u2192 ");
187
+ return parts.join("\n \u2192 ");
151
188
  }
152
- static _reportCycle(view, layoutCountThisPass, cleanStack) {
189
+ static _printHistoryEvent(event, isCurrent) {
190
+ const label = isCurrent ? "\u{1F195} NOW" : `#${event.occurrenceIndex}`;
191
+ const iterationLabel = `iter ${event.iteration + 1}`;
192
+ const snapshotInline = _UILayoutCycleTracer._formatSnapshotInline(event.snapshot);
193
+ const title = ` ${label} ${event.callerFunction}() [${iterationLabel}] ${snapshotInline}`;
194
+ const titleStyle = isCurrent ? "color: #F44336; font-weight: bold" : "color: #888; font-weight: normal";
195
+ if (isCurrent) {
196
+ console.group(`%c${title}`, titleStyle);
197
+ } else {
198
+ console.groupCollapsed(`%c${title}`, titleStyle);
199
+ }
200
+ console.log(`%c layoutCount at time: ${event.layoutCountAtTime}`, "color: #aaa");
201
+ console.log("%c Stack:", "font-weight: bold");
202
+ event.cleanStack.split("\n").forEach((frame) => console.log(" " + frame.trim()));
203
+ console.groupEnd();
204
+ }
205
+ static _reportCycle(view, history) {
206
+ const currentEvent = history[history.length - 1];
153
207
  const identifier = _UILayoutCycleTracer._viewIdentifier(view);
154
208
  const chain = _UILayoutCycleTracer._superviewChain(view);
209
+ const virtualLabel = currentEvent.snapshot.isVirtualLayouting ? "VIRTUAL" : "real";
155
210
  console.groupCollapsed(
156
- `%c[UILayoutCycleTracer] \u26A0\uFE0F Layout cycle: ${identifier} re-queued after being laid out ${layoutCountThisPass}x in iteration ${_UILayoutCycleTracer._currentIteration + 1}`,
211
+ `%c[UILayoutCycleTracer] \u26A0\uFE0F Cycle #${history.length}: ${identifier} from ${currentEvent.callerFunction}() [${virtualLabel}, ${currentEvent.snapshot.frameWidth.toFixed(0)}\xD7${currentEvent.snapshot.frameHeight.toFixed(0)}] laid out ${currentEvent.layoutCountAtTime}x iter ${currentEvent.iteration + 1}`,
157
212
  "color: #F44336; font-weight: bold"
158
213
  );
159
- console.log("%cView:", "font-weight: bold", view);
160
- console.log("%cSuperview chain:", "font-weight: bold", chain);
161
- console.log("%cLayout count this pass:", "font-weight: bold", layoutCountThisPass);
162
- console.log("%cIteration:", "font-weight: bold", _UILayoutCycleTracer._currentIteration + 1);
163
- console.log("%cCall stack (noise frames stripped):", "font-weight: bold");
164
- cleanStack.split("\n").forEach((frame) => console.log(" " + frame.trim()));
214
+ console.group(
215
+ `%c\u{1F4DC} Full history for this view this pass (${history.length} event${history.length === 1 ? "" : "s"})`,
216
+ "font-weight: bold; color: #FF9800"
217
+ );
218
+ for (let i = 0; i < history.length; i++) {
219
+ _UILayoutCycleTracer._printHistoryEvent(history[i], i === history.length - 1);
220
+ }
221
+ console.groupEnd();
222
+ console.groupCollapsed(
223
+ "%c\u{1F517} Superview chain (innermost \u2192 root)",
224
+ "font-weight: bold; color: #9C27B0"
225
+ );
226
+ console.log(" " + chain);
227
+ console.groupEnd();
228
+ console.log("%c\u{1F5BC} Raw view object:", "font-weight: bold", view);
229
+ console.groupEnd();
230
+ }
231
+ static printViewReport(view) {
232
+ if (!view) {
233
+ console.warn("[UILayoutCycleTracer] printViewReport: no view provided");
234
+ return;
235
+ }
236
+ const identifier = _UILayoutCycleTracer._viewIdentifier(view);
237
+ const snapshot = _UILayoutCycleTracer._snapshotView(view);
238
+ const chain = _UILayoutCycleTracer._superviewChain(view);
239
+ const history = _UILayoutCycleTracer._eventsThisPass.get(view);
240
+ console.group(
241
+ `%c[UILayoutCycleTracer] \u{1F50D} Manual report: ${identifier}`,
242
+ "color: #2196F3; font-weight: bold"
243
+ );
244
+ console.group("%c\u{1F4F8} Current state", "font-weight: bold; color: #2196F3");
245
+ console.log(_UILayoutCycleTracer._formatSnapshotInline(snapshot));
246
+ console.groupEnd();
247
+ if (history && history.length > 0) {
248
+ console.group(
249
+ `%c\u{1F4DC} Cycle history from last pass (${history.length} event${history.length === 1 ? "" : "s"})`,
250
+ "font-weight: bold; color: #FF9800"
251
+ );
252
+ for (let i = 0; i < history.length; i++) {
253
+ _UILayoutCycleTracer._printHistoryEvent(history[i], i === history.length - 1);
254
+ }
255
+ console.groupEnd();
256
+ } else {
257
+ console.log("%c\u{1F4DC} No cycle history recorded for this view in the last pass", "color: #4CAF50");
258
+ }
259
+ console.groupCollapsed(
260
+ "%c\u{1F517} Superview chain (innermost \u2192 root)",
261
+ "font-weight: bold; color: #9C27B0"
262
+ );
263
+ console.log(" " + chain);
264
+ console.groupEnd();
265
+ console.log("%c\u{1F5BC} Raw view object:", "font-weight: bold", view);
165
266
  console.groupEnd();
166
267
  }
167
268
  };
@@ -169,7 +270,7 @@ let UILayoutCycleTracer = _UILayoutCycleTracer;
169
270
  UILayoutCycleTracer._isEnabled = false;
170
271
  UILayoutCycleTracer._isPassActive = false;
171
272
  UILayoutCycleTracer._layoutCountsThisPass = /* @__PURE__ */ new Map();
172
- UILayoutCycleTracer._setNeedsLayoutCallsThisPass = /* @__PURE__ */ new Map();
273
+ UILayoutCycleTracer._eventsThisPass = /* @__PURE__ */ new Map();
173
274
  UILayoutCycleTracer._currentIteration = 0;
174
275
  UILayoutCycleTracer._totalReportsThisPass = 0;
175
276
  UILayoutCycleTracer.reportThreshold = 1;
@@ -187,6 +288,7 @@ UILayoutCycleTracer._noiseFramePrefixes = [
187
288
  "layoutViewsIfNeeded"
188
289
  ];
189
290
  window.UILayoutCycleTracer = UILayoutCycleTracer;
291
+ window.layoutReport = (view) => UILayoutCycleTracer.printViewReport(view);
190
292
  // Annotate the CommonJS export names for ESM import in node:
191
293
  0 && (module.exports = {
192
294
  UILayoutCycleTracer
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../scripts/UILayoutCycleTracer.ts"],
4
- "sourcesContent": ["/// #if DEV\n\n/**\n * UILayoutCycleTracer\n *\n * A development-only utility that detects and reports layout cycles in the\n * UIView layout system.\n *\n * A layout cycle occurs when layouting a view causes another setNeedsLayout()\n * call on the same view (or an ancestor) within the same layout pass, causing\n * the loop in layoutViewsIfNeeded() to iterate multiple times.\n *\n * Usage:\n * UILayoutCycleTracer.enable() \u2014 start tracing\n * UILayoutCycleTracer.disable() \u2014 stop tracing\n * UILayoutCycleTracer.isEnabled \u2014 check current state\n *\n * When a cycle is detected, a detailed report is printed to the console\n * including:\n * - Which view was re-queued\n * - Its full superview chain\n * - The call stack at the point of the re-queue (setNeedsLayout call)\n * - How many times the view has been laid out in this pass\n *\n * Integration:\n * Call UILayoutCycleTracer.willBeginLayoutPass() at the start of\n * layoutViewsIfNeeded(), UILayoutCycleTracer.didLayoutView(view) after each\n * view is laid out, and UILayoutCycleTracer.viewDidCallSetNeedsLayout(view)\n * from setNeedsLayout() while a pass is active.\n */\nexport class UILayoutCycleTracer {\n \n static _isEnabled: boolean = false\n static _isPassActive: boolean = false\n static _layoutCountsThisPass: Map<any, number> = new Map()\n static _setNeedsLayoutCallsThisPass: Map<any, { count: number; stacks: string[] }> = new Map()\n static _currentIteration: number = 0\n static _totalReportsThisPass: number = 0\n \n // How many times a view must be re-queued before reporting.\n // 1 means: report the first time a view is re-queued after being laid out.\n static reportThreshold: number = 1\n \n // Maximum number of cycle reports per layout pass to avoid console flooding.\n static maxReportsPerPass: number = 10\n \n // Prefixes of stack frames that belong to the tracer or framework internals\n // and should be stripped from the top of the captured stack so that the\n // first visible frame is always application code.\n static _noiseFramePrefixes: string[] = [\n \"UILayoutCycleTracer\",\n \"UIView.setNeedsLayout\",\n \"setNeedsLayout\",\n \"UIView.didLayoutSubviews\",\n \"didLayoutSubviews\",\n \"UIView.layoutSubviews\",\n \"UIView.layoutIfNeeded\",\n \"layoutIfNeeded\",\n \"UIView.layoutViewsIfNeeded\",\n \"layoutViewsIfNeeded\",\n ]\n \n static get isEnabled(): boolean {\n return UILayoutCycleTracer._isEnabled\n }\n \n \n static enable(reportThreshold: number = 1) {\n // Maximise the V8 stack trace depth so long chains are fully visible.\n // The default is 10 which truncates most interesting call stacks.\n ;(Error as any).stackTraceLimit = 100\n UILayoutCycleTracer.reportThreshold = reportThreshold\n UILayoutCycleTracer._isEnabled = true\n console.log(\n `%c[UILayoutCycleTracer] Layout cycle tracing ENABLED (threshold=${reportThreshold}, Error.stackTraceLimit=100)`,\n \"color: #4CAF50; font-weight: bold\"\n )\n }\n \n static disable() {\n ;(Error as any).stackTraceLimit = 10\n UILayoutCycleTracer._isEnabled = false\n console.log(\n \"%c[UILayoutCycleTracer] Layout cycle tracing DISABLED\",\n \"color: #9E9E9E; font-weight: bold\"\n )\n }\n \n /**\n * Called at the very start of each layoutViewsIfNeeded() invocation.\n */\n static willBeginLayoutPass() {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._isPassActive = true\n UILayoutCycleTracer._layoutCountsThisPass = new Map()\n UILayoutCycleTracer._setNeedsLayoutCallsThisPass = new Map()\n UILayoutCycleTracer._currentIteration = 0\n UILayoutCycleTracer._totalReportsThisPass = 0\n }\n \n /**\n * Called after each iteration increment in the layoutViewsIfNeeded() while loop.\n */\n static willBeginIteration(iteration: number) {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._currentIteration = iteration\n if (iteration > 0 && UILayoutCycleTracer._totalReportsThisPass > 0) {\n console.warn(\n `%c[UILayoutCycleTracer] Layout pass iteration ${iteration + 1} \u2014 views were re-queued during the previous iteration`,\n \"color: #FF9800; font-weight: bold\"\n )\n }\n }\n \n /**\n * Called after a view's layoutIfNeeded() completes.\n */\n static didLayoutView(view: any) {\n if (!UILayoutCycleTracer._isEnabled || !UILayoutCycleTracer._isPassActive) { return }\n const previous = UILayoutCycleTracer._layoutCountsThisPass.get(view) ?? 0\n UILayoutCycleTracer._layoutCountsThisPass.set(view, previous + 1)\n }\n \n /**\n * Called from setNeedsLayout() when a view enters the queue.\n * If a layout pass is currently active, this is a potential cycle.\n */\n static viewDidCallSetNeedsLayout(view: any) {\n if (!UILayoutCycleTracer._isEnabled || !UILayoutCycleTracer._isPassActive) { return }\n \n // Only report if this view has already been laid out at least once this pass.\n const layoutCount = UILayoutCycleTracer._layoutCountsThisPass.get(view) ?? 0\n if (layoutCount < UILayoutCycleTracer.reportThreshold) { return }\n \n if (UILayoutCycleTracer._totalReportsThisPass >= UILayoutCycleTracer.maxReportsPerPass) {\n if (UILayoutCycleTracer._totalReportsThisPass === UILayoutCycleTracer.maxReportsPerPass) {\n console.warn(\n `%c[UILayoutCycleTracer] Maximum reports per pass (${UILayoutCycleTracer.maxReportsPerPass}) reached. Further reports suppressed.`,\n \"color: #F44336\"\n )\n UILayoutCycleTracer._totalReportsThisPass++\n }\n return\n }\n \n UILayoutCycleTracer._totalReportsThisPass++\n \n const rawStack = new Error().stack ?? \"(stack unavailable)\"\n const cleanStack = UILayoutCycleTracer._cleanStack(rawStack)\n \n const existing = UILayoutCycleTracer._setNeedsLayoutCallsThisPass.get(view)\n if (existing) {\n existing.count++\n existing.stacks.push(cleanStack)\n }\n else {\n UILayoutCycleTracer._setNeedsLayoutCallsThisPass.set(view, { count: 1, stacks: [cleanStack] })\n }\n \n UILayoutCycleTracer._reportCycle(view, layoutCount, cleanStack)\n }\n \n /**\n * Called at the end of a layout pass.\n */\n static didFinishLayoutPass(iterationCount: number) {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._isPassActive = false\n \n if (iterationCount > 1 && UILayoutCycleTracer._totalReportsThisPass > 0) {\n console.warn(\n `%c[UILayoutCycleTracer] Layout pass completed in ${iterationCount} iteration(s). ` +\n `${UILayoutCycleTracer._totalReportsThisPass} cycle event(s) recorded.`,\n \"color: #FF9800\"\n )\n }\n }\n \n /**\n * Strips the \"Error\" header line and any leading framework/tracer noise\n * frames from a raw Error.stack string, so the first frame shown is always\n * the application code that triggered the re-queue.\n */\n static _cleanStack(rawStack: string): string {\n const lines = rawStack.split(\"\\n\")\n \n // Find the first line that is NOT the \"Error\" header and NOT a noise frame.\n let firstAppFrameIndex = 1 // skip \"Error\" on line 0\n for (let i = 1; i < lines.length; i++) {\n const trimmed = lines[i].trim()\n const isNoise = UILayoutCycleTracer._noiseFramePrefixes.some(prefix =>\n trimmed.includes(prefix)\n )\n if (!isNoise) {\n firstAppFrameIndex = i\n break\n }\n }\n \n return lines.slice(firstAppFrameIndex).join(\"\\n\")\n }\n \n static _viewIdentifier(view: any): string {\n const className = view?.constructor?.name ?? \"UnknownView\"\n const elementID = view?.elementID ?? view?._UIViewIndex ?? \"?\"\n return `${className}#${elementID}`\n }\n \n static _superviewChain(view: any): string {\n const parts: string[] = []\n let current = view\n let depth = 0\n while (current && depth < 20) {\n parts.push(UILayoutCycleTracer._viewIdentifier(current))\n current = current.superview\n depth++\n }\n return parts.join(\" \u2192 \")\n }\n \n static _reportCycle(view: any, layoutCountThisPass: number, cleanStack: string) {\n const identifier = UILayoutCycleTracer._viewIdentifier(view)\n const chain = UILayoutCycleTracer._superviewChain(view)\n \n console.groupCollapsed(\n `%c[UILayoutCycleTracer] \u26A0\uFE0F Layout cycle: ${identifier} re-queued after being laid out ${layoutCountThisPass}x in iteration ${UILayoutCycleTracer._currentIteration + 1}`,\n \"color: #F44336; font-weight: bold\"\n )\n console.log(\"%cView:\", \"font-weight: bold\", view)\n console.log(\"%cSuperview chain:\", \"font-weight: bold\", chain)\n console.log(\"%cLayout count this pass:\", \"font-weight: bold\", layoutCountThisPass)\n console.log(\"%cIteration:\", \"font-weight: bold\", UILayoutCycleTracer._currentIteration + 1)\n console.log(\"%cCall stack (noise frames stripped):\", \"font-weight: bold\")\n cleanStack.split(\"\\n\").forEach(frame => console.log(\" \" + frame.trim()))\n console.groupEnd()\n }\n \n}\n\nwindow.UILayoutCycleTracer = UILayoutCycleTracer\n\ndeclare global {\n interface Window {\n UILayoutCycleTracer?: typeof UILayoutCycleTracer\n }\n}\n\n/// #endif\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/// #if DEV\n\n/**\n * UILayoutCycleTracer\n *\n * A development-only utility that detects and reports layout cycles in the\n * UIView layout system.\n *\n * A layout cycle occurs when layouting a view causes another setNeedsLayout()\n * call on the same view (or an ancestor) within the same layout pass, causing\n * the loop in layoutViewsIfNeeded() to iterate multiple times.\n *\n * Usage:\n * UILayoutCycleTracer.enable() \u2014 start tracing\n * UILayoutCycleTracer.disable() \u2014 stop tracing\n * UILayoutCycleTracer.isEnabled \u2014 check current state\n *\n * When a cycle is detected, a detailed report is printed to the console\n * including:\n * - Which view was re-queued\n * - Its full superview chain\n * - A state snapshot (isVirtualLayouting, frame size, bounds size, class name)\n * - The full history of all previous cycle events for the same view this pass,\n * so the entire oscillation pattern is visible in one place\n * - The call stack at the point of the re-queue (setNeedsLayout call)\n * - How many times the view has been laid out in this pass\n *\n * Integration:\n * Call UILayoutCycleTracer.willBeginLayoutPass() at the start of\n * layoutViewsIfNeeded(), UILayoutCycleTracer.didLayoutView(view) after each\n * view is laid out, and UILayoutCycleTracer.viewDidCallSetNeedsLayout(view)\n * from setNeedsLayout() while a pass is active.\n */\n\ninterface UILayoutCycleViewSnapshot {\n isVirtualLayouting: boolean\n frameWidth: number\n frameHeight: number\n boundsWidth: number\n boundsHeight: number\n className: string\n elementID: string\n}\n\ninterface UILayoutCycleEvent {\n // Which occurrence this is for this view within the current pass (1-based)\n occurrenceIndex: number\n iteration: number\n layoutCountAtTime: number\n callerFunction: string\n snapshot: UILayoutCycleViewSnapshot\n cleanStack: string\n}\n\nexport class UILayoutCycleTracer {\n \n static _isEnabled: boolean = false\n static _isPassActive: boolean = false\n static _layoutCountsThisPass: Map<any, number> = new Map()\n // Full event history per view, accumulated across the entire pass\n static _eventsThisPass: Map<any, UILayoutCycleEvent[]> = new Map()\n static _currentIteration: number = 0\n static _totalReportsThisPass: number = 0\n \n // How many times a view must be re-queued before reporting.\n // 1 means: report the first time a view is re-queued after being laid out.\n static reportThreshold: number = 1\n \n // Maximum number of cycle reports per layout pass to avoid console flooding.\n static maxReportsPerPass: number = 10\n \n // Prefixes of stack frames that belong to the tracer or framework internals\n // and should be stripped from the top of the captured stack so that the\n // first visible frame is always application code.\n static _noiseFramePrefixes: string[] = [\n \"UILayoutCycleTracer\",\n \"UIView.setNeedsLayout\",\n \"setNeedsLayout\",\n \"UIView.didLayoutSubviews\",\n \"didLayoutSubviews\",\n \"UIView.layoutSubviews\",\n \"UIView.layoutIfNeeded\",\n \"layoutIfNeeded\",\n \"UIView.layoutViewsIfNeeded\",\n \"layoutViewsIfNeeded\",\n ]\n \n static get isEnabled(): boolean {\n return UILayoutCycleTracer._isEnabled\n }\n \n \n static enable(reportThreshold: number = 1) {\n // Maximise the V8 stack trace depth so long chains are fully visible.\n // The default is 10 which truncates most interesting call stacks.\n ;(Error as any).stackTraceLimit = 100\n UILayoutCycleTracer.reportThreshold = reportThreshold\n UILayoutCycleTracer._isEnabled = true\n console.log(\n `%c[UILayoutCycleTracer] Layout cycle tracing ENABLED (threshold=${reportThreshold}, Error.stackTraceLimit=100)`,\n \"color: #4CAF50; font-weight: bold\"\n )\n }\n \n static disable() {\n ;(Error as any).stackTraceLimit = 10\n UILayoutCycleTracer._isEnabled = false\n console.log(\n \"%c[UILayoutCycleTracer] Layout cycle tracing DISABLED\",\n \"color: #9E9E9E; font-weight: bold\"\n )\n }\n \n /**\n * Called at the very start of each layoutViewsIfNeeded() invocation.\n */\n static willBeginLayoutPass() {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._isPassActive = true\n UILayoutCycleTracer._layoutCountsThisPass = new Map()\n UILayoutCycleTracer._eventsThisPass = new Map()\n UILayoutCycleTracer._currentIteration = 0\n UILayoutCycleTracer._totalReportsThisPass = 0\n }\n \n /**\n * Called after each iteration increment in the layoutViewsIfNeeded() while loop.\n */\n static willBeginIteration(iteration: number) {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._currentIteration = iteration\n if (iteration > 0 && UILayoutCycleTracer._totalReportsThisPass > 0) {\n console.warn(\n `%c[UILayoutCycleTracer] Layout pass iteration ${iteration + 1} \u2014 views were re-queued during the previous iteration`,\n \"color: #FF9800; font-weight: bold\"\n )\n }\n }\n \n /**\n * Called after a view's layoutIfNeeded() completes.\n */\n static didLayoutView(view: any) {\n if (!UILayoutCycleTracer._isEnabled || !UILayoutCycleTracer._isPassActive) { return }\n const previous = UILayoutCycleTracer._layoutCountsThisPass.get(view) ?? 0\n UILayoutCycleTracer._layoutCountsThisPass.set(view, previous + 1)\n }\n \n /**\n * Called from setNeedsLayout() when a view enters the queue.\n * If a layout pass is currently active, this is a potential cycle.\n */\n static viewDidCallSetNeedsLayout(view: any) {\n if (!UILayoutCycleTracer._isEnabled || !UILayoutCycleTracer._isPassActive) { return }\n \n // Only report if this view has already been laid out at least once this pass.\n const layoutCount = UILayoutCycleTracer._layoutCountsThisPass.get(view) ?? 0\n if (layoutCount < UILayoutCycleTracer.reportThreshold) { return }\n \n if (UILayoutCycleTracer._totalReportsThisPass >= UILayoutCycleTracer.maxReportsPerPass) {\n if (UILayoutCycleTracer._totalReportsThisPass === UILayoutCycleTracer.maxReportsPerPass) {\n console.warn(\n `%c[UILayoutCycleTracer] Maximum reports per pass (${UILayoutCycleTracer.maxReportsPerPass}) reached. Further reports suppressed.`,\n \"color: #F44336\"\n )\n UILayoutCycleTracer._totalReportsThisPass++\n }\n return\n }\n \n UILayoutCycleTracer._totalReportsThisPass++\n \n const rawStack = new Error().stack ?? \"(stack unavailable)\"\n const cleanStack = UILayoutCycleTracer._cleanStack(rawStack)\n const snapshot = UILayoutCycleTracer._snapshotView(view)\n const callerFunction = UILayoutCycleTracer._extractCallerFunctionName(cleanStack)\n \n // Accumulate this event into the per-view history\n const existingHistory = UILayoutCycleTracer._eventsThisPass.get(view) ?? []\n const newEvent: UILayoutCycleEvent = {\n occurrenceIndex: existingHistory.length + 1,\n iteration: UILayoutCycleTracer._currentIteration,\n layoutCountAtTime: layoutCount,\n callerFunction,\n snapshot,\n cleanStack,\n }\n existingHistory.push(newEvent)\n UILayoutCycleTracer._eventsThisPass.set(view, existingHistory)\n \n UILayoutCycleTracer._reportCycle(view, existingHistory)\n }\n \n /**\n * Called at the end of a layout pass.\n */\n static didFinishLayoutPass(iterationCount: number) {\n if (!UILayoutCycleTracer._isEnabled) { return }\n UILayoutCycleTracer._isPassActive = false\n \n if (iterationCount > 1 && UILayoutCycleTracer._totalReportsThisPass > 0) {\n console.warn(\n `%c[UILayoutCycleTracer] Layout pass completed in ${iterationCount} iteration(s). ` +\n `${UILayoutCycleTracer._totalReportsThisPass} cycle event(s) recorded.`,\n \"color: #FF9800\"\n )\n }\n }\n \n /**\n * Strips the \"Error\" header line and any leading framework/tracer noise\n * frames from a raw Error.stack string, so the first frame shown is always\n * the application code that triggered the re-queue.\n */\n static _cleanStack(rawStack: string): string {\n const lines = rawStack.split(\"\\n\")\n \n // Find the first line that is NOT the \"Error\" header and NOT a noise frame.\n let firstAppFrameIndex = 1 // skip \"Error\" on line 0\n for (let i = 1; i < lines.length; i++) {\n const trimmed = lines[i].trim()\n const isNoise = UILayoutCycleTracer._noiseFramePrefixes.some(prefix =>\n trimmed.includes(prefix)\n )\n if (!isNoise) {\n firstAppFrameIndex = i\n break\n }\n }\n \n return lines.slice(firstAppFrameIndex).join(\"\\n\")\n }\n \n /**\n * Extracts the name of the first application function from a cleaned stack string.\n * Returns something like \"UIButton.layoutSubviews\" or \"CellView.layoutSubviews\".\n */\n static _extractCallerFunctionName(cleanStack: string): string {\n const firstLine = cleanStack.split(\"\\n\")[0]?.trim() ?? \"\"\n // V8 format: \" at ClassName.methodName (file:line:col)\"\n // or: \" at functionName (file:line:col)\"\n const match = firstLine.match(/at\\s+([\\w.<>$]+)\\s+\\(/)\n if (match) {\n return match[1]\n }\n // Fallback: return whatever is on the first line, truncated\n return firstLine.substring(0, 80) || \"(unknown)\"\n }\n \n /**\n * Captures a diagnostic snapshot of a view's current layout state.\n * Safe to call at any point \u2014 gracefully handles nil/missing properties.\n */\n static _snapshotView(view: any): UILayoutCycleViewSnapshot {\n const frame = view?.frame\n const bounds = view?.bounds\n return {\n isVirtualLayouting: view?.isVirtualLayouting ?? false,\n frameWidth: frame?.width ?? -1,\n frameHeight: frame?.height ?? -1,\n boundsWidth: bounds?.width ?? -1,\n boundsHeight: bounds?.height ?? -1,\n className: view?.constructor?.name ?? \"UnknownView\",\n elementID: view?.elementID ?? view?._UIViewIndex ?? \"?\",\n }\n }\n \n static _formatSnapshotInline(snapshot: UILayoutCycleViewSnapshot): string {\n const virtualTag = snapshot.isVirtualLayouting ? \"\uD83D\uDD2E VIRTUAL\" : \"\uD83D\uDCD0 real\"\n return (\n `${virtualTag} ` +\n `frame ${snapshot.frameWidth.toFixed(1)}\u00D7${snapshot.frameHeight.toFixed(1)} ` +\n `bounds ${snapshot.boundsWidth.toFixed(1)}\u00D7${snapshot.boundsHeight.toFixed(1)}`\n )\n }\n \n static _viewIdentifier(view: any): string {\n const className = view?.constructor?.name ?? \"UnknownView\"\n const elementID = view?.elementID ?? view?._UIViewIndex ?? \"?\"\n return `${className}#${elementID}`\n }\n \n static _superviewChain(view: any): string {\n const parts: string[] = []\n let current = view\n let depth = 0\n while (current && depth < 20) {\n const snapshot = UILayoutCycleTracer._snapshotView(current)\n const virtualTag = snapshot.isVirtualLayouting ? \"[VIRTUAL]\" : \"[real]\"\n parts.push(\n `${UILayoutCycleTracer._viewIdentifier(current)} ${virtualTag} ` +\n `frame=${snapshot.frameWidth.toFixed(0)}\u00D7${snapshot.frameHeight.toFixed(0)} ` +\n `bounds=${snapshot.boundsWidth.toFixed(0)}\u00D7${snapshot.boundsHeight.toFixed(0)}`\n )\n current = current.superview\n depth++\n }\n return parts.join(\"\\n \u2192 \")\n }\n \n /**\n * Prints a single event row inside the history section.\n * Past events are collapsed sub-groups (stack accessible but not noisy).\n * The current event is expanded so it's immediately visible.\n */\n static _printHistoryEvent(event: UILayoutCycleEvent, isCurrent: boolean) {\n const label = isCurrent ? \"\uD83C\uDD95 NOW\" : `#${event.occurrenceIndex}`\n const iterationLabel = `iter ${event.iteration + 1}`\n const snapshotInline = UILayoutCycleTracer._formatSnapshotInline(event.snapshot)\n const title = ` ${label} ${event.callerFunction}() [${iterationLabel}] ${snapshotInline}`\n const titleStyle = isCurrent\n ? \"color: #F44336; font-weight: bold\"\n : \"color: #888; font-weight: normal\"\n \n if (isCurrent) {\n console.group(`%c${title}`, titleStyle)\n }\n else {\n console.groupCollapsed(`%c${title}`, titleStyle)\n }\n \n console.log(`%c layoutCount at time: ${event.layoutCountAtTime}`, \"color: #aaa\")\n console.log(\"%c Stack:\", \"font-weight: bold\")\n event.cleanStack.split(\"\\n\").forEach(frame => console.log(\" \" + frame.trim()))\n console.groupEnd()\n }\n \n static _reportCycle(view: any, history: UILayoutCycleEvent[]) {\n const currentEvent = history[history.length - 1]\n const identifier = UILayoutCycleTracer._viewIdentifier(view)\n const chain = UILayoutCycleTracer._superviewChain(view)\n const virtualLabel = currentEvent.snapshot.isVirtualLayouting ? \"VIRTUAL\" : \"real\"\n \n console.groupCollapsed(\n `%c[UILayoutCycleTracer] \u26A0\uFE0F Cycle #${history.length}: ${identifier} ` +\n `from ${currentEvent.callerFunction}() ` +\n `[${virtualLabel}, ${currentEvent.snapshot.frameWidth.toFixed(0)}\u00D7${currentEvent.snapshot.frameHeight.toFixed(0)}] ` +\n `laid out ${currentEvent.layoutCountAtTime}x iter ${currentEvent.iteration + 1}`,\n \"color: #F44336; font-weight: bold\"\n )\n \n // \u2500\u2500 History \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Always expanded \u2014 this is the primary diagnostic value.\n console.group(\n `%c\uD83D\uDCDC Full history for this view this pass (${history.length} event${history.length === 1 ? \"\" : \"s\"})`,\n \"font-weight: bold; color: #FF9800\"\n )\n for (let i = 0; i < history.length; i++) {\n UILayoutCycleTracer._printHistoryEvent(history[i], i === history.length - 1)\n }\n console.groupEnd()\n \n // \u2500\u2500 Superview chain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n console.groupCollapsed(\n \"%c\uD83D\uDD17 Superview chain (innermost \u2192 root)\",\n \"font-weight: bold; color: #9C27B0\"\n )\n console.log(\" \" + chain)\n console.groupEnd()\n \n // \u2500\u2500 Raw view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n console.log(\"%c\uD83D\uDDBC Raw view object:\", \"font-weight: bold\", view)\n \n console.groupEnd()\n }\n \n /**\n * Manually prints a full diagnostic report for any view on demand.\n * Call from the browser console: layoutReport(someView)\n *\n * Shows:\n * - Current state snapshot (isVirtualLayouting, frame, bounds)\n * - Superview chain with state at each level\n * - Full cycle history for this view from the most recent pass, if any\n * - The raw view object for live inspection\n */\n static printViewReport(view: any) {\n if (!view) {\n console.warn(\"[UILayoutCycleTracer] printViewReport: no view provided\")\n return\n }\n \n const identifier = UILayoutCycleTracer._viewIdentifier(view)\n const snapshot = UILayoutCycleTracer._snapshotView(view)\n const chain = UILayoutCycleTracer._superviewChain(view)\n const history = UILayoutCycleTracer._eventsThisPass.get(view)\n \n console.group(\n `%c[UILayoutCycleTracer] \uD83D\uDD0D Manual report: ${identifier}`,\n \"color: #2196F3; font-weight: bold\"\n )\n \n // \u2500\u2500 Current state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n console.group(\"%c\uD83D\uDCF8 Current state\", \"font-weight: bold; color: #2196F3\")\n console.log(UILayoutCycleTracer._formatSnapshotInline(snapshot))\n console.groupEnd()\n \n // \u2500\u2500 Cycle history \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (history && history.length > 0) {\n console.group(\n `%c\uD83D\uDCDC Cycle history from last pass (${history.length} event${history.length === 1 ? \"\" : \"s\"})`,\n \"font-weight: bold; color: #FF9800\"\n )\n for (let i = 0; i < history.length; i++) {\n UILayoutCycleTracer._printHistoryEvent(history[i], i === history.length - 1)\n }\n console.groupEnd()\n }\n else {\n console.log(\"%c\uD83D\uDCDC No cycle history recorded for this view in the last pass\", \"color: #4CAF50\")\n }\n \n // \u2500\u2500 Superview chain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n console.groupCollapsed(\n \"%c\uD83D\uDD17 Superview chain (innermost \u2192 root)\",\n \"font-weight: bold; color: #9C27B0\"\n )\n console.log(\" \" + chain)\n console.groupEnd()\n \n // \u2500\u2500 Raw view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n console.log(\"%c\uD83D\uDDBC Raw view object:\", \"font-weight: bold\", view)\n \n console.groupEnd()\n }\n \n}\n\nwindow.UILayoutCycleTracer = UILayoutCycleTracer\n\n/**\n * Global convenience function available in the browser console.\n * Usage: layoutReport(someView)\n */\nwindow.layoutReport = (view: any) => UILayoutCycleTracer.printViewReport(view)\n\ndeclare global {\n interface Window {\n UILayoutCycleTracer?: typeof UILayoutCycleTracer\n layoutReport: (view: any) => void\n }\n}\n\n/// #endif\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -192,7 +192,7 @@ const _UITextView = class extends import_UIView.UIView {
192
192
  if (this.notificationAmount > 0) {
193
193
  return false;
194
194
  }
195
- const hasComplexHTML = /<(?!\/?(b|i|em|strong|span|br)\b)[^>]+>/i.test(content);
195
+ const hasComplexHTML = /<(?!\/?(b|i|em|strong|span)\b)[^>]+>/i.test(content);
196
196
  if (hasComplexHTML) {
197
197
  return false;
198
198
  }
@@ -416,6 +416,7 @@ const _UITextView = class extends import_UIView.UIView {
416
416
  }
417
417
  intrinsicContentHeight(constrainingWidth = 0) {
418
418
  var _a;
419
+ constrainingWidth = Math.max(constrainingWidth.integerValue, 0);
419
420
  const keyPath = (this.textElementView.viewHTMLElement.innerHTML || this.text) + "_csf_" + this._getFontCacheKey() + "." + ("" + constrainingWidth).replace(new RegExp("\\.", "g"), "_");
420
421
  let cacheObject = _UITextView._intrinsicHeightCache;
421
422
  if (this.changesOften) {
@@ -429,7 +430,7 @@ const _UITextView = class extends import_UIView.UIView {
429
430
  if (styles) {
430
431
  const size = import_UITextMeasurement.UITextMeasurement.calculateTextSize(
431
432
  this.textElementView.viewHTMLElement,
432
- ((this.text || this.textElementView.innerHTML || "") + "").replace(/<br\s*\/?>/gi, "\n"),
433
+ (this.text || this.textElementView.innerHTML || "") + "",
433
434
  constrainingWidth || void 0,
434
435
  void 0,
435
436
  styles
@@ -451,6 +452,7 @@ const _UITextView = class extends import_UIView.UIView {
451
452
  }
452
453
  intrinsicContentWidth(constrainingHeight = 0) {
453
454
  var _a;
455
+ constrainingHeight = Math.max(constrainingHeight.integerValue, 0);
454
456
  const keyPath = (this.textElementView.viewHTMLElement.innerHTML || this.text) + "_csf_" + this._getFontCacheKey() + "." + ("" + constrainingHeight).replace(new RegExp("\\.", "g"), "_");
455
457
  let cacheObject = _UITextView._intrinsicWidthCache;
456
458
  if (this.changesOften) {
@@ -499,8 +501,8 @@ const _UITextView = class extends import_UIView.UIView {
499
501
  }
500
502
  const height = this._textElementView.style.height;
501
503
  const width = this._textElementView.style.width;
502
- this._textElementView.style.height = "" + constrainingHeight;
503
- this._textElementView.style.width = "" + constrainingWidth;
504
+ this._textElementView.style.height = "" + constrainingHeight + "px";
505
+ this._textElementView.style.width = "" + constrainingWidth + "px";
504
506
  const left = this._textElementView.style.left;
505
507
  const right = this._textElementView.style.right;
506
508
  const bottom = this._textElementView.style.bottom;