silvery 0.17.3 → 0.18.0
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/README.md +7 -13
- package/dist/{UPNG-AVSMjiFE.mjs → UPNG-DvKjM6wE.mjs} +1 -1
- package/dist/{UPNG-AVSMjiFE.mjs.map → UPNG-DvKjM6wE.mjs.map} +1 -1
- package/dist/{__vite-browser-external-2447137e-D3GdsvS_.mjs → __vite-browser-external-2447137e-DPKHHqQK.mjs} +1 -1
- package/dist/{__vite-browser-external-2447137e-D3GdsvS_.mjs.map → __vite-browser-external-2447137e-DPKHHqQK.mjs.map} +1 -1
- package/dist/{animation-C_PTO0uH.mjs → animation-DhINOJk8.mjs} +1 -1
- package/dist/{animation-C_PTO0uH.mjs.map → animation-DhINOJk8.mjs.map} +1 -1
- package/dist/{ansi-CXLE_pt1.mjs → ansi-C6Qs1Wn2.mjs} +1 -1
- package/dist/{ansi-CXLE_pt1.mjs.map → ansi-C6Qs1Wn2.mjs.map} +1 -1
- package/dist/{ansi-zmNzgkPB.d.mts → ansi-CsjnZtAw.d.mts} +1 -1
- package/dist/{ansi-zmNzgkPB.d.mts.map → ansi-CsjnZtAw.d.mts.map} +1 -1
- package/dist/apng-CvSlLBtc.mjs +3 -0
- package/dist/{apng-ENBAJk-H.mjs → apng-DFFVOItr.mjs} +3 -3
- package/dist/{apng-ENBAJk-H.mjs.map → apng-DFFVOItr.mjs.map} +1 -1
- package/dist/{backend-CkIkIHR-.mjs → backend-DU0Y938U.mjs} +1 -1
- package/dist/{backend-CkIkIHR-.mjs.map → backend-DU0Y938U.mjs.map} +1 -1
- package/dist/{backends-CkvbG3js.mjs → backends-BihMKFY_.mjs} +3 -3
- package/dist/{backends-CkvbG3js.mjs.map → backends-BihMKFY_.mjs.map} +1 -1
- package/dist/backends-Dk_5G_gC.mjs +3 -0
- package/dist/cli-GwJ0S2In.mjs +4 -0
- package/dist/{context-QreF3UHr.mjs → context-BjWgrikx.mjs} +1 -1
- package/dist/{context-QreF3UHr.mjs.map → context-BjWgrikx.mjs.map} +1 -1
- package/dist/{derive-D7bFJdfU.d.mts → derive-O_Kb1Bk_.d.mts} +3 -3
- package/dist/derive-O_Kb1Bk_.d.mts.map +1 -0
- package/dist/{devtools-owvUPfBi.mjs → devtools-CeO9X_uv.mjs} +4 -4
- package/dist/{devtools-owvUPfBi.mjs.map → devtools-CeO9X_uv.mjs.map} +1 -1
- package/dist/devtools-nX4tj6OH.mjs +2 -0
- package/dist/{eta-DLiVPaSD.mjs → eta-BnQSZcWf.mjs} +1 -1
- package/dist/{eta-DLiVPaSD.mjs.map → eta-BnQSZcWf.mjs.map} +1 -1
- package/dist/{flexily-zero-adapter-DmG4Ge8t.mjs → flexily-zero-adapter-BOM0cl8R.mjs} +61 -9
- package/dist/flexily-zero-adapter-BOM0cl8R.mjs.map +1 -0
- package/dist/{flexily-zero-adapter-GHwEW11s.mjs → flexily-zero-adapter-V8R3HQtK.mjs} +1 -1
- package/dist/{gif-Bp6fIyN3.mjs → gif-B9Uq4qZA.mjs} +3 -3
- package/dist/{gif-Bp6fIyN3.mjs.map → gif-B9Uq4qZA.mjs.map} +1 -1
- package/dist/gif-BdrLRBmM.mjs +3 -0
- package/dist/{gifenc-GiVCZ9-3.mjs → gifenc-DfhOb4xr.mjs} +1 -1
- package/dist/{gifenc-GiVCZ9-3.mjs.map → gifenc-DfhOb4xr.mjs.map} +1 -1
- package/dist/{image-Dx7gYjkq.mjs → image-B0zMbVUr.mjs} +136 -5
- package/dist/image-B0zMbVUr.mjs.map +1 -0
- package/dist/index-Bh3U1K09.d.mts +10823 -0
- package/dist/index-Bh3U1K09.d.mts.map +1 -0
- package/dist/{index-p-wBs_wH.d.mts → index-C4vrhbud.d.mts} +1 -1
- package/dist/{index-p-wBs_wH.d.mts.map → index-C4vrhbud.d.mts.map} +1 -1
- package/dist/{index-DCVL3jHo.d.mts → index-dehZ18K-.d.mts} +144 -99
- package/dist/index-dehZ18K-.d.mts.map +1 -0
- package/dist/index.d.mts +7 -7219
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +13 -9343
- package/dist/index.mjs.map +1 -1
- package/dist/{key-mapping-BsUHe_nk.mjs → key-mapping-7k2ufK2b.mjs} +1 -1
- package/dist/{key-mapping-DsyfLEdC.mjs → key-mapping-WLUmxjx1.mjs} +1 -1
- package/dist/{key-mapping-DsyfLEdC.mjs.map → key-mapping-WLUmxjx1.mjs.map} +1 -1
- package/dist/{layout-engine-D_lSR4i9.mjs → layout-engine--drvrWjD.mjs} +1 -1
- package/dist/{layout-engine-B3dsnVLU.mjs → layout-engine-Dr3cY5U4.mjs} +3 -3
- package/dist/{layout-engine-B3dsnVLU.mjs.map → layout-engine-Dr3cY5U4.mjs.map} +1 -1
- package/dist/{multi-progress-CQVB9lES.mjs → multi-progress-CcdqJFlf.mjs} +3 -3
- package/dist/{multi-progress-CQVB9lES.mjs.map → multi-progress-CcdqJFlf.mjs.map} +1 -1
- package/dist/{multi-progress-C0-rkn86.d.mts → multi-progress-DQ-uUzLf.d.mts} +2 -2
- package/dist/{multi-progress-C0-rkn86.d.mts.map → multi-progress-DQ-uUzLf.d.mts.map} +1 -1
- package/dist/{node-Dedx-6xF.mjs → node-CP5WChgr.mjs} +1 -1
- package/dist/{node-Dedx-6xF.mjs.map → node-CP5WChgr.mjs.map} +1 -1
- package/dist/{progress-bar-COPSBlT9.mjs → progress-bar-IrUjkLfU.mjs} +4 -4
- package/dist/{progress-bar-COPSBlT9.mjs.map → progress-bar-IrUjkLfU.mjs.map} +1 -1
- package/dist/{reconciler-B-NaZvbO.mjs → reconciler-B8uxQxaU.mjs} +57 -81
- package/dist/reconciler-B8uxQxaU.mjs.map +1 -0
- package/dist/{render-string-CZKpuKXo.mjs → render-string-BwLG7rIX.mjs} +1 -1
- package/dist/{pipeline-BmfaZb1O.mjs → render-string-DVfgc8xr.mjs} +836 -508
- package/dist/render-string-DVfgc8xr.mjs.map +1 -0
- package/dist/{resvg-js-V6oMi8CY.mjs → resvg-js-Cwipz-_J.mjs} +1 -1
- package/dist/{resvg-js-V6oMi8CY.mjs.map → resvg-js-Cwipz-_J.mjs.map} +1 -1
- package/dist/runtime.d.mts +2 -2
- package/dist/runtime.mjs +3 -3
- package/dist/{spinner-Cgej6Vnb.d.mts → spinner-BRkaJI0N.d.mts} +2 -2
- package/dist/{spinner-Cgej6Vnb.d.mts.map → spinner-BRkaJI0N.d.mts.map} +1 -1
- package/dist/{spinner-DSByknyx.mjs → spinner-BmldKx0M.mjs} +3 -3
- package/dist/{spinner-DSByknyx.mjs.map → spinner-BmldKx0M.mjs.map} +1 -1
- package/dist/{src-C9f3hiVG.mjs → src-C0sOQW-t.mjs} +402 -156
- package/dist/src-C0sOQW-t.mjs.map +1 -0
- package/dist/src-CJPXf3fC.mjs +18348 -0
- package/dist/src-CJPXf3fC.mjs.map +1 -0
- package/dist/{src-fJVbhdn-.mjs → src-D8kLrQBT.mjs} +1 -1
- package/dist/{src-fJVbhdn-.mjs.map → src-D8kLrQBT.mjs.map} +1 -1
- package/dist/{src-9B5k0JmY.mjs → src-D_BS-as7.mjs} +1130 -100
- package/dist/src-D_BS-as7.mjs.map +1 -0
- package/dist/theme.d.mts +45 -30
- package/dist/theme.d.mts.map +1 -1
- package/dist/theme.mjs +3 -3
- package/dist/{types-CDgkE-Rw.d.mts → types-B4A8Ebba.d.mts} +1 -1
- package/dist/{types-CDgkE-Rw.d.mts.map → types-B4A8Ebba.d.mts.map} +1 -1
- package/dist/types-e4dpfbSa.mjs +468 -0
- package/dist/types-e4dpfbSa.mjs.map +1 -0
- package/dist/ui/animation.d.mts +1 -1
- package/dist/ui/animation.mjs +1 -1
- package/dist/ui/ansi.d.mts +1 -1
- package/dist/ui/ansi.mjs +1 -1
- package/dist/ui/cli.d.mts +3 -3
- package/dist/ui/cli.mjs +5 -5
- package/dist/ui/display.d.mts +2 -2
- package/dist/ui/display.mjs +1 -1
- package/dist/ui/display.mjs.map +1 -1
- package/dist/ui/image.d.mts +1 -1
- package/dist/ui/image.mjs +1 -1
- package/dist/ui/input.d.mts +3 -3
- package/dist/ui/input.mjs +2 -2
- package/dist/ui/input.mjs.map +1 -1
- package/dist/ui/progress.d.mts +3 -3
- package/dist/ui/progress.mjs +4 -4
- package/dist/ui/progress.mjs.map +1 -1
- package/dist/ui/react.d.mts +3 -3
- package/dist/ui/react.mjs +4 -4
- package/dist/ui/react.mjs.map +1 -1
- package/dist/ui/utils.mjs +1 -1
- package/dist/ui/wrappers.d.mts +2 -2
- package/dist/ui/wrappers.mjs +1 -1
- package/dist/ui.d.mts +5 -5
- package/dist/ui.mjs +6 -6
- package/dist/{useLatest-BMIYXd6e.d.mts → useLatest-6xqnGIU6.d.mts} +1 -1
- package/dist/{useLatest-BMIYXd6e.d.mts.map → useLatest-6xqnGIU6.d.mts.map} +1 -1
- package/dist/{with-text-input-CmHf_9d6.d.mts → with-text-input-lUh9gYAG.d.mts} +3 -3
- package/dist/{with-text-input-CmHf_9d6.d.mts.map → with-text-input-lUh9gYAG.d.mts.map} +1 -1
- package/dist/{wrapper-Dqh0zi2W.mjs → wrapper-CE6GQ27z.mjs} +1 -1
- package/dist/{wrapper-Dqh0zi2W.mjs.map → wrapper-CE6GQ27z.mjs.map} +1 -1
- package/dist/{wrappers-hhL8EQ_n.mjs → wrappers-JrEYTuKA.mjs} +4 -4
- package/dist/wrappers-JrEYTuKA.mjs.map +1 -0
- package/dist/yoga-adapter-B8LZpQcE.mjs +2 -0
- package/dist/{yoga-adapter-BJ9SOhTY.mjs → yoga-adapter-Bc8XT9cN.mjs} +11 -2
- package/dist/yoga-adapter-Bc8XT9cN.mjs.map +1 -0
- package/package.json +20 -17
- package/dist/apng-DCWY913R.mjs +0 -3
- package/dist/backends-CyJqNLeK.mjs +0 -3
- package/dist/cli-B-k7Bm56.mjs +0 -4
- package/dist/derive-D7bFJdfU.d.mts.map +0 -1
- package/dist/devtools-DS9NseGT.mjs +0 -2
- package/dist/flexily-zero-adapter-DmG4Ge8t.mjs.map +0 -1
- package/dist/gif-BaJNREpP.mjs +0 -3
- package/dist/image-Dx7gYjkq.mjs.map +0 -1
- package/dist/index-CBcSpGSM.d.mts +0 -3416
- package/dist/index-CBcSpGSM.d.mts.map +0 -1
- package/dist/index-DCVL3jHo.d.mts.map +0 -1
- package/dist/pipeline-BmfaZb1O.mjs.map +0 -1
- package/dist/reconciler-B-NaZvbO.mjs.map +0 -1
- package/dist/render-string-Bvh1XzBv.mjs +0 -201
- package/dist/render-string-Bvh1XzBv.mjs.map +0 -1
- package/dist/runtime-PH2xY1DM.mjs +0 -8723
- package/dist/runtime-PH2xY1DM.mjs.map +0 -1
- package/dist/src-9B5k0JmY.mjs.map +0 -1
- package/dist/src-C9f3hiVG.mjs.map +0 -1
- package/dist/types-Bhj5QkIQ.mjs +0 -13
- package/dist/types-Bhj5QkIQ.mjs.map +0 -1
- package/dist/useLayout-BG2cGl15.mjs +0 -139
- package/dist/useLayout-BG2cGl15.mjs.map +0 -1
- package/dist/wrappers-hhL8EQ_n.mjs.map +0 -1
- package/dist/yoga-adapter-BJ9SOhTY.mjs.map +0 -1
- package/dist/yoga-adapter-Daq6-dw1.mjs +0 -2
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { t as
|
|
1
|
+
import { c as signal, i as syncRectSignals, o as computed, t as rectEqual } from "./types-e4dpfbSa.mjs";
|
|
2
|
+
import { c as TermContext, o as StderrContext, s as StdoutContext } from "./context-BjWgrikx.mjs";
|
|
3
|
+
import { At as bufferToText, Dt as TerminalBuffer, Et as DEFAULT_BG, Ft as createTextFrame, H as ensureEmojiPresentation, I as canBreakAnywhere, It as init_buffer, K as graphemeWidth, Lt as isDefaultBg, Ot as ansi256ToRgb, Pt as createMutableCell, U as getActiveLineHeight, V as displayWidthAnsi, _t as wrapText, a as hostConfig, at as parseAnsiText, b as createTerm, c as clearDirtyTracking, d as collectPlainText, et as isWordBoundary, f as advanceRenderEpoch, ft as splitGraphemes, g as isDirty, h as isCurrentEpoch, kt as bufferToStyledText, l as hasScrollDirty, lt as sliceByWidth, m as isAnyDirty, ot as runWithMeasurer, p as getRenderEpoch, pt as splitGraphemesAnsiAware, q as hasAnsi, r as getContainerRoot, t as createContainer, u as measureStats, ut as sliceByWidthFromEnd } from "./reconciler-B8uxQxaU.mjs";
|
|
4
|
+
import { T as resolveThemeColor, k as blend, t as init_src, x as monoAttrsForColorString } from "./src-D_BS-as7.mjs";
|
|
5
|
+
import { A as pushContextTheme, D as getActiveTheme, E as getActiveColorLevel, O as init_state, k as popContextTheme, t as init_src$1, x as init_resolve } from "./src-C0sOQW-t.mjs";
|
|
6
|
+
import { i as isLayoutEngineInitialized, r as getLayoutEngine } from "./layout-engine-Dr3cY5U4.mjs";
|
|
7
|
+
import React, { act } from "react";
|
|
6
8
|
import { createLogger } from "loggily";
|
|
9
|
+
import Reconciler from "react-reconciler";
|
|
7
10
|
//#region packages/ag-term/src/pipeline/prepared-text.ts
|
|
8
11
|
const MAX_FORMAT_ENTRIES = 4;
|
|
9
12
|
/** Content-affecting flags that invalidate plain text. */
|
|
@@ -409,26 +412,29 @@ function measurePhase(root, ctx) {
|
|
|
409
412
|
traverseTree$1(root, (node) => {
|
|
410
413
|
if (!node.layoutNode) return;
|
|
411
414
|
const props = node.props;
|
|
412
|
-
const isFitContent = props.width === "fit-content" || props.height === "fit-content";
|
|
413
415
|
const isSnugContent = props.width === "snug-content";
|
|
414
|
-
|
|
416
|
+
const isHeightFitContent = props.height === "fit-content";
|
|
417
|
+
if (isSnugContent || isHeightFitContent) {
|
|
415
418
|
let availableWidth;
|
|
416
|
-
|
|
417
|
-
if (
|
|
419
|
+
let definiteUpperWidth = typeof props.width === "number" && isHeightFitContent ? props.width : typeof props.maxWidth === "number" ? props.maxWidth : void 0;
|
|
420
|
+
if (definiteUpperWidth === void 0) definiteUpperWidth = findAncestorDefiniteWidth(node);
|
|
421
|
+
if (definiteUpperWidth !== void 0) {
|
|
418
422
|
const padding = getPadding(props);
|
|
419
|
-
availableWidth =
|
|
423
|
+
availableWidth = definiteUpperWidth - padding.left - padding.right;
|
|
420
424
|
if (props.borderStyle) {
|
|
421
425
|
const border = getBorderSize(props);
|
|
422
426
|
availableWidth -= border.left + border.right;
|
|
423
427
|
}
|
|
424
428
|
if (availableWidth < 1) availableWidth = 1;
|
|
425
429
|
}
|
|
426
|
-
const intrinsicSize = measureIntrinsicSize(node, ctx, availableWidth);
|
|
427
430
|
if (isSnugContent) {
|
|
428
|
-
const shrunkWidth = computeSnugContentWidth(node,
|
|
429
|
-
node.layoutNode.
|
|
430
|
-
}
|
|
431
|
-
if (
|
|
431
|
+
const shrunkWidth = computeSnugContentWidth(node, measureIntrinsicSize(node, ctx, availableWidth).width, ctx);
|
|
432
|
+
node.layoutNode.setMaxWidth(shrunkWidth);
|
|
433
|
+
}
|
|
434
|
+
if (isHeightFitContent) {
|
|
435
|
+
const intrinsicSize = measureIntrinsicSize(node, ctx, availableWidth);
|
|
436
|
+
node.layoutNode.setHeight(intrinsicSize.height);
|
|
437
|
+
}
|
|
432
438
|
}
|
|
433
439
|
});
|
|
434
440
|
}
|
|
@@ -533,6 +539,39 @@ function computeSnugContentWidth(node, fitContentWidth, ctx) {
|
|
|
533
539
|
return shrinkwrapWidth(analysis, contentWidth) + overhead;
|
|
534
540
|
}
|
|
535
541
|
/**
|
|
542
|
+
* Walk up the tree from a node to find the nearest ancestor with a definite
|
|
543
|
+
* width (a fixed number, not "fit-content" or "snug-content"). Returns the
|
|
544
|
+
* ancestor's inner content width (after subtracting its own padding and border).
|
|
545
|
+
* Returns undefined if no definite-width ancestor is found.
|
|
546
|
+
*/
|
|
547
|
+
function findAncestorDefiniteWidth(node) {
|
|
548
|
+
let current = node.parent;
|
|
549
|
+
while (current) {
|
|
550
|
+
const p = current.props;
|
|
551
|
+
if (typeof p.width === "number") {
|
|
552
|
+
let inner = p.width;
|
|
553
|
+
const padding = getPadding(p);
|
|
554
|
+
inner -= padding.left + padding.right;
|
|
555
|
+
if (p.borderStyle) {
|
|
556
|
+
const border = getBorderSize(p);
|
|
557
|
+
inner -= border.left + border.right;
|
|
558
|
+
}
|
|
559
|
+
return inner > 0 ? inner : 1;
|
|
560
|
+
}
|
|
561
|
+
if (typeof p.maxWidth === "number") {
|
|
562
|
+
let inner = p.maxWidth;
|
|
563
|
+
const padding = getPadding(p);
|
|
564
|
+
inner -= padding.left + padding.right;
|
|
565
|
+
if (p.borderStyle) {
|
|
566
|
+
const border = getBorderSize(p);
|
|
567
|
+
inner -= border.left + border.right;
|
|
568
|
+
}
|
|
569
|
+
return inner > 0 ? inner : 1;
|
|
570
|
+
}
|
|
571
|
+
current = current.parent;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
536
575
|
* Traverse tree in depth-first order.
|
|
537
576
|
*/
|
|
538
577
|
function traverseTree$1(node, callback) {
|
|
@@ -554,7 +593,7 @@ function getTextWidth$1(text, ctx) {
|
|
|
554
593
|
*
|
|
555
594
|
* Run Yoga layout calculation and propagate dimensions to all nodes.
|
|
556
595
|
*/
|
|
557
|
-
const log$
|
|
596
|
+
const log$2 = createLogger("silvery:layout");
|
|
558
597
|
/**
|
|
559
598
|
* Run Yoga layout calculation and propagate dimensions to all nodes.
|
|
560
599
|
*
|
|
@@ -565,15 +604,17 @@ const log$3 = createLogger("silvery:layout");
|
|
|
565
604
|
function layoutPhase(root, width, height) {
|
|
566
605
|
const prevLayout = root.boxRect;
|
|
567
606
|
const dimensionsChanged = prevLayout && (prevLayout.width !== width || prevLayout.height !== height);
|
|
568
|
-
if (!dimensionsChanged && !
|
|
569
|
-
|
|
607
|
+
if (!dimensionsChanged && !root.layoutNode?.isDirty()) {
|
|
608
|
+
if (isDirty(root.dirtyBits, root.dirtyEpoch, 16)) propagateCascadeInputs(root);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
570
611
|
if (root.layoutNode) {
|
|
571
612
|
const nodeCount = countNodes(root);
|
|
572
613
|
measureStats.reset();
|
|
573
614
|
const t0 = Date.now();
|
|
574
615
|
root.layoutNode.calculateLayout(width, height);
|
|
575
616
|
const elapsed = Date.now() - t0;
|
|
576
|
-
log$
|
|
617
|
+
log$2.debug?.(`calculateLayout: ${elapsed}ms (${nodeCount} nodes) measure: calls=${measureStats.calls} hits=${measureStats.cacheHits} collects=${measureStats.textCollects} displayWidth=${measureStats.displayWidthCalls}`);
|
|
577
618
|
}
|
|
578
619
|
propagateLayout(root, 0, 0, !dimensionsChanged);
|
|
579
620
|
}
|
|
@@ -599,8 +640,8 @@ function countNodes(node) {
|
|
|
599
640
|
* subtrees whose inputs didn't change
|
|
600
641
|
* - If the parent's rect matches, all descendants' rects also match
|
|
601
642
|
* (Flexily computes absolute positions from parent dimensions)
|
|
602
|
-
* - prevLayout
|
|
603
|
-
*
|
|
643
|
+
* - prevLayout and layoutChangedThisFrame (stale epoch, won't match
|
|
644
|
+
* current) all retain correct values
|
|
604
645
|
*
|
|
605
646
|
* @param node The node to process
|
|
606
647
|
* @param parentX Absolute X position of parent
|
|
@@ -616,7 +657,6 @@ function propagateLayout(node, parentX, parentY, incrementalSkip) {
|
|
|
616
657
|
width: 0,
|
|
617
658
|
height: 0
|
|
618
659
|
};
|
|
619
|
-
node.layoutDirty = false;
|
|
620
660
|
for (const child of node.children) propagateLayout(child, parentX, parentY, incrementalSkip);
|
|
621
661
|
return;
|
|
622
662
|
}
|
|
@@ -626,12 +666,11 @@ function propagateLayout(node, parentX, parentY, incrementalSkip) {
|
|
|
626
666
|
width: node.layoutNode.getComputedWidth(),
|
|
627
667
|
height: node.layoutNode.getComputedHeight()
|
|
628
668
|
};
|
|
629
|
-
if (incrementalSkip && node.boxRect && !
|
|
669
|
+
if (incrementalSkip && node.boxRect && !isDirty(node.dirtyBits, node.dirtyEpoch, 16) && !isDirty(node.dirtyBits, node.dirtyEpoch, 8)) {
|
|
630
670
|
if (rect.x === node.boxRect.x && rect.y === node.boxRect.y && rect.width === node.boxRect.width && rect.height === node.boxRect.height) return;
|
|
631
671
|
}
|
|
632
672
|
node.prevLayout = node.boxRect;
|
|
633
673
|
node.boxRect = rect;
|
|
634
|
-
node.layoutDirty = false;
|
|
635
674
|
node.layoutChangedThisFrame = !!(node.prevLayout && !rectEqual(node.prevLayout, node.boxRect)) ? getRenderEpoch() : -1;
|
|
636
675
|
if (process?.env?.SILVERY_STRICT && isCurrentEpoch(node.layoutChangedThisFrame)) {
|
|
637
676
|
if (rectEqual(node.prevLayout, node.boxRect)) {
|
|
@@ -665,6 +704,34 @@ function propagateLayout(node, parentX, parentY, incrementalSkip) {
|
|
|
665
704
|
} else if (node.dirtyEpoch === getRenderEpoch()) node.dirtyBits &= -97;
|
|
666
705
|
}
|
|
667
706
|
/**
|
|
707
|
+
* Lightweight cascade input caching when the layout phase skips.
|
|
708
|
+
*
|
|
709
|
+
* When no layout nodes are dirty and dimensions haven't changed,
|
|
710
|
+
* `layoutPhase` returns early and `propagateLayout` never runs.
|
|
711
|
+
* But structural changes (absolute child mount/unmount, descendant overflow)
|
|
712
|
+
* still need cascade input bits (ABS_CHILD_BIT, DESC_OVERFLOW_BIT) to be
|
|
713
|
+
* computed for the render phase.
|
|
714
|
+
*
|
|
715
|
+
* This traversal follows only subtreeDirty paths (O(changed) not O(N))
|
|
716
|
+
* and computes the same cascade inputs as propagateLayout's caching block.
|
|
717
|
+
* No layout changes, no prevLayout updates, no layoutChangedThisFrame.
|
|
718
|
+
*/
|
|
719
|
+
function propagateCascadeInputs(node) {
|
|
720
|
+
if (!isDirty(node.dirtyBits, node.dirtyEpoch, 16)) return;
|
|
721
|
+
if (!node.children || node.children.length === 0) return;
|
|
722
|
+
for (const child of node.children) if (isDirty(child.dirtyBits, child.dirtyEpoch, 16)) propagateCascadeInputs(child);
|
|
723
|
+
const epoch = getRenderEpoch();
|
|
724
|
+
const absChild = _hasAbsoluteChildMutated(node.children);
|
|
725
|
+
const descOverflow = node.boxRect ? _hasDescendantOverflowChanged(node, node.boxRect) : false;
|
|
726
|
+
let bits = node.dirtyBits;
|
|
727
|
+
if (absChild) bits |= 32;
|
|
728
|
+
else bits &= -33;
|
|
729
|
+
if (descOverflow) bits |= 64;
|
|
730
|
+
else bits &= -65;
|
|
731
|
+
node.dirtyBits = bits;
|
|
732
|
+
node.dirtyEpoch = epoch;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
668
735
|
* Check if any direct child is position="absolute" and had structural changes.
|
|
669
736
|
*/
|
|
670
737
|
function _hasAbsoluteChildMutated(children) {
|
|
@@ -702,7 +769,7 @@ function _checkDescendantOverflow(children, nodeLeft, nodeTop, nodeRight, nodeBo
|
|
|
702
769
|
/**
|
|
703
770
|
* Notify all layout subscribers of dimension changes.
|
|
704
771
|
*
|
|
705
|
-
* Called
|
|
772
|
+
* Called by the pipeline AFTER scrollrectPhase completes,
|
|
706
773
|
* so useScrollRect can read correct screen positions.
|
|
707
774
|
*
|
|
708
775
|
* Notifies when EITHER boxRect, scrollRect, or screenRect changed.
|
|
@@ -712,13 +779,64 @@ function _checkDescendantOverflow(children, nodeLeft, nodeTop, nodeRight, nodeBo
|
|
|
712
779
|
* offset changes even when scrollRect stays the same.
|
|
713
780
|
*/
|
|
714
781
|
function notifyLayoutSubscribers(node) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
782
|
+
rectEqual(node.prevLayout, node.boxRect);
|
|
783
|
+
rectEqual(node.prevScrollRect, node.scrollRect);
|
|
784
|
+
rectEqual(node.prevScreenRect, node.screenRect);
|
|
785
|
+
syncRectSignals(node);
|
|
719
786
|
for (const child of node.children) notifyLayoutSubscribers(child);
|
|
720
787
|
}
|
|
721
788
|
/**
|
|
789
|
+
* Verify that no child's boxRect.width exceeds its parent's inner content width.
|
|
790
|
+
*
|
|
791
|
+
* This catches fit-content/snug-content bugs at the source — any measure-phase
|
|
792
|
+
* or correction-pass error fires immediately.
|
|
793
|
+
*
|
|
794
|
+
* - SILVERY_STRICT=1: console.warn on violation
|
|
795
|
+
* - SILVERY_STRICT=2: throw on violation
|
|
796
|
+
*
|
|
797
|
+
* Exceptions:
|
|
798
|
+
* - Parent has overflow: "scroll" or "hidden" (overflow is allowed)
|
|
799
|
+
* - Child has position: "absolute" (absolute nodes can overflow)
|
|
800
|
+
*/
|
|
801
|
+
function strictLayoutOverflowCheck(root) {
|
|
802
|
+
const strict = process?.env?.SILVERY_STRICT;
|
|
803
|
+
if (!strict) return;
|
|
804
|
+
const shouldThrow = strict === "2";
|
|
805
|
+
function walk(node) {
|
|
806
|
+
for (const child of node.children) {
|
|
807
|
+
if (child.boxRect && node.boxRect) {
|
|
808
|
+
const childProps = child.props;
|
|
809
|
+
if (childProps.position === "absolute") {
|
|
810
|
+
walk(child);
|
|
811
|
+
continue;
|
|
812
|
+
}
|
|
813
|
+
const parentProps = node.props;
|
|
814
|
+
if (parentProps.overflow === "scroll" || parentProps.overflow === "hidden") {
|
|
815
|
+
walk(child);
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
818
|
+
const border = parentProps.borderStyle ? getBorderSize(parentProps) : {
|
|
819
|
+
top: 0,
|
|
820
|
+
bottom: 0,
|
|
821
|
+
left: 0,
|
|
822
|
+
right: 0
|
|
823
|
+
};
|
|
824
|
+
const padding = getPadding(parentProps);
|
|
825
|
+
const parentInnerWidth = node.boxRect.width - padding.left - padding.right - border.left - border.right;
|
|
826
|
+
if (child.boxRect.width > parentInnerWidth) {
|
|
827
|
+
const childId = childProps.id ?? child.type;
|
|
828
|
+
const parentId = parentProps.id ?? node.type;
|
|
829
|
+
const msg = `[SILVERY_STRICT] Layout overflow: child "${childId}" width ${child.boxRect.width} exceeds parent "${parentId}" inner width ${parentInnerWidth} (parent box: ${node.boxRect.width}, border: ${border.left}+${border.right}, padding: ${padding.left}+${padding.right})`;
|
|
830
|
+
if (shouldThrow) throw new Error(msg);
|
|
831
|
+
else console.warn(msg);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
walk(child);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
walk(root);
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
722
840
|
* Calculate scroll state for all overflow='scroll' containers.
|
|
723
841
|
*
|
|
724
842
|
* This phase runs after layout to determine which children are visible
|
|
@@ -1095,6 +1213,7 @@ function detectPipelineFeatures(root) {
|
|
|
1095
1213
|
init_buffer();
|
|
1096
1214
|
init_state();
|
|
1097
1215
|
init_resolve();
|
|
1216
|
+
init_src();
|
|
1098
1217
|
const namedColors = {
|
|
1099
1218
|
black: 0,
|
|
1100
1219
|
red: 1,
|
|
@@ -1132,7 +1251,7 @@ function blendColors(c1, c2, t) {
|
|
|
1132
1251
|
* Supports: mix(c1,c2,amount), $token (theme), named colors, hex (#rgb, #rrggbb), rgb(r,g,b)
|
|
1133
1252
|
*/
|
|
1134
1253
|
function parseColor(color) {
|
|
1135
|
-
if (color === "inherit") return null;
|
|
1254
|
+
if (color === "inherit" || color === "currentColor") return null;
|
|
1136
1255
|
if (color === "$default") return DEFAULT_BG;
|
|
1137
1256
|
if (color.startsWith("mix(") && color.endsWith(")")) {
|
|
1138
1257
|
const inner = color.slice(4, -1);
|
|
@@ -1158,6 +1277,7 @@ function parseColor(color) {
|
|
|
1158
1277
|
}
|
|
1159
1278
|
}
|
|
1160
1279
|
if (color.startsWith("$")) {
|
|
1280
|
+
if (getActiveColorLevel() === "none") return null;
|
|
1161
1281
|
const resolved = resolveThemeColor(color, getActiveTheme());
|
|
1162
1282
|
if (resolved && resolved !== color) return parseColor(resolved);
|
|
1163
1283
|
return null;
|
|
@@ -1270,24 +1390,61 @@ function getBorderChars(style) {
|
|
|
1270
1390
|
return borders[style ?? "single"];
|
|
1271
1391
|
}
|
|
1272
1392
|
/**
|
|
1393
|
+
* Collect monochrome attrs from a color string (`"$primary"` → `["bold"]`).
|
|
1394
|
+
*
|
|
1395
|
+
* At mono tier, `parseColor` strips the color (returns `null`). The hierarchy
|
|
1396
|
+
* signal lives in the attrs bag. This helper merges the mapped attrs from
|
|
1397
|
+
* `DEFAULT_MONO_ATTRS` into a mutable accumulator. Called per color-carrying
|
|
1398
|
+
* prop in `getTextStyle`.
|
|
1399
|
+
*
|
|
1400
|
+
* No-op when the color is not a `$token` — non-token hex / named colors
|
|
1401
|
+
* pass through with no attrs (spec: "apps that hardcoded #FF0000 get nothing").
|
|
1402
|
+
*/
|
|
1403
|
+
function collectMonoAttrs(color, into) {
|
|
1404
|
+
if (!color) return;
|
|
1405
|
+
const attrs = monoAttrsForColorString(color, getActiveTheme());
|
|
1406
|
+
if (!attrs) return;
|
|
1407
|
+
for (const a of attrs) into.add(a);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1273
1410
|
* Get text style from props.
|
|
1274
1411
|
*/
|
|
1275
1412
|
function getTextStyle(props) {
|
|
1276
1413
|
let underlineStyle;
|
|
1277
1414
|
if (props.underlineStyle !== void 0) underlineStyle = props.underlineStyle;
|
|
1278
1415
|
else if (props.underline) underlineStyle = "single";
|
|
1416
|
+
let bold = props.bold;
|
|
1417
|
+
let dim = props.dim || props.dimColor;
|
|
1418
|
+
let italic = props.italic;
|
|
1419
|
+
let underline = props.underline || !!underlineStyle;
|
|
1420
|
+
let strikethrough = props.strikethrough;
|
|
1421
|
+
let inverse = props.inverse;
|
|
1422
|
+
if (getActiveColorLevel() === "none") {
|
|
1423
|
+
const monoAttrs = /* @__PURE__ */ new Set();
|
|
1424
|
+
collectMonoAttrs(props.color, monoAttrs);
|
|
1425
|
+
collectMonoAttrs(props.backgroundColor, monoAttrs);
|
|
1426
|
+
if (monoAttrs.has("bold")) bold = true;
|
|
1427
|
+
if (monoAttrs.has("dim")) dim = true;
|
|
1428
|
+
if (monoAttrs.has("italic")) italic = true;
|
|
1429
|
+
if (monoAttrs.has("underline")) {
|
|
1430
|
+
underline = true;
|
|
1431
|
+
if (!underlineStyle) underlineStyle = "single";
|
|
1432
|
+
}
|
|
1433
|
+
if (monoAttrs.has("strikethrough")) strikethrough = true;
|
|
1434
|
+
if (monoAttrs.has("inverse")) inverse = true;
|
|
1435
|
+
}
|
|
1279
1436
|
return {
|
|
1280
1437
|
fg: props.color ? parseColor(props.color) : null,
|
|
1281
1438
|
bg: props.backgroundColor ? parseColor(props.backgroundColor) : null,
|
|
1282
1439
|
underlineColor: props.underlineColor ? parseColor(props.underlineColor) : null,
|
|
1283
1440
|
attrs: {
|
|
1284
|
-
bold
|
|
1285
|
-
dim
|
|
1286
|
-
italic
|
|
1287
|
-
underline
|
|
1441
|
+
bold,
|
|
1442
|
+
dim,
|
|
1443
|
+
italic,
|
|
1444
|
+
underline,
|
|
1288
1445
|
underlineStyle,
|
|
1289
|
-
strikethrough
|
|
1290
|
-
inverse
|
|
1446
|
+
strikethrough,
|
|
1447
|
+
inverse
|
|
1291
1448
|
}
|
|
1292
1449
|
};
|
|
1293
1450
|
}
|
|
@@ -1306,7 +1463,7 @@ function getTextWidth(text, ctx) {
|
|
|
1306
1463
|
//#endregion
|
|
1307
1464
|
//#region packages/ag-term/src/pipeline/render-text.ts
|
|
1308
1465
|
init_buffer();
|
|
1309
|
-
const log$
|
|
1466
|
+
const log$1 = createLogger("silvery:content");
|
|
1310
1467
|
/** Cached bg conflict mode. Read from env once at module load. */
|
|
1311
1468
|
let bgConflictMode = (() => {
|
|
1312
1469
|
const env = typeof process !== "undefined" ? process.env.SILVERY_BG_CONFLICT?.toLowerCase() : void 0;
|
|
@@ -1386,9 +1543,12 @@ function styleToAnsi(style) {
|
|
|
1386
1543
|
}[style.underlineStyle] ?? "4");
|
|
1387
1544
|
else if (style.underline) parts.push("4");
|
|
1388
1545
|
if (style.underlineColor) {
|
|
1389
|
-
const
|
|
1390
|
-
if (
|
|
1391
|
-
|
|
1546
|
+
const underlineSource = style.underlineColor === "currentColor" || style.underlineColor === "inherit" ? style.color : style.underlineColor;
|
|
1547
|
+
if (underlineSource) {
|
|
1548
|
+
const ulColor = parseColor(underlineSource);
|
|
1549
|
+
if (ulColor !== null) if (typeof ulColor === "number") parts.push(`58;5;${ulColor}`);
|
|
1550
|
+
else parts.push(`58;2;${ulColor.r};${ulColor.g};${ulColor.b}`);
|
|
1551
|
+
}
|
|
1392
1552
|
}
|
|
1393
1553
|
if (style.inverse) parts.push("7");
|
|
1394
1554
|
if (style.strikethrough) parts.push("9");
|
|
@@ -1401,7 +1561,7 @@ function styleToAnsi(style) {
|
|
|
1401
1561
|
*/
|
|
1402
1562
|
function mergeStyleContext(parent, childProps) {
|
|
1403
1563
|
return {
|
|
1404
|
-
color: childProps.color ?? parent.color,
|
|
1564
|
+
color: (childProps.color === "inherit" || childProps.color === "currentColor" ? parent.color : childProps.color) ?? parent.color,
|
|
1405
1565
|
backgroundColor: childProps.backgroundColor ?? parent.backgroundColor,
|
|
1406
1566
|
bold: childProps.bold ?? parent.bold,
|
|
1407
1567
|
dim: childProps.dim ?? childProps.dimColor ?? parent.dim,
|
|
@@ -1829,7 +1989,7 @@ function renderAnsiTextLineReturn(buffer, x, y, text, baseStyle, maxCol, inherit
|
|
|
1829
1989
|
const key = `${JSON.stringify(existingBufBg)}-${segment.bg}-${preview}`;
|
|
1830
1990
|
if (!effectiveWarnedBgConflicts.has(key)) {
|
|
1831
1991
|
effectiveWarnedBgConflicts.add(key);
|
|
1832
|
-
log$
|
|
1992
|
+
log$1.warn?.(msg);
|
|
1833
1993
|
}
|
|
1834
1994
|
}
|
|
1835
1995
|
}
|
|
@@ -1970,13 +2130,25 @@ function renderText(node, buffer, layout, props, nodeState, inheritedBg, inherit
|
|
|
1970
2130
|
let text;
|
|
1971
2131
|
let bgSegments;
|
|
1972
2132
|
let childSpans;
|
|
2133
|
+
const rootContext = {
|
|
2134
|
+
color: props.color,
|
|
2135
|
+
backgroundColor: props.backgroundColor,
|
|
2136
|
+
bold: props.bold,
|
|
2137
|
+
dim: props.dim || props.dimColor,
|
|
2138
|
+
italic: props.italic,
|
|
2139
|
+
underline: !!(props.underline || props.underlineStyle),
|
|
2140
|
+
underlineStyle: props.underlineStyle,
|
|
2141
|
+
underlineColor: props.underlineColor,
|
|
2142
|
+
inverse: props.inverse,
|
|
2143
|
+
strikethrough: props.strikethrough
|
|
2144
|
+
};
|
|
1973
2145
|
const cachedCollected = getCachedCollectedText(node, maxDisplayWidth);
|
|
1974
2146
|
if (cachedCollected) {
|
|
1975
2147
|
text = cachedCollected.text;
|
|
1976
2148
|
bgSegments = cachedCollected.bgSegments;
|
|
1977
2149
|
childSpans = cachedCollected.childSpans;
|
|
1978
2150
|
} else {
|
|
1979
|
-
const collected = collectTextWithBg(node,
|
|
2151
|
+
const collected = collectTextWithBg(node, rootContext, 0, maxDisplayWidth, ctx);
|
|
1980
2152
|
text = collected.text;
|
|
1981
2153
|
bgSegments = collected.bgSegments;
|
|
1982
2154
|
childSpans = collected.childSpans;
|
|
@@ -1984,6 +2156,7 @@ function renderText(node, buffer, layout, props, nodeState, inheritedBg, inherit
|
|
|
1984
2156
|
}
|
|
1985
2157
|
const style = getTextStyle(props);
|
|
1986
2158
|
if (style.fg === null && inheritedFg !== void 0) style.fg = inheritedFg;
|
|
2159
|
+
if (props.underlineColor === "currentColor" || props.underlineColor === "inherit") style.underlineColor = style.fg;
|
|
1987
2160
|
const trim = !(style.bg !== null || bgSegments.length > 0 || inheritedBg !== void 0 && inheritedBg !== null);
|
|
1988
2161
|
const internalTransform = props.internal_transform;
|
|
1989
2162
|
let lines;
|
|
@@ -2084,7 +2257,7 @@ function getEffectiveBg(props) {
|
|
|
2084
2257
|
/**
|
|
2085
2258
|
* Render a Box node.
|
|
2086
2259
|
*/
|
|
2087
|
-
function renderBox(_node, buffer, layout, props, nodeState, skipBgFill = false, inheritedBg, bgOnlyChange = false) {
|
|
2260
|
+
function renderBox(_node, buffer, layout, props, nodeState, skipBgFill = false, inheritedBg, bgOnlyChange = false, inheritedFg) {
|
|
2088
2261
|
const { scrollOffset, clipBounds } = nodeState;
|
|
2089
2262
|
const { x, width, height } = layout;
|
|
2090
2263
|
const y = layout.y - scrollOffset;
|
|
@@ -2111,14 +2284,16 @@ function renderBox(_node, buffer, layout, props, nodeState, skipBgFill = false,
|
|
|
2111
2284
|
} else if (bgOnlyChange) buffer.fillBg(x, y, width, height, bg);
|
|
2112
2285
|
else buffer.fill(x, y, width, height, { bg });
|
|
2113
2286
|
}
|
|
2114
|
-
if (props.borderStyle) renderBorder(buffer, x, y, width, height, props, clipBounds, inheritedBg);
|
|
2287
|
+
if (props.borderStyle) renderBorder(buffer, x, y, width, height, props, clipBounds, inheritedBg, inheritedFg);
|
|
2115
2288
|
}
|
|
2116
2289
|
/**
|
|
2117
2290
|
* Render a border around a box.
|
|
2118
2291
|
*/
|
|
2119
|
-
function renderBorder(buffer, x, y, width, height, props, clipBounds, inheritedBg) {
|
|
2292
|
+
function renderBorder(buffer, x, y, width, height, props, clipBounds, inheritedBg, inheritedFg) {
|
|
2120
2293
|
const chars = getBorderChars(props.borderStyle ?? "single");
|
|
2121
|
-
|
|
2294
|
+
let color;
|
|
2295
|
+
if (props.borderColor === "currentColor" || props.borderColor === "inherit") color = props.color ? parseColor(props.color) : inheritedFg ?? null;
|
|
2296
|
+
else color = props.borderColor ? parseColor(props.borderColor) : null;
|
|
2122
2297
|
const baseBg = props.backgroundColor ? parseColor(props.backgroundColor) : inheritedBg ?? null;
|
|
2123
2298
|
const borderBgStr = props.borderBackgroundColor;
|
|
2124
2299
|
const borderBgBase = borderBgStr ? parseColor(borderBgStr) : baseBg;
|
|
@@ -2203,14 +2378,21 @@ function renderBorder(buffer, x, y, width, height, props, clipBounds, inheritedB
|
|
|
2203
2378
|
* Render an outline around a box.
|
|
2204
2379
|
*
|
|
2205
2380
|
* Unlike borders, outlines do NOT affect layout dimensions. They draw border
|
|
2206
|
-
* characters
|
|
2207
|
-
* This
|
|
2381
|
+
* characters OUTSIDE the box — one cell beyond each edge, in the gap/margin
|
|
2382
|
+
* space between siblings. This matches CSS `outline` semantics.
|
|
2383
|
+
*
|
|
2384
|
+
* The outline occupies cells at (x-1, y-1) through (x+width, y+height) —
|
|
2385
|
+
* entirely outside the box's own rect. Content is never overlapped.
|
|
2208
2386
|
*/
|
|
2209
2387
|
function renderOutline(buffer, x, y, width, height, props, clipBounds, inheritedBg) {
|
|
2210
2388
|
const chars = getBorderChars(props.outlineStyle ?? "single");
|
|
2211
2389
|
const color = props.outlineColor ? parseColor(props.outlineColor) : null;
|
|
2212
2390
|
const bg = props.backgroundColor ? parseColor(props.backgroundColor) : inheritedBg ?? null;
|
|
2213
2391
|
const attrs = props.outlineDimColor ? { dim: true } : {};
|
|
2392
|
+
const ox = x - 1;
|
|
2393
|
+
const oy = y - 1;
|
|
2394
|
+
const ow = width + 2;
|
|
2395
|
+
const oh = height + 2;
|
|
2214
2396
|
const isRowVisible = (row) => {
|
|
2215
2397
|
if (!clipBounds) return row >= 0 && row < buffer.height;
|
|
2216
2398
|
return row >= clipBounds.top && row < clipBounds.bottom && row < buffer.height;
|
|
@@ -2223,20 +2405,20 @@ function renderOutline(buffer, x, y, width, height, props, clipBounds, inherited
|
|
|
2223
2405
|
const showBottom = props.outlineBottom !== false;
|
|
2224
2406
|
const showLeft = props.outlineLeft !== false;
|
|
2225
2407
|
const showRight = props.outlineRight !== false;
|
|
2226
|
-
if (showTop && isRowVisible(
|
|
2227
|
-
if (showLeft && isColVisible(
|
|
2408
|
+
if (showTop && isRowVisible(oy)) {
|
|
2409
|
+
if (showLeft && isColVisible(ox)) buffer.setCell(ox, oy, {
|
|
2228
2410
|
char: chars.topLeft,
|
|
2229
2411
|
fg: color,
|
|
2230
2412
|
bg,
|
|
2231
2413
|
attrs
|
|
2232
2414
|
});
|
|
2233
|
-
for (let col =
|
|
2415
|
+
for (let col = ox + 1; col < ox + ow - 1 && col < buffer.width; col++) if (isColVisible(col)) buffer.setCell(col, oy, {
|
|
2234
2416
|
char: chars.horizontal,
|
|
2235
2417
|
fg: color,
|
|
2236
2418
|
bg,
|
|
2237
2419
|
attrs
|
|
2238
2420
|
});
|
|
2239
|
-
if (showRight &&
|
|
2421
|
+
if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) buffer.setCell(ox + ow - 1, oy, {
|
|
2240
2422
|
char: chars.topRight,
|
|
2241
2423
|
fg: color,
|
|
2242
2424
|
bg,
|
|
@@ -2244,17 +2426,17 @@ function renderOutline(buffer, x, y, width, height, props, clipBounds, inherited
|
|
|
2244
2426
|
});
|
|
2245
2427
|
}
|
|
2246
2428
|
const outlineRightVertical = chars.rightVertical ?? chars.vertical;
|
|
2247
|
-
const sideStart = showTop ?
|
|
2248
|
-
const sideEnd = showBottom ?
|
|
2429
|
+
const sideStart = showTop ? oy + 1 : oy;
|
|
2430
|
+
const sideEnd = showBottom ? oy + oh - 1 : oy + oh;
|
|
2249
2431
|
for (let row = sideStart; row < sideEnd; row++) {
|
|
2250
2432
|
if (!isRowVisible(row)) continue;
|
|
2251
|
-
if (showLeft && isColVisible(
|
|
2433
|
+
if (showLeft && isColVisible(ox)) buffer.setCell(ox, row, {
|
|
2252
2434
|
char: chars.vertical,
|
|
2253
2435
|
fg: color,
|
|
2254
2436
|
bg,
|
|
2255
2437
|
attrs
|
|
2256
2438
|
});
|
|
2257
|
-
if (showRight &&
|
|
2439
|
+
if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) buffer.setCell(ox + ow - 1, row, {
|
|
2258
2440
|
char: outlineRightVertical,
|
|
2259
2441
|
fg: color,
|
|
2260
2442
|
bg,
|
|
@@ -2262,21 +2444,21 @@ function renderOutline(buffer, x, y, width, height, props, clipBounds, inherited
|
|
|
2262
2444
|
});
|
|
2263
2445
|
}
|
|
2264
2446
|
const outlineBottomHorizontal = chars.bottomHorizontal ?? chars.horizontal;
|
|
2265
|
-
const bottomY =
|
|
2447
|
+
const bottomY = oy + oh - 1;
|
|
2266
2448
|
if (showBottom && isRowVisible(bottomY)) {
|
|
2267
|
-
if (showLeft && isColVisible(
|
|
2449
|
+
if (showLeft && isColVisible(ox)) buffer.setCell(ox, bottomY, {
|
|
2268
2450
|
char: chars.bottomLeft,
|
|
2269
2451
|
fg: color,
|
|
2270
2452
|
bg,
|
|
2271
2453
|
attrs
|
|
2272
2454
|
});
|
|
2273
|
-
for (let col =
|
|
2455
|
+
for (let col = ox + 1; col < ox + ow - 1 && col < buffer.width; col++) if (isColVisible(col)) buffer.setCell(col, bottomY, {
|
|
2274
2456
|
char: outlineBottomHorizontal,
|
|
2275
2457
|
fg: color,
|
|
2276
2458
|
bg,
|
|
2277
2459
|
attrs
|
|
2278
2460
|
});
|
|
2279
|
-
if (showRight &&
|
|
2461
|
+
if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) buffer.setCell(ox + ow - 1, bottomY, {
|
|
2280
2462
|
char: chars.bottomRight,
|
|
2281
2463
|
fg: color,
|
|
2282
2464
|
bg,
|
|
@@ -2350,6 +2532,196 @@ function padCenter(text, width) {
|
|
|
2350
2532
|
return " ".repeat(leftPad) + text + " ".repeat(rightPad);
|
|
2351
2533
|
}
|
|
2352
2534
|
//#endregion
|
|
2535
|
+
//#region packages/ag-term/src/pipeline/decoration-phase.ts
|
|
2536
|
+
/**
|
|
2537
|
+
* Restore cells at previously-drawn outline positions to their pre-outline
|
|
2538
|
+
* state. Called at the start of each incremental render, before the content
|
|
2539
|
+
* phase, on the cloned buffer. No-op when there are no previous snapshots
|
|
2540
|
+
* (fresh render or no outlines last frame).
|
|
2541
|
+
*/
|
|
2542
|
+
function clearPreviousOutlines(buffer) {
|
|
2543
|
+
const snapshots = buffer.outlineSnapshots;
|
|
2544
|
+
if (!snapshots || snapshots.length === 0) return;
|
|
2545
|
+
for (const snap of snapshots) buffer.setCell(snap.x, snap.y, snap.cell);
|
|
2546
|
+
buffer.outlineSnapshots = [];
|
|
2547
|
+
}
|
|
2548
|
+
/**
|
|
2549
|
+
* Walk the node tree, drawing outlines for every node with `outlineStyle`.
|
|
2550
|
+
* Captures per-cell snapshots so the next frame can restore these positions.
|
|
2551
|
+
*
|
|
2552
|
+
* Called AFTER the content render phase on every frame (both fresh and
|
|
2553
|
+
* incremental). Mirrors `renderNodeToBuffer`'s state threading for scroll
|
|
2554
|
+
* offsets, clip bounds, and inherited background — but does nothing except
|
|
2555
|
+
* visit the tree and draw outlines.
|
|
2556
|
+
*/
|
|
2557
|
+
function renderDecorationPass(buffer, root) {
|
|
2558
|
+
const snapshots = [];
|
|
2559
|
+
walk(root, buffer, 0, void 0, { color: null }, snapshots);
|
|
2560
|
+
buffer.outlineSnapshots = snapshots;
|
|
2561
|
+
}
|
|
2562
|
+
/**
|
|
2563
|
+
* Recursive tree walk. Each invocation corresponds to the state at a single
|
|
2564
|
+
* node — scroll offset, clip bounds, inherited background — matching what
|
|
2565
|
+
* `renderNodeToBuffer` would have threaded through its `NodeRenderState`.
|
|
2566
|
+
*/
|
|
2567
|
+
function walk(node, buffer, scrollOffset, clipBounds, inheritedBg, snapshots) {
|
|
2568
|
+
if (!node.layoutNode) return;
|
|
2569
|
+
const layout = node.boxRect;
|
|
2570
|
+
if (!layout) return;
|
|
2571
|
+
if (node.hidden) return;
|
|
2572
|
+
const props = node.props;
|
|
2573
|
+
if (props.display === "none") return;
|
|
2574
|
+
const y = layout.y - scrollOffset;
|
|
2575
|
+
if (y >= buffer.height || y + layout.height <= 0) return;
|
|
2576
|
+
const effectiveBg = getEffectiveBg(props);
|
|
2577
|
+
const theme = props.theme;
|
|
2578
|
+
const childInheritedBg = effectiveBg ? { color: parseColor(effectiveBg) } : theme ? { color: parseColor(theme.bg) } : inheritedBg;
|
|
2579
|
+
if (node.type === "silvery-box" && props.outlineStyle) {
|
|
2580
|
+
const boxInheritedBg = effectiveBg ? void 0 : inheritedBg.color;
|
|
2581
|
+
const positions = collectOutlineCells(layout.x, y, layout.width, layout.height, props, clipBounds, buffer);
|
|
2582
|
+
for (const pos of positions) snapshots.push({
|
|
2583
|
+
x: pos.x,
|
|
2584
|
+
y: pos.y,
|
|
2585
|
+
cell: buffer.getCell(pos.x, pos.y)
|
|
2586
|
+
});
|
|
2587
|
+
renderOutline(buffer, layout.x, y, layout.width, layout.height, props, clipBounds, boxInheritedBg);
|
|
2588
|
+
}
|
|
2589
|
+
if (node.children.length === 0) return;
|
|
2590
|
+
const isScrollContainer = props.overflow === "scroll" && node.scrollState;
|
|
2591
|
+
const clipX = (props.overflowX ?? props.overflow) === "hidden";
|
|
2592
|
+
const clipY = (props.overflowY ?? props.overflow) === "hidden";
|
|
2593
|
+
if (isScrollContainer) {
|
|
2594
|
+
const ss = node.scrollState;
|
|
2595
|
+
const childClip = computeChildClip(layout, props, clipBounds, 0, false, true);
|
|
2596
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
2597
|
+
const child = node.children[i];
|
|
2598
|
+
if (!child) continue;
|
|
2599
|
+
if (child.props.position === "sticky") continue;
|
|
2600
|
+
if (i < ss.firstVisibleChild || i > ss.lastVisibleChild) continue;
|
|
2601
|
+
walk(child, buffer, ss.offset, childClip, childInheritedBg, snapshots);
|
|
2602
|
+
}
|
|
2603
|
+
if (ss.stickyChildren) for (const sticky of ss.stickyChildren) {
|
|
2604
|
+
const child = node.children[sticky.index];
|
|
2605
|
+
if (!child) continue;
|
|
2606
|
+
walk(child, buffer, sticky.naturalTop - sticky.renderOffset, childClip, childInheritedBg, snapshots);
|
|
2607
|
+
}
|
|
2608
|
+
} else {
|
|
2609
|
+
const childClip = clipX || clipY ? computeChildClip(layout, props, clipBounds, scrollOffset, clipX, clipY) : clipBounds;
|
|
2610
|
+
for (const child of node.children) walk(child, buffer, scrollOffset, childClip, childInheritedBg, snapshots);
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Compute the cells an outline would write, given the same inputs
|
|
2615
|
+
* `renderOutline` uses. Kept in lockstep with render-box.ts — any change to
|
|
2616
|
+
* the outline geometry (e.g., new outline styles, per-side toggles) must be
|
|
2617
|
+
* mirrored here. If they drift, the snapshots won't cover every cell the
|
|
2618
|
+
* next `renderOutline` call writes, and stale pixels will leak through.
|
|
2619
|
+
*
|
|
2620
|
+
* Uses the SAME visibility checks as `renderOutline` so the snapshot set is
|
|
2621
|
+
* an exact match for the cell set the renderer will overwrite.
|
|
2622
|
+
*/
|
|
2623
|
+
function collectOutlineCells(x, y, width, height, props, clipBounds, buffer) {
|
|
2624
|
+
const out = [];
|
|
2625
|
+
const ox = x - 1;
|
|
2626
|
+
const oy = y - 1;
|
|
2627
|
+
const ow = width + 2;
|
|
2628
|
+
const oh = height + 2;
|
|
2629
|
+
const isRowVisible = (row) => {
|
|
2630
|
+
if (!clipBounds) return row >= 0 && row < buffer.height;
|
|
2631
|
+
return row >= clipBounds.top && row < clipBounds.bottom && row < buffer.height;
|
|
2632
|
+
};
|
|
2633
|
+
const isColVisible = (col) => {
|
|
2634
|
+
if (clipBounds?.left === void 0 || clipBounds.right === void 0) return col >= 0 && col < buffer.width;
|
|
2635
|
+
return col >= clipBounds.left && col < clipBounds.right && col < buffer.width;
|
|
2636
|
+
};
|
|
2637
|
+
const showTop = props.outlineTop !== false;
|
|
2638
|
+
const showBottom = props.outlineBottom !== false;
|
|
2639
|
+
const showLeft = props.outlineLeft !== false;
|
|
2640
|
+
const showRight = props.outlineRight !== false;
|
|
2641
|
+
if (showTop && isRowVisible(oy)) {
|
|
2642
|
+
if (showLeft && isColVisible(ox)) out.push({
|
|
2643
|
+
x: ox,
|
|
2644
|
+
y: oy
|
|
2645
|
+
});
|
|
2646
|
+
for (let col = ox + 1; col < ox + ow - 1 && col < buffer.width; col++) if (isColVisible(col)) out.push({
|
|
2647
|
+
x: col,
|
|
2648
|
+
y: oy
|
|
2649
|
+
});
|
|
2650
|
+
if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) out.push({
|
|
2651
|
+
x: ox + ow - 1,
|
|
2652
|
+
y: oy
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
const sideStart = showTop ? oy + 1 : oy;
|
|
2656
|
+
const sideEnd = showBottom ? oy + oh - 1 : oy + oh;
|
|
2657
|
+
for (let row = sideStart; row < sideEnd; row++) {
|
|
2658
|
+
if (!isRowVisible(row)) continue;
|
|
2659
|
+
if (showLeft && isColVisible(ox)) out.push({
|
|
2660
|
+
x: ox,
|
|
2661
|
+
y: row
|
|
2662
|
+
});
|
|
2663
|
+
if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) out.push({
|
|
2664
|
+
x: ox + ow - 1,
|
|
2665
|
+
y: row
|
|
2666
|
+
});
|
|
2667
|
+
}
|
|
2668
|
+
const bottomY = oy + oh - 1;
|
|
2669
|
+
if (showBottom && isRowVisible(bottomY)) {
|
|
2670
|
+
if (showLeft && isColVisible(ox)) out.push({
|
|
2671
|
+
x: ox,
|
|
2672
|
+
y: bottomY
|
|
2673
|
+
});
|
|
2674
|
+
for (let col = ox + 1; col < ox + ow - 1 && col < buffer.width; col++) if (isColVisible(col)) out.push({
|
|
2675
|
+
x: col,
|
|
2676
|
+
y: bottomY
|
|
2677
|
+
});
|
|
2678
|
+
if (showRight && ox + ow - 1 < buffer.width && isColVisible(ox + ow - 1)) out.push({
|
|
2679
|
+
x: ox + ow - 1,
|
|
2680
|
+
y: bottomY
|
|
2681
|
+
});
|
|
2682
|
+
}
|
|
2683
|
+
return out;
|
|
2684
|
+
}
|
|
2685
|
+
/**
|
|
2686
|
+
* Local copy of `computeChildClipBounds` from render-phase.ts. The decoration
|
|
2687
|
+
* walker can't import private render-phase helpers without tangling modules,
|
|
2688
|
+
* so we reproduce the same computation here. Must stay in sync.
|
|
2689
|
+
*/
|
|
2690
|
+
function computeChildClip(layout, props, parentClip, scrollOffset, horizontal, vertical) {
|
|
2691
|
+
const border = props.borderStyle ? getBorderSize(props) : {
|
|
2692
|
+
top: 0,
|
|
2693
|
+
bottom: 0,
|
|
2694
|
+
left: 0,
|
|
2695
|
+
right: 0
|
|
2696
|
+
};
|
|
2697
|
+
const padding = getPadding(props);
|
|
2698
|
+
const adjustedY = layout.y - scrollOffset;
|
|
2699
|
+
const nodeClip = vertical ? {
|
|
2700
|
+
top: adjustedY + border.top + padding.top,
|
|
2701
|
+
bottom: adjustedY + layout.height - border.bottom - padding.bottom
|
|
2702
|
+
} : {
|
|
2703
|
+
top: -Infinity,
|
|
2704
|
+
bottom: Infinity
|
|
2705
|
+
};
|
|
2706
|
+
if (horizontal) {
|
|
2707
|
+
nodeClip.left = layout.x + border.left + padding.left;
|
|
2708
|
+
nodeClip.right = layout.x + layout.width - border.right - padding.right;
|
|
2709
|
+
}
|
|
2710
|
+
if (!parentClip) return nodeClip;
|
|
2711
|
+
const result = {
|
|
2712
|
+
top: vertical ? Math.max(parentClip.top, nodeClip.top) : parentClip.top,
|
|
2713
|
+
bottom: vertical ? Math.min(parentClip.bottom, nodeClip.bottom) : parentClip.bottom
|
|
2714
|
+
};
|
|
2715
|
+
if (horizontal && nodeClip.left !== void 0 && nodeClip.right !== void 0) {
|
|
2716
|
+
result.left = Math.max(parentClip.left ?? 0, nodeClip.left);
|
|
2717
|
+
result.right = Math.min(parentClip.right ?? Infinity, nodeClip.right);
|
|
2718
|
+
} else if (parentClip.left !== void 0 && parentClip.right !== void 0) {
|
|
2719
|
+
result.left = parentClip.left;
|
|
2720
|
+
result.right = parentClip.right;
|
|
2721
|
+
}
|
|
2722
|
+
return result;
|
|
2723
|
+
}
|
|
2724
|
+
//#endregion
|
|
2353
2725
|
//#region packages/ag-term/src/pipeline/cascade-predicates.ts
|
|
2354
2726
|
/**
|
|
2355
2727
|
* Compute all cascade predicate values from boolean inputs.
|
|
@@ -2374,350 +2746,6 @@ function computeCascade(inputs) {
|
|
|
2374
2746
|
};
|
|
2375
2747
|
}
|
|
2376
2748
|
//#endregion
|
|
2377
|
-
//#region ../../node_modules/.bun/alien-signals@3.1.2/node_modules/alien-signals/esm/system.mjs
|
|
2378
|
-
function createReactiveSystem({ update, notify, unwatched }) {
|
|
2379
|
-
return {
|
|
2380
|
-
link,
|
|
2381
|
-
unlink,
|
|
2382
|
-
propagate,
|
|
2383
|
-
checkDirty,
|
|
2384
|
-
shallowPropagate
|
|
2385
|
-
};
|
|
2386
|
-
function link(dep, sub, version) {
|
|
2387
|
-
const prevDep = sub.depsTail;
|
|
2388
|
-
if (prevDep !== void 0 && prevDep.dep === dep) return;
|
|
2389
|
-
const nextDep = prevDep !== void 0 ? prevDep.nextDep : sub.deps;
|
|
2390
|
-
if (nextDep !== void 0 && nextDep.dep === dep) {
|
|
2391
|
-
nextDep.version = version;
|
|
2392
|
-
sub.depsTail = nextDep;
|
|
2393
|
-
return;
|
|
2394
|
-
}
|
|
2395
|
-
const prevSub = dep.subsTail;
|
|
2396
|
-
if (prevSub !== void 0 && prevSub.version === version && prevSub.sub === sub) return;
|
|
2397
|
-
const newLink = sub.depsTail = dep.subsTail = {
|
|
2398
|
-
version,
|
|
2399
|
-
dep,
|
|
2400
|
-
sub,
|
|
2401
|
-
prevDep,
|
|
2402
|
-
nextDep,
|
|
2403
|
-
prevSub,
|
|
2404
|
-
nextSub: void 0
|
|
2405
|
-
};
|
|
2406
|
-
if (nextDep !== void 0) nextDep.prevDep = newLink;
|
|
2407
|
-
if (prevDep !== void 0) prevDep.nextDep = newLink;
|
|
2408
|
-
else sub.deps = newLink;
|
|
2409
|
-
if (prevSub !== void 0) prevSub.nextSub = newLink;
|
|
2410
|
-
else dep.subs = newLink;
|
|
2411
|
-
}
|
|
2412
|
-
function unlink(link, sub = link.sub) {
|
|
2413
|
-
const dep = link.dep;
|
|
2414
|
-
const prevDep = link.prevDep;
|
|
2415
|
-
const nextDep = link.nextDep;
|
|
2416
|
-
const nextSub = link.nextSub;
|
|
2417
|
-
const prevSub = link.prevSub;
|
|
2418
|
-
if (nextDep !== void 0) nextDep.prevDep = prevDep;
|
|
2419
|
-
else sub.depsTail = prevDep;
|
|
2420
|
-
if (prevDep !== void 0) prevDep.nextDep = nextDep;
|
|
2421
|
-
else sub.deps = nextDep;
|
|
2422
|
-
if (nextSub !== void 0) nextSub.prevSub = prevSub;
|
|
2423
|
-
else dep.subsTail = prevSub;
|
|
2424
|
-
if (prevSub !== void 0) prevSub.nextSub = nextSub;
|
|
2425
|
-
else if ((dep.subs = nextSub) === void 0) unwatched(dep);
|
|
2426
|
-
return nextDep;
|
|
2427
|
-
}
|
|
2428
|
-
function propagate(link) {
|
|
2429
|
-
let next = link.nextSub;
|
|
2430
|
-
let stack;
|
|
2431
|
-
top: do {
|
|
2432
|
-
const sub = link.sub;
|
|
2433
|
-
let flags = sub.flags;
|
|
2434
|
-
if (!(flags & 60)) sub.flags = flags | 32;
|
|
2435
|
-
else if (!(flags & 12)) flags = 0;
|
|
2436
|
-
else if (!(flags & 4)) sub.flags = flags & -9 | 32;
|
|
2437
|
-
else if (!(flags & 48) && isValidLink(link, sub)) {
|
|
2438
|
-
sub.flags = flags | 40;
|
|
2439
|
-
flags &= 1;
|
|
2440
|
-
} else flags = 0;
|
|
2441
|
-
if (flags & 2) notify(sub);
|
|
2442
|
-
if (flags & 1) {
|
|
2443
|
-
const subSubs = sub.subs;
|
|
2444
|
-
if (subSubs !== void 0) {
|
|
2445
|
-
const nextSub = (link = subSubs).nextSub;
|
|
2446
|
-
if (nextSub !== void 0) {
|
|
2447
|
-
stack = {
|
|
2448
|
-
value: next,
|
|
2449
|
-
prev: stack
|
|
2450
|
-
};
|
|
2451
|
-
next = nextSub;
|
|
2452
|
-
}
|
|
2453
|
-
continue;
|
|
2454
|
-
}
|
|
2455
|
-
}
|
|
2456
|
-
if ((link = next) !== void 0) {
|
|
2457
|
-
next = link.nextSub;
|
|
2458
|
-
continue;
|
|
2459
|
-
}
|
|
2460
|
-
while (stack !== void 0) {
|
|
2461
|
-
link = stack.value;
|
|
2462
|
-
stack = stack.prev;
|
|
2463
|
-
if (link !== void 0) {
|
|
2464
|
-
next = link.nextSub;
|
|
2465
|
-
continue top;
|
|
2466
|
-
}
|
|
2467
|
-
}
|
|
2468
|
-
break;
|
|
2469
|
-
} while (true);
|
|
2470
|
-
}
|
|
2471
|
-
function checkDirty(link, sub) {
|
|
2472
|
-
let stack;
|
|
2473
|
-
let checkDepth = 0;
|
|
2474
|
-
let dirty = false;
|
|
2475
|
-
top: do {
|
|
2476
|
-
const dep = link.dep;
|
|
2477
|
-
const flags = dep.flags;
|
|
2478
|
-
if (sub.flags & 16) dirty = true;
|
|
2479
|
-
else if ((flags & 17) === 17) {
|
|
2480
|
-
if (update(dep)) {
|
|
2481
|
-
const subs = dep.subs;
|
|
2482
|
-
if (subs.nextSub !== void 0) shallowPropagate(subs);
|
|
2483
|
-
dirty = true;
|
|
2484
|
-
}
|
|
2485
|
-
} else if ((flags & 33) === 33) {
|
|
2486
|
-
if (link.nextSub !== void 0 || link.prevSub !== void 0) stack = {
|
|
2487
|
-
value: link,
|
|
2488
|
-
prev: stack
|
|
2489
|
-
};
|
|
2490
|
-
link = dep.deps;
|
|
2491
|
-
sub = dep;
|
|
2492
|
-
++checkDepth;
|
|
2493
|
-
continue;
|
|
2494
|
-
}
|
|
2495
|
-
if (!dirty) {
|
|
2496
|
-
const nextDep = link.nextDep;
|
|
2497
|
-
if (nextDep !== void 0) {
|
|
2498
|
-
link = nextDep;
|
|
2499
|
-
continue;
|
|
2500
|
-
}
|
|
2501
|
-
}
|
|
2502
|
-
while (checkDepth--) {
|
|
2503
|
-
const firstSub = sub.subs;
|
|
2504
|
-
const hasMultipleSubs = firstSub.nextSub !== void 0;
|
|
2505
|
-
if (hasMultipleSubs) {
|
|
2506
|
-
link = stack.value;
|
|
2507
|
-
stack = stack.prev;
|
|
2508
|
-
} else link = firstSub;
|
|
2509
|
-
if (dirty) {
|
|
2510
|
-
if (update(sub)) {
|
|
2511
|
-
if (hasMultipleSubs) shallowPropagate(firstSub);
|
|
2512
|
-
sub = link.sub;
|
|
2513
|
-
continue;
|
|
2514
|
-
}
|
|
2515
|
-
dirty = false;
|
|
2516
|
-
} else sub.flags &= -33;
|
|
2517
|
-
sub = link.sub;
|
|
2518
|
-
const nextDep = link.nextDep;
|
|
2519
|
-
if (nextDep !== void 0) {
|
|
2520
|
-
link = nextDep;
|
|
2521
|
-
continue top;
|
|
2522
|
-
}
|
|
2523
|
-
}
|
|
2524
|
-
return dirty;
|
|
2525
|
-
} while (true);
|
|
2526
|
-
}
|
|
2527
|
-
function shallowPropagate(link) {
|
|
2528
|
-
do {
|
|
2529
|
-
const sub = link.sub;
|
|
2530
|
-
const flags = sub.flags;
|
|
2531
|
-
if ((flags & 48) === 32) {
|
|
2532
|
-
sub.flags = flags | 16;
|
|
2533
|
-
if ((flags & 6) === 2) notify(sub);
|
|
2534
|
-
}
|
|
2535
|
-
} while ((link = link.nextSub) !== void 0);
|
|
2536
|
-
}
|
|
2537
|
-
function isValidLink(checkLink, sub) {
|
|
2538
|
-
let link = sub.depsTail;
|
|
2539
|
-
while (link !== void 0) {
|
|
2540
|
-
if (link === checkLink) return true;
|
|
2541
|
-
link = link.prevDep;
|
|
2542
|
-
}
|
|
2543
|
-
return false;
|
|
2544
|
-
}
|
|
2545
|
-
}
|
|
2546
|
-
//#endregion
|
|
2547
|
-
//#region ../../node_modules/.bun/alien-signals@3.1.2/node_modules/alien-signals/esm/index.mjs
|
|
2548
|
-
let cycle = 0;
|
|
2549
|
-
let notifyIndex = 0;
|
|
2550
|
-
let queuedLength = 0;
|
|
2551
|
-
let activeSub;
|
|
2552
|
-
const queued = [];
|
|
2553
|
-
const { link, unlink, propagate, checkDirty, shallowPropagate } = createReactiveSystem({
|
|
2554
|
-
update(node) {
|
|
2555
|
-
if (node.depsTail !== void 0) return updateComputed(node);
|
|
2556
|
-
else return updateSignal(node);
|
|
2557
|
-
},
|
|
2558
|
-
notify(effect) {
|
|
2559
|
-
let insertIndex = queuedLength;
|
|
2560
|
-
let firstInsertedIndex = insertIndex;
|
|
2561
|
-
do {
|
|
2562
|
-
queued[insertIndex++] = effect;
|
|
2563
|
-
effect.flags &= -3;
|
|
2564
|
-
effect = effect.subs?.sub;
|
|
2565
|
-
if (effect === void 0 || !(effect.flags & 2)) break;
|
|
2566
|
-
} while (true);
|
|
2567
|
-
queuedLength = insertIndex;
|
|
2568
|
-
while (firstInsertedIndex < --insertIndex) {
|
|
2569
|
-
const left = queued[firstInsertedIndex];
|
|
2570
|
-
queued[firstInsertedIndex++] = queued[insertIndex];
|
|
2571
|
-
queued[insertIndex] = left;
|
|
2572
|
-
}
|
|
2573
|
-
},
|
|
2574
|
-
unwatched(node) {
|
|
2575
|
-
if (!(node.flags & 1)) effectScopeOper.call(node);
|
|
2576
|
-
else if (node.depsTail !== void 0) {
|
|
2577
|
-
node.depsTail = void 0;
|
|
2578
|
-
node.flags = 17;
|
|
2579
|
-
purgeDeps(node);
|
|
2580
|
-
}
|
|
2581
|
-
}
|
|
2582
|
-
});
|
|
2583
|
-
function setActiveSub(sub) {
|
|
2584
|
-
const prevSub = activeSub;
|
|
2585
|
-
activeSub = sub;
|
|
2586
|
-
return prevSub;
|
|
2587
|
-
}
|
|
2588
|
-
function signal(initialValue) {
|
|
2589
|
-
return signalOper.bind({
|
|
2590
|
-
currentValue: initialValue,
|
|
2591
|
-
pendingValue: initialValue,
|
|
2592
|
-
subs: void 0,
|
|
2593
|
-
subsTail: void 0,
|
|
2594
|
-
flags: 1
|
|
2595
|
-
});
|
|
2596
|
-
}
|
|
2597
|
-
function computed(getter) {
|
|
2598
|
-
return computedOper.bind({
|
|
2599
|
-
value: void 0,
|
|
2600
|
-
subs: void 0,
|
|
2601
|
-
subsTail: void 0,
|
|
2602
|
-
deps: void 0,
|
|
2603
|
-
depsTail: void 0,
|
|
2604
|
-
flags: 0,
|
|
2605
|
-
getter
|
|
2606
|
-
});
|
|
2607
|
-
}
|
|
2608
|
-
function updateComputed(c) {
|
|
2609
|
-
++cycle;
|
|
2610
|
-
c.depsTail = void 0;
|
|
2611
|
-
c.flags = 5;
|
|
2612
|
-
const prevSub = setActiveSub(c);
|
|
2613
|
-
try {
|
|
2614
|
-
const oldValue = c.value;
|
|
2615
|
-
return oldValue !== (c.value = c.getter(oldValue));
|
|
2616
|
-
} finally {
|
|
2617
|
-
activeSub = prevSub;
|
|
2618
|
-
c.flags &= -5;
|
|
2619
|
-
purgeDeps(c);
|
|
2620
|
-
}
|
|
2621
|
-
}
|
|
2622
|
-
function updateSignal(s) {
|
|
2623
|
-
s.flags = 1;
|
|
2624
|
-
return s.currentValue !== (s.currentValue = s.pendingValue);
|
|
2625
|
-
}
|
|
2626
|
-
function run(e) {
|
|
2627
|
-
const flags = e.flags;
|
|
2628
|
-
if (flags & 16 || flags & 32 && checkDirty(e.deps, e)) {
|
|
2629
|
-
++cycle;
|
|
2630
|
-
e.depsTail = void 0;
|
|
2631
|
-
e.flags = 6;
|
|
2632
|
-
const prevSub = setActiveSub(e);
|
|
2633
|
-
try {
|
|
2634
|
-
e.fn();
|
|
2635
|
-
} finally {
|
|
2636
|
-
activeSub = prevSub;
|
|
2637
|
-
e.flags &= -5;
|
|
2638
|
-
purgeDeps(e);
|
|
2639
|
-
}
|
|
2640
|
-
} else e.flags = 2;
|
|
2641
|
-
}
|
|
2642
|
-
function flush() {
|
|
2643
|
-
try {
|
|
2644
|
-
while (notifyIndex < queuedLength) {
|
|
2645
|
-
const effect = queued[notifyIndex];
|
|
2646
|
-
queued[notifyIndex++] = void 0;
|
|
2647
|
-
run(effect);
|
|
2648
|
-
}
|
|
2649
|
-
} finally {
|
|
2650
|
-
while (notifyIndex < queuedLength) {
|
|
2651
|
-
const effect = queued[notifyIndex];
|
|
2652
|
-
queued[notifyIndex++] = void 0;
|
|
2653
|
-
effect.flags |= 10;
|
|
2654
|
-
}
|
|
2655
|
-
notifyIndex = 0;
|
|
2656
|
-
queuedLength = 0;
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
function computedOper() {
|
|
2660
|
-
const flags = this.flags;
|
|
2661
|
-
if (flags & 16 || flags & 32 && (checkDirty(this.deps, this) || (this.flags = flags & -33, false))) {
|
|
2662
|
-
if (updateComputed(this)) {
|
|
2663
|
-
const subs = this.subs;
|
|
2664
|
-
if (subs !== void 0) shallowPropagate(subs);
|
|
2665
|
-
}
|
|
2666
|
-
} else if (!flags) {
|
|
2667
|
-
this.flags = 5;
|
|
2668
|
-
const prevSub = setActiveSub(this);
|
|
2669
|
-
try {
|
|
2670
|
-
this.value = this.getter();
|
|
2671
|
-
} finally {
|
|
2672
|
-
activeSub = prevSub;
|
|
2673
|
-
this.flags &= -5;
|
|
2674
|
-
}
|
|
2675
|
-
}
|
|
2676
|
-
const sub = activeSub;
|
|
2677
|
-
if (sub !== void 0) link(this, sub, cycle);
|
|
2678
|
-
return this.value;
|
|
2679
|
-
}
|
|
2680
|
-
function signalOper(...value) {
|
|
2681
|
-
if (value.length) {
|
|
2682
|
-
if (this.pendingValue !== (this.pendingValue = value[0])) {
|
|
2683
|
-
this.flags = 17;
|
|
2684
|
-
const subs = this.subs;
|
|
2685
|
-
if (subs !== void 0) {
|
|
2686
|
-
propagate(subs);
|
|
2687
|
-
flush();
|
|
2688
|
-
}
|
|
2689
|
-
}
|
|
2690
|
-
} else {
|
|
2691
|
-
if (this.flags & 16) {
|
|
2692
|
-
if (updateSignal(this)) {
|
|
2693
|
-
const subs = this.subs;
|
|
2694
|
-
if (subs !== void 0) shallowPropagate(subs);
|
|
2695
|
-
}
|
|
2696
|
-
}
|
|
2697
|
-
let sub = activeSub;
|
|
2698
|
-
while (sub !== void 0) {
|
|
2699
|
-
if (sub.flags & 3) {
|
|
2700
|
-
link(this, sub, cycle);
|
|
2701
|
-
break;
|
|
2702
|
-
}
|
|
2703
|
-
sub = sub.subs?.sub;
|
|
2704
|
-
}
|
|
2705
|
-
return this.currentValue;
|
|
2706
|
-
}
|
|
2707
|
-
}
|
|
2708
|
-
function effectScopeOper() {
|
|
2709
|
-
this.depsTail = void 0;
|
|
2710
|
-
this.flags = 0;
|
|
2711
|
-
purgeDeps(this);
|
|
2712
|
-
const sub = this.subs;
|
|
2713
|
-
if (sub !== void 0) unlink(sub);
|
|
2714
|
-
}
|
|
2715
|
-
function purgeDeps(sub) {
|
|
2716
|
-
const depsTail = sub.depsTail;
|
|
2717
|
-
let dep = depsTail !== void 0 ? depsTail.nextDep : sub.deps;
|
|
2718
|
-
while (dep !== void 0) dep = unlink(dep, sub);
|
|
2719
|
-
}
|
|
2720
|
-
//#endregion
|
|
2721
2749
|
//#region packages/ag-term/src/pipeline/reactive-node.ts
|
|
2722
2750
|
/**
|
|
2723
2751
|
* Reactive Node State — alien-signals wrappers for cascade derivations.
|
|
@@ -2926,6 +2954,7 @@ function renderPhase(root, prevBuffer, ctx) {
|
|
|
2926
2954
|
const buffer = hasPrevBuffer ? prevBuffer.clone() : new TerminalBuffer(layout.width, layout.height);
|
|
2927
2955
|
const tClone = instr.enabled ? performance.now() - t0 : 0;
|
|
2928
2956
|
buffer.setSelectableMode(true);
|
|
2957
|
+
clearPreviousOutlines(buffer);
|
|
2929
2958
|
const t1 = instr.enabled ? performance.now() : 0;
|
|
2930
2959
|
renderNodeToBuffer(root, buffer, {
|
|
2931
2960
|
scrollOffset: 0,
|
|
@@ -2941,6 +2970,7 @@ function renderPhase(root, prevBuffer, ctx) {
|
|
|
2941
2970
|
inheritedFg: null
|
|
2942
2971
|
}, ctx);
|
|
2943
2972
|
const tRender = instr.enabled ? performance.now() - t1 : 0;
|
|
2973
|
+
renderDecorationPass(buffer, root);
|
|
2944
2974
|
if (instr.enabled) emitRenderPhaseStats(instr.stats, instr.nodeTrace, instr.nodeTraceEnabled, tClone, tRender);
|
|
2945
2975
|
syncPrevLayout(root, isCurrentEpoch(root.layoutChangedThisFrame) || isDirty(root.dirtyBits, root.dirtyEpoch, 16) || !hasPrevBuffer);
|
|
2946
2976
|
advanceRenderEpoch();
|
|
@@ -2964,7 +2994,7 @@ function renderPhase(root, prevBuffer, ctx) {
|
|
|
2964
2994
|
* Previously walked ALL nodes O(N) every frame. Now only visits nodes
|
|
2965
2995
|
* with layoutChangedThisFrame (set by propagateLayout in layout phase).
|
|
2966
2996
|
* Falls back to full walk when layout phase ran (dimensions changed or
|
|
2967
|
-
*
|
|
2997
|
+
* Flexily isDirty) since any node may have moved.
|
|
2968
2998
|
*
|
|
2969
2999
|
* For cursor move (no layout change): O(1) — no nodes to sync.
|
|
2970
3000
|
* For resize: O(N) — all nodes may have moved (same as before).
|
|
@@ -3212,7 +3242,7 @@ function renderNodeToBuffer(node, buffer, nodeState, ctx) {
|
|
|
3212
3242
|
const useTextStyleFastPath = false;
|
|
3213
3243
|
executeRegionClearing(node, buffer, layout, scrollOffset, clipBounds, bufferIsCloned, layoutChanged, contentRegionCleared, descendantOverflowChanged, instr.enabled, instr.stats, nodeState.inheritedBg);
|
|
3214
3244
|
const needsOwnRepaint = !hasPrevBuffer || ancestorCleared || ancestorLayoutChanged || cascade.contentAreaAffected || isDirty(node.dirtyBits, node.dirtyEpoch, 2) || cascade.bgRefillNeeded;
|
|
3215
|
-
|
|
3245
|
+
node.type === "silvery-box" && !getEffectiveBg(props) && nodeState.inheritedBg.color;
|
|
3216
3246
|
if (needsOwnRepaint) renderOwnContent(node, buffer, layout, props, nodeState, skipBgFill, instr.enabled, instr.stats, ctx, cascade.bgOnlyChange, useTextStyleFastPath);
|
|
3217
3247
|
const effectiveBg = getEffectiveBg(props);
|
|
3218
3248
|
const childInheritedBg = effectiveBg ? {
|
|
@@ -3222,7 +3252,7 @@ function renderNodeToBuffer(node, buffer, nodeState, ctx) {
|
|
|
3222
3252
|
color: parseColor(nodeTheme.bg),
|
|
3223
3253
|
ancestorRect: node.boxRect
|
|
3224
3254
|
} : nodeState.inheritedBg;
|
|
3225
|
-
const childInheritedFg = props.color ? parseColor(props.color) : nodeTheme ? parseColor(nodeTheme.fg) : nodeState.inheritedFg;
|
|
3255
|
+
const childInheritedFg = props.color === "inherit" || props.color === "currentColor" ? nodeState.inheritedFg : props.color ? parseColor(props.color) : nodeTheme ? parseColor(nodeTheme.fg) : nodeState.inheritedFg;
|
|
3226
3256
|
const childState = {
|
|
3227
3257
|
...nodeState,
|
|
3228
3258
|
inheritedBg: childInheritedBg,
|
|
@@ -3232,10 +3262,6 @@ function renderNodeToBuffer(node, buffer, nodeState, ctx) {
|
|
|
3232
3262
|
renderScrollContainerChildren(node, buffer, props, childState, contentRegionCleared, childrenNeedFreshRender, ctx);
|
|
3233
3263
|
renderScrollIndicators(node, buffer, layout, props, node.scrollState, ctx);
|
|
3234
3264
|
} else renderNormalChildren(node, buffer, props, childState, childPositionChanged, contentRegionCleared, childrenNeedFreshRender, ctx);
|
|
3235
|
-
if (node.type === "silvery-box" && props.outlineStyle) {
|
|
3236
|
-
const { x, width, height } = layout;
|
|
3237
|
-
renderOutline(buffer, x, layout.y - scrollOffset, width, height, props, clipBounds, boxInheritedBg);
|
|
3238
|
-
}
|
|
3239
3265
|
clearNodeDirtyFlags(node);
|
|
3240
3266
|
} finally {
|
|
3241
3267
|
if (nodeTheme) popContextTheme();
|
|
@@ -3359,7 +3385,7 @@ function renderOwnContent(node, buffer, layout, props, nodeState, skipBgFill, in
|
|
|
3359
3385
|
const boxInheritedBg = node.type === "silvery-box" && !getEffectiveBg(props) ? nodeState.inheritedBg.color : void 0;
|
|
3360
3386
|
if (node.type === "silvery-box") {
|
|
3361
3387
|
if (instrumentEnabled) stats.boxNodes++;
|
|
3362
|
-
renderBox(node, buffer, layout, props, nodeState, skipBgFill, boxInheritedBg, bgOnlyChange);
|
|
3388
|
+
renderBox(node, buffer, layout, props, nodeState, skipBgFill, boxInheritedBg, bgOnlyChange, nodeState.inheritedFg);
|
|
3363
3389
|
} else if (node.type === "silvery-text") {
|
|
3364
3390
|
if (instrumentEnabled) stats.textNodes++;
|
|
3365
3391
|
const textInheritedBg = nodeState.inheritedBg.color;
|
|
@@ -3994,6 +4020,189 @@ function clippedFill(buffer, x, width, top, bottom, clipBounds, outerBottom, bg)
|
|
|
3994
4020
|
});
|
|
3995
4021
|
}
|
|
3996
4022
|
//#endregion
|
|
4023
|
+
//#region packages/ag-term/src/pipeline/backdrop-phase.ts
|
|
4024
|
+
init_src$1();
|
|
4025
|
+
init_buffer();
|
|
4026
|
+
const FADE_ATTR = "data-backdrop-fade";
|
|
4027
|
+
const FADE_EXCLUDE_ATTR = "data-backdrop-fade-excluded";
|
|
4028
|
+
/**
|
|
4029
|
+
* Apply backdrop-fade to the buffer based on tree markers.
|
|
4030
|
+
*
|
|
4031
|
+
* Returns `true` if at least one region was modified; `false` if nothing
|
|
4032
|
+
* changed (no markers found, or colorLevel is `none`).
|
|
4033
|
+
*/
|
|
4034
|
+
/**
|
|
4035
|
+
* Quick check: does the tree contain any backdrop markers? Used as a gate so
|
|
4036
|
+
* we don't clone the buffer every frame when no fade is active. Walks the
|
|
4037
|
+
* full tree once (O(N)) — the alternative (tracking dirty markers in the
|
|
4038
|
+
* reconciler) is more complex and the walk is cheap compared to the pass.
|
|
4039
|
+
*/
|
|
4040
|
+
function hasBackdropMarkers(root) {
|
|
4041
|
+
const props = root.props;
|
|
4042
|
+
if (props[FADE_ATTR] !== void 0 || props[FADE_EXCLUDE_ATTR] !== void 0) return true;
|
|
4043
|
+
for (const child of root.children) if (hasBackdropMarkers(child)) return true;
|
|
4044
|
+
return false;
|
|
4045
|
+
}
|
|
4046
|
+
function applyBackdropFade(root, buffer, options) {
|
|
4047
|
+
const colorLevel = options?.colorLevel ?? "truecolor";
|
|
4048
|
+
if (colorLevel === "none") return false;
|
|
4049
|
+
const includes = [];
|
|
4050
|
+
const excludes = [];
|
|
4051
|
+
collectBackdropMarkers(root, includes, excludes);
|
|
4052
|
+
if (includes.length === 0 && excludes.length === 0) return false;
|
|
4053
|
+
const strategy = colorLevel === "basic" ? "dim" : "blend";
|
|
4054
|
+
let modified = false;
|
|
4055
|
+
for (const { rect, amount } of includes) {
|
|
4056
|
+
if (amount <= 0) continue;
|
|
4057
|
+
if (fadeRect(buffer, rect, amount, strategy)) modified = true;
|
|
4058
|
+
}
|
|
4059
|
+
if (excludes.length > 0) {
|
|
4060
|
+
const fullRect = {
|
|
4061
|
+
x: 0,
|
|
4062
|
+
y: 0,
|
|
4063
|
+
width: buffer.width,
|
|
4064
|
+
height: buffer.height
|
|
4065
|
+
};
|
|
4066
|
+
for (const { rect, amount } of excludes) {
|
|
4067
|
+
if (amount <= 0) continue;
|
|
4068
|
+
if (fadeRectExcluding(buffer, fullRect, rect, amount, strategy)) modified = true;
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
return modified;
|
|
4072
|
+
}
|
|
4073
|
+
function collectBackdropMarkers(node, includes, excludes) {
|
|
4074
|
+
const props = node.props;
|
|
4075
|
+
const includeRaw = props[FADE_ATTR];
|
|
4076
|
+
const excludeRaw = props[FADE_EXCLUDE_ATTR];
|
|
4077
|
+
if (includeRaw !== void 0 || excludeRaw !== void 0) {
|
|
4078
|
+
const rect = node.screenRect ?? node.scrollRect ?? node.boxRect;
|
|
4079
|
+
if (rect && rect.width > 0 && rect.height > 0) {
|
|
4080
|
+
const inc = parseFade(includeRaw);
|
|
4081
|
+
if (inc !== null) includes.push({
|
|
4082
|
+
rect,
|
|
4083
|
+
amount: inc
|
|
4084
|
+
});
|
|
4085
|
+
const exc = parseFade(excludeRaw);
|
|
4086
|
+
if (exc !== null) excludes.push({
|
|
4087
|
+
rect,
|
|
4088
|
+
amount: exc
|
|
4089
|
+
});
|
|
4090
|
+
}
|
|
4091
|
+
}
|
|
4092
|
+
for (const child of node.children) collectBackdropMarkers(child, includes, excludes);
|
|
4093
|
+
}
|
|
4094
|
+
function parseFade(raw) {
|
|
4095
|
+
if (raw === void 0 || raw === null) return null;
|
|
4096
|
+
const n = typeof raw === "number" ? raw : Number(raw);
|
|
4097
|
+
if (!Number.isFinite(n)) return null;
|
|
4098
|
+
if (n <= 0) return null;
|
|
4099
|
+
return n > 1 ? 1 : n;
|
|
4100
|
+
}
|
|
4101
|
+
function fadeRect(buffer, rect, amount, strategy) {
|
|
4102
|
+
const x0 = Math.max(0, rect.x);
|
|
4103
|
+
const y0 = Math.max(0, rect.y);
|
|
4104
|
+
const x1 = Math.min(buffer.width, rect.x + rect.width);
|
|
4105
|
+
const y1 = Math.min(buffer.height, rect.y + rect.height);
|
|
4106
|
+
if (x0 >= x1 || y0 >= y1) return false;
|
|
4107
|
+
let any = false;
|
|
4108
|
+
for (let y = y0; y < y1; y++) for (let x = x0; x < x1; x++) if (fadeCell(buffer, x, y, amount, strategy)) any = true;
|
|
4109
|
+
return any;
|
|
4110
|
+
}
|
|
4111
|
+
function fadeRectExcluding(buffer, outer, inner, amount, strategy) {
|
|
4112
|
+
const ox0 = Math.max(0, outer.x);
|
|
4113
|
+
const oy0 = Math.max(0, outer.y);
|
|
4114
|
+
const ox1 = Math.min(buffer.width, outer.x + outer.width);
|
|
4115
|
+
const oy1 = Math.min(buffer.height, outer.y + outer.height);
|
|
4116
|
+
const ix0 = Math.max(ox0, inner.x);
|
|
4117
|
+
const iy0 = Math.max(oy0, inner.y);
|
|
4118
|
+
const ix1 = Math.min(ox1, inner.x + inner.width);
|
|
4119
|
+
const iy1 = Math.min(oy1, inner.y + inner.height);
|
|
4120
|
+
const innerValid = ix0 < ix1 && iy0 < iy1;
|
|
4121
|
+
let any = false;
|
|
4122
|
+
for (let y = oy0; y < oy1; y++) for (let x = ox0; x < ox1; x++) {
|
|
4123
|
+
if (innerValid && x >= ix0 && x < ix1 && y >= iy0 && y < iy1) continue;
|
|
4124
|
+
if (fadeCell(buffer, x, y, amount, strategy)) any = true;
|
|
4125
|
+
}
|
|
4126
|
+
return any;
|
|
4127
|
+
}
|
|
4128
|
+
/**
|
|
4129
|
+
* Fade a single cell. Returns true if the cell was modified.
|
|
4130
|
+
*
|
|
4131
|
+
* - `blend` strategy: mix fg toward bg in OKLab. When either color is null or
|
|
4132
|
+
* the default-bg sentinel, also stamps `dim`.
|
|
4133
|
+
* - `dim` strategy: stamp `dim` attribute.
|
|
4134
|
+
*
|
|
4135
|
+
* Wide-char continuation cells are skipped — they share styling with the
|
|
4136
|
+
* leading cell and modifying them separately would desync.
|
|
4137
|
+
*/
|
|
4138
|
+
function fadeCell(buffer, x, y, amount, strategy) {
|
|
4139
|
+
if (buffer.isCellContinuation(x, y)) return false;
|
|
4140
|
+
const cell = buffer.getCell(x, y);
|
|
4141
|
+
if (strategy === "dim") {
|
|
4142
|
+
if (cell.attrs.dim) return false;
|
|
4143
|
+
buffer.setCell(x, y, {
|
|
4144
|
+
...cell,
|
|
4145
|
+
attrs: {
|
|
4146
|
+
...cell.attrs,
|
|
4147
|
+
dim: true
|
|
4148
|
+
}
|
|
4149
|
+
});
|
|
4150
|
+
return true;
|
|
4151
|
+
}
|
|
4152
|
+
const fgHex = colorToHex(cell.fg);
|
|
4153
|
+
const bgHex = colorToHex(cell.bg);
|
|
4154
|
+
if (fgHex && bgHex) {
|
|
4155
|
+
const blendedRgb = hexToRgb(blend(fgHex, bgHex, amount));
|
|
4156
|
+
if (!blendedRgb) return false;
|
|
4157
|
+
buffer.setCell(x, y, {
|
|
4158
|
+
...cell,
|
|
4159
|
+
fg: blendedRgb
|
|
4160
|
+
});
|
|
4161
|
+
return true;
|
|
4162
|
+
}
|
|
4163
|
+
if (cell.attrs.dim) return false;
|
|
4164
|
+
buffer.setCell(x, y, {
|
|
4165
|
+
...cell,
|
|
4166
|
+
attrs: {
|
|
4167
|
+
...cell.attrs,
|
|
4168
|
+
dim: true
|
|
4169
|
+
}
|
|
4170
|
+
});
|
|
4171
|
+
return true;
|
|
4172
|
+
}
|
|
4173
|
+
/** Convert a buffer Color to a `#rrggbb` hex string, or null if unresolvable. */
|
|
4174
|
+
function colorToHex(color) {
|
|
4175
|
+
if (color === null) return null;
|
|
4176
|
+
if (typeof color === "number") {
|
|
4177
|
+
const rgb = ansi256ToRgb(color);
|
|
4178
|
+
return rgbToHex(rgb.r, rgb.g, rgb.b);
|
|
4179
|
+
}
|
|
4180
|
+
if (isDefaultBg(color)) return null;
|
|
4181
|
+
return rgbToHex(color.r, color.g, color.b);
|
|
4182
|
+
}
|
|
4183
|
+
function rgbToHex(r, g, b) {
|
|
4184
|
+
const clamp = (n) => {
|
|
4185
|
+
return Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, "0");
|
|
4186
|
+
};
|
|
4187
|
+
return `#${clamp(r)}${clamp(g)}${clamp(b)}`;
|
|
4188
|
+
}
|
|
4189
|
+
function hexToRgb(hex) {
|
|
4190
|
+
if (typeof hex !== "string") return null;
|
|
4191
|
+
let s = hex;
|
|
4192
|
+
if (s.startsWith("#")) s = s.slice(1);
|
|
4193
|
+
if (s.length === 3) s = s[0] + s[0] + s[1] + s[1] + s[2] + s[2];
|
|
4194
|
+
if (s.length !== 6) return null;
|
|
4195
|
+
const r = parseInt(s.slice(0, 2), 16);
|
|
4196
|
+
const g = parseInt(s.slice(2, 4), 16);
|
|
4197
|
+
const b = parseInt(s.slice(4, 6), 16);
|
|
4198
|
+
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b)) return null;
|
|
4199
|
+
return {
|
|
4200
|
+
r,
|
|
4201
|
+
g,
|
|
4202
|
+
b
|
|
4203
|
+
};
|
|
4204
|
+
}
|
|
4205
|
+
//#endregion
|
|
3997
4206
|
//#region \0@oxc-project+runtime@0.122.0/helpers/usingCtx.js
|
|
3998
4207
|
function _usingCtx() {
|
|
3999
4208
|
var r = "function" == typeof SuppressedError ? SuppressedError : function(r, e) {
|
|
@@ -4054,7 +4263,7 @@ function _usingCtx() {
|
|
|
4054
4263
|
/**
|
|
4055
4264
|
* Ag — tree + layout engine + renderer.
|
|
4056
4265
|
*
|
|
4057
|
-
*
|
|
4266
|
+
* The sole pipeline entry point. Two independent phases:
|
|
4058
4267
|
* - ag.layout(dims) — measure + flexbox → positions/sizes
|
|
4059
4268
|
* - ag.render() — positioned tree → cell grid → TextFrame
|
|
4060
4269
|
*
|
|
@@ -4069,20 +4278,22 @@ function _usingCtx() {
|
|
|
4069
4278
|
* ```
|
|
4070
4279
|
*/
|
|
4071
4280
|
init_buffer();
|
|
4072
|
-
const log
|
|
4073
|
-
const baseLog
|
|
4281
|
+
const log = createLogger("silvery:render");
|
|
4282
|
+
const baseLog = createLogger("@silvery/ag-react");
|
|
4074
4283
|
function createAg(root, options) {
|
|
4075
4284
|
const measurer = options?.measurer;
|
|
4285
|
+
const colorLevel = options?.colorLevel ?? "truecolor";
|
|
4076
4286
|
const ctx = measurer ? { measurer } : void 0;
|
|
4077
4287
|
let _prevBuffer = null;
|
|
4078
4288
|
let hasScroll = false;
|
|
4079
4289
|
let hasSticky = false;
|
|
4080
4290
|
function doLayout(cols, rows, opts) {
|
|
4081
4291
|
try {
|
|
4082
|
-
var _usingCtx$
|
|
4292
|
+
var _usingCtx$1 = _usingCtx();
|
|
4083
4293
|
const prevRootLayout = root.boxRect;
|
|
4084
|
-
if (!(prevRootLayout && (prevRootLayout.width !== cols || prevRootLayout.height !== rows)) && !
|
|
4085
|
-
log
|
|
4294
|
+
if (!(prevRootLayout && (prevRootLayout.width !== cols || prevRootLayout.height !== rows)) && !root.layoutNode?.isDirty() && !hasScrollDirty()) {
|
|
4295
|
+
log.debug?.("layout: skipped (Flexily clean, no scrollDirty, dimensions unchanged)");
|
|
4296
|
+
layoutPhase(root, cols, rows);
|
|
4086
4297
|
return {
|
|
4087
4298
|
tMeasure: 0,
|
|
4088
4299
|
tLayout: 0,
|
|
@@ -4091,7 +4302,7 @@ function createAg(root, options) {
|
|
|
4091
4302
|
tNotify: 0
|
|
4092
4303
|
};
|
|
4093
4304
|
}
|
|
4094
|
-
const render = _usingCtx$
|
|
4305
|
+
const render = _usingCtx$1.u(baseLog.span("pipeline", {
|
|
4095
4306
|
width: cols,
|
|
4096
4307
|
height: rows
|
|
4097
4308
|
}));
|
|
@@ -4102,7 +4313,7 @@ function createAg(root, options) {
|
|
|
4102
4313
|
const t = performance.now();
|
|
4103
4314
|
measurePhase(root, ctx);
|
|
4104
4315
|
tMeasure = performance.now() - t;
|
|
4105
|
-
log
|
|
4316
|
+
log.debug?.(`measure: ${tMeasure.toFixed(2)}ms`);
|
|
4106
4317
|
} catch (_) {
|
|
4107
4318
|
_usingCtx3.e = _;
|
|
4108
4319
|
} finally {
|
|
@@ -4115,12 +4326,13 @@ function createAg(root, options) {
|
|
|
4115
4326
|
const t = performance.now();
|
|
4116
4327
|
layoutPhase(root, cols, rows);
|
|
4117
4328
|
tLayout = performance.now() - t;
|
|
4118
|
-
log
|
|
4329
|
+
log.debug?.(`layout: ${tLayout.toFixed(2)}ms`);
|
|
4119
4330
|
} catch (_) {
|
|
4120
4331
|
_usingCtx4.e = _;
|
|
4121
4332
|
} finally {
|
|
4122
4333
|
_usingCtx4.d();
|
|
4123
4334
|
}
|
|
4335
|
+
strictLayoutOverflowCheck(root);
|
|
4124
4336
|
if (!hasScroll || !hasSticky) {
|
|
4125
4337
|
const features = detectPipelineFeatures(root);
|
|
4126
4338
|
if (features.hasScroll) hasScroll = true;
|
|
@@ -4182,9 +4394,9 @@ function createAg(root, options) {
|
|
|
4182
4394
|
tNotify
|
|
4183
4395
|
};
|
|
4184
4396
|
} catch (_) {
|
|
4185
|
-
_usingCtx$
|
|
4397
|
+
_usingCtx$1.e = _;
|
|
4186
4398
|
} finally {
|
|
4187
|
-
_usingCtx$
|
|
4399
|
+
_usingCtx$1.d();
|
|
4188
4400
|
}
|
|
4189
4401
|
}
|
|
4190
4402
|
function doRender(opts) {
|
|
@@ -4196,9 +4408,17 @@ function createAg(root, options) {
|
|
|
4196
4408
|
const t = performance.now();
|
|
4197
4409
|
buffer = renderPhase(root, prevBuffer, ctx);
|
|
4198
4410
|
tContent = performance.now() - t;
|
|
4199
|
-
log
|
|
4411
|
+
log.debug?.(`content: ${tContent.toFixed(2)}ms`);
|
|
4412
|
+
}
|
|
4413
|
+
let carryForwardBuffer;
|
|
4414
|
+
if (hasBackdropMarkers(root)) {
|
|
4415
|
+
carryForwardBuffer = buffer.clone();
|
|
4416
|
+
if (!opts?.fresh) _prevBuffer = carryForwardBuffer;
|
|
4417
|
+
applyBackdropFade(root, buffer, { colorLevel });
|
|
4418
|
+
} else {
|
|
4419
|
+
carryForwardBuffer = buffer;
|
|
4420
|
+
if (!opts?.fresh) _prevBuffer = buffer;
|
|
4200
4421
|
}
|
|
4201
|
-
if (!opts?.fresh) _prevBuffer = buffer;
|
|
4202
4422
|
clearDirtyTracking();
|
|
4203
4423
|
const acc = globalThis.__silvery_bench_phases;
|
|
4204
4424
|
if (acc) {
|
|
@@ -4208,6 +4428,7 @@ function createAg(root, options) {
|
|
|
4208
4428
|
return {
|
|
4209
4429
|
frame: createTextFrame(buffer),
|
|
4210
4430
|
buffer,
|
|
4431
|
+
carryForwardBuffer,
|
|
4211
4432
|
prevBuffer,
|
|
4212
4433
|
tContent
|
|
4213
4434
|
};
|
|
@@ -4226,10 +4447,8 @@ function createAg(root, options) {
|
|
|
4226
4447
|
prevScrollRect: null,
|
|
4227
4448
|
prevScreenRect: null,
|
|
4228
4449
|
layoutChangedThisFrame: -1,
|
|
4229
|
-
layoutDirty: true,
|
|
4230
4450
|
dirtyBits: 31,
|
|
4231
|
-
dirtyEpoch: getRenderEpoch()
|
|
4232
|
-
layoutSubscribers: /* @__PURE__ */ new Set()
|
|
4451
|
+
dirtyEpoch: getRenderEpoch()
|
|
4233
4452
|
};
|
|
4234
4453
|
}
|
|
4235
4454
|
function agInsertChild(parent, child, index) {
|
|
@@ -4262,6 +4481,7 @@ function createAg(root, options) {
|
|
|
4262
4481
|
return {
|
|
4263
4482
|
frame: result.frame,
|
|
4264
4483
|
buffer: result.buffer,
|
|
4484
|
+
carryForwardBuffer: result.carryForwardBuffer,
|
|
4265
4485
|
prevBuffer: result.prevBuffer
|
|
4266
4486
|
};
|
|
4267
4487
|
},
|
|
@@ -4289,99 +4509,207 @@ function createAg(root, options) {
|
|
|
4289
4509
|
};
|
|
4290
4510
|
}
|
|
4291
4511
|
//#endregion
|
|
4292
|
-
//#region packages/ag-
|
|
4512
|
+
//#region packages/ag-react/src/reconciler/string-reconciler.ts
|
|
4293
4513
|
/**
|
|
4294
|
-
*
|
|
4514
|
+
* Separate React reconciler instance for renderStringSync.
|
|
4295
4515
|
*
|
|
4296
|
-
*
|
|
4516
|
+
* renderStringSync may be called from within React effects (e.g., useScrollback
|
|
4517
|
+
* freezing items to scrollback). If it uses the same reconciler singleton as the
|
|
4518
|
+
* main render tree, this causes re-entrancy: the nested reconciliation interferes
|
|
4519
|
+
* with the outer one, producing empty output.
|
|
4297
4520
|
*
|
|
4298
|
-
*
|
|
4299
|
-
*
|
|
4300
|
-
|
|
4521
|
+
* By using a dedicated reconciler instance, renderStringSync operates on an
|
|
4522
|
+
* independent fiber tree with no shared reconciler state.
|
|
4523
|
+
*/
|
|
4524
|
+
/**
|
|
4525
|
+
* Dedicated reconciler for string rendering.
|
|
4301
4526
|
*
|
|
4302
|
-
*
|
|
4303
|
-
*
|
|
4304
|
-
*
|
|
4305
|
-
|
|
4527
|
+
* Uses the same host config functions but overrides isPrimaryRenderer to false,
|
|
4528
|
+
* since this is a secondary renderer used only for one-shot string rendering.
|
|
4529
|
+
* This avoids conflicts with the main reconciler's hook ownership.
|
|
4530
|
+
*/
|
|
4531
|
+
const stringReconciler = Reconciler({
|
|
4532
|
+
...hostConfig,
|
|
4533
|
+
isPrimaryRenderer: false
|
|
4534
|
+
});
|
|
4535
|
+
//#endregion
|
|
4536
|
+
//#region packages/ag-react/src/render-string.tsx
|
|
4537
|
+
/**
|
|
4538
|
+
* renderString - Static one-shot rendering to string
|
|
4306
4539
|
*
|
|
4307
|
-
*
|
|
4308
|
-
*
|
|
4309
|
-
*
|
|
4310
|
-
*
|
|
4540
|
+
* Renders a React element to a string without needing a terminal.
|
|
4541
|
+
* Use for:
|
|
4542
|
+
* - CI output (no cursor control needed)
|
|
4543
|
+
* - Piped output
|
|
4544
|
+
* - One-shot reports/summaries
|
|
4545
|
+
* - Testing component output
|
|
4311
4546
|
*
|
|
4312
|
-
*
|
|
4313
|
-
*
|
|
4314
|
-
*
|
|
4547
|
+
* @example
|
|
4548
|
+
* ```tsx
|
|
4549
|
+
* import { renderString, Box, Text } from '@silvery/ag-react'
|
|
4315
4550
|
*
|
|
4316
|
-
*
|
|
4317
|
-
*
|
|
4318
|
-
*
|
|
4551
|
+
* // Basic usage
|
|
4552
|
+
* const output = renderString(<Summary stats={stats} />)
|
|
4553
|
+
* console.log(output)
|
|
4554
|
+
*
|
|
4555
|
+
* // Custom width
|
|
4556
|
+
* const wide = renderString(<Report />, { width: 120 })
|
|
4557
|
+
*
|
|
4558
|
+
* // Plain text (no ANSI)
|
|
4559
|
+
* const plain = renderString(<Report />, { plain: true })
|
|
4560
|
+
* ```
|
|
4319
4561
|
*/
|
|
4320
|
-
|
|
4321
|
-
|
|
4562
|
+
init_buffer();
|
|
4563
|
+
let engineInitialized = false;
|
|
4564
|
+
async function ensureLayoutEngine() {
|
|
4565
|
+
if (engineInitialized || isLayoutEngineInitialized()) return;
|
|
4566
|
+
const { ensureDefaultLayoutEngine } = await import("./layout-engine--drvrWjD.mjs");
|
|
4567
|
+
await ensureDefaultLayoutEngine();
|
|
4568
|
+
engineInitialized = true;
|
|
4569
|
+
}
|
|
4322
4570
|
/**
|
|
4323
|
-
*
|
|
4571
|
+
* Render a React element to a string (async version).
|
|
4572
|
+
*
|
|
4573
|
+
* Automatically initializes the layout engine if needed.
|
|
4574
|
+
* Use this when you're not sure if the layout engine is ready.
|
|
4324
4575
|
*
|
|
4325
|
-
*
|
|
4326
|
-
*
|
|
4327
|
-
*
|
|
4576
|
+
* @param element - React element to render
|
|
4577
|
+
* @param options - Render options (width, height, plain)
|
|
4578
|
+
* @returns Rendered string (with or without ANSI codes)
|
|
4579
|
+
*
|
|
4580
|
+
* @example
|
|
4581
|
+
* ```tsx
|
|
4582
|
+
* const output = await renderString(<Summary stats={stats} />)
|
|
4583
|
+
* console.log(output)
|
|
4584
|
+
* ```
|
|
4328
4585
|
*/
|
|
4329
|
-
function
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4586
|
+
async function renderString(element, options = {}) {
|
|
4587
|
+
await ensureLayoutEngine();
|
|
4588
|
+
return renderStringSync(element, options);
|
|
4589
|
+
}
|
|
4590
|
+
/**
|
|
4591
|
+
* Render a React element to a string (sync version).
|
|
4592
|
+
*
|
|
4593
|
+
* Requires the layout engine to be already initialized.
|
|
4594
|
+
* Throws if the layout engine is not ready.
|
|
4595
|
+
*
|
|
4596
|
+
* @param element - React element to render
|
|
4597
|
+
* @param options - Render options (width, height, plain)
|
|
4598
|
+
* @returns Rendered string (with or without ANSI codes)
|
|
4599
|
+
*
|
|
4600
|
+
* @example
|
|
4601
|
+
* ```tsx
|
|
4602
|
+
* // After layout engine is initialized
|
|
4603
|
+
* const output = renderStringSync(<Summary stats={stats} />)
|
|
4604
|
+
* console.log(output)
|
|
4605
|
+
* ```
|
|
4606
|
+
*/
|
|
4607
|
+
function renderStringSync(element, options = {}) {
|
|
4608
|
+
if (!isLayoutEngineInitialized()) throw new Error("Layout engine not initialized. Use renderString() (async) or initialize with setLayoutEngine().");
|
|
4609
|
+
const { width = 80, height = 24, plain = false, pipelineConfig, trimTrailingWhitespace = true, trimEmptyLines = true, onContentHeight, alwaysStyled = false } = options;
|
|
4610
|
+
let hadReactCommit = false;
|
|
4611
|
+
const container = createContainer(() => {
|
|
4612
|
+
hadReactCommit = true;
|
|
4347
4613
|
});
|
|
4348
|
-
|
|
4349
|
-
const
|
|
4350
|
-
|
|
4351
|
-
let tOutput;
|
|
4352
|
-
{
|
|
4353
|
-
const t4 = performance.now();
|
|
4354
|
-
const outputFn = config?.outputPhaseFn ?? outputPhase;
|
|
4355
|
-
try {
|
|
4356
|
-
output = outputFn(prevBuffer, buffer, mode, scrollbackOffset, termRows, cursorPos);
|
|
4357
|
-
} catch (e) {
|
|
4358
|
-
if (e instanceof Error) e.__silvery_buffer = buffer;
|
|
4359
|
-
throw e;
|
|
4360
|
-
}
|
|
4361
|
-
tOutput = performance.now() - t4;
|
|
4362
|
-
log.debug?.(`output: ${tOutput.toFixed(2)}ms (${output.length} bytes)`);
|
|
4363
|
-
}
|
|
4364
|
-
const total = performance.now() - start;
|
|
4365
|
-
globalThis.__silvery_last_pipeline = {
|
|
4366
|
-
layout: tLayout,
|
|
4367
|
-
output: tOutput,
|
|
4368
|
-
total,
|
|
4369
|
-
incremental: prevBuffer !== null
|
|
4614
|
+
let uncaughtError = null;
|
|
4615
|
+
const onUncaughtError = (error) => {
|
|
4616
|
+
uncaughtError = error;
|
|
4370
4617
|
};
|
|
4371
|
-
|
|
4372
|
-
const
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4618
|
+
const fiberRoot = stringReconciler.createContainer(container, 1, null, false, null, "", onUncaughtError, () => {}, () => {}, null);
|
|
4619
|
+
const mockStdout = {
|
|
4620
|
+
columns: width,
|
|
4621
|
+
rows: height,
|
|
4622
|
+
write: () => true,
|
|
4623
|
+
isTTY: false,
|
|
4624
|
+
on: () => mockStdout,
|
|
4625
|
+
off: () => mockStdout,
|
|
4626
|
+
once: () => mockStdout,
|
|
4627
|
+
removeListener: () => mockStdout,
|
|
4628
|
+
addListener: () => mockStdout
|
|
4382
4629
|
};
|
|
4630
|
+
const mockTerm = createTerm({ color: plain ? null : "truecolor" });
|
|
4631
|
+
const wrapped = React.createElement(TermContext.Provider, { value: mockTerm }, React.createElement(StdoutContext.Provider, { value: {
|
|
4632
|
+
stdout: mockStdout,
|
|
4633
|
+
write: () => {}
|
|
4634
|
+
} }, React.createElement(StderrContext.Provider, { value: {
|
|
4635
|
+
stderr: process.stderr,
|
|
4636
|
+
write: (data) => {
|
|
4637
|
+
process.stderr.write(data);
|
|
4638
|
+
}
|
|
4639
|
+
} }, element)));
|
|
4640
|
+
withActEnvironment(() => {
|
|
4641
|
+
act(() => {
|
|
4642
|
+
stringReconciler.updateContainerSync(wrapped, fiberRoot, null, null);
|
|
4643
|
+
stringReconciler.flushSyncWork();
|
|
4644
|
+
});
|
|
4645
|
+
});
|
|
4646
|
+
if (uncaughtError) throw uncaughtError instanceof Error ? uncaughtError : new Error(String(uncaughtError));
|
|
4647
|
+
let buffer;
|
|
4648
|
+
let rootNode;
|
|
4649
|
+
const MAX_ITERATIONS = 5;
|
|
4650
|
+
for (let i = 0; i < MAX_ITERATIONS; i++) {
|
|
4651
|
+
hadReactCommit = false;
|
|
4652
|
+
withActEnvironment(() => {
|
|
4653
|
+
act(() => {
|
|
4654
|
+
const root = getContainerRoot(container);
|
|
4655
|
+
rootNode = root;
|
|
4656
|
+
const measurer = pipelineConfig?.measurer;
|
|
4657
|
+
const doRender = () => {
|
|
4658
|
+
const ag = createAg(root, { measurer });
|
|
4659
|
+
ag.layout({
|
|
4660
|
+
cols: width,
|
|
4661
|
+
rows: height
|
|
4662
|
+
});
|
|
4663
|
+
return ag.render();
|
|
4664
|
+
};
|
|
4665
|
+
buffer = (measurer ? runWithMeasurer(measurer, doRender) : doRender()).buffer;
|
|
4666
|
+
});
|
|
4667
|
+
if (!hadReactCommit) act(() => {
|
|
4668
|
+
stringReconciler.flushSyncWork();
|
|
4669
|
+
});
|
|
4670
|
+
});
|
|
4671
|
+
if (!hadReactCommit) break;
|
|
4672
|
+
}
|
|
4673
|
+
if (onContentHeight && rootNode) {
|
|
4674
|
+
let maxBottom = 0;
|
|
4675
|
+
let hasChildren = false;
|
|
4676
|
+
for (const child of rootNode.children) if (child.boxRect) {
|
|
4677
|
+
hasChildren = true;
|
|
4678
|
+
const props = child.props;
|
|
4679
|
+
const mb = props.marginBottom ?? props.marginY ?? props.margin ?? 0;
|
|
4680
|
+
const childBottom = child.boxRect.y + child.boxRect.height + mb;
|
|
4681
|
+
if (childBottom > maxBottom) maxBottom = childBottom;
|
|
4682
|
+
}
|
|
4683
|
+
onContentHeight(hasChildren ? maxBottom : 0);
|
|
4684
|
+
}
|
|
4685
|
+
withActEnvironment(() => {
|
|
4686
|
+
act(() => {
|
|
4687
|
+
stringReconciler.updateContainerSync(null, fiberRoot, null, null);
|
|
4688
|
+
stringReconciler.flushSyncWork();
|
|
4689
|
+
});
|
|
4690
|
+
});
|
|
4691
|
+
return plain && !alwaysStyled ? bufferToText(buffer, {
|
|
4692
|
+
trimTrailingWhitespace,
|
|
4693
|
+
trimEmptyLines
|
|
4694
|
+
}) : bufferToStyledText(buffer, {
|
|
4695
|
+
trimTrailingWhitespace,
|
|
4696
|
+
trimEmptyLines
|
|
4697
|
+
});
|
|
4698
|
+
}
|
|
4699
|
+
/**
|
|
4700
|
+
* Run a function with IS_REACT_ACT_ENVIRONMENT temporarily set to true.
|
|
4701
|
+
* This ensures act() captures forceUpdate/setState from layout notifications.
|
|
4702
|
+
*/
|
|
4703
|
+
function withActEnvironment(fn) {
|
|
4704
|
+
const prev = globalThis.IS_REACT_ACT_ENVIRONMENT;
|
|
4705
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
|
|
4706
|
+
try {
|
|
4707
|
+
fn();
|
|
4708
|
+
} finally {
|
|
4709
|
+
globalThis.IS_REACT_ACT_ENVIRONMENT = prev;
|
|
4710
|
+
}
|
|
4383
4711
|
}
|
|
4384
4712
|
//#endregion
|
|
4385
|
-
export {
|
|
4713
|
+
export { _usingCtx as i, renderStringSync as n, createAg as r, renderString as t };
|
|
4386
4714
|
|
|
4387
|
-
//# sourceMappingURL=
|
|
4715
|
+
//# sourceMappingURL=render-string-DVfgc8xr.mjs.map
|