silvery 0.19.2 → 0.21.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 +9 -4
- package/dist/Text-Lq0dmj8-.mjs +239 -0
- package/dist/Text-Lq0dmj8-.mjs.map +1 -0
- package/dist/UPNG-Bo33r8rA.mjs +3 -0
- package/dist/UPNG-DosRPdF4.mjs +5075 -0
- package/dist/UPNG-DosRPdF4.mjs.map +1 -0
- package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs +6 -0
- package/dist/__vite-browser-external-2447137e-D_JM6skp.mjs.map +1 -0
- package/dist/{animation-Cn64yepo.mjs → animation-ZMN2_XKv.mjs} +2 -2
- package/dist/animation-ZMN2_XKv.mjs.map +1 -0
- package/dist/{ansi-Cc33mW54.d.mts → ansi-2Xn0yatP.d.mts} +1 -1
- package/dist/{ansi-Cc33mW54.d.mts.map → ansi-2Xn0yatP.d.mts.map} +1 -1
- package/dist/{ansi-CLOitHKx.mjs → ansi-D1KQMAbf.mjs} +1 -1
- package/dist/{ansi-CLOitHKx.mjs.map → ansi-D1KQMAbf.mjs.map} +1 -1
- package/dist/ansi-yC4RyBNY.mjs +22441 -0
- package/dist/ansi-yC4RyBNY.mjs.map +1 -0
- package/dist/apng-CR08rIaH.mjs +58 -0
- package/dist/apng-CR08rIaH.mjs.map +1 -0
- package/dist/apng-DaHfVaVI.mjs +3 -0
- package/dist/assets/resvgjs.darwin-arm64-BtufyGW1.node +0 -0
- package/dist/assets/skia.darwin-arm64-DQs5sT6N.node +0 -0
- package/dist/backend-B-WYLUib.mjs +13396 -0
- package/dist/backend-B-WYLUib.mjs.map +1 -0
- package/dist/backends-CUtan80W.mjs +3 -0
- package/dist/backends-DIVYzKqd.mjs +1083 -0
- package/dist/backends-DIVYzKqd.mjs.map +1 -0
- package/dist/bound-term-0sPrrzH1.d.mts +4640 -0
- package/dist/bound-term-0sPrrzH1.d.mts.map +1 -0
- package/dist/canvas-1v7dPT-_.mjs +3 -0
- package/dist/canvas-CSuPOMNt.mjs +1442 -0
- package/dist/canvas-CSuPOMNt.mjs.map +1 -0
- package/dist/{chunk-Vs_PY4HZ.mjs → chunk-BSw8zbkd.mjs} +1 -1
- package/dist/cli-dvo0r2fs.mjs +4 -0
- package/dist/compare-CQodSH4G.mjs +376 -0
- package/dist/compare-CQodSH4G.mjs.map +1 -0
- package/dist/compare-DHlcxEYA.mjs +3 -0
- package/dist/context-BU5LkkIy.mjs.map +1 -1
- package/dist/devtools-CJdt5H0X.mjs +2 -0
- package/dist/{devtools-DxkSLXDA.mjs → devtools-DcQjgyjL.mjs} +5 -4
- package/dist/{devtools-DxkSLXDA.mjs.map → devtools-DcQjgyjL.mjs.map} +1 -1
- package/dist/easing-BI-ASGMO.d.mts +24 -0
- package/dist/easing-BI-ASGMO.d.mts.map +1 -0
- package/dist/{eta-Bb3RH3wh.mjs → eta-CJlGH06n.mjs} +1 -1
- package/dist/{eta-Bb3RH3wh.mjs.map → eta-CJlGH06n.mjs.map} +1 -1
- package/dist/flexily-zero-adapter-C3Vj0fPt.mjs +306 -0
- package/dist/flexily-zero-adapter-C3Vj0fPt.mjs.map +1 -0
- package/dist/{flexily-zero-adapter-CMxXhdOL.mjs → flexily-zero-adapter-C4lW_Ov5.mjs} +1 -1
- package/dist/fonts-BFmhXDv7.mjs +88 -0
- package/dist/fonts-BFmhXDv7.mjs.map +1 -0
- package/dist/gif-C_AjaT9d.mjs +188 -0
- package/dist/gif-C_AjaT9d.mjs.map +1 -0
- package/dist/gif-DaC4XrxA.mjs +3 -0
- package/dist/gifenc-BOUT-KFB.mjs +730 -0
- package/dist/gifenc-BOUT-KFB.mjs.map +1 -0
- package/dist/image-C2Birh2x.mjs +1252 -0
- package/dist/image-C2Birh2x.mjs.map +1 -0
- package/dist/index-BUMxS65f.d.mts +453 -0
- package/dist/index-BUMxS65f.d.mts.map +1 -0
- package/dist/{index-D3saHouR.d.mts → index-CSQf13CI.d.mts} +1057 -1133
- package/dist/index-CSQf13CI.d.mts.map +1 -0
- package/dist/{index-BXslOebb.d.mts → index-Cl9KKjQ_.d.mts} +4919 -3921
- package/dist/index-Cl9KKjQ_.d.mts.map +1 -0
- package/dist/index-XbNrPhWl.d.mts +336 -0
- package/dist/index-XbNrPhWl.d.mts.map +1 -0
- package/dist/index.d.mts +8 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +14 -12
- package/dist/index.mjs.map +1 -1
- package/dist/key-mapping-CS-YD_cD.mjs +132 -0
- package/dist/key-mapping-CS-YD_cD.mjs.map +1 -0
- package/dist/key-mapping-Yn-Jgrij.mjs +3 -0
- package/dist/{layout-engine-B6Cdz1yZ.mjs → layout-engine-C07LEXWT.mjs} +1 -1
- package/dist/layout-engine-C2px0RJE.mjs +67 -0
- package/dist/layout-engine-C2px0RJE.mjs.map +1 -0
- package/dist/layout-signals-Cnw6xk8Q.mjs +988 -0
- package/dist/layout-signals-Cnw6xk8Q.mjs.map +1 -0
- package/dist/mouse-events-Dki3ISIp.mjs +1044 -0
- package/dist/mouse-events-Dki3ISIp.mjs.map +1 -0
- package/dist/{multi-progress-Bq9Oi_WI.mjs → multi-progress-CIRjrzma.mjs} +3 -3
- package/dist/{multi-progress-Bq9Oi_WI.mjs.map → multi-progress-CIRjrzma.mjs.map} +1 -1
- package/dist/{multi-progress-DAQC7eap.d.mts → multi-progress-DHZ2xUT2.d.mts} +2 -2
- package/dist/{multi-progress-DAQC7eap.d.mts.map → multi-progress-DHZ2xUT2.d.mts.map} +1 -1
- package/dist/{node-BeWlnCPY.mjs → node-CjM5Rt-M.mjs} +4 -4
- package/dist/node-CjM5Rt-M.mjs.map +1 -0
- package/dist/playwright-D5YiZcNS.mjs +76397 -0
- package/dist/playwright-D5YiZcNS.mjs.map +1 -0
- package/dist/png-codec-Dp84742B.mjs +36 -0
- package/dist/png-codec-Dp84742B.mjs.map +1 -0
- package/dist/png-codec-QwOtJ8Zs.mjs +3 -0
- package/dist/progress-DB_Xo071.mjs +675 -0
- package/dist/progress-DB_Xo071.mjs.map +1 -0
- package/dist/{progress-bar-CXE5Qfkd.mjs → progress-bar-oJwq22CR.mjs} +4 -4
- package/dist/{progress-bar-CXE5Qfkd.mjs.map → progress-bar-oJwq22CR.mjs.map} +1 -1
- package/dist/rasterizer-BRXrDdWx.mjs +3 -0
- package/dist/rasterizer-CpEhJvdR.mjs +296 -0
- package/dist/rasterizer-CpEhJvdR.mjs.map +1 -0
- package/dist/reconciler-DldIJB93.mjs +2083 -0
- package/dist/reconciler-DldIJB93.mjs.map +1 -0
- package/dist/{render-string-CDCeYkS3.mjs → render-string-BcoCpjCB.mjs} +1 -1
- package/dist/{render-string-Darrg7ku.mjs → render-string-DkQacASz.mjs} +2707 -549
- package/dist/render-string-DkQacASz.mjs.map +1 -0
- package/dist/resvg-js-DkOndZI3.mjs +203 -0
- package/dist/resvg-js-DkOndZI3.mjs.map +1 -0
- package/dist/runtime.d.mts +3 -2
- package/dist/runtime.mjs +3 -3
- package/dist/schemes-JjNp4aSl.mjs +2611 -0
- package/dist/schemes-JjNp4aSl.mjs.map +1 -0
- package/dist/{spinner-CGo34vyR.d.mts → spinner-CZINHpkV.d.mts} +2 -2
- package/dist/{spinner-CGo34vyR.d.mts.map → spinner-CZINHpkV.d.mts.map} +1 -1
- package/dist/{spinner-CeOmcuw_.mjs → spinner-D9lrHr8s.mjs} +7 -7
- package/dist/spinner-D9lrHr8s.mjs.map +1 -0
- package/dist/src-5w9QR6_8.mjs +1071 -0
- package/dist/src-5w9QR6_8.mjs.map +1 -0
- package/dist/src-BNTToU7l.mjs +4387 -0
- package/dist/src-BNTToU7l.mjs.map +1 -0
- package/dist/{src-CF-6UN01.mjs → src-BR4xNwdG.mjs} +10436 -2622
- package/dist/src-BR4xNwdG.mjs.map +1 -0
- package/dist/{types-Bk2yw9Qj.mjs → src-DKp-_OFG.mjs} +34 -94
- package/dist/src-DKp-_OFG.mjs.map +1 -0
- package/dist/src-bt8wSrfJ.mjs +258 -0
- package/dist/src-bt8wSrfJ.mjs.map +1 -0
- package/dist/src-e33Y6kNJ.mjs +3 -0
- package/dist/src-iDwu25UD.mjs +1814 -0
- package/dist/src-iDwu25UD.mjs.map +1 -0
- package/dist/steps-Bp2uNqnn.d.mts +202 -0
- package/dist/steps-Bp2uNqnn.d.mts.map +1 -0
- package/dist/svg-15lZZzxq.mjs +486 -0
- package/dist/svg-15lZZzxq.mjs.map +1 -0
- package/dist/svg-Cz0UXcDj.mjs +255 -0
- package/dist/svg-Cz0UXcDj.mjs.map +1 -0
- package/dist/svg-DY72a4HK.mjs +3 -0
- package/dist/svg-g1D6ErwR.d.mts +82 -0
- package/dist/svg-g1D6ErwR.d.mts.map +1 -0
- package/dist/term.d.mts +3 -0
- package/dist/term.mjs +9 -0
- package/dist/term.mjs.map +1 -0
- package/dist/theme.d.mts +95 -2
- package/dist/theme.d.mts.map +1 -0
- package/dist/theme.mjs +9 -3
- package/dist/theme.mjs.map +1 -0
- package/dist/{types-BH_v3iMT.d.mts → types-kt_fKR37.d.mts} +2 -15
- package/dist/types-kt_fKR37.d.mts.map +1 -0
- package/dist/ui/animation.d.mts +2 -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 +1 -1
- package/dist/ui/image.d.mts +2 -2
- package/dist/ui/image.mjs +2 -2
- package/dist/ui/input.d.mts +1 -1
- package/dist/ui/input.mjs +4 -2
- package/dist/ui/input.mjs.map +1 -1
- package/dist/ui/progress.d.mts +5 -249
- package/dist/ui/progress.mjs +5 -858
- package/dist/ui/react.d.mts +1 -1
- package/dist/ui/react.mjs +2 -2
- package/dist/ui/recording-chrome-react.d.mts +21 -0
- package/dist/ui/recording-chrome-react.d.mts.map +1 -0
- package/dist/ui/recording-chrome-react.mjs +105 -0
- package/dist/ui/recording-chrome-react.mjs.map +1 -0
- package/dist/ui/recording-chrome.d.mts +2 -0
- package/dist/ui/recording-chrome.mjs +2 -0
- package/dist/ui/utils.mjs +1 -1
- package/dist/ui/wrappers.d.mts +3 -3
- package/dist/ui/wrappers.mjs +2 -2
- package/dist/ui.d.mts +7 -6
- package/dist/ui.mjs +8 -7
- package/dist/{useLatest-Bg2x4bfP.d.mts → useLatest-DRDDVwjh.d.mts} +5 -25
- package/dist/useLatest-DRDDVwjh.d.mts.map +1 -0
- package/dist/{with-text-input-CRfoiFFG.d.mts → with-text-input-YeohVLeo.d.mts} +4 -55
- package/dist/with-text-input-YeohVLeo.d.mts.map +1 -0
- package/dist/wrapper-C70ATkVv.mjs +3527 -0
- package/dist/wrapper-C70ATkVv.mjs.map +1 -0
- package/dist/{wrappers-UTADQkSY.mjs → wrappers-BCUYITrY.mjs} +5 -157
- package/dist/wrappers-BCUYITrY.mjs.map +1 -0
- package/dist/{yoga-adapter-8oRGRw8V.mjs → yoga-adapter-BnZX1PAY.mjs} +28 -2
- package/dist/yoga-adapter-BnZX1PAY.mjs.map +1 -0
- package/dist/yoga-adapter-DxgsQ_gg.mjs +2 -0
- package/dist/zipBundle-3nqeDRtm.mjs +3 -0
- package/dist/zipBundle-VNAYFmqJ.mjs +2003 -0
- package/dist/zipBundle-VNAYFmqJ.mjs.map +1 -0
- package/package.json +20 -9
- package/dist/animation-Cn64yepo.mjs.map +0 -1
- package/dist/cli-BKp0YtBD.mjs +0 -4
- package/dist/devtools-9QY4teqI.mjs +0 -2
- package/dist/flexily-zero-adapter-BlQa46nr.mjs +0 -3385
- package/dist/flexily-zero-adapter-BlQa46nr.mjs.map +0 -1
- package/dist/image-CTII5QWI.mjs +0 -477
- package/dist/image-CTII5QWI.mjs.map +0 -1
- package/dist/index-BXslOebb.d.mts.map +0 -1
- package/dist/index-BnA7mNpo.d.mts +0 -175
- package/dist/index-BnA7mNpo.d.mts.map +0 -1
- package/dist/index-D3saHouR.d.mts.map +0 -1
- package/dist/layout-engine-ClUgv6jB.mjs +0 -50
- package/dist/layout-engine-ClUgv6jB.mjs.map +0 -1
- package/dist/node-BeWlnCPY.mjs.map +0 -1
- package/dist/reconciler-Cwgm8hRR.mjs +0 -8459
- package/dist/reconciler-Cwgm8hRR.mjs.map +0 -1
- package/dist/render-string-Darrg7ku.mjs.map +0 -1
- package/dist/spinner-CeOmcuw_.mjs.map +0 -1
- package/dist/src-B5GjfG7g.mjs +0 -4305
- package/dist/src-B5GjfG7g.mjs.map +0 -1
- package/dist/src-CChwjk0Z.mjs +0 -738
- package/dist/src-CChwjk0Z.mjs.map +0 -1
- package/dist/src-CF-6UN01.mjs.map +0 -1
- package/dist/src-NCKb8kE5.mjs +0 -2660
- package/dist/src-NCKb8kE5.mjs.map +0 -1
- package/dist/types-BH_v3iMT.d.mts.map +0 -1
- package/dist/types-Bk2yw9Qj.mjs.map +0 -1
- package/dist/ui/progress.d.mts.map +0 -1
- package/dist/ui/progress.mjs.map +0 -1
- package/dist/useLatest-Bg2x4bfP.d.mts.map +0 -1
- package/dist/with-text-input-CRfoiFFG.d.mts.map +0 -1
- package/dist/wrappers-UTADQkSY.mjs.map +0 -1
- package/dist/yoga-adapter-8oRGRw8V.mjs.map +0 -1
- package/dist/yoga-adapter-D_CcxSt5.mjs +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare-CQodSH4G.mjs","names":[],"sources":["../../termless/src/compare.ts"],"sourcesContent":["/**\n * Cross-renderer capture + comparison harness.\n *\n * Given a Terminal (xterm.js, vt100, vterm, etc.) and the command that\n * put it into its current state, captures three renderings of the same\n * buffer state:\n *\n * 1. Canvas — ghostty-web's CanvasRenderer via @termless/ghostty's native\n * canvas pipeline (@napi-rs/canvas + ghostty-web WASM, no Chromium)\n * (real-fidelity truecolor + glyph shaping)\n * 2. SVG — termless's deterministic SVG renderer (text-tags + rects)\n * 3. Peekaboo — a real terminal app (Ghostty, iTerm2, Terminal.app)\n * re-running the command; macOS-only; GUI-required\n *\n * Returns the three PNGs plus a comparison report (dimensions, cell-grid\n * alignment, similarity). Used by the `toMatchAcrossRenderers` matcher.\n *\n * Design choices:\n * - All three renderers see the same buffer state via the same source\n * (xterm.js for canvas+SVG; the real terminal's own parser for peekaboo).\n * - A unified theme + font config is applied to all three when possible.\n * Canvas: passes theme + fontPath to @termless/ghostty's renderTerminalPng.\n * SVG: passes theme + cellWidth/Height. Peekaboo: launches the terminal app\n * with whatever the user's config dictates — we can't override that in-flight.\n * - Peekaboo is opt-in (set `includePeekaboo: true`) because it requires\n * GUI access + spawns a real window.\n */\n\nimport type { Terminal, SvgScreenshotOptions } from \"./terminal/types.ts\"\nimport { renderTerminalPng, type CanvasTheme, type RenderOptions, type RenderMeta } from \"@termless/ghostty\"\n\nexport interface CrossRendererOptions {\n /**\n * Command that puts the terminal into its current state. Re-run in a\n * real terminal app when `includePeekaboo: true`. If omitted, peekaboo\n * is skipped regardless of `includePeekaboo`.\n */\n command?: string[]\n /** Unified theme — applied to both canvas + SVG paths. */\n theme?: CanvasTheme\n /** Path to .ttf/.otf font file; canvas embeds it via @font-face. */\n fontPath?: string\n /** Font size (canvas). */\n fontSize?: number\n /** SVG cell dimensions (canvas measures its own from the font). */\n cellWidth?: number\n cellHeight?: number\n /** Pixel-rounded font family for SVG (matches canvas when same family). */\n fontFamily?: string\n /** Output dir for diff bundle. When set, all PNGs + report.json are saved. */\n saveTo?: string\n /** Capture peekaboo too. macOS-only, GUI-required, slow. */\n includePeekaboo?: boolean\n /** Terminal app for peekaboo. Default: \"ghostty\". */\n peekabooApp?: \"ghostty\" | \"iterm2\" | \"terminal\" | \"wezterm\" | \"kitty\"\n /** Ghostty-specific config overrides (written to a temp config-file). */\n ghosttyConfig?: { theme?: string; fontFamily?: string; fontSize?: number; backgroundOpacity?: number }\n /** Strip window chrome from peekaboo capture (Ghostty: enabled with window-decoration=false in temp config). */\n cropChrome?: boolean\n /** Env vars for the peekaboo spawned shell (e.g. KM_ROOT). */\n peekabooEnv?: Record<string, string>\n /** Working directory for the peekaboo spawned shell. */\n peekabooCwd?: string\n /** Terminal dimensions — must match the source terminal's cols/rows. */\n cols?: number\n rows?: number\n}\n\nexport interface CrossRendererResult {\n canvas: Uint8Array\n svg: Uint8Array\n peekaboo?: Uint8Array\n report: CrossRendererReport\n}\n\nexport interface CrossRendererReport {\n /** Decoded PNG dimensions per renderer. */\n dimensions: Record<\"canvas\" | \"svg\" | \"peekaboo\", { width: number; height: number } | null>\n /** Logical dimensions normalized by DPR (estimated from cols/rows + cell metrics). */\n logical: Record<\"canvas\" | \"svg\" | \"peekaboo\", { width: number; height: number } | null>\n /** Buffer text equality — all three renderers see the same buffer iff this is true. */\n textEquality: boolean\n /** dHash (perceptual hash) per renderer that has a rasterized PNG. */\n hashes?: Record<\"canvas\" | \"peekaboo\", string | null>\n /** Hamming distances between pairs (0=identical, 32=completely different). */\n hashDistances?: { canvasVsPeekaboo: number | null }\n /** Files written (when saveTo set). */\n files?: { canvas: string; svg: string; peekaboo?: string; report: string }\n /** Notes on residual divergence — why pixels don't match perfectly. */\n notes: string[]\n}\n\n/**\n * Read PNG width + height from header bytes. PNG IHDR is at offset 16\n * (4-byte big-endian width, 4-byte big-endian height).\n */\nexport function pngDimensions(png: Uint8Array): { width: number; height: number } {\n const view = new DataView(png.buffer, png.byteOffset, png.byteLength)\n return { width: view.getUint32(16, false), height: view.getUint32(20, false) }\n}\n\n/**\n * Capture the same terminal buffer state via canvas, SVG, and (optionally)\n * peekaboo. Returns the three PNGs + a comparison report.\n *\n * Caller is responsible for putting the terminal into the desired state\n * via `feed()` or `spawn()` before invoking this.\n */\nexport async function captureCrossRenderer(\n terminal: Terminal,\n options: CrossRendererOptions = {},\n): Promise<CrossRendererResult> {\n const notes: string[] = []\n\n // ── canvas ──────────────────────────────────────────────\n // Capture with returnMeta so we can pass measured cell dimensions to\n // SVG for unification — otherwise SVG's 9.6×20 defaults won't match\n // canvas's font-measured ~10×17 metrics and dimension comparisons fail.\n //\n // Phase 9: routes through @termless/ghostty's renderTerminalPng (native\n // canvas via @napi-rs/canvas + ghostty-web WASM). Replaces the deleted\n // screenshotCanvasPng (Playwright + Chromium) path.\n const canvasOptions: RenderOptions & { returnMeta: true } = {\n returnMeta: true,\n ...(options.cols != null ? { cols: options.cols } : {}),\n ...(options.rows != null ? { rows: options.rows } : {}),\n ...(options.theme ? { theme: options.theme } : {}),\n ...(options.fontPath ? { fontPath: options.fontPath } : {}),\n ...(options.fontSize ? { fontSize: options.fontSize } : {}),\n ...(options.fontFamily ? { fontFamily: options.fontFamily } : {}),\n }\n const canvasResult = (await renderTerminalPng(terminal, canvasOptions)) as unknown as {\n png: Uint8Array\n meta: RenderMeta\n }\n const canvasBytes = canvasResult.png\n const canvasMeta = canvasResult.meta\n // Cell metrics from ghostty-web's CanvasRenderer are reported in CSS\n // (logical) pixels — same units SVG uses for `cellWidth`/`cellHeight`.\n // The canvas's actual pixel dimensions ARE multiplied by DPR (so a 40×6\n // grid at 10×17 logical = 400×102 logical = 800×204 canvas at DPR 2),\n // but cellWidth/cellHeight stay in logical px.\n const measuredCellWidth = canvasMeta.cellWidth || 9.6\n const measuredCellHeight = canvasMeta.cellHeight || 20\n\n // ── svg ────────────────────────────────────────────────\n // The SVG renderer emits an SVG string; pass canvas's measured cell\n // dimensions when caller doesn't override — so the two paths produce\n // matched aspect ratios + sizes (modulo DPR).\n const svgString = terminal.screenshotSvg({\n ...(options.theme ? { theme: { foreground: options.theme.foreground, background: options.theme.background } } : {}),\n cellWidth: options.cellWidth ?? measuredCellWidth,\n cellHeight: options.cellHeight ?? measuredCellHeight,\n ...(options.fontFamily ? { fontFamily: options.fontFamily } : {}),\n } as SvgScreenshotOptions)\n // Store as utf-8 bytes for now. Rasterization is the caller's job.\n const svgBytes = new TextEncoder().encode(svgString)\n\n // ── peekaboo (opt-in) ──────────────────────────────────\n // Use osascript + screencapture directly — peekaboo's full backend\n // requires node-pty (xterm.js data-layer PTY), which is heavy native\n // dep we don't need for screenshot-only capture. Inline the launch +\n // bounds-based capture + cleanup we want.\n let peekabooBytes: Uint8Array | undefined\n if (options.includePeekaboo) {\n if (process.platform !== \"darwin\") {\n notes.push(\"peekaboo skipped: requires macOS\")\n } else if (!options.command) {\n notes.push(\"peekaboo skipped: `command` not provided (needed to re-run in real terminal)\")\n } else {\n try {\n peekabooBytes = await captureRealTerminal({\n app: options.peekabooApp ?? \"ghostty\",\n command: options.command,\n waitMs: 2500,\n ...(options.cropChrome != null ? { cropChrome: options.cropChrome } : {}),\n ...(options.ghosttyConfig ? { ghosttyConfig: options.ghosttyConfig } : {}),\n ...(options.peekabooEnv ? { env: options.peekabooEnv } : {}),\n ...(options.peekabooCwd ? { cwd: options.peekabooCwd } : {}),\n })\n } catch (err) {\n notes.push(`peekaboo failed: ${err instanceof Error ? err.message : String(err)}`)\n }\n }\n }\n\n // ── Compute report ─────────────────────────────────────\n const canvasDim = pngDimensions(canvasBytes)\n let svgDim: { width: number; height: number } | null = null\n // Parse <svg width=\"X\" height=\"Y\"> from the SVG string for logical dim.\n const m = svgString.match(/<svg[^>]+width=\"(\\d+)\"[^>]+height=\"(\\d+)\"/)\n if (m) svgDim = { width: Number.parseInt(m[1]!, 10), height: Number.parseInt(m[2]!, 10) }\n const peekDim = peekabooBytes ? pngDimensions(peekabooBytes) : null\n\n // Estimate logical dimensions by dividing by suspected DPR.\n // Canvas reports DPR via the meta passed in; we don't have access here,\n // so estimate from cols × ~10px (typical cell width). For now, simply\n // report raw and let the caller decide.\n const logical = {\n canvas: canvasDim,\n svg: svgDim,\n peekaboo: peekDim, // includes window chrome — caller should crop\n }\n\n // Compute perceptual hashes for canvas + peekaboo (SVG is XML, not a\n // raster — skipped). pHash is dep-free via sips on macOS; falls back\n // to a no-op zero-hash on other platforms or when sips is missing.\n let canvasHash: string | null = null\n let peekHash: string | null = null\n if (process.platform === \"darwin\") {\n try {\n canvasHash = await dHash(canvasBytes)\n } catch {\n // ignore — leaves hash null\n }\n if (peekabooBytes) {\n try {\n peekHash = await dHash(peekabooBytes)\n } catch {\n // ignore — leaves hash null\n }\n }\n }\n\n const report: CrossRendererReport = {\n dimensions: { canvas: canvasDim, svg: svgDim, peekaboo: peekDim },\n logical,\n textEquality: true, // canvas + SVG both read from the same terminal buffer\n hashes: { canvas: canvasHash, peekaboo: peekHash },\n hashDistances: {\n canvasVsPeekaboo: canvasHash && peekHash ? hashDistance(canvasHash, peekHash) : null,\n },\n notes,\n }\n\n // ── Persist diff bundle ────────────────────────────────\n if (options.saveTo) {\n const { mkdirSync, writeFileSync } = await import(\"node:fs\")\n const { join } = await import(\"node:path\")\n mkdirSync(options.saveTo, { recursive: true })\n const canvasPath = join(options.saveTo, \"canvas.png\")\n const svgPngPath = join(options.saveTo, \"svg.svg\") // SVG kept as XML for now\n const peekPath = join(options.saveTo, \"peekaboo.png\")\n const reportPath = join(options.saveTo, \"report.json\")\n writeFileSync(canvasPath, canvasBytes)\n writeFileSync(svgPngPath, svgString, \"utf-8\")\n if (peekabooBytes) writeFileSync(peekPath, peekabooBytes)\n writeFileSync(reportPath, JSON.stringify(report, null, 2))\n report.files = {\n canvas: canvasPath,\n svg: svgPngPath,\n ...(peekabooBytes ? { peekaboo: peekPath } : {}),\n report: reportPath,\n }\n }\n\n return { canvas: canvasBytes, svg: svgBytes, peekaboo: peekabooBytes, report }\n}\n\n// ─────────────────────────────────────────────────────────\n// Real-terminal capture (macOS)\n// ─────────────────────────────────────────────────────────\n//\n// Spawns a terminal app via `open -a`, captures the window via\n// `screencapture -R <bounds>` (non-interactive — no popup picker), then\n// closes the spawned window. Uses System Events to query position+size\n// for the bounds, and Ghostty/iTerm2/Terminal AppleScript to close.\n//\n// Bypasses peekaboo's full backend (which spawns an xterm.js PTY for\n// data-layer access). For screenshot-only purposes, we don't need that.\n\nconst APP_BUNDLE_NAMES = {\n ghostty: \"Ghostty\",\n iterm2: \"iTerm\",\n terminal: \"Terminal\",\n wezterm: \"WezTerm\",\n kitty: \"kitty\",\n} as const\n\nasync function captureRealTerminal(opts: {\n app: keyof typeof APP_BUNDLE_NAMES\n command: string[]\n waitMs: number\n /** Strip Ghostty's window chrome (titlebar, padding) from the capture. */\n cropChrome?: boolean\n /** Ghostty config overrides (theme, font, padding). Ignored for non-Ghostty. */\n ghosttyConfig?: { theme?: string; fontFamily?: string; fontSize?: number; backgroundOpacity?: number }\n /** Environment variables for the spawned shell. */\n env?: Record<string, string>\n /** Working directory for the spawned shell. */\n cwd?: string\n}): Promise<Uint8Array> {\n const { spawnSync } = await import(\"node:child_process\")\n const { mkdtempSync, readFileSync, rmSync, writeFileSync, chmodSync } = await import(\"node:fs\")\n const { tmpdir } = await import(\"node:os\")\n const { join } = await import(\"node:path\")\n const bundle = APP_BUNDLE_NAMES[opts.app]\n\n const idsBefore = listWindowIds(opts.app)\n // Also snapshot CGWindowIDs (numeric, macOS-native) so we can diff\n // post-launch to find OUR new window — independent of AppleScript ids.\n const cgIdsBefore = listCGWindowIds(bundle)\n const tmpDir = mkdtempSync(join(tmpdir(), \"term-real-\"))\n const script = join(tmpDir, \"payload.sh\")\n // Build the script: prepend env exports + cd, then the command.\n const envLines = opts.env ? Object.entries(opts.env).map(([k, v]) => `export ${k}=${JSON.stringify(v)}`) : []\n const cdLine = opts.cwd ? `cd ${JSON.stringify(opts.cwd)}` : \"\"\n // Build the command line. For bash -c shape ([\"/bin/bash\", \"-c\", \"...\"]),\n // unwrap to the inner shell command. For other shapes, shell-quote each\n // argv element so spaces/special-chars in paths don't split.\n const cmdLine =\n opts.command[0] === \"/bin/bash\" && opts.command[1] === \"-c\" && opts.command.length === 3\n ? opts.command[2]\n : opts.command.map((arg) => `'${arg.replace(/'/g, \"'\\\\''\")}'`).join(\" \")\n // Append a sleep so the window doesn't auto-close before screencapture\n // fires. The harness's `pkill -f <script>` in finally{} kills bash mid-\n // sleep, triggering Ghostty's wait-after-command=false → window closes.\n writeFileSync(script, `#!/bin/bash\\n${envLines.join(\"\\n\")}\\n${cdLine}\\n${cmdLine}\\nsleep 60\\n`)\n chmodSync(script, 0o755)\n\n // Ghostty on macOS doesn't accept `-e <command>` via `open --args` —\n // per the docs, \"launching the terminal emulator from the CLI is not\n // supported\". Instead, set `command = <script>` in a temp config file\n // and pass it via `--config-file=<path>`.\n //\n // Additional config keys for reliable capture + cleanup:\n // - confirm-close-surface = false: no \"Are you sure?\" dialog\n // - wait-after-command = false: (default) window closes when command exits\n // - window-padding-x/y = 0: cell-grid fills the window\n let ghosttyConfigPath: string | undefined\n if (opts.app === \"ghostty\") {\n ghosttyConfigPath = join(tmpDir, \"ghostty.cfg\")\n const cfgLines = [\n `command = ${script}`,\n \"confirm-close-surface = false\",\n \"wait-after-command = false\",\n \"window-padding-x = 0\",\n \"window-padding-y = 0\",\n ...(opts.ghosttyConfig?.theme ? [`theme = ${opts.ghosttyConfig.theme}`] : []),\n ...(opts.ghosttyConfig?.fontFamily ? [`font-family = ${opts.ghosttyConfig.fontFamily}`] : []),\n ...(opts.ghosttyConfig?.fontSize != null ? [`font-size = ${opts.ghosttyConfig.fontSize}`] : []),\n ...(opts.ghosttyConfig?.backgroundOpacity != null\n ? [`background-opacity = ${opts.ghosttyConfig.backgroundOpacity}`]\n : []),\n ]\n writeFileSync(ghosttyConfigPath, cfgLines.join(\"\\n\") + \"\\n\")\n }\n\n // Launch — `open -g` opens without activating, so the user's current\n // window stays focused. The new terminal still spawns visibly (macOS\n // can't truly hide a window) but doesn't steal keyboard focus.\n if (opts.app === \"ghostty\") {\n // On macOS, Ghostty doesn't accept `-e <command>` via `open --args`.\n // The temp config-file (above) carries `command = <script>` instead.\n //\n // CRITICAL: do NOT use `-n` (open --new-instance). It spawns a fresh\n // Ghostty.app PROCESS each time, creating a stack of dock icons that\n // accumulates over the user's session. The user can't `quit` them\n // selectively without losing their primary Ghostty (which hosts\n // their actual terminal sessions / cmux). Use a regular `open` so\n // existing Ghostty makes a new window inside its single process.\n const args = [\"-g\", \"-a\", \"Ghostty\", \"--args\"]\n if (ghosttyConfigPath) args.push(`--config-file=${ghosttyConfigPath}`)\n spawnSync(\"open\", args, { stdio: \"ignore\" })\n } else if (opts.app === \"kitty\") {\n spawnSync(\"open\", [\"-g\", \"-a\", \"kitty\", \"--args\", script], { stdio: \"ignore\" })\n } else if (opts.app === \"wezterm\") {\n spawnSync(\"open\", [\"-g\", \"-a\", \"WezTerm\", \"--args\", \"start\", \"--\", \"bash\", \"-c\", script], { stdio: \"ignore\" })\n } else {\n spawnSync(\"open\", [\"-g\", \"-a\", bundle, script], { stdio: \"ignore\" })\n }\n\n // Poll for the new window.\n let newId: string | undefined\n for (let i = 0; i < 40 && !newId; i++) {\n await new Promise((r) => setTimeout(r, 200))\n const idsNow = listWindowIds(opts.app)\n newId = idsNow.find((id) => !idsBefore.includes(id))\n }\n\n try {\n await new Promise((r) => setTimeout(r, opts.waitMs))\n\n spawnSync(\"osascript\", [\"-e\", `tell application \"${bundle}\" to activate`], { stdio: \"ignore\" })\n await new Promise((r) => setTimeout(r, 300))\n\n // Get the spawned window's CGWindowID via a Swift one-liner. This is\n // the macOS window-number used by `screencapture -l <id>` — captures\n // THAT specific window's pixels even when occluded by other windows.\n // The AppleScript `id of front window` returns a different identifier\n // (a \"tab-group-XXX\" string for Ghostty) that screencapture doesn't\n // accept; bounds-based `-R` capture picks up whatever's visible at the\n // screen coordinates, which fails when other apps overlap. CGWindowID\n // is the right primitive.\n //\n // env -i bypasses the user's Nix-managed PATH so xcrun resolves the\n // macOS-bundled Swift toolchain (which matches the macOS SDK).\n const cgIds = listCGWindowIds(bundle)\n if (cgIds.length === 0) {\n throw new Error(`captureRealTerminal: no ${bundle} windows visible (CGWindowList returned empty)`)\n }\n // Pick the window that's NEW since launch — diff against the\n // pre-launch CGWindowID set. Falls back to the highest id when the\n // diff is empty (shouldn't happen but defensive).\n const freshIds = cgIds.filter((id) => !cgIdsBefore.includes(id))\n const cgId = (freshIds[0] ?? cgIds.sort((a, b) => Number(a) - Number(b)).pop())!.toString()\n const outPath = join(tmpDir, \"capture.png\")\n const cap = spawnSync(\"screencapture\", [\"-l\", cgId, \"-o\", \"-x\", outPath], { stdio: [\"ignore\", \"ignore\", \"pipe\"] })\n if (cap.status !== 0) throw new Error(`screencapture -l ${cgId} failed: ${cap.stderr?.toString().trim()}`)\n const bytes = readFileSync(outPath)\n void opts.cropChrome // currently unused; chrome handling lives in caller\n return new Uint8Array(bytes.buffer, bytes.byteOffset, bytes.byteLength)\n } finally {\n // Cleanup strategy (in order):\n // 1. Kill the script process that's running inside the window.\n // With close-on-exit=true, killing the script causes Ghostty to\n // close the window automatically. Works even without\n // AppleScript permissions.\n // 2. AppleScript close-by-id as a backstop for apps without\n // close-on-exit.\n try {\n spawnSync(\"pkill\", [\"-f\", script], { stdio: \"ignore\", timeout: 2000 })\n } catch {\n // ignore\n }\n // Give close-on-exit a beat to fire.\n await new Promise((r) => setTimeout(r, 400))\n if (newId) {\n const closeScript = `tell application \"${bundle}\" to close (every window whose id is ${newId})`\n spawnSync(\"osascript\", [\"-e\", closeScript], { stdio: \"ignore\", timeout: 3000 })\n }\n // KEEP tmpDir when CROSS_RENDERER_DEBUG=1 — useful for inspecting the\n // generated script + Ghostty config when capture fails. Default: clean.\n if (process.env.CROSS_RENDERER_DEBUG !== \"1\") {\n rmSync(tmpDir, { recursive: true, force: true })\n } else {\n // eslint-disable-next-line no-console\n console.error(`[cross-renderer] preserved tmpDir: ${tmpDir}`)\n }\n }\n}\n\n// ─────────────────────────────────────────────────────────\n// Image similarity — perceptual hash (no external deps)\n// ─────────────────────────────────────────────────────────\n//\n// pHash via Difference Hash (dHash): downscale to 9×8 grayscale, compute\n// 64 bits where each bit is \"left-pixel < right-pixel\". Tolerant to\n// resolution, font hinting, and small color differences. Two PNGs with\n// the same cell-grid structure should hash within Hamming distance ~5.\n// True identical: 0. Completely different: 32.\n\n/** Compute a 64-bit difference hash for a PNG. Returns a 16-char hex string. */\nexport async function dHash(pngBytes: Uint8Array): Promise<string> {\n // Decode PNG via playwright's image — heavy. Alternative: shell out to\n // sips (macOS) for resize+grayscale, then read raw bytes.\n const { spawnSync } = await import(\"node:child_process\")\n const { mkdtempSync, writeFileSync, readFileSync, rmSync } = await import(\"node:fs\")\n const { tmpdir } = await import(\"node:os\")\n const { join } = await import(\"node:path\")\n const tmp = mkdtempSync(join(tmpdir(), \"dhash-\"))\n try {\n const inPath = join(tmp, \"in.png\")\n const outPath = join(tmp, \"out.png\")\n writeFileSync(inPath, pngBytes)\n // Resize to 9×8 grayscale BMP (raw-ish format easy to parse).\n // sips -s format bmp -s formatOptions normal -z 8 9 -s pixelsPerInchH 72 ...\n const r = spawnSync(\"sips\", [\"-s\", \"format\", \"png\", \"-z\", \"8\", \"9\", inPath, \"--out\", outPath], { stdio: \"ignore\" })\n if (r.status !== 0) {\n // Fallback: return null-like hash so similarity returns Infinity\n return \"0\".repeat(16)\n }\n const png = readFileSync(outPath)\n // We don't have a PNG decoder in dep-free TS. Use Bun's built-in\n // image bitmap via Canvas — but Bun has no canvas. Workaround: shell\n // out again to sips to convert to raw RGB.\n const rawPath = join(tmp, \"raw.bmp\")\n const r2 = spawnSync(\"sips\", [\"-s\", \"format\", \"bmp\", outPath, \"--out\", rawPath], { stdio: \"ignore\" })\n if (r2.status !== 0) return \"0\".repeat(16)\n const bmp = readFileSync(rawPath)\n // BMP header: 14 bytes file header + 40 bytes DIB header for BITMAPINFOHEADER.\n // Pixel data starts at offset given by the file header (bytes 10-13).\n const view = new DataView(bmp.buffer, bmp.byteOffset, bmp.byteLength)\n const pixOffset = view.getUint32(10, true)\n const width = view.getInt32(18, true)\n const height = view.getInt32(22, true)\n const bpp = view.getUint16(28, true)\n if (bpp !== 24 && bpp !== 32) return \"0\".repeat(16)\n const bytesPerPx = bpp / 8\n const rowSize = Math.floor((bpp * width + 31) / 32) * 4\n // BMP rows are bottom-up by default.\n const gray = new Uint8Array(Math.abs(width) * Math.abs(height))\n for (let row = 0; row < Math.abs(height); row++) {\n const srcRow = height > 0 ? Math.abs(height) - 1 - row : row\n for (let col = 0; col < Math.abs(width); col++) {\n const idx = pixOffset + srcRow * rowSize + col * bytesPerPx\n // BMP is BGR (or BGRA)\n const b = bmp[idx] ?? 0\n const g = bmp[idx + 1] ?? 0\n const r = bmp[idx + 2] ?? 0\n gray[row * Math.abs(width) + col] = Math.round(0.299 * r + 0.587 * g + 0.114 * b)\n }\n }\n // 8 rows × 9 cols → 8 rows × 8 bits = 64 bits\n let hash = 0n\n for (let row = 0; row < 8; row++) {\n for (let col = 0; col < 8; col++) {\n const left = gray[row * Math.abs(width) + col] ?? 0\n const right = gray[row * Math.abs(width) + col + 1] ?? 0\n if (left < right) hash |= 1n << BigInt(row * 8 + col)\n }\n }\n void png\n return hash.toString(16).padStart(16, \"0\")\n } finally {\n rmSync(tmp, { recursive: true, force: true })\n }\n}\n\n/** Hamming distance between two hex hashes (64 bits / 16 hex chars). */\nexport function hashDistance(a: string, b: string): number {\n let diff = 0n\n diff = BigInt(\"0x\" + a) ^ BigInt(\"0x\" + b)\n let count = 0\n while (diff > 0n) {\n if (diff & 1n) count++\n diff >>= 1n\n }\n return count\n}\n\n/** Get CGWindowIDs (numeric) for all on-screen windows owned by an app. */\nfunction listCGWindowIds(bundle: string): string[] {\n const { spawnSync } = require(\"node:child_process\") as typeof import(\"node:child_process\")\n const swiftSnippet = `import Foundation\nimport CoreGraphics\nlet opts: CGWindowListOption = [.optionOnScreenOnly]\nguard let windows = CGWindowListCopyWindowInfo(opts, kCGNullWindowID) as? [[String: Any]] else { exit(1) }\nfor w in windows {\n if let owner = w[\"kCGWindowOwnerName\"] as? String, owner == \"${bundle}\" {\n if let id = w[\"kCGWindowNumber\"] as? Int {\n print(id)\n }\n }\n}`\n const r = spawnSync(\"env\", [\"-i\", \"PATH=/usr/bin:/bin\", \"xcrun\", \"swift\", \"-\"], {\n input: swiftSnippet,\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n encoding: \"utf-8\",\n })\n if (r.status !== 0) return []\n return (r.stdout ?? \"\")\n .trim()\n .split(\"\\n\")\n .map((s: string) => s.trim())\n .filter(Boolean)\n}\n\nfunction listWindowIds(app: keyof typeof APP_BUNDLE_NAMES): string[] {\n // sync version of peekaboo's listGhosttyWindowIds — applies to any\n // terminal app, returns AppleScript window ids comma-separated.\n const { spawnSync } = require(\"node:child_process\") as typeof import(\"node:child_process\")\n const bundle = APP_BUNDLE_NAMES[app]\n const r = spawnSync(\n \"osascript\",\n [\n \"-e\",\n `if application \"${bundle}\" is running then\n tell application \"${bundle}\" to return id of every window as text\n else\n return \"\"\n end if`,\n ],\n { stdio: [\"ignore\", \"pipe\", \"pipe\"], encoding: \"utf-8\" },\n )\n if (r.status !== 0) return []\n const out = (r.stdout ?? \"\").trim()\n if (!out) return []\n return out\n .split(\", \")\n .map((s: string) => s.trim())\n .filter(Boolean)\n}\n"],"mappings":";;;;;;;AAgGA,SAAgB,cAAc,KAAoD;CAChF,MAAM,OAAO,IAAI,SAAS,IAAI,QAAQ,IAAI,YAAY,IAAI,WAAW;AACrE,QAAO;EAAE,OAAO,KAAK,UAAU,IAAI,MAAM;EAAE,QAAQ,KAAK,UAAU,IAAI,MAAM;EAAE;;;;;;;;;AAUhF,eAAsB,qBACpB,UACA,UAAgC,EAAE,EACJ;CAC9B,MAAM,QAAkB,EAAE;CAmB1B,MAAM,eAAgB,MAAM,kBAAkB,UATc;EAC1D,YAAY;EACZ,GAAI,QAAQ,QAAQ,OAAO,EAAE,MAAM,QAAQ,MAAM,GAAG,EAAE;EACtD,GAAI,QAAQ,QAAQ,OAAO,EAAE,MAAM,QAAQ,MAAM,GAAG,EAAE;EACtD,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,OAAO,GAAG,EAAE;EACjD,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;EAC1D,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;EAC1D,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,YAAY,GAAG,EAAE;EACjE,CACqE;CAItE,MAAM,cAAc,aAAa;CACjC,MAAM,aAAa,aAAa;CAMhC,MAAM,oBAAoB,WAAW,aAAa;CAClD,MAAM,qBAAqB,WAAW,cAAc;CAMpD,MAAM,YAAY,SAAS,cAAc;EACvC,GAAI,QAAQ,QAAQ,EAAE,OAAO;GAAE,YAAY,QAAQ,MAAM;GAAY,YAAY,QAAQ,MAAM;GAAY,EAAE,GAAG,EAAE;EAClH,WAAW,QAAQ,aAAa;EAChC,YAAY,QAAQ,cAAc;EAClC,GAAI,QAAQ,aAAa,EAAE,YAAY,QAAQ,YAAY,GAAG,EAAE;EACjE,CAAyB;CAE1B,MAAM,WAAW,IAAI,aAAa,CAAC,OAAO,UAAU;CAOpD,IAAI;AACJ,KAAI,QAAQ,gBACV,KAAI,QAAQ,aAAa,SACvB,OAAM,KAAK,mCAAmC;UACrC,CAAC,QAAQ,QAClB,OAAM,KAAK,+EAA+E;KAE1F,KAAI;AACF,kBAAgB,MAAM,oBAAoB;GACxC,KAAK,QAAQ,eAAe;GAC5B,SAAS,QAAQ;GACjB,QAAQ;GACR,GAAI,QAAQ,cAAc,OAAO,EAAE,YAAY,QAAQ,YAAY,GAAG,EAAE;GACxE,GAAI,QAAQ,gBAAgB,EAAE,eAAe,QAAQ,eAAe,GAAG,EAAE;GACzE,GAAI,QAAQ,cAAc,EAAE,KAAK,QAAQ,aAAa,GAAG,EAAE;GAC3D,GAAI,QAAQ,cAAc,EAAE,KAAK,QAAQ,aAAa,GAAG,EAAE;GAC5D,CAAC;UACK,KAAK;AACZ,QAAM,KAAK,oBAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;;CAMxF,MAAM,YAAY,cAAc,YAAY;CAC5C,IAAI,SAAmD;CAEvD,MAAM,IAAI,UAAU,MAAM,4CAA4C;AACtE,KAAI,EAAG,UAAS;EAAE,OAAO,OAAO,SAAS,EAAE,IAAK,GAAG;EAAE,QAAQ,OAAO,SAAS,EAAE,IAAK,GAAG;EAAE;CACzF,MAAM,UAAU,gBAAgB,cAAc,cAAc,GAAG;CAM/D,MAAM,UAAU;EACd,QAAQ;EACR,KAAK;EACL,UAAU;EACX;CAKD,IAAI,aAA4B;CAChC,IAAI,WAA0B;AAC9B,KAAI,QAAQ,aAAa,UAAU;AACjC,MAAI;AACF,gBAAa,MAAM,MAAM,YAAY;UAC/B;AAGR,MAAI,cACF,KAAI;AACF,cAAW,MAAM,MAAM,cAAc;UAC/B;;CAMZ,MAAM,SAA8B;EAClC,YAAY;GAAE,QAAQ;GAAW,KAAK;GAAQ,UAAU;GAAS;EACjE;EACA,cAAc;EACd,QAAQ;GAAE,QAAQ;GAAY,UAAU;GAAU;EAClD,eAAe,EACb,kBAAkB,cAAc,WAAW,aAAa,YAAY,SAAS,GAAG,MACjF;EACD;EACD;AAGD,KAAI,QAAQ,QAAQ;EAClB,MAAM,EAAE,WAAW,kBAAkB,MAAM,OAAO;EAClD,MAAM,EAAE,SAAS,MAAM,OAAO;AAC9B,YAAU,QAAQ,QAAQ,EAAE,WAAW,MAAM,CAAC;EAC9C,MAAM,aAAa,KAAK,QAAQ,QAAQ,aAAa;EACrD,MAAM,aAAa,KAAK,QAAQ,QAAQ,UAAU;EAClD,MAAM,WAAW,KAAK,QAAQ,QAAQ,eAAe;EACrD,MAAM,aAAa,KAAK,QAAQ,QAAQ,cAAc;AACtD,gBAAc,YAAY,YAAY;AACtC,gBAAc,YAAY,WAAW,QAAQ;AAC7C,MAAI,cAAe,eAAc,UAAU,cAAc;AACzD,gBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC;AAC1D,SAAO,QAAQ;GACb,QAAQ;GACR,KAAK;GACL,GAAI,gBAAgB,EAAE,UAAU,UAAU,GAAG,EAAE;GAC/C,QAAQ;GACT;;AAGH,QAAO;EAAE,QAAQ;EAAa,KAAK;EAAU,UAAU;EAAe;EAAQ;;AAuBhF,eAAe,oBAAoB,MAYX;CACtB,MAAM,EAAE,cAAc,MAAM,OAAO;CACnC,MAAM,EAAE,aAAa,cAAc,QAAQ,eAAe,cAAc,MAAM,OAAO;CACrF,MAAM,EAAE,WAAW,MAAM,OAAO;CAChC,MAAM,EAAE,SAAS,MAAM,OAAO;CAC9B,MAAM,SAAS,iBAAiB,KAAK;CAErC,MAAM,YAAY,cAAc,KAAK,IAAI;CAGzC,MAAM,cAAc,gBAAgB,OAAO;CAC3C,MAAM,SAAS,YAAY,KAAK,QAAQ,EAAE,aAAa,CAAC;CACxD,MAAM,SAAS,KAAK,QAAQ,aAAa;CAEzC,MAAM,WAAW,KAAK,MAAM,OAAO,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC,GAAG,OAAO,UAAU,EAAE,GAAG,KAAK,UAAU,EAAE,GAAG,GAAG,EAAE;CAC7G,MAAM,SAAS,KAAK,MAAM,MAAM,KAAK,UAAU,KAAK,IAAI,KAAK;CAI7D,MAAM,UACJ,KAAK,QAAQ,OAAO,eAAe,KAAK,QAAQ,OAAO,QAAQ,KAAK,QAAQ,WAAW,IACnF,KAAK,QAAQ,KACb,KAAK,QAAQ,KAAK,QAAQ,IAAI,IAAI,QAAQ,MAAM,QAAQ,CAAC,GAAG,CAAC,KAAK,IAAI;AAI5E,eAAc,QAAQ,gBAAgB,SAAS,KAAK,KAAK,CAAC,IAAI,OAAO,IAAI,QAAQ,cAAc;AAC/F,WAAU,QAAQ,IAAM;CAWxB,IAAI;AACJ,KAAI,KAAK,QAAQ,WAAW;AAC1B,sBAAoB,KAAK,QAAQ,cAAc;EAC/C,MAAM,WAAW;GACf,aAAa;GACb;GACA;GACA;GACA;GACA,GAAI,KAAK,eAAe,QAAQ,CAAC,WAAW,KAAK,cAAc,QAAQ,GAAG,EAAE;GAC5E,GAAI,KAAK,eAAe,aAAa,CAAC,iBAAiB,KAAK,cAAc,aAAa,GAAG,EAAE;GAC5F,GAAI,KAAK,eAAe,YAAY,OAAO,CAAC,eAAe,KAAK,cAAc,WAAW,GAAG,EAAE;GAC9F,GAAI,KAAK,eAAe,qBAAqB,OACzC,CAAC,wBAAwB,KAAK,cAAc,oBAAoB,GAChE,EAAE;GACP;AACD,gBAAc,mBAAmB,SAAS,KAAK,KAAK,GAAG,KAAK;;AAM9D,KAAI,KAAK,QAAQ,WAAW;EAU1B,MAAM,OAAO;GAAC;GAAM;GAAM;GAAW;GAAS;AAC9C,MAAI,kBAAmB,MAAK,KAAK,iBAAiB,oBAAoB;AACtE,YAAU,QAAQ,MAAM,EAAE,OAAO,UAAU,CAAC;YACnC,KAAK,QAAQ,QACtB,WAAU,QAAQ;EAAC;EAAM;EAAM;EAAS;EAAU;EAAO,EAAE,EAAE,OAAO,UAAU,CAAC;UACtE,KAAK,QAAQ,UACtB,WAAU,QAAQ;EAAC;EAAM;EAAM;EAAW;EAAU;EAAS;EAAM;EAAQ;EAAM;EAAO,EAAE,EAAE,OAAO,UAAU,CAAC;KAE9G,WAAU,QAAQ;EAAC;EAAM;EAAM;EAAQ;EAAO,EAAE,EAAE,OAAO,UAAU,CAAC;CAItE,IAAI;AACJ,MAAK,IAAI,IAAI,GAAG,IAAI,MAAM,CAAC,OAAO,KAAK;AACrC,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAI,CAAC;AAE5C,UADe,cAAc,KAAK,IAAI,CACvB,MAAM,OAAO,CAAC,UAAU,SAAS,GAAG,CAAC;;AAGtD,KAAI;AACF,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,KAAK,OAAO,CAAC;AAEpD,YAAU,aAAa,CAAC,MAAM,qBAAqB,OAAO,eAAe,EAAE,EAAE,OAAO,UAAU,CAAC;AAC/F,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAI,CAAC;EAa5C,MAAM,QAAQ,gBAAgB,OAAO;AACrC,MAAI,MAAM,WAAW,EACnB,OAAM,IAAI,MAAM,2BAA2B,OAAO,gDAAgD;EAMpG,MAAM,QADW,MAAM,QAAQ,OAAO,CAAC,YAAY,SAAS,GAAG,CAAC,CACzC,MAAM,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,GAAG,OAAO,EAAE,CAAC,CAAC,KAAK,EAAG,UAAU;EAC3F,MAAM,UAAU,KAAK,QAAQ,cAAc;EAC3C,MAAM,MAAM,UAAU,iBAAiB;GAAC;GAAM;GAAM;GAAM;GAAM;GAAQ,EAAE,EAAE,OAAO;GAAC;GAAU;GAAU;GAAO,EAAE,CAAC;AAClH,MAAI,IAAI,WAAW,EAAG,OAAM,IAAI,MAAM,oBAAoB,KAAK,WAAW,IAAI,QAAQ,UAAU,CAAC,MAAM,GAAG;EAC1G,MAAM,QAAQ,aAAa,QAAQ;AAC9B,OAAK;AACV,SAAO,IAAI,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,WAAW;WAC/D;AAQR,MAAI;AACF,aAAU,SAAS,CAAC,MAAM,OAAO,EAAE;IAAE,OAAO;IAAU,SAAS;IAAM,CAAC;UAChE;AAIR,QAAM,IAAI,SAAS,MAAM,WAAW,GAAG,IAAI,CAAC;AAC5C,MAAI,MAEF,WAAU,aAAa,CAAC,MADJ,qBAAqB,OAAO,uCAAuC,MAAM,GACnD,EAAE;GAAE,OAAO;GAAU,SAAS;GAAM,CAAC;AAIjF,MAAI,QAAQ,IAAI,yBAAyB,IACvC,QAAO,QAAQ;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;MAGhD,SAAQ,MAAM,sCAAsC,SAAS;;;;AAgBnE,eAAsB,MAAM,UAAuC;CAGjE,MAAM,EAAE,cAAc,MAAM,OAAO;CACnC,MAAM,EAAE,aAAa,eAAe,cAAc,WAAW,MAAM,OAAO;CAC1E,MAAM,EAAE,WAAW,MAAM,OAAO;CAChC,MAAM,EAAE,SAAS,MAAM,OAAO;CAC9B,MAAM,MAAM,YAAY,KAAK,QAAQ,EAAE,SAAS,CAAC;AACjD,KAAI;EACF,MAAM,SAAS,KAAK,KAAK,SAAS;EAClC,MAAM,UAAU,KAAK,KAAK,UAAU;AACpC,gBAAc,QAAQ,SAAS;AAI/B,MADU,UAAU,QAAQ;GAAC;GAAM;GAAU;GAAO;GAAM;GAAK;GAAK;GAAQ;GAAS;GAAQ,EAAE,EAAE,OAAO,UAAU,CAAC,CAC7G,WAAW,EAEf,QAAO,IAAI,OAAO,GAAG;AAEX,eAAa,QAAQ;EAIjC,MAAM,UAAU,KAAK,KAAK,UAAU;AAEpC,MADW,UAAU,QAAQ;GAAC;GAAM;GAAU;GAAO;GAAS;GAAS;GAAQ,EAAE,EAAE,OAAO,UAAU,CAAC,CAC9F,WAAW,EAAG,QAAO,IAAI,OAAO,GAAG;EAC1C,MAAM,MAAM,aAAa,QAAQ;EAGjC,MAAM,OAAO,IAAI,SAAS,IAAI,QAAQ,IAAI,YAAY,IAAI,WAAW;EACrE,MAAM,YAAY,KAAK,UAAU,IAAI,KAAK;EAC1C,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK;EACrC,MAAM,SAAS,KAAK,SAAS,IAAI,KAAK;EACtC,MAAM,MAAM,KAAK,UAAU,IAAI,KAAK;AACpC,MAAI,QAAQ,MAAM,QAAQ,GAAI,QAAO,IAAI,OAAO,GAAG;EACnD,MAAM,aAAa,MAAM;EACzB,MAAM,UAAU,KAAK,OAAO,MAAM,QAAQ,MAAM,GAAG,GAAG;EAEtD,MAAM,OAAO,IAAI,WAAW,KAAK,IAAI,MAAM,GAAG,KAAK,IAAI,OAAO,CAAC;AAC/D,OAAK,IAAI,MAAM,GAAG,MAAM,KAAK,IAAI,OAAO,EAAE,OAAO;GAC/C,MAAM,SAAS,SAAS,IAAI,KAAK,IAAI,OAAO,GAAG,IAAI,MAAM;AACzD,QAAK,IAAI,MAAM,GAAG,MAAM,KAAK,IAAI,MAAM,EAAE,OAAO;IAC9C,MAAM,MAAM,YAAY,SAAS,UAAU,MAAM;IAEjD,MAAM,IAAI,IAAI,QAAQ;IACtB,MAAM,IAAI,IAAI,MAAM,MAAM;IAC1B,MAAM,IAAI,IAAI,MAAM,MAAM;AAC1B,SAAK,MAAM,KAAK,IAAI,MAAM,GAAG,OAAO,KAAK,MAAM,OAAQ,IAAI,OAAQ,IAAI,OAAQ,EAAE;;;EAIrF,IAAI,OAAO;AACX,OAAK,IAAI,MAAM,GAAG,MAAM,GAAG,MACzB,MAAK,IAAI,MAAM,GAAG,MAAM,GAAG,MAGzB,MAFa,KAAK,MAAM,KAAK,IAAI,MAAM,GAAG,QAAQ,MACpC,KAAK,MAAM,KAAK,IAAI,MAAM,GAAG,MAAM,MAAM,GACrC,SAAQ,MAAM,OAAO,MAAM,IAAI,IAAI;AAIzD,SAAO,KAAK,SAAS,GAAG,CAAC,SAAS,IAAI,IAAI;WAClC;AACR,SAAO,KAAK;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;;;;AAKjD,SAAgB,aAAa,GAAW,GAAmB;CACzD,IAAI,OAAO;AACX,QAAO,OAAO,OAAO,EAAE,GAAG,OAAO,OAAO,EAAE;CAC1C,IAAI,QAAQ;AACZ,QAAO,OAAO,IAAI;AAChB,MAAI,OAAO,GAAI;AACf,WAAS;;AAEX,QAAO;;;AAIT,SAAS,gBAAgB,QAA0B;CACjD,MAAM,EAAE,cAAA,UAAsB,qBAAqB;CAYnD,MAAM,IAAI,UAAU,OAAO;EAAC;EAAM;EAAsB;EAAS;EAAS;EAAI,EAAE;EAC9E,OAZmB;;;;;iEAK0C,OAAO;;;;;;EAQpE,OAAO;GAAC;GAAQ;GAAQ;GAAO;EAC/B,UAAU;EACX,CAAC;AACF,KAAI,EAAE,WAAW,EAAG,QAAO,EAAE;AAC7B,SAAQ,EAAE,UAAU,IACjB,MAAM,CACN,MAAM,KAAK,CACX,KAAK,MAAc,EAAE,MAAM,CAAC,CAC5B,OAAO,QAAQ;;AAGpB,SAAS,cAAc,KAA8C;CAGnE,MAAM,EAAE,cAAA,UAAsB,qBAAqB;CACnD,MAAM,SAAS,iBAAiB;CAChC,MAAM,IAAI,UACR,aACA,CACE,MACA,mBAAmB,OAAO;6BACH,OAAO;;;eAI/B,EACD;EAAE,OAAO;GAAC;GAAU;GAAQ;GAAO;EAAE,UAAU;EAAS,CACzD;AACD,KAAI,EAAE,WAAW,EAAG,QAAO,EAAE;CAC7B,MAAM,OAAO,EAAE,UAAU,IAAI,MAAM;AACnC,KAAI,CAAC,IAAK,QAAO,EAAE;AACnB,QAAO,IACJ,MAAM,KAAK,CACX,KAAK,MAAc,EAAE,MAAM,CAAC,CAC5B,OAAO,QAAQ;;;;WAxiBwF;AAkPtG,oBAAmB;EACvB,SAAS;EACT,QAAQ;EACR,UAAU;EACV,SAAS;EACT,OAAO;EACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-BU5LkkIy.mjs","names":[],"sources":["../packages/ag-react/src/context.ts"],"sourcesContent":["/**\n * Silvery React Contexts\n *\n * Provides contexts for:\n * - TermContext: Access to Term instance (for styling/detection)\n * - NodeContext: Access to the current SilveryNode (for useBoxRect)\n * - RuntimeContext: Unified input/app controls (replaces Events/Input/Stdin/App contexts)\n * - StdoutContext: Access to stdout\n * - StderrContext: Access to stderr\n */\n\nimport type { Term } from \"@silvery/ag-term/ansi\"\nimport { createContext } from \"react\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport type { Key } from \"@silvery/ag/keys\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Term Context\n// ============================================================================\n\n/**\n * Context that provides access to the Term instance.\n * Used by useTerm() hook to access terminal capabilities and styling.\n */\nexport const TermContext = createContext<Term | null>(null)\n\n// ============================================================================\n// Node Context\n// ============================================================================\n\n/**\n * Context that provides access to the current SilveryNode.\n * Used by useBoxRect() to subscribe to layout changes.\n *\n * Each Box component wraps its children in a NodeContext.Provider\n * with its corresponding SilveryNode.\n */\nexport const NodeContext = createContext<AgNode | null>(null)\n\n// ============================================================================\n// Stdio Context\n// ============================================================================\n\nexport interface StdoutContextValue {\n /** Standard output stream */\n stdout: NodeJS.WriteStream\n /** Write to stdout */\n write: (data: string) => void\n /**\n * Notify the scheduler that lines were written to stdout externally.\n * Used by useScrollback to report lines written between renders so that\n * inline mode cursor positioning accounts for the displacement.\n */\n notifyScrollback?: (lines: number) => void\n /**\n * Reset inline cursor state in the output phase.\n * Used by useScrollback on resize to clear cursor tracking before\n * re-emitting frozen items at the new width.\n */\n resetInlineCursor?: () => void\n /**\n * Get inline cursor row relative to render region start. -1 if unknown.\n * Used by useScrollback to position frozen items at the render region start.\n */\n getInlineCursorRow?: () => number\n /**\n * Promote frozen content to scrollback via the output phase.\n * Instead of writing directly to stdout (which causes flicker),\n * this passes the content to the output phase which writes frozen content\n * + live content in a single target.write() — no blanking, no cursor desync.\n */\n promoteScrollback?: (frozenContent: string, frozenLineCount: number) => void\n}\n\n/**\n * Context for stdout access.\n * Used by useStdout() hook.\n */\nexport const StdoutContext = createContext<StdoutContextValue | null>(null)\n\nexport interface StderrContextValue {\n /** Standard error stream */\n stderr: NodeJS.WriteStream\n /** Write to stderr */\n write: (data: string) => void\n}\n\n/**\n * Context for stderr access.\n * Used by useStderr() hook.\n */\nexport const StderrContext = createContext<StderrContextValue | null>(null)\n\n// ============================================================================\n// Runtime Context (typed bidirectional event bus — TEA)\n// ============================================================================\n\n/**\n * Base events every runtime provides.\n *\n * Retained for backwards compatibility (legacy `useRuntime<E>()` generic\n * slot). The canonical subscription surface is {@link ChainAppContextValue}\n * — input / paste / focus flow through the apply-chain plugin stores and\n * app-defined events ride on {@link ChainCustomEvents}.\n */\nexport interface BaseRuntimeEvents {\n /** Keyboard input: [parsedInput, keyMetadata] */\n input: [input: string, key: Key]\n /** Bracketed paste: [pastedText] */\n paste: [text: string]\n /** Terminal window focus change: [isFocused] */\n focus: [focused: boolean]\n}\n\n/**\n * Minimal runtime handle — the trimmed RuntimeContextValue exposes only\n * app-lifecycle controls (`exit`, and the opt-in pause / resume pair\n * used by console-mode suspension). Input / paste / focus subscriptions\n * live on {@link ChainAppContextValue}; custom view ↔ runtime events\n * live on {@link ChainCustomEvents} (`chain.events.emit / on`).\n *\n * The generic parameter is retained for source compatibility with\n * callers of `useRuntime<LinkEvents>()`; it has no effect on the type\n * surface below.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport interface RuntimeContextValue<_E extends BaseRuntimeEvents = BaseRuntimeEvents> {\n /** Exit the application with optional error. */\n exit: (error?: Error) => void\n /** Pause rendering output (used by console suspend). */\n pause?: () => void\n /** Resume rendering after a pause. Forces a full redraw. */\n resume?: () => void\n}\n\n/**\n * Context that provides the trimmed runtime handle.\n *\n * When non-null: interactive mode — `useExit()` works. Input / paste /\n * focus subscriptions use `ChainAppContext`.\n *\n * When null: static mode — `useExit()` throws. Hooks subscribe through\n * `ChainAppContext` when present, otherwise no-op.\n */\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null)\n\n// ============================================================================\n// Chain App Context (TEA Phase 2 — apply-chain plugin stores)\n// ============================================================================\n\n/**\n * Minimal key shape forwarded to chain handlers. Kept structural (not\n * imported from @silvery/ag/keys) so this context stays dependency-free;\n * consumers narrow to `Key` at the call site.\n */\nexport interface ChainKey {\n ctrl?: boolean\n shift?: boolean\n meta?: boolean\n super?: boolean\n hyper?: boolean\n alt?: boolean\n eventType?: \"press\" | \"repeat\" | \"release\" | undefined\n}\n\n/** Handler registered with the fallback useInput store. */\nexport type ChainInputHandler = (input: string, key: ChainKey) => void | \"exit\"\n\n/** Handler registered with the paste store. */\nexport type ChainPasteHandler = (text: string) => void\n\n/** Handler registered with the terminal focus store. */\nexport type ChainFocusHandler = (focused: boolean) => void\n\n/** Raw-key observer handler — fires for every input:key op (press/repeat/release/modifier-only). */\nexport type ChainRawKeyHandler = (input: string, key: ChainKey) => void\n\n/** Handler registered with the custom-events store — payload is app-defined. */\nexport type ChainCustomEventHandler = (...args: unknown[]) => void\n\n/** Input-fallback store slice exposed by withInputChain. */\nexport interface ChainInputStore {\n register(handler: ChainInputHandler, active?: boolean): () => void\n setActive(handler: ChainInputHandler, active: boolean): void\n}\n\n/** Raw-key observer slice — sees every key event before focus/useInput filters. */\nexport interface ChainRawKeyObserver {\n register(handler: ChainRawKeyHandler): () => void\n}\n\n/** Paste store slice exposed by withPasteChain. */\nexport interface ChainPasteStore {\n register(handler: ChainPasteHandler): () => void\n}\n\n/** Window focus slice exposed alongside the chain for createApp. */\nexport interface ChainFocusEvents {\n register(handler: ChainFocusHandler): () => void\n}\n\n/**\n * Custom-events slice — app-defined view ↔ runtime events (e.g.\n * `link:open` fired by `<Link>` and consumed by km-tui's\n * `useLinkOpen`). Channels are arbitrary strings chosen by the app;\n * payloads are untyped at the bus layer.\n */\nexport interface ChainCustomEvents {\n on(channel: string, handler: ChainCustomEventHandler): () => void\n emit(channel: string, ...args: unknown[]): void\n}\n\n/**\n * Context that exposes the apply-chain plugin stores.\n *\n * Provided by `createApp()` (and `run()` eventually) when an apply-chain\n * runtime is present. Hooks prefer this context; they fall back to\n * {@link RuntimeContext} when the chain is absent (e.g. children inside\n * an `InputBoundary`, which provides its own isolated RuntimeContext but\n * no chain).\n */\nexport interface ChainAppContextValue {\n readonly input: ChainInputStore\n readonly paste: ChainPasteStore\n readonly focusEvents: ChainFocusEvents\n /**\n * Raw-key observer — sees every `input:key` op (press/repeat/release/\n * modifier-only), unfiltered by focus or useInput. Used by hooks like\n * `useModifierKeys` that need to track sub-press state regardless of\n * whether a focused element consumed the key.\n */\n readonly rawKeys: ChainRawKeyObserver\n /**\n * Custom event bus — replaces the legacy `RuntimeContextValue.on /\n * emit` surface for app-defined channels. Consumers subscribe via\n * `events.on(\"channel\", handler)` and producers fire via\n * `events.emit(\"channel\", …payload)`.\n */\n readonly events: ChainCustomEvents\n}\n\nexport const ChainAppContext = createContext<ChainAppContextValue | null>(null)\n\n// ============================================================================\n// Cache Backend Context (mode-agnostic cache selection)\n// ============================================================================\n\n/**\n * Cache backend type — determines where ListView stores cached items.\n * - \"terminal\": Write to stdout as native scrollback (inline mode)\n * - \"virtual\": In-memory HistoryBuffer ring buffer (fullscreen + virtualInline)\n * - \"retain\": Cache items but keep them in the render tree (plain fullscreen\n * without virtual scrollback — the virtualizer handles windowing)\n */\nexport type CacheBackend = \"terminal\" | \"virtual\" | \"retain\"\n\n/**\n * Context that provides the cache backend to ListView.\n * Set by the runtime based on rendering mode:\n * - alternateScreen: false (inline) → \"terminal\"\n * - alternateScreen: true + virtualInline → \"virtual\"\n * - alternateScreen: true (plain fullscreen) → \"retain\"\n *\n * Default: \"virtual\" (safe fallback for test renderers — items unmount as expected)\n */\nexport const CacheBackendContext = createContext<CacheBackend>(\"virtual\")\n\n// ============================================================================\n// Focus Manager Context (tree-based focus system)\n// ============================================================================\n\n/**\n * Context for the tree-based focus manager.\n * Provides the FocusManager instance to useFocusable(), useFocusWithin(), and useFocusManager() hooks.\n */\nexport const FocusManagerContext = createContext<FocusManager | null>(null)\n\n// ============================================================================\n// Capability Registry Context\n// ============================================================================\n\n/**\n * Minimal capability lookup interface — matches CapabilityRegistry.get().\n * Defined here to avoid a dependency from ag-react → @silvery/create internals.\n */\nexport interface CapabilityLookup {\n get<T>(key: symbol): T | undefined\n}\n\n/**\n * Context for the capability registry (from @silvery/create composition).\n *\n * Provided by createApp() when a capabilityRegistry exists on the app object.\n * Hooks like useSelection() use this to discover interaction features\n * (e.g., SelectionFeature) without coupling to the composition layer.\n *\n * Returns null in simple `run()` or `render()` apps that don't use pipe() composition.\n */\nexport const CapabilityRegistryContext = createContext<CapabilityLookup | null>(null)\n"],"mappings":";;;;;;AAyBA,MAAa,cAAc,cAA2B,KAAK;;;;;;;;AAa3D,MAAa,cAAc,cAA6B,KAAK;;;;;AAyC7D,MAAa,gBAAgB,cAAyC,KAAK;;;;;AAa3E,MAAa,gBAAgB,cAAyC,KAAK;;;;;;;;;;AAqD3E,MAAa,iBAAiB,cAA0C,KAAK;AAiG7E,MAAa,kBAAkB,cAA2C,KAAK;;;;;;;;;;AAwB/E,MAAa,sBAAsB,cAA4B,UAAU;;;;;AAUzE,MAAa,sBAAsB,cAAmC,KAAK;;;;;;;;;;AAuB3E,MAAa,4BAA4B,cAAuC,KAAK"}
|
|
1
|
+
{"version":3,"file":"context-BU5LkkIy.mjs","names":[],"sources":["../packages/ag-react/src/context.ts"],"sourcesContent":["/**\n * Silvery React Contexts\n *\n * Provides contexts for:\n * - TermContext: Access to Term instance (for styling/detection)\n * - NodeContext: Access to the current SilveryNode (for useBoxRect)\n * - RuntimeContext: Unified input/app controls (replaces Events/Input/Stdin/App contexts)\n * - StdoutContext: Access to stdout\n * - StderrContext: Access to stderr\n */\n\nimport type { Term } from \"@silvery/ag-term/ansi\"\nimport { createContext } from \"react\"\nimport type { FocusManager } from \"@silvery/ag/focus-manager\"\nimport type { Key } from \"@silvery/ag/keys\"\nimport type { AgNode } from \"@silvery/ag/types\"\n\n// ============================================================================\n// Term Context\n// ============================================================================\n\n/**\n * Context that provides access to the Term instance.\n * Used by useTerm() hook to access terminal capabilities and styling.\n */\nexport const TermContext = createContext<Term | null>(null)\n\n// ============================================================================\n// Node Context\n// ============================================================================\n\n/**\n * Context that provides access to the current SilveryNode.\n * Used by useBoxRect() to subscribe to layout changes.\n *\n * Each Box component wraps its children in a NodeContext.Provider\n * with its corresponding SilveryNode.\n */\nexport const NodeContext = createContext<AgNode | null>(null)\n\n// ============================================================================\n// Stdio Context\n// ============================================================================\n\nexport interface StdoutContextValue {\n /** Standard output stream */\n stdout: NodeJS.WriteStream\n /** Write to stdout */\n write: (data: string) => void\n /**\n * Queue a typed terminal artifact to be flushed after the current rendered\n * frame. Prefer this over raw writeAfterFrame for protocol-owned render\n * artifacts such as terminal images.\n */\n queueFrameArtifact?: (artifact: TerminalFrameArtifact) => void\n /**\n * Queue bytes to be written immediately after the current rendered frame.\n * Escape-sequence components such as terminal images use this so their\n * placements land after the cell buffer paint instead of racing it during\n * React layout effects.\n */\n writeAfterFrame?: (data: string) => void\n /**\n * Notify the scheduler that lines were written to stdout externally.\n * Used by useScrollback to report lines written between renders so that\n * inline mode cursor positioning accounts for the displacement.\n */\n notifyScrollback?: (lines: number) => void\n /**\n * Reset inline cursor state in the output phase.\n * Used by useScrollback on resize to clear cursor tracking before\n * re-emitting frozen items at the new width.\n */\n resetInlineCursor?: () => void\n /**\n * Get inline cursor row relative to render region start. -1 if unknown.\n * Used by useScrollback to position frozen items at the render region start.\n */\n getInlineCursorRow?: () => number\n /**\n * Promote frozen content to scrollback via the output phase.\n * Instead of writing directly to stdout (which causes flicker),\n * this passes the content to the output phase which writes frozen content\n * + live content in a single target.write() — no blanking, no cursor desync.\n */\n promoteScrollback?: (frozenContent: string, frozenLineCount: number) => void\n}\n\n/**\n * Context for stdout access.\n * Used by useStdout() hook.\n */\nexport const StdoutContext = createContext<StdoutContextValue | null>(null)\n\nexport type TerminalFrameArtifact = {\n readonly kind: \"terminal-sequence\"\n readonly owner: string\n readonly sequence: string\n readonly zIndex?: number\n}\n\nexport interface StderrContextValue {\n /** Standard error stream */\n stderr: NodeJS.WriteStream\n /** Write to stderr */\n write: (data: string) => void\n}\n\n/**\n * Context for stderr access.\n * Used by useStderr() hook.\n */\nexport const StderrContext = createContext<StderrContextValue | null>(null)\n\n// ============================================================================\n// Runtime Context (typed bidirectional event bus — TEA)\n// ============================================================================\n\n/**\n * Base events every runtime provides.\n *\n * Retained for backwards compatibility (legacy `useRuntime<E>()` generic\n * slot). The canonical subscription surface is {@link ChainAppContextValue}\n * — input / paste / focus flow through the apply-chain plugin stores and\n * app-defined events ride on {@link ChainCustomEvents}.\n */\nexport interface BaseRuntimeEvents {\n /** Keyboard input: [parsedInput, keyMetadata] */\n input: [input: string, key: Key]\n /** Bracketed paste: [pastedText] */\n paste: [text: string]\n /** Terminal window focus change: [isFocused] */\n focus: [focused: boolean]\n}\n\n/**\n * Minimal runtime handle — the trimmed RuntimeContextValue exposes only\n * app-lifecycle controls (`exit`, and the opt-in pause / resume pair\n * used by console-mode suspension). Input / paste / focus subscriptions\n * live on {@link ChainAppContextValue}; custom view ↔ runtime events\n * live on {@link ChainCustomEvents} (`chain.events.emit / on`).\n *\n * The generic parameter is retained for source compatibility with\n * callers of `useRuntime<LinkEvents>()`; it has no effect on the type\n * surface below.\n */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport interface PanicOptions {\n /**\n * Prefix for the copyable terminal diagnostic.\n *\n * Example: `{ title: \"silvercode\" }` prints `silvercode: <message>`.\n */\n title?: string\n /** Additional context lines printed below the panic summary. */\n details?: string | ReadonlyArray<string>\n /** Process exit code to set. Default: 1. */\n exitCode?: number\n}\n\nexport type PanicHandler = (reason: unknown, options?: PanicOptions) => void\n\nexport interface RuntimeContextValue<_E extends BaseRuntimeEvents = BaseRuntimeEvents> {\n /** Exit the application with optional error. */\n exit: (error?: Error) => void\n /** Exit the application and print a copyable diagnostic after terminal cleanup. */\n panic: PanicHandler\n /** Pause rendering output (used by console suspend). */\n pause?: () => void\n /** Resume rendering after a pause. Forces a full redraw. */\n resume?: () => void\n}\n\n/**\n * Context that provides the trimmed runtime handle.\n *\n * When non-null: interactive mode — `useExit()` works. Input / paste /\n * focus subscriptions use `ChainAppContext`.\n *\n * When null: static mode — `useExit()` throws. Hooks subscribe through\n * `ChainAppContext` when present, otherwise no-op.\n */\nexport const RuntimeContext = createContext<RuntimeContextValue | null>(null)\n\n// ============================================================================\n// Chain App Context (TEA Phase 2 — apply-chain plugin stores)\n// ============================================================================\n\n/**\n * Minimal key shape forwarded to chain handlers. Kept structural (not\n * imported from @silvery/ag/keys) so this context stays dependency-free;\n * consumers narrow to `Key` at the call site.\n */\nexport interface ChainKey {\n ctrl?: boolean\n shift?: boolean\n meta?: boolean\n super?: boolean\n hyper?: boolean\n alt?: boolean\n eventType?: \"press\" | \"repeat\" | \"release\" | undefined\n}\n\n/** Handler registered with the fallback useInput store. */\nexport type ChainInputHandler = (input: string, key: ChainKey) => void | \"exit\"\n\n/** Handler registered with the paste store. */\nexport type ChainPasteHandler = (text: string) => void\n\n/** Handler registered with the terminal focus store. */\nexport type ChainFocusHandler = (focused: boolean) => void\n\n/** Raw-key observer handler — fires for every input:key op (press/repeat/release/modifier-only). */\nexport type ChainRawKeyHandler = (input: string, key: ChainKey) => void\n\n/** Handler registered with the custom-events store — payload is app-defined. */\nexport type ChainCustomEventHandler = (...args: unknown[]) => void\n\n/** Input-fallback store slice exposed by withInputChain. */\nexport interface ChainInputStore {\n register(handler: ChainInputHandler, active?: boolean): () => void\n setActive(handler: ChainInputHandler, active: boolean): void\n}\n\n/** Raw-key observer slice — sees every key event before focus/useInput filters. */\nexport interface ChainRawKeyObserver {\n register(handler: ChainRawKeyHandler): () => void\n}\n\n/** Paste store slice exposed by withPasteChain. */\nexport interface ChainPasteStore {\n register(handler: ChainPasteHandler): () => void\n}\n\n/** Window focus slice exposed alongside the chain for createApp. */\nexport interface ChainFocusEvents {\n register(handler: ChainFocusHandler): () => void\n}\n\n/**\n * Custom-events slice — app-defined view ↔ runtime events (e.g.\n * `link:open` fired by `<Link>` and consumed by km-tui's\n * `useLinkOpen`). Channels are arbitrary strings chosen by the app;\n * payloads are untyped at the bus layer.\n */\nexport interface ChainCustomEvents {\n on(channel: string, handler: ChainCustomEventHandler): () => void\n emit(channel: string, ...args: unknown[]): void\n}\n\n/**\n * Context that exposes the apply-chain plugin stores.\n *\n * Provided by `createApp()` (and `run()` eventually) when an apply-chain\n * runtime is present. Hooks prefer this context; they fall back to\n * {@link RuntimeContext} when the chain is absent (e.g. children inside\n * an `InputBoundary`, which provides its own isolated RuntimeContext but\n * no chain).\n */\nexport interface ChainAppContextValue {\n readonly input: ChainInputStore\n readonly paste: ChainPasteStore\n readonly focusEvents: ChainFocusEvents\n /**\n * Raw-key observer — sees every `input:key` op (press/repeat/release/\n * modifier-only), unfiltered by focus or useInput. Used by hooks like\n * `useModifierKeys` that need to track sub-press state regardless of\n * whether a focused element consumed the key.\n */\n readonly rawKeys: ChainRawKeyObserver\n /**\n * Custom event bus — replaces the legacy `RuntimeContextValue.on /\n * emit` surface for app-defined channels. Consumers subscribe via\n * `events.on(\"channel\", handler)` and producers fire via\n * `events.emit(\"channel\", …payload)`.\n */\n readonly events: ChainCustomEvents\n}\n\nexport const ChainAppContext = createContext<ChainAppContextValue | null>(null)\n\n// ============================================================================\n// Cache Backend Context (mode-agnostic cache selection)\n// ============================================================================\n\n/**\n * Cache backend type — determines where ListView stores cached items.\n * - \"terminal\": Write to stdout as native scrollback (inline mode)\n * - \"virtual\": In-memory HistoryBuffer ring buffer (fullscreen + virtualInline)\n * - \"retain\": Cache items but keep them in the render tree (plain fullscreen\n * without virtual scrollback — the virtualizer handles windowing)\n */\nexport type CacheBackend = \"terminal\" | \"virtual\" | \"retain\"\n\n/**\n * Context that provides the cache backend to ListView.\n * Set by the runtime based on rendering mode:\n * - alternateScreen: false (inline) → \"terminal\"\n * - alternateScreen: true + virtualInline → \"virtual\"\n * - alternateScreen: true (plain fullscreen) → \"retain\"\n *\n * Default: \"virtual\" (safe fallback for test renderers — items unmount as expected)\n */\nexport const CacheBackendContext = createContext<CacheBackend>(\"virtual\")\n\n// ============================================================================\n// Focus Manager Context (tree-based focus system)\n// ============================================================================\n\n/**\n * Context for the tree-based focus manager.\n * Provides the FocusManager instance to useFocusable(), useFocusWithin(), and useFocusManager() hooks.\n */\nexport const FocusManagerContext = createContext<FocusManager | null>(null)\n\n// ============================================================================\n// Capability Registry Context\n// ============================================================================\n\n/**\n * Minimal capability lookup interface — matches CapabilityRegistry.get().\n * Defined here to avoid a dependency from ag-react → @silvery/create internals.\n */\nexport interface CapabilityLookup {\n get<T>(key: symbol): T | undefined\n}\n\n/**\n * Context for the capability registry (from @silvery/create composition).\n *\n * Provided by createApp() when a capabilityRegistry exists on the app object.\n * Hooks like useSelection() use this to discover interaction features\n * (e.g., SelectionFeature) without coupling to the composition layer.\n *\n * Returns null in simple `run()` or `render()` apps that don't use pipe() composition.\n */\nexport const CapabilityRegistryContext = createContext<CapabilityLookup | null>(null)\n"],"mappings":";;;;;;AAyBA,MAAa,cAAc,cAA2B,KAAK;;;;;;;;AAa3D,MAAa,cAAc,cAA6B,KAAK;;;;;AAsD7D,MAAa,gBAAgB,cAAyC,KAAK;;;;;AAoB3E,MAAa,gBAAgB,cAAyC,KAAK;;;;;;;;;;AAsE3E,MAAa,iBAAiB,cAA0C,KAAK;AAiG7E,MAAa,kBAAkB,cAA2C,KAAK;;;;;;;;;;AAwB/E,MAAa,sBAAsB,cAA4B,UAAU;;;;;AAUzE,MAAa,sBAAsB,cAAmC,KAAK;;;;;;;;;;AAuB3E,MAAa,4BAA4B,cAAuC,KAAK"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as __toESM } from "./chunk-BSw8zbkd.mjs";
|
|
2
|
+
import { i as reconciler } from "./reconciler-DldIJB93.mjs";
|
|
2
3
|
import { createLogger } from "loggily";
|
|
3
4
|
//#region packages/ag-term/src/devtools.ts
|
|
4
5
|
/**
|
|
@@ -39,7 +40,7 @@ async function connectDevTools() {
|
|
|
39
40
|
if (connected) return true;
|
|
40
41
|
try {
|
|
41
42
|
if (typeof globalThis.WebSocket === "undefined") try {
|
|
42
|
-
const ws = await import("
|
|
43
|
+
const ws = await import("./wrapper-C70ATkVv.mjs");
|
|
43
44
|
globalThis.WebSocket = ws.default ?? ws;
|
|
44
45
|
} catch {
|
|
45
46
|
log.warn?.("WebSocket polyfill (ws) not available. Install ws for DevTools support: bun add -d ws");
|
|
@@ -56,7 +57,7 @@ async function connectDevTools() {
|
|
|
56
57
|
isEnabled: true,
|
|
57
58
|
isValid: true
|
|
58
59
|
}];
|
|
59
|
-
const devtools = await import("
|
|
60
|
+
const devtools = await import("./backend-B-WYLUib.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
|
|
60
61
|
devtools.initialize();
|
|
61
62
|
devtools.connectToDevTools();
|
|
62
63
|
reconciler.injectIntoDevTools();
|
|
@@ -85,4 +86,4 @@ async function autoConnectDevTools() {
|
|
|
85
86
|
//#endregion
|
|
86
87
|
export { connectDevTools as n, isDevToolsConnected as r, autoConnectDevTools as t };
|
|
87
88
|
|
|
88
|
-
//# sourceMappingURL=devtools-
|
|
89
|
+
//# sourceMappingURL=devtools-DcQjgyjL.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"devtools-
|
|
1
|
+
{"version":3,"file":"devtools-DcQjgyjL.mjs","names":[],"sources":["../packages/ag-term/src/devtools.ts"],"sourcesContent":["/**\n * React DevTools integration for silvery.\n *\n * Provides optional connection to React DevTools standalone app for\n * debugging TUI component trees. Requires `react-devtools-core` to be\n * installed (optional peer dependency).\n *\n * Usage:\n * 1. Install: `bun add -d react-devtools-core`\n * 2. Run devtools: `npx react-devtools`\n * 3. Launch app with: `DEBUG_DEVTOOLS=1 bun run app.ts`\n *\n * Or call `connectDevTools()` manually from your app code.\n *\n * @module\n */\n\nimport { reconciler } from \"@silvery/ag-react/reconciler\"\nimport { createLogger } from \"loggily\"\n\nconst log = createLogger(\"silvery:devtools\")\n\nlet connected = false\n\n/**\n * Connect to React DevTools standalone app.\n *\n * This lazy-loads `react-devtools-core` so it has zero impact on\n * production bundles. The connection is established via WebSocket\n * to the devtools electron app (default: ws://localhost:8097).\n *\n * Safe to call multiple times -- subsequent calls are no-ops.\n *\n * @example\n * ```ts\n * import { connectDevTools } from '@silvery/ag-react';\n * await connectDevTools();\n * // Now open React DevTools standalone to inspect the component tree\n * ```\n */\nexport async function connectDevTools(): Promise<boolean> {\n if (connected) return true\n\n try {\n // Polyfill WebSocket for Node.js environments (required by react-devtools-core)\n if (typeof globalThis.WebSocket === \"undefined\") {\n try {\n // @ts-expect-error -- ws is an optional peer dependency\n const ws = await import(\"ws\")\n globalThis.WebSocket = ws.default ?? ws\n } catch {\n // ws not available -- devtools won't be able to connect\n log.warn?.(\n \"WebSocket polyfill (ws) not available. \" +\n \"Install ws for DevTools support: bun add -d ws\",\n )\n return false\n }\n }\n\n // Ensure window/self exist for react-devtools-core internals\n if (typeof globalThis.window === \"undefined\") {\n // @ts-expect-error -- polyfill for devtools\n globalThis.window = globalThis\n }\n\n // Configure component filters to hide silvery internals from the DevTools tree.\n // Filter types from react-devtools-shared/src/types.js:\n // 1 = ComponentFilterElementType, value 7 = HostComponent\n // 2 = ComponentFilterDisplayName (regex on displayName)\n if (!globalThis.__REACT_DEVTOOLS_COMPONENT_FILTERS__) {\n globalThis.__REACT_DEVTOOLS_COMPONENT_FILTERS__ = [\n { type: 1, value: 7, isEnabled: true },\n { type: 2, value: \"SilveryApp\", isEnabled: true, isValid: true },\n ]\n }\n\n // @ts-expect-error -- react-devtools-core has no type declarations\n const devtools = await import(\"react-devtools-core\")\n devtools.initialize()\n devtools.connectToDevTools()\n\n // Inject renderer info so DevTools can identify silvery.\n // rendererPackageName and rendererVersion are read from the host config\n // passed to Reconciler() -- see reconciler/host-config.ts.\n reconciler.injectIntoDevTools()\n\n connected = true\n return true\n } catch (error: unknown) {\n const message = error instanceof Error ? error.message : String(error)\n log.warn?.(\n `Failed to connect to React DevTools. ` +\n `Install react-devtools-core: bun add -d react-devtools-core\\n` +\n ` Error: ${message}`,\n )\n return false\n }\n}\n\n/**\n * Check if DevTools are currently connected.\n */\nexport function isDevToolsConnected(): boolean {\n return connected\n}\n\n/**\n * Auto-connect to DevTools if DEBUG_DEVTOOLS=1 environment variable is set.\n * Called internally during render initialization.\n */\nexport async function autoConnectDevTools(): Promise<void> {\n if (process.env.DEBUG_DEVTOOLS === \"1\" || process.env.DEBUG_DEVTOOLS === \"true\") {\n await connectDevTools()\n }\n}\n\n// Global type augmentation for devtools polyfills\ndeclare global {\n var __REACT_DEVTOOLS_COMPONENT_FILTERS__: Array<{\n type: number\n value: number | string\n isEnabled: boolean\n isValid?: boolean\n }>\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,MAAM,MAAM,aAAa,mBAAmB;AAE5C,IAAI,YAAY;;;;;;;;;;;;;;;;;AAkBhB,eAAsB,kBAAoC;AACxD,KAAI,UAAW,QAAO;AAEtB,KAAI;AAEF,MAAI,OAAO,WAAW,cAAc,YAClC,KAAI;GAEF,MAAM,KAAK,MAAM,OAAO;AACxB,cAAW,YAAY,GAAG,WAAW;UAC/B;AAEN,OAAI,OACF,wFAED;AACD,UAAO;;AAKX,MAAI,OAAO,WAAW,WAAW,YAE/B,YAAW,SAAS;AAOtB,MAAI,CAAC,WAAW,qCACd,YAAW,uCAAuC,CAChD;GAAE,MAAM;GAAG,OAAO;GAAG,WAAW;GAAM,EACtC;GAAE,MAAM;GAAG,OAAO;GAAc,WAAW;GAAM,SAAS;GAAM,CACjE;EAIH,MAAM,WAAW,MAAM,OAAO,0BAAA,MAAA,MAAA,wBAAA,EAAA,SAAA,EAAA,CAAA;AAC9B,WAAS,YAAY;AACrB,WAAS,mBAAmB;AAK5B,aAAW,oBAAoB;AAE/B,cAAY;AACZ,SAAO;UACA,OAAgB;EACvB,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,MAAI,OACF;WAEc,UACf;AACD,SAAO;;;;;;AAOX,SAAgB,sBAA+B;AAC7C,QAAO;;;;;;AAOT,eAAsB,sBAAqC;AACzD,KAAI,QAAQ,IAAI,mBAAmB,OAAO,QAAQ,IAAI,mBAAmB,OACvE,OAAM,iBAAiB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region packages/ag-react/src/ui/animation/easing.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Easing Functions
|
|
4
|
+
*
|
|
5
|
+
* Maps time progress (0-1) to value progress (0-1) for smooth animations.
|
|
6
|
+
* Includes common presets and a resolver for name-or-function usage.
|
|
7
|
+
*/
|
|
8
|
+
/** Easing function: maps time progress (0-1) to value progress (0-1) */
|
|
9
|
+
type EasingFn = (t: number) => number;
|
|
10
|
+
declare const easings: {
|
|
11
|
+
readonly linear: (t: number) => number;
|
|
12
|
+
readonly ease: (t: number) => number;
|
|
13
|
+
readonly easeIn: (t: number) => number;
|
|
14
|
+
readonly easeOut: (t: number) => number;
|
|
15
|
+
readonly easeInOut: (t: number) => number;
|
|
16
|
+
readonly easeInCubic: (t: number) => number;
|
|
17
|
+
readonly easeOutCubic: (t: number) => number;
|
|
18
|
+
};
|
|
19
|
+
type EasingName = keyof typeof easings;
|
|
20
|
+
/** Resolve an easing — accepts a name string or a custom function. */
|
|
21
|
+
declare function resolveEasing(easing: EasingName | EasingFn): EasingFn;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { resolveEasing as i, EasingName as n, easings as r, EasingFn as t };
|
|
24
|
+
//# sourceMappingURL=easing-BI-ASGMO.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"easing-BI-ASGMO.d.mts","names":[],"sources":["../packages/ag-react/src/ui/animation/easing.ts"],"mappings":";;AAYA;;;;;AAMA;AAAA,KANY,QAAA,IAAY,CAAA;AAAA,cAMX,OAAA;EAAA;;;;;;;;KAUD,UAAA,gBAA0B,OAAA;;iBAOtB,aAAA,CAAc,MAAA,EAAQ,UAAA,GAAa,QAAA,GAAW,QAAA"}
|
|
@@ -107,4 +107,4 @@ function createETATracker(bufferSize = 10) {
|
|
|
107
107
|
//#endregion
|
|
108
108
|
export { getETA as a, formatETA as i, calculateETA as n, createETATracker as r, DEFAULT_ETA_BUFFER_SIZE as t };
|
|
109
109
|
|
|
110
|
-
//# sourceMappingURL=eta-
|
|
110
|
+
//# sourceMappingURL=eta-CJlGH06n.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eta-
|
|
1
|
+
{"version":3,"file":"eta-CJlGH06n.mjs","names":[],"sources":["../packages/ag-react/src/ui/utils/eta.ts"],"sourcesContent":["/**\n * Shared ETA calculation utilities\n */\n\n/** Sample point for ETA calculation */\nexport interface ETASample {\n time: number\n value: number\n}\n\n/** ETA calculation result */\nexport interface ETAResult {\n /** Estimated seconds remaining, or null if insufficient data */\n seconds: number | null\n /** Formatted ETA string (e.g., \"1:30\", \"2:15:30\", \"--:--\") */\n formatted: string\n}\n\n/**\n * Calculate ETA from a buffer of samples\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns ETA in seconds (null if insufficient data)\n *\n * @example\n * ```ts\n * const buffer = [\n * { time: 1000, value: 0 },\n * { time: 2000, value: 10 },\n * ];\n * const eta = calculateETA(buffer, 10, 100);\n * // eta = 9 (9 seconds remaining at 10 items/sec)\n * ```\n */\nexport function calculateETA(buffer: ETASample[], current: number, total: number): number | null {\n if (buffer.length < 2) {\n return null\n }\n\n const first = buffer[0]!\n const last = buffer[buffer.length - 1]!\n\n const elapsed = (last.time - first.time) / 1000 // seconds\n const progress = last.value - first.value\n\n if (elapsed <= 0 || progress <= 0) {\n return null\n }\n\n const rate = progress / elapsed // items per second\n const remaining = total - current\n\n return remaining / rate\n}\n\n/**\n * Format ETA seconds as human-readable string\n *\n * @param eta - ETA in seconds (null for unknown)\n * @returns Formatted string (e.g., \"1:30\", \"2:15:30\", \"--:--\", \">1d\")\n *\n * @example\n * ```ts\n * formatETA(90) // \"1:30\"\n * formatETA(3665) // \"1:01:05\"\n * formatETA(null) // \"--:--\"\n * formatETA(100000) // \">1d\"\n * ```\n */\nexport function formatETA(eta: number | null): string {\n if (eta === null || !isFinite(eta)) {\n return \"--:--\"\n }\n\n if (eta > 86400) {\n // > 24 hours\n return \">1d\"\n }\n\n const hours = Math.floor(eta / 3600)\n const minutes = Math.floor((eta % 3600) / 60)\n const seconds = Math.floor(eta % 60)\n\n if (hours > 0) {\n return `${hours}:${minutes.toString().padStart(2, \"0\")}:${seconds.toString().padStart(2, \"0\")}`\n }\n\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`\n}\n\n/**\n * Calculate and format ETA in one call\n *\n * @param buffer - Array of {time, value} samples\n * @param current - Current progress value\n * @param total - Total target value\n * @returns Object with seconds (number|null) and formatted string\n */\nexport function getETA(buffer: ETASample[], current: number, total: number): ETAResult {\n const seconds = calculateETA(buffer, current, total)\n return {\n seconds,\n formatted: formatETA(seconds),\n }\n}\n\n/** Default buffer size for ETA smoothing */\nexport const DEFAULT_ETA_BUFFER_SIZE = 10\n\n/**\n * Create an ETA tracker with automatic buffer management\n *\n * @param bufferSize - Number of samples to keep (default: 10)\n * @returns ETA tracker object\n *\n * @example\n * ```ts\n * const tracker = createETATracker();\n * tracker.record(0);\n * // ... later ...\n * tracker.record(50);\n * const eta = tracker.getETA(50, 100);\n * console.log(eta.formatted); // \"0:30\"\n * ```\n */\nexport function createETATracker(bufferSize = DEFAULT_ETA_BUFFER_SIZE) {\n const buffer: ETASample[] = []\n\n return {\n /** Record a new sample */\n record(value: number): void {\n buffer.push({ time: Date.now(), value })\n if (buffer.length > bufferSize) {\n buffer.shift()\n }\n },\n\n /** Get current ETA */\n getETA(current: number, total: number): ETAResult {\n return getETA(buffer, current, total)\n },\n\n /** Reset the buffer */\n reset(): void {\n buffer.length = 0\n },\n\n /** Get buffer for external use */\n getBuffer(): readonly ETASample[] {\n return buffer\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoCA,SAAgB,aAAa,QAAqB,SAAiB,OAA8B;AAC/F,KAAI,OAAO,SAAS,EAClB,QAAO;CAGT,MAAM,QAAQ,OAAO;CACrB,MAAM,OAAO,OAAO,OAAO,SAAS;CAEpC,MAAM,WAAW,KAAK,OAAO,MAAM,QAAQ;CAC3C,MAAM,WAAW,KAAK,QAAQ,MAAM;AAEpC,KAAI,WAAW,KAAK,YAAY,EAC9B,QAAO;CAGT,MAAM,OAAO,WAAW;AAGxB,SAFkB,QAAQ,WAEP;;;;;;;;;;;;;;;;AAiBrB,SAAgB,UAAU,KAA4B;AACpD,KAAI,QAAQ,QAAQ,CAAC,SAAS,IAAI,CAChC,QAAO;AAGT,KAAI,MAAM,MAER,QAAO;CAGT,MAAM,QAAQ,KAAK,MAAM,MAAM,KAAK;CACpC,MAAM,UAAU,KAAK,MAAO,MAAM,OAAQ,GAAG;CAC7C,MAAM,UAAU,KAAK,MAAM,MAAM,GAAG;AAEpC,KAAI,QAAQ,EACV,QAAO,GAAG,MAAM,GAAG,QAAQ,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,QAAQ,UAAU,CAAC,SAAS,GAAG,IAAI;AAG/F,QAAO,GAAG,QAAQ,GAAG,QAAQ,UAAU,CAAC,SAAS,GAAG,IAAI;;;;;;;;;;AAW1D,SAAgB,OAAO,QAAqB,SAAiB,OAA0B;CACrF,MAAM,UAAU,aAAa,QAAQ,SAAS,MAAM;AACpD,QAAO;EACL;EACA,WAAW,UAAU,QAAQ;EAC9B;;;AAIH,MAAa,0BAA0B;;;;;;;;;;;;;;;;;AAkBvC,SAAgB,iBAAiB,aAAA,IAAsC;CACrE,MAAM,SAAsB,EAAE;AAE9B,QAAO;EAEL,OAAO,OAAqB;AAC1B,UAAO,KAAK;IAAE,MAAM,KAAK,KAAK;IAAE;IAAO,CAAC;AACxC,OAAI,OAAO,SAAS,WAClB,QAAO,OAAO;;EAKlB,OAAO,SAAiB,OAA0B;AAChD,UAAO,OAAO,QAAQ,SAAS,MAAM;;EAIvC,QAAc;AACZ,UAAO,SAAS;;EAIlB,YAAkC;AAChC,UAAO;;EAEV"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { ALIGN_AUTO, ALIGN_BASELINE, ALIGN_CENTER, ALIGN_FLEX_END, ALIGN_FLEX_START, ALIGN_SPACE_AROUND, ALIGN_SPACE_BETWEEN, ALIGN_SPACE_EVENLY, ALIGN_STRETCH, DIRECTION_LTR, DISPLAY_FLEX, DISPLAY_NONE, EDGE_ALL, EDGE_BOTTOM, EDGE_HORIZONTAL, EDGE_LEFT, EDGE_RIGHT, EDGE_TOP, EDGE_VERTICAL, FLEX_DIRECTION_COLUMN, FLEX_DIRECTION_COLUMN_REVERSE, FLEX_DIRECTION_ROW, FLEX_DIRECTION_ROW_REVERSE, GUTTER_ALL, GUTTER_COLUMN, GUTTER_ROW, JUSTIFY_CENTER, JUSTIFY_FLEX_END, JUSTIFY_FLEX_START, JUSTIFY_SPACE_AROUND, JUSTIFY_SPACE_BETWEEN, JUSTIFY_SPACE_EVENLY, MEASURE_MODE_AT_MOST, MEASURE_MODE_EXACTLY, MEASURE_MODE_MIN_CONTENT, MEASURE_MODE_UNDEFINED, Node, OVERFLOW_HIDDEN, OVERFLOW_SCROLL, OVERFLOW_VISIBLE, POSITION_TYPE_ABSOLUTE, POSITION_TYPE_RELATIVE, POSITION_TYPE_STATIC, WRAP_NO_WRAP, WRAP_WRAP, WRAP_WRAP_REVERSE } from "flexily";
|
|
2
|
+
//#region packages/ag-term/src/adapters/flexily-zero-adapter.ts
|
|
3
|
+
/**
|
|
4
|
+
* Flexily Layout Engine Adapter
|
|
5
|
+
*
|
|
6
|
+
* Wraps Flexily to implement the LayoutEngine interface.
|
|
7
|
+
* Uses the default zero-allocation algorithm from flexily.
|
|
8
|
+
*/
|
|
9
|
+
const UNIT_CQI = 6;
|
|
10
|
+
const UNIT_CQMIN = 7;
|
|
11
|
+
/**
|
|
12
|
+
* Capabilities advertised by the flexily-zero adapter.
|
|
13
|
+
*
|
|
14
|
+
* All six A0 primitives shipped: four at A0.1 (engine-native CQ resolution at
|
|
15
|
+
* vendor/flexily `46141e9`), one at A0.2 (fitWidth single-pass lane snap at
|
|
16
|
+
* vendor/flexily `4b44bf9`), one at A0.3 (CSS math functions at vendor/flexily
|
|
17
|
+
* `0aebb95`).
|
|
18
|
+
*
|
|
19
|
+
* - `containerQueries` → A0.1 ✓ — Pass 1 freezeQuerySize + Pass 2 ancestor walk
|
|
20
|
+
* - `containSize` → A0.1 ✓ — Phase 9 inline-axis shrink-wrap gate
|
|
21
|
+
* - `containerQueryUnits` → A0.1 ✓ — UNIT_CQI / UNIT_CQMIN parsed and resolved
|
|
22
|
+
* - `childStyleMutation` → A0.1 ✓ — setContainerQueryStyle hook (A0.0 substrate)
|
|
23
|
+
* - `fitWidth` → A0.2 ✓ — Phase 3 lane-select prefix; single-pass lane snap
|
|
24
|
+
* - `styleMathFunctions` → A0.3 ✓ — UNIT_CALC + MathExpr; late-bound per
|
|
25
|
+
* vendor/flexily/docs/two-phase-layout.md
|
|
26
|
+
*/
|
|
27
|
+
const FLEXILY_CAPABILITIES = Object.freeze({
|
|
28
|
+
containerQueries: true,
|
|
29
|
+
containSize: true,
|
|
30
|
+
containerQueryUnits: true,
|
|
31
|
+
fitWidth: true,
|
|
32
|
+
styleMathFunctions: true,
|
|
33
|
+
childStyleMutation: true
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* Wraps a Flexily zero-alloc node to implement LayoutNode interface.
|
|
37
|
+
* Since Flexily already has a Yoga-compatible API, this is mostly delegation.
|
|
38
|
+
*/
|
|
39
|
+
var FlexilyZeroNodeAdapter = class {
|
|
40
|
+
node;
|
|
41
|
+
constructor(node) {
|
|
42
|
+
this.node = node;
|
|
43
|
+
}
|
|
44
|
+
/** Get the underlying Flexily node (for tree operations) */
|
|
45
|
+
getFlexilyNode() {
|
|
46
|
+
return this.node;
|
|
47
|
+
}
|
|
48
|
+
insertChild(child, index) {
|
|
49
|
+
const flexilyChild = child.getFlexilyNode();
|
|
50
|
+
this.node.insertChild(flexilyChild, index);
|
|
51
|
+
}
|
|
52
|
+
removeChild(child) {
|
|
53
|
+
const flexilyChild = child.getFlexilyNode();
|
|
54
|
+
this.node.removeChild(flexilyChild);
|
|
55
|
+
}
|
|
56
|
+
free() {
|
|
57
|
+
this.node.free();
|
|
58
|
+
}
|
|
59
|
+
setMeasureFunc(measureFunc) {
|
|
60
|
+
this.node.setMeasureFunc((width, widthMode, height, heightMode) => {
|
|
61
|
+
return measureFunc(width, this.measureModeToString(widthMode), height, this.measureModeToString(heightMode));
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
markDirty() {
|
|
65
|
+
this.node.markDirty();
|
|
66
|
+
}
|
|
67
|
+
isDirty() {
|
|
68
|
+
return this.node.isDirty();
|
|
69
|
+
}
|
|
70
|
+
measureModeToString(mode) {
|
|
71
|
+
if (mode === MEASURE_MODE_EXACTLY) return "exactly";
|
|
72
|
+
if (mode === MEASURE_MODE_AT_MOST) return "at-most";
|
|
73
|
+
if (mode === MEASURE_MODE_MIN_CONTENT) return "min-content";
|
|
74
|
+
return "undefined";
|
|
75
|
+
}
|
|
76
|
+
setWidth(value) {
|
|
77
|
+
this.node.setWidth(value);
|
|
78
|
+
}
|
|
79
|
+
setWidthPercent(value) {
|
|
80
|
+
this.node.setWidthPercent(value);
|
|
81
|
+
}
|
|
82
|
+
setWidthAuto() {
|
|
83
|
+
this.node.setWidthAuto();
|
|
84
|
+
}
|
|
85
|
+
setWidthFitContent() {
|
|
86
|
+
this.node.setWidthFitContent();
|
|
87
|
+
}
|
|
88
|
+
setWidthSnugContent() {
|
|
89
|
+
this.node.setWidthSnugContent();
|
|
90
|
+
}
|
|
91
|
+
setHeight(value) {
|
|
92
|
+
this.node.setHeight(value);
|
|
93
|
+
}
|
|
94
|
+
setHeightPercent(value) {
|
|
95
|
+
this.node.setHeightPercent(value);
|
|
96
|
+
}
|
|
97
|
+
setHeightAuto() {
|
|
98
|
+
this.node.setHeightAuto();
|
|
99
|
+
}
|
|
100
|
+
setMinWidth(value) {
|
|
101
|
+
this.node.setMinWidth(value);
|
|
102
|
+
}
|
|
103
|
+
setMinWidthPercent(value) {
|
|
104
|
+
this.node.setMinWidthPercent(value);
|
|
105
|
+
}
|
|
106
|
+
setMinHeight(value) {
|
|
107
|
+
this.node.setMinHeight(value);
|
|
108
|
+
}
|
|
109
|
+
setMinHeightPercent(value) {
|
|
110
|
+
this.node.setMinHeightPercent(value);
|
|
111
|
+
}
|
|
112
|
+
setMaxWidth(value) {
|
|
113
|
+
this.node.setMaxWidth(value);
|
|
114
|
+
}
|
|
115
|
+
setMaxWidthPercent(value) {
|
|
116
|
+
this.node.setMaxWidthPercent(value);
|
|
117
|
+
}
|
|
118
|
+
setMaxHeight(value) {
|
|
119
|
+
this.node.setMaxHeight(value);
|
|
120
|
+
}
|
|
121
|
+
setMaxHeightPercent(value) {
|
|
122
|
+
this.node.setMaxHeightPercent(value);
|
|
123
|
+
}
|
|
124
|
+
setFlexGrow(value) {
|
|
125
|
+
this.node.setFlexGrow(value);
|
|
126
|
+
}
|
|
127
|
+
setFlexShrink(value) {
|
|
128
|
+
this.node.setFlexShrink(value);
|
|
129
|
+
}
|
|
130
|
+
setFlexBasis(value) {
|
|
131
|
+
this.node.setFlexBasis(value);
|
|
132
|
+
}
|
|
133
|
+
setFlexBasisPercent(value) {
|
|
134
|
+
this.node.setFlexBasisPercent(value);
|
|
135
|
+
}
|
|
136
|
+
setFlexBasisAuto() {
|
|
137
|
+
this.node.setFlexBasisAuto();
|
|
138
|
+
}
|
|
139
|
+
setFlexDirection(direction) {
|
|
140
|
+
this.node.setFlexDirection(direction);
|
|
141
|
+
}
|
|
142
|
+
setFlexWrap(wrap) {
|
|
143
|
+
this.node.setFlexWrap(wrap);
|
|
144
|
+
}
|
|
145
|
+
setAlignItems(align) {
|
|
146
|
+
this.node.setAlignItems(align);
|
|
147
|
+
}
|
|
148
|
+
setAlignSelf(align) {
|
|
149
|
+
this.node.setAlignSelf(align);
|
|
150
|
+
}
|
|
151
|
+
setAlignContent(align) {
|
|
152
|
+
this.node.setAlignContent(align);
|
|
153
|
+
}
|
|
154
|
+
setJustifyContent(justify) {
|
|
155
|
+
this.node.setJustifyContent(justify);
|
|
156
|
+
}
|
|
157
|
+
setPadding(edge, value) {
|
|
158
|
+
this.node.setPadding(edge, value);
|
|
159
|
+
}
|
|
160
|
+
setMargin(edge, value) {
|
|
161
|
+
this.node.setMargin(edge, value);
|
|
162
|
+
}
|
|
163
|
+
setBorder(edge, value) {
|
|
164
|
+
this.node.setBorder(edge, value);
|
|
165
|
+
}
|
|
166
|
+
setGap(gutter, value) {
|
|
167
|
+
this.node.setGap(gutter, value);
|
|
168
|
+
}
|
|
169
|
+
setDisplay(display) {
|
|
170
|
+
this.node.setDisplay(display);
|
|
171
|
+
}
|
|
172
|
+
setPositionType(positionType) {
|
|
173
|
+
this.node.setPositionType(positionType);
|
|
174
|
+
}
|
|
175
|
+
setPosition(edge, value) {
|
|
176
|
+
this.node.setPosition(edge, value);
|
|
177
|
+
}
|
|
178
|
+
setPositionPercent(edge, value) {
|
|
179
|
+
this.node.setPositionPercent(edge, value);
|
|
180
|
+
}
|
|
181
|
+
setOverflow(overflow) {
|
|
182
|
+
this.node.setOverflow(overflow);
|
|
183
|
+
}
|
|
184
|
+
setContainerType(containerType) {
|
|
185
|
+
this.node.setContainerType(containerType);
|
|
186
|
+
}
|
|
187
|
+
setContainSize(value) {
|
|
188
|
+
this.node.setContainSize(value);
|
|
189
|
+
}
|
|
190
|
+
setFitWidth(lanes) {
|
|
191
|
+
if (lanes === void 0 || lanes.length === 0) {
|
|
192
|
+
this.node.setFitWidth(void 0);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this.node.setFitWidth(lanes.map((entry) => {
|
|
196
|
+
if (typeof entry === "number") return entry;
|
|
197
|
+
const unit = entry.unit === "cqi" ? UNIT_CQI : UNIT_CQMIN;
|
|
198
|
+
return {
|
|
199
|
+
value: entry.value,
|
|
200
|
+
unit
|
|
201
|
+
};
|
|
202
|
+
}));
|
|
203
|
+
}
|
|
204
|
+
setAspectRatio(value) {
|
|
205
|
+
this.node.setAspectRatio(value);
|
|
206
|
+
}
|
|
207
|
+
calculateLayout(width, height, direction) {
|
|
208
|
+
this.node.calculateLayout(width, height, direction ?? DIRECTION_LTR);
|
|
209
|
+
}
|
|
210
|
+
getComputedLeft() {
|
|
211
|
+
return this.node.getComputedLeft();
|
|
212
|
+
}
|
|
213
|
+
getComputedTop() {
|
|
214
|
+
return this.node.getComputedTop();
|
|
215
|
+
}
|
|
216
|
+
getComputedWidth() {
|
|
217
|
+
return this.node.getComputedWidth();
|
|
218
|
+
}
|
|
219
|
+
getComputedHeight() {
|
|
220
|
+
return this.node.getComputedHeight();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Layout engine implementation using Flexily zero-allocation variant.
|
|
225
|
+
* Optimized for high-frequency layout with reduced GC pressure.
|
|
226
|
+
*/
|
|
227
|
+
var FlexilyZeroLayoutEngine = class {
|
|
228
|
+
_constants = {
|
|
229
|
+
FLEX_DIRECTION_COLUMN,
|
|
230
|
+
FLEX_DIRECTION_COLUMN_REVERSE,
|
|
231
|
+
FLEX_DIRECTION_ROW,
|
|
232
|
+
FLEX_DIRECTION_ROW_REVERSE,
|
|
233
|
+
WRAP_NO_WRAP,
|
|
234
|
+
WRAP_WRAP,
|
|
235
|
+
WRAP_WRAP_REVERSE,
|
|
236
|
+
ALIGN_AUTO,
|
|
237
|
+
ALIGN_FLEX_START,
|
|
238
|
+
ALIGN_CENTER,
|
|
239
|
+
ALIGN_FLEX_END,
|
|
240
|
+
ALIGN_STRETCH,
|
|
241
|
+
ALIGN_BASELINE,
|
|
242
|
+
ALIGN_SPACE_BETWEEN,
|
|
243
|
+
ALIGN_SPACE_AROUND,
|
|
244
|
+
ALIGN_SPACE_EVENLY,
|
|
245
|
+
JUSTIFY_FLEX_START,
|
|
246
|
+
JUSTIFY_CENTER,
|
|
247
|
+
JUSTIFY_FLEX_END,
|
|
248
|
+
JUSTIFY_SPACE_BETWEEN,
|
|
249
|
+
JUSTIFY_SPACE_AROUND,
|
|
250
|
+
JUSTIFY_SPACE_EVENLY,
|
|
251
|
+
EDGE_LEFT,
|
|
252
|
+
EDGE_TOP,
|
|
253
|
+
EDGE_RIGHT,
|
|
254
|
+
EDGE_BOTTOM,
|
|
255
|
+
EDGE_HORIZONTAL,
|
|
256
|
+
EDGE_VERTICAL,
|
|
257
|
+
EDGE_ALL,
|
|
258
|
+
GUTTER_COLUMN,
|
|
259
|
+
GUTTER_ROW,
|
|
260
|
+
GUTTER_ALL,
|
|
261
|
+
DISPLAY_FLEX,
|
|
262
|
+
DISPLAY_NONE,
|
|
263
|
+
POSITION_TYPE_STATIC,
|
|
264
|
+
POSITION_TYPE_RELATIVE,
|
|
265
|
+
POSITION_TYPE_ABSOLUTE,
|
|
266
|
+
OVERFLOW_VISIBLE,
|
|
267
|
+
OVERFLOW_HIDDEN,
|
|
268
|
+
OVERFLOW_SCROLL,
|
|
269
|
+
DIRECTION_LTR,
|
|
270
|
+
MEASURE_MODE_UNDEFINED,
|
|
271
|
+
MEASURE_MODE_EXACTLY,
|
|
272
|
+
MEASURE_MODE_AT_MOST
|
|
273
|
+
};
|
|
274
|
+
createNode() {
|
|
275
|
+
return new FlexilyZeroNodeAdapter(Node.create({ defaults: "css" }));
|
|
276
|
+
}
|
|
277
|
+
get constants() {
|
|
278
|
+
return this._constants;
|
|
279
|
+
}
|
|
280
|
+
get name() {
|
|
281
|
+
return "flexily-zero";
|
|
282
|
+
}
|
|
283
|
+
get capabilities() {
|
|
284
|
+
return FLEXILY_CAPABILITIES;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* Create a Flexily zero-allocation layout engine with CSS-correct defaults.
|
|
289
|
+
*
|
|
290
|
+
* silvery's standard layout engine. Sets `flexShrink: 1`,
|
|
291
|
+
* `alignContent: stretch`, and CSS §4.5 flex-item auto min-size on every
|
|
292
|
+
* Node, matching browser flexbox semantics. Unlike Yoga, Flexily doesn't
|
|
293
|
+
* require async initialization.
|
|
294
|
+
*
|
|
295
|
+
* Yoga-flavored semantics are deliberately not exposed here — consumers
|
|
296
|
+
* needing Yoga compat should use `createFlexily({ defaults: "yoga" })` from
|
|
297
|
+
* flexily directly. The Ink-compat layer is the single internal user that
|
|
298
|
+
* stays on Yoga; it imports the `@internal` {@link createFlexilyZeroEngineForInkCompat}.
|
|
299
|
+
*/
|
|
300
|
+
function createFlexilyZeroEngine() {
|
|
301
|
+
return new FlexilyZeroLayoutEngine();
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
304
|
+
export { createFlexilyZeroEngine as n, FlexilyZeroLayoutEngine as t };
|
|
305
|
+
|
|
306
|
+
//# sourceMappingURL=flexily-zero-adapter-C3Vj0fPt.mjs.map
|