@zzish/math-rich-input 0.1.58 → 0.1.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/standalone.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import React from "react";
2
2
  import { createRoot } from "react-dom/client";
3
+ import { flushSync } from "react-dom";
3
4
  import { MathRichInput } from "./index.js";
5
+ import { setupKatex } from "./katexHelper";
4
6
 
5
7
  // Import CSS files
6
8
  import "./MathRichInput.css";
@@ -31,6 +33,8 @@ class MathRichInputElement extends HTMLElement {
31
33
  this.lastRenderTime = 0;
32
34
  this.renderThrottleMs = 16; // ~60fps throttle
33
35
  this.isDuringDrag = false;
36
+ /** The element waiting for the first, synchronous render (`drawFirstRender`); null once drawn. */
37
+ this.firstElement = null;
34
38
  /** Pending teardown, cancelled when a disconnect turns out to have been a move. */
35
39
  this.teardownTimeout = null;
36
40
 
@@ -376,6 +380,38 @@ class MathRichInputElement extends HTMLElement {
376
380
  }
377
381
  }
378
382
 
383
+ /**
384
+ * DRAW THE FIRST RENDER BEFORE THE PAGE CAN PAINT.
385
+ *
386
+ * `root.render()` is not synchronous. React picks its priority from `window.event`: inside a discrete
387
+ * event such as a click it is drawn at once, anywhere else it is scheduled and drawn in slices with the
388
+ * browser painting in between. A host that connects fields outside an event — a framework flushing its
389
+ * DOM in a microtask, or this bundle arriving and upgrading fields already on the page — showed every
390
+ * field empty, then filled them one at a time while the layout around them grew (the Notebook's Studio,
391
+ * WORK-491). The first render is therefore flushed synchronously.
392
+ *
393
+ * In a microtask rather than here, because a field can be connected while React is committing (a
394
+ * React host renders `<math-rich-input>` in its own tree), and `flushSync` cannot flush from inside a
395
+ * commit: it warns and does nothing. A microtask runs once the commit has finished and still before the
396
+ * browser paints — React's own documented pattern for this case. A render asked for again before then
397
+ * replaces the element, so what is drawn is the latest.
398
+ */
399
+ drawFirstRender(root, element) {
400
+ const pending = this.firstElement;
401
+ this.firstElement = element;
402
+ if (pending) return;
403
+ queueMicrotask(() => {
404
+ const latest = this.firstElement;
405
+ this.firstElement = null;
406
+ if (this.root !== root || !latest) return;
407
+ try {
408
+ flushSync(() => root.render(latest));
409
+ } catch (error) {
410
+ console.error("❌ MathRichInput: First render failed:", error);
411
+ }
412
+ });
413
+ }
414
+
379
415
  render() {
380
416
  // Check if this is a force render (bypass all safety checks)
381
417
  const isForceRender = globalDragState.forceRenderQueue.has(this);
@@ -423,9 +459,11 @@ class MathRichInputElement extends HTMLElement {
423
459
  }
424
460
 
425
461
  // Create React root if it doesn't exist
462
+ let firstRender = false;
426
463
  if (!this.root) {
427
464
  try {
428
465
  this.root = createRoot(container);
466
+ firstRender = true;
429
467
  } catch (error) {
430
468
  console.error(
431
469
  "❌ MathRichInput: Failed to create React root:",
@@ -441,9 +479,7 @@ class MathRichInputElement extends HTMLElement {
441
479
  return;
442
480
  }
443
481
 
444
- // Render with additional error boundary
445
- this.root.render(
446
- React.createElement(MathRichInput, {
482
+ const element = React.createElement(MathRichInput, {
447
483
  value: value,
448
484
  placeholder: placeholder,
449
485
  readOnly: readonly,
@@ -482,8 +518,10 @@ class MathRichInputElement extends HTMLElement {
482
518
  console.error("❌ MathRichInput: onChange error:", error);
483
519
  }
484
520
  },
485
- })
486
- );
521
+ });
522
+
523
+ if (firstRender || this.firstElement) this.drawFirstRender(this.root, element);
524
+ else this.root.render(element);
487
525
 
488
526
  // Render completed successfully
489
527
  } catch (error) {
@@ -503,6 +541,11 @@ class MathRichInputElement extends HTMLElement {
503
541
  }
504
542
  }
505
543
 
544
+ // KaTeX's stylesheet goes on the page when the element is defined, not when the first field mounts: a
545
+ // host that loads this bundle ahead of its first field (the Notebook does, at idle) then has the
546
+ // stylesheet and its fonts in place before any field is drawn. Same stylesheet, same id; see katexHelper.
547
+ setupKatex();
548
+
506
549
  // Define the web component
507
550
  customElements.define("math-rich-input", MathRichInputElement);
508
551