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":"progress-DB_Xo071.mjs","names":["isAsyncGenerator","runAsyncGenerator","isDeclareSteps"],"sources":["../packages/ag-react/src/ui/progress/als-context.ts","../packages/ag-react/src/ui/progress/step-node.ts","../packages/ag-react/src/ui/progress/declarative.ts","../packages/ag-react/src/ui/progress/steps.ts"],"sourcesContent":["/**\n * AsyncLocalStorage context for step progress reporting\n *\n * Provides a `step()` function that work functions can call to report progress.\n * Returns a no-op context when called outside of a steps() execution context,\n * so functions work in tests without the progress UI.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\"\nimport type { TaskHandle } from \"../cli/multi-progress\"\n\n/**\n * Context available to work functions during step execution\n */\nexport interface StepContext {\n /** Update progress on current step */\n progress(current: number, total: number): void\n\n /** Create a sub-step (auto-completes previous sub-step) */\n sub(label: string): void\n\n /** Get current step label (for debugging) */\n readonly label: string\n}\n\n/**\n * Internal context with additional fields for the runner\n */\nexport interface InternalStepContext extends StepContext {\n /** TaskHandle for this step */\n readonly handle: TaskHandle\n\n /** Add a sub-step handle (called by runner) */\n _addSubHandle(label: string, handle: TaskHandle): void\n\n /** Get a pre-declared sub-step handle by label */\n _getSubHandle(label: string): TaskHandle | undefined\n\n /** Set the current sub-step (when starting a pre-declared step) */\n _setCurrentSubHandle(label: string, handle: TaskHandle): void\n\n /** Complete current sub-step (called by runner) */\n _completeSubStep(): void\n}\n\n// AsyncLocalStorage instance\nconst stepContext = new AsyncLocalStorage<InternalStepContext>()\n\n/**\n * Get the current step context\n *\n * Safe to call anywhere - returns a no-op context when called outside\n * of a steps() execution context.\n *\n * @example\n * ```typescript\n * async function processFiles(files: string[]) {\n * for (let i = 0; i < files.length; i++) {\n * step().progress(i + 1, files.length);\n * await process(files[i]);\n * }\n * }\n *\n * // In tests (no steps context)\n * await processFiles([\"a.md\", \"b.md\"]); // step() returns no-op, no errors\n *\n * // In production (with steps context)\n * await steps({ process: processFiles }).run(); // Shows progress\n * ```\n */\nexport function step(): StepContext {\n return stepContext.getStore() ?? NO_OP_CONTEXT\n}\n\n/**\n * Run a function with step context (internal use by runner)\n */\nexport function runWithStepContext<T>(ctx: InternalStepContext, fn: () => T): T {\n return stepContext.run(ctx, fn)\n}\n\n/**\n * Create an internal step context for the runner\n */\nexport function createStepContext(\n label: string,\n handle: TaskHandle,\n onSubStep?: (label: string) => TaskHandle,\n): InternalStepContext {\n let currentSubLabel: string | undefined\n let currentSubHandle: TaskHandle | null = null\n let subStepStartTime = 0\n const declaredHandles = new Map<string, TaskHandle>()\n\n return {\n get label() {\n return label\n },\n\n get handle() {\n return handle\n },\n\n progress(current: number, total: number) {\n if (currentSubHandle) {\n currentSubHandle.setTitle(`${currentSubLabel} (${current}/${total})`)\n } else {\n handle.setTitle(`${label} (${current}/${total})`)\n }\n },\n\n sub(subLabel: string) {\n // Complete previous sub-step if any\n this._completeSubStep()\n\n currentSubLabel = subLabel\n subStepStartTime = Date.now()\n\n if (onSubStep) {\n currentSubHandle = onSubStep(subLabel)\n currentSubHandle.start()\n }\n },\n\n _addSubHandle(subLabel: string, subHandle: TaskHandle) {\n declaredHandles.set(subLabel, subHandle)\n },\n\n _getSubHandle(subLabel: string) {\n return declaredHandles.get(subLabel)\n },\n\n _setCurrentSubHandle(subLabel: string, subHandle: TaskHandle) {\n currentSubLabel = subLabel\n currentSubHandle = subHandle\n subStepStartTime = Date.now()\n },\n\n _completeSubStep() {\n if (currentSubHandle && currentSubLabel) {\n const elapsed = Date.now() - subStepStartTime\n // Use numeric timing - preserves current title (which may have progress info)\n currentSubHandle.complete(elapsed)\n currentSubHandle = null\n currentSubLabel = undefined\n }\n },\n }\n}\n\n/**\n * No-op context for when step() is called outside execution context\n */\nconst NO_OP_CONTEXT: StepContext = {\n progress: () => {},\n sub: () => {},\n get label() {\n return \"\"\n },\n}\n","/**\n * Step node tree structure for declarative steps\n *\n * Parses the user's declarative object structure into an internal tree\n * that can be rendered and executed.\n */\n\n/**\n * A single step in the tree\n */\nexport interface StepNode {\n /** Display label (auto-generated or custom) */\n label: string\n\n /** Object key from the declaration */\n key: string\n\n /** Work function (if leaf node) */\n work?: (...args: unknown[]) => unknown\n\n /** Child steps (if group node) */\n children?: StepNode[]\n\n /** Indentation level for display */\n indent: number\n}\n\n/**\n * What users can declare as a step value\n */\nexport type StepValue =\n | ((...args: unknown[]) => unknown) // Function (auto-named)\n | [string, (...args: unknown[]) => unknown] // [label, function]\n | StepsDef // Nested group\n\n/**\n * The declarative structure users provide\n */\nexport type StepsDef = {\n [key: string]: StepValue\n}\n\n/**\n * Parse a declarative steps definition into a tree of StepNodes\n *\n * @param def - The declarative structure\n * @param indent - Current indentation level (internal)\n * @returns Array of StepNodes\n */\nexport function parseStepsDef(def: StepsDef, indent = 0): StepNode[] {\n const nodes: StepNode[] = []\n\n for (const [key, value] of Object.entries(def)) {\n if (typeof value === \"function\") {\n // Function: auto-generate label from key\n nodes.push({\n key,\n label: generateLabel(key),\n work: value,\n indent,\n })\n } else if (Array.isArray(value) && value.length === 2) {\n // Tuple: [label, function]\n const [label, work] = value as [string, (...args: unknown[]) => unknown]\n nodes.push({\n key,\n label,\n work,\n indent,\n })\n } else if (typeof value === \"object\" && value !== null) {\n // Nested group\n const children = parseStepsDef(value as StepsDef, indent + 1)\n nodes.push({\n key,\n label: generateLabel(key),\n children,\n indent,\n })\n }\n }\n\n return nodes\n}\n\n/**\n * Flatten the tree for sequential execution\n *\n * Returns nodes in depth-first order, with groups followed by their children.\n */\nexport function flattenStepNodes(nodes: StepNode[]): StepNode[] {\n const result: StepNode[] = []\n\n for (const node of nodes) {\n result.push(node)\n if (node.children) {\n result.push(...flattenStepNodes(node.children))\n }\n }\n\n return result\n}\n\n/**\n * Get only leaf nodes (nodes with work functions)\n */\nexport function getLeafNodes(nodes: StepNode[]): StepNode[] {\n const result: StepNode[] = []\n\n for (const node of nodes) {\n if (node.work) {\n result.push(node)\n }\n if (node.children) {\n result.push(...getLeafNodes(node.children))\n }\n }\n\n return result\n}\n\n/**\n * Generate a display label from a camelCase function name\n *\n * @example\n * generateLabel(\"loadModules\") // \"Load modules\"\n * generateLabel(\"parseMarkdown\") // \"Parse markdown\"\n * generateLabel(\"initBoardStateGenerator\") // \"Init board state generator\"\n */\nexport function generateLabel(fnName: string): string {\n return fnName\n .replace(/([A-Z])/g, \" $1\") // Insert space before capitals\n .replace(/(\\d+)/g, \" $1\") // Insert space before numbers\n .toLowerCase() // Convert all to lowercase\n .trim() // Remove leading/trailing spaces\n .replace(/\\s+/g, \" \") // Collapse multiple spaces\n .replace(/^./, (s) => s.toUpperCase()) // Capitalize only first letter\n}\n\n/**\n * Check if a value is a StepsDef (nested group)\n */\nexport function isStepsDef(value: unknown): value is StepsDef {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n typeof value !== \"function\"\n )\n}\n\n/**\n * Check if a value is a tuple [label, function]\n */\nexport function isLabelTuple(value: unknown): value is [string, (...args: unknown[]) => unknown] {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n typeof value[0] === \"string\" &&\n typeof value[1] === \"function\"\n )\n}\n","/**\n * Declarative steps implementation\n *\n * Provides the declarative overload for steps() that accepts an object\n * structure and shows all steps upfront before execution.\n */\n\nimport { MultiProgress, type TaskHandle } from \"../cli/multi-progress\"\nimport {\n step as getStepContext,\n createStepContext,\n runWithStepContext,\n type InternalStepContext,\n} from \"./als-context\"\nimport {\n parseStepsDef,\n flattenStepNodes,\n getLeafNodes,\n type StepNode,\n type StepsDef,\n} from \"./step-node\"\n\n// Re-export step() for convenience\nexport { step } from \"./als-context\"\n\n// Node.js globals for yielding to event loop\ndeclare function setImmediate(callback: (value?: unknown) => void): unknown\ndeclare function setTimeout(callback: (value?: unknown) => void, ms: number): unknown\n\n/**\n * Options for run() and pipe() execution\n */\nexport interface ExecuteOptions {\n /** Clear progress display after completion (default: false) */\n clear?: boolean\n}\n\n/**\n * Extract the return type from a generator or async generator\n */\ntype GeneratorReturn<T> =\n T extends Generator<unknown, infer R, unknown>\n ? R\n : T extends AsyncGenerator<unknown, infer R, unknown>\n ? R\n : T\n\n/**\n * Unwrap the result type, handling generators specially\n */\ntype UnwrapResult<T> = Awaited<GeneratorReturn<Awaited<T>>>\n\n/**\n * Result type: maps step keys to their return values\n */\ntype StepResults<T extends StepsDef> = {\n [K in keyof T]: T[K] extends (...args: unknown[]) => infer R\n ? UnwrapResult<R>\n : T[K] extends [string, (...args: unknown[]) => infer R]\n ? UnwrapResult<R>\n : T[K] extends StepsDef\n ? StepResults<T[K]>\n : unknown\n}\n\n/**\n * The runner object returned by steps()\n */\nexport interface StepsRunner<T extends StepsDef> {\n /** Internal: the parsed step nodes (for testing) */\n readonly _steps: StepNode[]\n\n /**\n * Execute all steps sequentially\n * @returns Results keyed by step name\n */\n run(options?: ExecuteOptions): Promise<StepResults<T>>\n\n /**\n * Execute all steps in a pipeline (each receives previous result)\n * @returns Final step's result\n */\n pipe(options?: ExecuteOptions): Promise<unknown>\n\n /**\n * Manually signal completion (for manual execution mode)\n */\n done(options?: { clear?: boolean }): void\n}\n\n/**\n * Create a declarative steps runner\n *\n * @param def - Object structure defining steps\n * @returns StepsRunner with run(), pipe(), and done() methods\n *\n * @example\n * ```typescript\n * const loader = stepsDeclarative({\n * loadModules, // \"Load modules\"\n * loadRepo: { // \"Load repo\" (group)\n * discover, // \"Discover\"\n * parse, // \"Parse\"\n * },\n * });\n *\n * const results = await loader.run({ clear: true });\n * ```\n */\nexport function stepsDeclarative<T extends StepsDef>(def: T): StepsRunner<T> {\n const rootNodes = parseStepsDef(def)\n const allNodes = flattenStepNodes(rootNodes)\n\n let multi: MultiProgress | null = null\n const handles = new Map<StepNode, TaskHandle>()\n\n // Build group tracking: map each group to its leaf nodes\n const groupLeaves = new Map<StepNode, StepNode[]>()\n const leafToGroups = new Map<StepNode, StepNode[]>()\n\n for (const node of allNodes) {\n if (node.children) {\n const leaves = getLeafNodes([node])\n groupLeaves.set(node, leaves)\n for (const leaf of leaves) {\n const groups = leafToGroups.get(leaf) ?? []\n groups.push(node)\n leafToGroups.set(leaf, groups)\n }\n }\n }\n\n return {\n get _steps() {\n return rootNodes\n },\n\n async run(options?: ExecuteOptions): Promise<StepResults<T>> {\n multi = new MultiProgress()\n\n // Register all steps upfront (shows pending state)\n registerAllSteps(allNodes, multi, handles)\n\n // Group timing tracking\n const groupStartTimes = new Map<StepNode, number>()\n const completedLeaves = new Set<StepNode>()\n\n multi.start()\n\n // Yield to event loop to ensure initial render is displayed\n // before we start modifying task states\n await new Promise((resolve) => setImmediate(resolve))\n\n const results: Record<string, unknown> = {}\n\n try {\n // Execute each step with work\n for (const node of allNodes) {\n if (node.work) {\n // Start parent groups if not started\n const groups = leafToGroups.get(node) ?? []\n for (const group of groups) {\n if (!groupStartTimes.has(group)) {\n groupStartTimes.set(group, Date.now())\n handles.get(group)?.start()\n }\n }\n\n const result = await executeStep(node, handles, multi)\n setNestedResult(results, node.key, result)\n\n // Mark leaf as complete and check group completion\n completedLeaves.add(node)\n for (const group of groups) {\n const leaves = groupLeaves.get(group) ?? []\n if (leaves.every((l) => completedLeaves.has(l))) {\n const elapsed = Date.now() - groupStartTimes.get(group)!\n handles.get(group)?.complete(elapsed)\n }\n }\n }\n }\n } finally {\n multi.stop(options?.clear ?? false)\n }\n\n return results as StepResults<T>\n },\n\n async pipe(options?: ExecuteOptions): Promise<unknown> {\n multi = new MultiProgress()\n\n // Register all steps upfront\n registerAllSteps(allNodes, multi, handles)\n\n // Group timing tracking\n const groupStartTimes = new Map<StepNode, number>()\n const completedLeaves = new Set<StepNode>()\n\n multi.start()\n\n // Yield to event loop to ensure initial render is displayed\n // before we start modifying task states\n await new Promise((resolve) => setImmediate(resolve))\n\n let previousResult: unknown = undefined\n\n try {\n // Execute each step, passing previous result\n for (const node of allNodes) {\n if (node.work) {\n // Start parent groups if not started\n const groups = leafToGroups.get(node) ?? []\n for (const group of groups) {\n if (!groupStartTimes.has(group)) {\n groupStartTimes.set(group, Date.now())\n handles.get(group)?.start()\n }\n }\n\n previousResult = await executeStep(node, handles, multi, previousResult)\n\n // Mark leaf as complete and check group completion\n completedLeaves.add(node)\n for (const group of groups) {\n const leaves = groupLeaves.get(group) ?? []\n if (leaves.every((l) => completedLeaves.has(l))) {\n const elapsed = Date.now() - groupStartTimes.get(group)!\n handles.get(group)?.complete(elapsed)\n }\n }\n }\n }\n } finally {\n multi.stop(options?.clear ?? false)\n }\n\n return previousResult\n },\n\n done(options?: { clear?: boolean }) {\n if (multi) {\n multi.stop(options?.clear ?? false)\n multi = null\n }\n },\n }\n}\n\n/**\n * Register all steps with MultiProgress upfront\n */\nfunction registerAllSteps(\n nodes: StepNode[],\n multi: MultiProgress,\n handles: Map<StepNode, TaskHandle>,\n): void {\n // Register in order without insertAfter - simpler and correct\n for (const node of nodes) {\n const isGroup = node.children && !node.work\n const handle = multi.add(node.label, {\n type: isGroup ? \"group\" : \"spinner\",\n indent: node.indent,\n })\n handles.set(node, handle)\n }\n}\n\n/**\n * Execute a single step\n */\nasync function executeStep(\n node: StepNode,\n handles: Map<StepNode, TaskHandle>,\n multi: MultiProgress,\n input?: unknown,\n): Promise<unknown> {\n const handle = handles.get(node)!\n const startTime = Date.now()\n\n // Yield to event loop before starting\n await new Promise((resolve) => setImmediate(resolve))\n\n // Create step context for ALS\n const ctx = createStepContext(node.label, handle, (subLabel) => {\n // Create sub-step handle when step().sub() is called\n return multi.add(subLabel, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: handle.id,\n })\n })\n\n handle.start()\n\n try {\n // Run work function with ALS context\n const result = await runWithStepContext(ctx, () => {\n if (input !== undefined) {\n return (node.work as (input: unknown) => unknown)(input)\n }\n return node.work!()\n })\n\n // Handle generator results\n if (isGenerator(result)) {\n return await runGenerator(result, ctx, node, multi)\n }\n\n if (isAsyncGenerator(result)) {\n return await runAsyncGenerator(result, ctx, node, multi)\n }\n\n // Complete any remaining sub-step\n ctx._completeSubStep()\n\n // Complete the step with timing\n const elapsed = Date.now() - startTime\n handle.complete(elapsed)\n\n return result\n } catch (error) {\n handle.fail()\n throw error\n }\n}\n\n/**\n * Run a sync generator step\n */\nasync function runGenerator<T>(\n gen: Generator<unknown, T, unknown>,\n ctx: InternalStepContext,\n node: StepNode,\n multi: MultiProgress,\n): Promise<T> {\n const startTime = Date.now()\n let result = gen.next()\n let hasSubSteps = false\n // Track last inserted handle to maintain correct order\n // Each new sub-step inserts after the previous one, not after parent\n let lastInsertedId = ctx.handle.id\n\n while (!result.done) {\n const value = result.value\n\n // Handle yielded values\n if (isDeclareSteps(value)) {\n // Declare all sub-steps upfront (show as pending)\n if (!hasSubSteps) {\n hasSubSteps = true\n ctx.handle.setType(\"group\")\n }\n for (const label of value.declare) {\n const subHandle = multi.add(label, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: lastInsertedId,\n })\n lastInsertedId = subHandle.id\n ctx._addSubHandle(label, subHandle)\n }\n } else if (typeof value === \"string\") {\n // String = start a sub-step with this label\n ctx._completeSubStep()\n\n // First sub-step: change parent from spinner to group (no animation)\n if (!hasSubSteps) {\n hasSubSteps = true\n ctx.handle.setType(\"group\")\n }\n\n // Check if already declared, otherwise create new\n const existingHandle = ctx._getSubHandle?.(value)\n if (existingHandle) {\n ctx._setCurrentSubHandle(value, existingHandle)\n existingHandle.start()\n } else {\n const subHandle = multi.add(value, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: lastInsertedId,\n })\n lastInsertedId = subHandle.id\n ctx._addSubHandle(value, subHandle)\n subHandle.start()\n }\n } else if (isProgressUpdate(value)) {\n // Progress update\n ctx.progress(value.current ?? 0, value.total ?? 0)\n }\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = gen.next()\n }\n\n // Complete any remaining sub-step\n ctx._completeSubStep()\n\n // Complete the step with timing\n const elapsed = Date.now() - startTime\n ctx.handle.complete(elapsed)\n\n return result.value\n}\n\n/**\n * Run an async generator step\n */\nasync function runAsyncGenerator<T>(\n gen: AsyncGenerator<unknown, T, unknown>,\n ctx: InternalStepContext,\n node: StepNode,\n multi: MultiProgress,\n): Promise<T> {\n const startTime = Date.now()\n let result = await gen.next()\n let hasSubSteps = false\n // Track last inserted handle to maintain correct order\n // Each new sub-step inserts after the previous one, not after parent\n let lastInsertedId = ctx.handle.id\n\n while (!result.done) {\n const value = result.value\n\n // Handle yielded values\n if (isDeclareSteps(value)) {\n // Declare all sub-steps upfront (show as pending)\n if (!hasSubSteps) {\n hasSubSteps = true\n ctx.handle.setType(\"group\")\n }\n for (const label of value.declare) {\n const subHandle = multi.add(label, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: lastInsertedId,\n })\n lastInsertedId = subHandle.id\n ctx._addSubHandle(label, subHandle)\n }\n } else if (typeof value === \"string\") {\n // String = start a sub-step with this label\n ctx._completeSubStep()\n\n // First sub-step: change parent from spinner to group (no animation)\n if (!hasSubSteps) {\n hasSubSteps = true\n ctx.handle.setType(\"group\")\n }\n\n // Check if already declared, otherwise create new\n const existingHandle = ctx._getSubHandle(value)\n if (existingHandle) {\n ctx._setCurrentSubHandle(value, existingHandle)\n existingHandle.start()\n } else {\n const subHandle = multi.add(value, {\n type: \"spinner\",\n indent: node.indent + 1,\n insertAfter: lastInsertedId,\n })\n lastInsertedId = subHandle.id\n ctx._addSubHandle(value, subHandle)\n subHandle.start()\n }\n } else if (isProgressUpdate(value)) {\n // Progress update\n ctx.progress(value.current ?? 0, value.total ?? 0)\n }\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = await gen.next()\n }\n\n // Complete any remaining sub-step\n ctx._completeSubStep()\n\n // Complete the step with timing\n const elapsed = Date.now() - startTime\n ctx.handle.complete(elapsed)\n\n return result.value\n}\n\n/**\n * Set a nested result value by key path\n */\nfunction setNestedResult(results: Record<string, unknown>, key: string, value: unknown): void {\n // For now, flat keys only - nested groups would need path handling\n results[key] = value\n}\n\n/**\n * Type guards\n */\nfunction isGenerator(value: unknown): value is Generator<unknown, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as Generator).next === \"function\" &&\n typeof (value as Generator)[Symbol.iterator] === \"function\"\n )\n}\n\nfunction isAsyncGenerator(value: unknown): value is AsyncGenerator<unknown, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as AsyncGenerator).next === \"function\" &&\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] === \"function\"\n )\n}\n\ninterface ProgressUpdate {\n current?: number\n total?: number\n}\n\ninterface DeclareSteps {\n declare: string[]\n}\n\nfunction isProgressUpdate(value: unknown): value is ProgressUpdate {\n return (\n value !== null &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n (\"current\" in value || \"total\" in value)\n )\n}\n\nfunction isDeclareSteps(value: unknown): value is DeclareSteps {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"declare\" in value &&\n Array.isArray((value as DeclareSteps).declare)\n )\n}\n","/**\n * Sequential step runner with progress display\n *\n * Supports two modes:\n *\n * ## Declarative Mode (recommended)\n *\n * Pass an object to declare all steps upfront:\n *\n * ```typescript\n * import { steps, step } from \"./index\";\n *\n * const loader = steps({\n * loadModules, // Auto-named: \"Load modules\"\n * loadRepo: { // Group: \"Load repo\"\n * discover, // \"Discover\"\n * parse, // \"Parse\"\n * },\n * });\n *\n * const results = await loader.run({ clear: true });\n *\n * // Inside work functions, use step() to report progress:\n * async function discover() {\n * const files = await glob(\"**\\/*.md\");\n * for (let i = 0; i < files.length; i++) {\n * step().progress(i + 1, files.length);\n * await process(files[i]);\n * }\n * return files;\n * }\n * ```\n *\n * ## Fluent Mode (legacy)\n *\n * Chain steps with `.run()` and execute with `.execute()`:\n *\n * ```typescript\n * await steps()\n * .run(\"Loading modules\", () => import(\"./app\"))\n * .run(\"Building view\", async () => buildView())\n * .execute();\n * ```\n *\n * Generators can yield sub-step progress:\n * - Yield a **string** to create a new sub-step\n * - Yield an **object** `{ current, total }` to update progress on current sub-step\n */\n\nimport { MultiProgress, type TaskHandle } from \"../cli/multi-progress\"\nimport { stepsDeclarative, type StepsRunner } from \"./declarative\"\nimport type { StepsDef } from \"./step-node\"\n\n// Re-export step() context helper\nexport { step } from \"./als-context\"\n\n// Re-export types from declarative\nexport type { StepsRunner } from \"./declarative\"\nexport type { StepsDef, StepNode } from \"./step-node\"\nexport type { StepContext } from \"./als-context\"\n\n// Node.js globals for yielding to event loop\ndeclare function setImmediate(callback: (value?: unknown) => void): unknown\ndeclare function setTimeout(callback: (value?: unknown) => void, ms: number): unknown\n\n/** Progress update (object form) */\ninterface ProgressUpdate {\n current?: number\n total?: number\n}\n\n/** Declare all sub-steps upfront (optional, yield as first value) */\ninterface DeclareSteps {\n declare: string[]\n}\n\n/** What generators can yield */\ntype StepYield = string | ProgressUpdate | DeclareSteps\n\n/**\n * Controller for creating and updating sub-steps\n *\n * Passed to work functions that need to report sub-step progress.\n */\nexport interface StepController {\n /** Create a new sub-step (auto-completes previous sub-step) */\n new (label: string): void\n\n /** Update progress on current sub-step */\n progress(current: number, total: number): void\n\n /** Explicitly complete current sub-step (optional - new() auto-completes) */\n done(): void\n}\n\n/** Work function types */\ntype SyncWork<T> = () => T\ntype AsyncWork<T> = () => PromiseLike<T>\ntype SyncGeneratorWork<T> = () => Generator<StepYield, T, unknown>\ntype AsyncGeneratorWork<T> = () => AsyncGenerator<StepYield, T, unknown>\n/** Work function with step controller for sub-step progress */\ntype WorkWithStep<T> = (step: StepController) => T | PromiseLike<T>\n\ntype WorkFn<T> =\n | SyncWork<T>\n | AsyncWork<T>\n | SyncGeneratorWork<T>\n | AsyncGeneratorWork<T>\n | WorkWithStep<T>\n\n/** Step definition */\ninterface StepDef<T = unknown> {\n title: string\n work: WorkFn<T>\n}\n\n/** Options for execute() */\nexport interface ExecuteOptions {\n /** Clear progress display after completion (default: false) */\n clear?: boolean\n}\n\nexport interface StepBuilder {\n /**\n * Add a step to run\n *\n * @param title - Display title for this step\n * @param work - Function to execute. Can be:\n * - Sync function: `() => result`\n * - Async function: `async () => result`\n * - Sync generator: `function* () { yield \"sub-step\"; return result; }`\n * - Async generator: `async function* () { yield \"sub-step\"; return result; }`\n *\n * Generators can yield:\n * - `\"string\"` - Creates a new sub-step with that label\n * - `{ current: N, total: M }` - Updates progress on current sub-step\n */\n run<T>(title: string, work: WorkFn<T>): StepBuilder\n\n /**\n * Execute all steps in sequence\n * @param options - Execution options\n * @returns Results keyed by step title\n */\n execute(options?: ExecuteOptions): Promise<Record<string, unknown>>\n}\n\n/**\n * Create a step runner\n *\n * @overload Declarative mode - pass an object to declare steps upfront\n * @overload Fluent mode - chain steps with .run() and execute with .execute()\n *\n * @example Declarative mode (recommended)\n * ```typescript\n * const loader = steps({\n * loadModules,\n * loadRepo: { discover, parse },\n * });\n * const results = await loader.run({ clear: true });\n * ```\n *\n * @example Fluent mode\n * ```typescript\n * await steps()\n * .run(\"Step 1\", doStep1)\n * .run(\"Step 2\", doStep2)\n * .execute();\n * ```\n */\nexport function steps<T extends StepsDef>(def: T): StepsRunner<T>\nexport function steps(): StepBuilder\nexport function steps<T extends StepsDef>(def?: T): StepsRunner<T> | StepBuilder {\n // Declarative mode: object passed\n if (def !== undefined) {\n return stepsDeclarative(def)\n }\n\n // Fluent mode: no arguments\n return createFluentBuilder()\n}\n\n/**\n * Create a no-op StepController for work functions that don't use sub-steps.\n * This satisfies the type union while being harmless if not used.\n */\nfunction createNoopStepController(): StepController {\n const controller = (_label: string) => {}\n controller.progress = (_current: number, _total: number) => {}\n controller.done = () => {}\n // StepController uses `new` as a callable signature, not a constructor\n return controller as unknown as StepController\n}\n\n/**\n * Create the fluent step builder (legacy mode)\n */\nfunction createFluentBuilder(): StepBuilder {\n const stepList: StepDef[] = []\n\n const builder: StepBuilder = {\n run<T>(title: string, work: WorkFn<T>): StepBuilder {\n stepList.push({ title, work: work as WorkFn<unknown> })\n return builder\n },\n\n async execute(options?: ExecuteOptions): Promise<Record<string, unknown>> {\n const multi = new MultiProgress()\n const handles = new Map<string, TaskHandle>()\n const results: Record<string, unknown> = {}\n\n // Register all steps upfront (shows pending state)\n for (const step of stepList) {\n handles.set(step.title, multi.add(step.title, { type: \"spinner\" }))\n }\n\n multi.start()\n\n try {\n for (const step of stepList) {\n const handle = handles.get(step.title)!\n\n // Yield to event loop before potentially blocking work\n await new Promise((resolve) => setImmediate(resolve))\n\n const result = step.work(createNoopStepController())\n\n if (isAsyncGenerator(result)) {\n // Async generator: parent stays static while sub-steps animate\n results[step.title] = await runAsyncGenerator(result, handle, step.title, multi)\n } else if (isSyncGenerator(result)) {\n // Sync generator: same handling\n results[step.title] = await runSyncGenerator(result, handle, step.title, multi)\n } else if (isPromiseLike(result)) {\n handle.start()\n results[step.title] = await result\n handle.complete()\n } else {\n handle.start()\n results[step.title] = result\n handle.complete()\n }\n }\n } finally {\n multi.stop(options?.clear ?? false)\n }\n\n return results\n },\n }\n\n return builder\n}\n\n/**\n * Process a yielded value:\n * - { declare: [...] } = declare all sub-steps upfront (show as pending)\n * - string = start/create a sub-step with that label\n * - { current, total } = update progress on current sub-step\n */\nfunction processYield(value: StepYield, state: GeneratorState, multi: MultiProgress): void {\n // Handle declaration of all sub-steps upfront\n if (isDeclareSteps(value)) {\n for (const label of value.declare) {\n const handle = multi.add(label, {\n type: \"spinner\",\n indent: 1,\n insertAfter: state.lastInsertId,\n })\n state.lastInsertId = handle.id\n state.declaredSteps.set(label, handle)\n }\n return\n }\n\n if (typeof value === \"string\") {\n // String = start a sub-step with this label\n if (state.currentHandle && state.currentLabel) {\n const elapsed = Date.now() - state.subStepStartTime\n state.currentHandle.complete(elapsed)\n }\n\n state.currentLabel = value\n state.subStepStartTime = Date.now()\n\n // Use pre-declared handle if available, otherwise create new one\n const declared = state.declaredSteps.get(value)\n if (declared) {\n state.currentHandle = declared\n state.currentHandle.start()\n } else {\n state.currentHandle = multi.add(value, {\n type: \"spinner\",\n indent: 1,\n insertAfter: state.lastInsertId,\n })\n state.lastInsertId = state.currentHandle.id\n state.currentHandle.start()\n }\n } else if (value && typeof value === \"object\") {\n // Object = progress update on current sub-step\n const { current, total } = value as ProgressUpdate\n if (state.currentHandle && total && total > 0) {\n state.currentHandle.setTitle(`${state.currentLabel} (${current ?? 0}/${total})`)\n }\n }\n}\n\nfunction isDeclareSteps(value: StepYield): value is DeclareSteps {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"declare\" in value &&\n Array.isArray((value as DeclareSteps).declare)\n )\n}\n\n/** State for generator processing */\ninterface GeneratorState {\n currentLabel: string | undefined\n currentHandle: TaskHandle | null\n lastInsertId: string\n subStepStartTime: number\n startTime: number\n /** Pre-declared sub-steps (pending until started) */\n declaredSteps: Map<string, TaskHandle>\n}\n\n/**\n * Run an async generator step\n */\nasync function runAsyncGenerator<T>(\n gen: AsyncGenerator<StepYield, T, unknown>,\n parentHandle: TaskHandle,\n parentTitle: string,\n multi: MultiProgress,\n): Promise<T> {\n const state: GeneratorState = {\n currentLabel: undefined,\n currentHandle: null,\n lastInsertId: parentHandle.id,\n subStepStartTime: Date.now(),\n startTime: Date.now(),\n declaredSteps: new Map(),\n }\n\n let result = await gen.next()\n\n while (!result.done) {\n processYield(result.value, state, multi)\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = await gen.next()\n }\n\n // Complete final sub-step\n if (state.currentHandle && state.currentLabel) {\n const elapsed = Date.now() - state.subStepStartTime\n state.currentHandle.complete(elapsed)\n }\n\n // Complete parent step\n const totalElapsed = Date.now() - state.startTime\n parentHandle.complete(totalElapsed)\n\n return result.value\n}\n\n/**\n * Run a sync generator step\n */\nasync function runSyncGenerator<T>(\n gen: Generator<StepYield, T, unknown>,\n parentHandle: TaskHandle,\n parentTitle: string,\n multi: MultiProgress,\n): Promise<T> {\n const state: GeneratorState = {\n currentLabel: undefined,\n currentHandle: null,\n lastInsertId: parentHandle.id,\n subStepStartTime: Date.now(),\n startTime: Date.now(),\n declaredSteps: new Map(),\n }\n\n let result = gen.next()\n\n while (!result.done) {\n processYield(result.value, state, multi)\n\n // Yield to event loop for animation\n await new Promise((resolve) => setTimeout(resolve, 0))\n\n result = gen.next()\n }\n\n // Complete final sub-step\n if (state.currentHandle && state.currentLabel) {\n const elapsed = Date.now() - state.subStepStartTime\n state.currentHandle.complete(elapsed)\n }\n\n // Complete parent step\n const totalElapsed = Date.now() - state.startTime\n parentHandle.complete(totalElapsed)\n\n return result.value\n}\n\nfunction isAsyncGenerator(value: unknown): value is AsyncGenerator<StepYield, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as AsyncGenerator).next === \"function\" &&\n typeof (value as AsyncGenerator)[Symbol.asyncIterator] === \"function\"\n )\n}\n\nfunction isSyncGenerator(value: unknown): value is Generator<StepYield, unknown, unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as Generator).next === \"function\" &&\n typeof (value as Generator)[Symbol.iterator] === \"function\"\n )\n}\n\nfunction isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return (\n value !== null &&\n typeof value === \"object\" &&\n typeof (value as PromiseLike<unknown>).then === \"function\"\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as chalk } from "./spinner-
|
|
2
|
-
import { d as getTerminalWidth, f as isTTY, i as CURSOR_HIDE, m as write, s as CURSOR_SHOW } from "./ansi-
|
|
3
|
-
import { i as formatETA, n as calculateETA } from "./eta-
|
|
1
|
+
import { a as chalk } from "./spinner-D9lrHr8s.mjs";
|
|
2
|
+
import { d as getTerminalWidth, f as isTTY, i as CURSOR_HIDE, m as write, s as CURSOR_SHOW } from "./ansi-D1KQMAbf.mjs";
|
|
3
|
+
import { i as formatETA, n as calculateETA } from "./eta-CJlGH06n.mjs";
|
|
4
4
|
//#region packages/ag-react/src/ui/cli/progress-bar.ts
|
|
5
5
|
/**
|
|
6
6
|
* CLI ProgressBar - Determinate progress indicator with ETA
|
|
@@ -152,4 +152,4 @@ var ProgressBar = class {
|
|
|
152
152
|
//#endregion
|
|
153
153
|
export { ProgressBar as t };
|
|
154
154
|
|
|
155
|
-
//# sourceMappingURL=progress-bar-
|
|
155
|
+
//# sourceMappingURL=progress-bar-oJwq22CR.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress-bar-
|
|
1
|
+
{"version":3,"file":"progress-bar-oJwq22CR.mjs","names":[],"sources":["../packages/ag-react/src/ui/cli/progress-bar.ts"],"sourcesContent":["/**\n * CLI ProgressBar - Determinate progress indicator with ETA\n */\n\nimport chalk from \"@silvery/ink/chalk\"\nimport type { ProgressBarOptions } from \"../types.js\"\nimport {\n CURSOR_HIDE,\n CURSOR_SHOW,\n CURSOR_TO_START,\n CLEAR_LINE_END,\n write,\n isTTY,\n getTerminalWidth,\n} from \"./ansi\"\nimport { calculateETA, formatETA, DEFAULT_ETA_BUFFER_SIZE, type ETASample } from \"../utils/eta\"\n\n/** Default format string */\nconst DEFAULT_FORMAT = \":bar :percent | :current/:total | ETA: :eta\"\n\n/**\n * ProgressBar class for CLI progress indication\n *\n * @example\n * ```ts\n * const bar = new ProgressBar({ total: 100 });\n * bar.start();\n * for (let i = 0; i <= 100; i++) {\n * await doWork();\n * bar.update(i);\n * }\n * bar.stop();\n * ```\n */\nexport class ProgressBar {\n private total: number\n private format: string\n private width: number\n private complete: string\n private incomplete: string\n private stream: NodeJS.WriteStream\n private hideCursor: boolean\n private phases: Record<string, string>\n\n private current = 0\n private phase: string | null = null\n private startTime: number | null = null\n private isActive = false\n\n // ETA smoothing - track last N update times\n private etaBuffer: ETASample[] = []\n\n constructor(options: ProgressBarOptions = {}) {\n this.total = options.total ?? 100\n this.format = options.format ?? DEFAULT_FORMAT\n this.width = options.width ?? 40\n this.complete = options.complete ?? \"█\"\n this.incomplete = options.incomplete ?? \"░\"\n this.stream = options.stream ?? process.stdout\n this.hideCursor = options.hideCursor ?? true\n this.phases = options.phases ?? {}\n }\n\n /**\n * Start the progress bar\n */\n start(initialValue = 0, initialTotal?: number): this {\n if (initialTotal !== undefined) {\n this.total = initialTotal\n }\n\n this.current = initialValue\n this.startTime = Date.now()\n this.isActive = true\n this.etaBuffer = [{ time: this.startTime, value: initialValue }]\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_HIDE, this.stream)\n }\n\n this.render()\n return this\n }\n\n /**\n * Update progress value\n */\n update(value: number, tokens?: Record<string, string | number>): this {\n this.current = Math.min(value, this.total)\n\n // Update ETA buffer\n const now = Date.now()\n this.etaBuffer.push({ time: now, value: this.current })\n if (this.etaBuffer.length > DEFAULT_ETA_BUFFER_SIZE) {\n this.etaBuffer.shift()\n }\n\n if (this.isActive) {\n this.render(tokens)\n }\n\n return this\n }\n\n /**\n * Increment progress by amount (default: 1)\n */\n increment(amount = 1, tokens?: Record<string, string | number>): this {\n return this.update(this.current + amount, tokens)\n }\n\n /**\n * Set the current phase (for multi-phase progress)\n */\n setPhase(phaseName: string, options?: { current?: number; total?: number }): this {\n this.phase = phaseName\n\n if (options?.total !== undefined) {\n this.total = options.total\n }\n if (options?.current !== undefined) {\n this.current = options.current\n // Reset ETA buffer on phase change\n this.etaBuffer = [{ time: Date.now(), value: this.current }]\n }\n\n if (this.isActive) {\n this.render()\n }\n\n return this\n }\n\n /**\n * Stop the progress bar\n */\n stop(clear = false): this {\n if (!this.isActive) {\n return this\n }\n\n this.isActive = false\n\n if (clear && isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${CLEAR_LINE_END}`, this.stream)\n } else {\n write(\"\\n\", this.stream)\n }\n\n if (this.hideCursor && isTTY(this.stream)) {\n write(CURSOR_SHOW, this.stream)\n }\n\n return this\n }\n\n /** Get ETA in seconds using smoothed rate */\n private getETASeconds(): number | null {\n return calculateETA(this.etaBuffer, this.current, this.total)\n }\n\n /**\n * Render the progress bar\n */\n private render(tokens?: Record<string, string | number>): void {\n const percent = this.total > 0 ? this.current / this.total : 0\n const eta = this.getETASeconds()\n\n // Build the bar\n const completeLength = Math.round(this.width * percent)\n const incompleteLength = this.width - completeLength\n const bar = this.complete.repeat(completeLength) + this.incomplete.repeat(incompleteLength)\n\n // Get phase display name\n const phaseDisplay = this.phase ? (this.phases[this.phase] ?? this.phase) : \"\"\n\n // Calculate rate\n const elapsed = this.startTime ? (Date.now() - this.startTime) / 1000 : 0\n const rate = elapsed > 0 ? this.current / elapsed : 0\n\n // Replace tokens in format string\n let output = this.format\n .replace(\":bar\", chalk.cyan(bar))\n .replace(\":percent\", `${Math.round(percent * 100)}%`.padStart(4))\n .replace(\":current\", String(this.current))\n .replace(\":total\", String(this.total))\n .replace(\":eta\", formatETA(eta))\n .replace(\":elapsed\", formatETA(elapsed))\n .replace(\":rate\", rate.toFixed(1))\n .replace(\":phase\", chalk.dim(phaseDisplay))\n\n // Replace custom tokens\n if (tokens) {\n for (const [key, value] of Object.entries(tokens)) {\n output = output.replace(`:${key}`, String(value))\n }\n }\n\n // Truncate to terminal width\n const termWidth = getTerminalWidth(this.stream)\n if (output.length > termWidth) {\n output = output.slice(0, termWidth - 1)\n }\n\n if (isTTY(this.stream)) {\n write(`${CURSOR_TO_START}${output}${CLEAR_LINE_END}`, this.stream)\n }\n }\n\n /**\n * Get current progress ratio (0-1)\n */\n get ratio(): number {\n return this.total > 0 ? this.current / this.total : 0\n }\n\n /**\n * Get current progress percentage (0-100)\n */\n get percentage(): number {\n return Math.round(this.ratio * 100)\n }\n\n /**\n * Dispose the progress bar (calls stop)\n */\n [Symbol.dispose](): void {\n this.stop()\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { n as __esmMin, o as __toESM } from "./chunk-BSw8zbkd.mjs";
|
|
2
|
+
import { c as init_fonts, n as BUNDLED_FONTS, o as bundledFontFiles, s as bundledFontsDir } from "./fonts-BFmhXDv7.mjs";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
//#region ../termless/src/view/rasterizer.ts
|
|
6
|
+
async function loadResvg() {
|
|
7
|
+
if (resvgModule) return resvgModule;
|
|
8
|
+
resvgModule = await import("./resvg-js-DkOndZI3.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
|
|
9
|
+
return resvgModule;
|
|
10
|
+
}
|
|
11
|
+
function createResvgRasterizer(Resvg) {
|
|
12
|
+
const fontFiles = bundledFontFiles();
|
|
13
|
+
const build = (svg, scale) => new Resvg.Resvg(svg, {
|
|
14
|
+
fitTo: {
|
|
15
|
+
mode: "zoom",
|
|
16
|
+
value: scale
|
|
17
|
+
},
|
|
18
|
+
font: {
|
|
19
|
+
loadSystemFonts: true,
|
|
20
|
+
defaultFontFamily: "Menlo",
|
|
21
|
+
fontFiles
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
kind: "resvg",
|
|
26
|
+
async rasterize(svg, scale) {
|
|
27
|
+
const rendered = build(svg, scale).render();
|
|
28
|
+
return {
|
|
29
|
+
pixels: new Uint8Array(rendered.pixels),
|
|
30
|
+
width: rendered.width,
|
|
31
|
+
height: rendered.height
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
async toPng(svg, scale) {
|
|
35
|
+
return build(svg, scale).render().asPng();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
async function loadCanvas() {
|
|
40
|
+
if (canvasModule) return canvasModule;
|
|
41
|
+
const mod = await import("./canvas-1v7dPT-_.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
|
|
42
|
+
const dir = bundledFontsDir();
|
|
43
|
+
for (const { file, family } of BUNDLED_FONTS) {
|
|
44
|
+
const path = join(dir, file);
|
|
45
|
+
if (existsSync(path)) mod.GlobalFonts.registerFromPath(path, family);
|
|
46
|
+
}
|
|
47
|
+
canvasModule = mod;
|
|
48
|
+
return canvasModule;
|
|
49
|
+
}
|
|
50
|
+
/** Read the intrinsic `width`/`height` from an SVG's root attributes. */
|
|
51
|
+
function svgIntrinsicSize(svg) {
|
|
52
|
+
const w = svg.match(/<svg[^>]*\bwidth="([\d.]+)"/)?.[1];
|
|
53
|
+
const h = svg.match(/<svg[^>]*\bheight="([\d.]+)"/)?.[1];
|
|
54
|
+
return {
|
|
55
|
+
width: w ? Math.round(Number(w)) : 0,
|
|
56
|
+
height: h ? Math.round(Number(h)) : 0
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function createCanvasRasterizer(mod) {
|
|
60
|
+
const draw = (svg, scale) => {
|
|
61
|
+
const img = new mod.Image();
|
|
62
|
+
img.src = Buffer.from(svg);
|
|
63
|
+
const intrinsic = svgIntrinsicSize(svg);
|
|
64
|
+
const width = Math.max(1, Math.round((img.width || intrinsic.width) * scale));
|
|
65
|
+
const height = Math.max(1, Math.round((img.height || intrinsic.height) * scale));
|
|
66
|
+
const canvas = mod.createCanvas(width, height);
|
|
67
|
+
canvas.getContext("2d").drawImage(img, 0, 0, width, height);
|
|
68
|
+
return {
|
|
69
|
+
canvas,
|
|
70
|
+
width,
|
|
71
|
+
height
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
return {
|
|
75
|
+
kind: "canvas",
|
|
76
|
+
async rasterize(svg, scale) {
|
|
77
|
+
const { canvas, width, height } = draw(svg, scale);
|
|
78
|
+
const data = canvas.getContext("2d").getImageData(0, 0, width, height).data;
|
|
79
|
+
return {
|
|
80
|
+
pixels: new Uint8Array(data),
|
|
81
|
+
width,
|
|
82
|
+
height
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
async toPng(svg, scale) {
|
|
86
|
+
const { canvas } = draw(svg, scale);
|
|
87
|
+
return new Uint8Array(canvas.toBuffer("image/png"));
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function loadSwash() {
|
|
92
|
+
if (swashModule) return swashModule;
|
|
93
|
+
const mod = await import("./src-bt8wSrfJ.mjs");
|
|
94
|
+
if (!mod.swashRenderAvailable()) throw new Error("@termless/swash-render native binding is not built");
|
|
95
|
+
swashModule = mod;
|
|
96
|
+
return mod;
|
|
97
|
+
}
|
|
98
|
+
async function createSwashRasterizer(mod) {
|
|
99
|
+
const svgFallback = createResvgRasterizer(await loadResvg());
|
|
100
|
+
const toBitmap = (terminal, scale) => {
|
|
101
|
+
const s = Math.max(1, scale);
|
|
102
|
+
const bmp = mod.renderCells(terminal, {
|
|
103
|
+
cellWidth: 9.6 * s,
|
|
104
|
+
cellHeight: 20 * s,
|
|
105
|
+
padding: Math.round(SWASH_EDGE_PADDING * s)
|
|
106
|
+
});
|
|
107
|
+
return {
|
|
108
|
+
pixels: new Uint8Array(bmp.pixels),
|
|
109
|
+
width: bmp.width,
|
|
110
|
+
height: bmp.height
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
return {
|
|
114
|
+
kind: "swash",
|
|
115
|
+
rasterize: svgFallback.rasterize,
|
|
116
|
+
toPng: svgFallback.toPng,
|
|
117
|
+
async rasterizeCells(terminal, scale) {
|
|
118
|
+
return toBitmap(terminal, scale);
|
|
119
|
+
},
|
|
120
|
+
async cellsToPng(terminal, scale) {
|
|
121
|
+
const { pixels, width, height } = toBitmap(terminal, scale);
|
|
122
|
+
const UPNG = await import("./UPNG-Bo33r8rA.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
|
|
123
|
+
const ab = pixels.buffer.slice(pixels.byteOffset, pixels.byteOffset + pixels.byteLength);
|
|
124
|
+
return new Uint8Array(UPNG.encode([ab], width, height, 0));
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Lazily import `playwright`. Playwright is an **optional** dependency — a
|
|
130
|
+
* default `bun install` omits it. Throws a clear, actionable error if absent,
|
|
131
|
+
* naming both the package install and the browser-download step.
|
|
132
|
+
*/
|
|
133
|
+
async function loadPlaywright() {
|
|
134
|
+
if (playwrightModule) return playwrightModule;
|
|
135
|
+
playwrightModule = await import("./playwright-D5YiZcNS.mjs");
|
|
136
|
+
return playwrightModule;
|
|
137
|
+
}
|
|
138
|
+
/** Read intrinsic width/height from an SVG root — reused for the viewport. */
|
|
139
|
+
function browserSvgSize(svg) {
|
|
140
|
+
const { width, height } = svgIntrinsicSize(svg);
|
|
141
|
+
return {
|
|
142
|
+
width: Math.max(1, width),
|
|
143
|
+
height: Math.max(1, height)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Inline the bundled `@font-face` faces into an SVG if it does not already
|
|
148
|
+
* carry them. The `record` GIF/APNG pipeline produces frame SVGs without
|
|
149
|
+
* `embedFonts`, so Chromium would otherwise fall back to host fonts (or tofu
|
|
150
|
+
* for symbol / emoji code points). Embedding makes the page deterministic.
|
|
151
|
+
*/
|
|
152
|
+
async function ensureEmbeddedFonts(svg) {
|
|
153
|
+
if (svg.includes("@font-face")) return svg;
|
|
154
|
+
const { embeddedFontFaceDefs } = await import("./svg-DY72a4HK.mjs");
|
|
155
|
+
const defs = embeddedFontFaceDefs();
|
|
156
|
+
if (!defs) return svg;
|
|
157
|
+
return svg.replace(/(<svg[^>]*>)/, `$1${defs}`);
|
|
158
|
+
}
|
|
159
|
+
/** Wrap an SVG in a minimal full-bleed HTML document for Chromium. */
|
|
160
|
+
function browserHtml(svg, width, height) {
|
|
161
|
+
return `<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;padding:0;width:${width}px;height:${height}px;overflow:hidden;background:transparent}svg{display:block}</style></head><body>${svg}</body></html>`;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The `browser` rasterizer — rasterizes SVG frames via headless Chromium.
|
|
165
|
+
*
|
|
166
|
+
* The browser instance is launched once and reused across every `rasterize` /
|
|
167
|
+
* `toPng` call; `dispose()` closes it. This is the absolute-max-fidelity path
|
|
168
|
+
* (Chrome-grade shaping, fallback, ligatures, color emoji) — opt-in only.
|
|
169
|
+
*/
|
|
170
|
+
function createBrowserRasterizer(playwright) {
|
|
171
|
+
let browserPromise = null;
|
|
172
|
+
const getBrowser = () => {
|
|
173
|
+
if (!browserPromise) browserPromise = playwright.chromium.launch({
|
|
174
|
+
headless: true,
|
|
175
|
+
args: ["--force-color-profile=srgb"]
|
|
176
|
+
}).catch((cause) => {
|
|
177
|
+
browserPromise = null;
|
|
178
|
+
throw new Error("--renderer browser could not launch Chromium. Install the browser binary:\n npx playwright install chromium", { cause });
|
|
179
|
+
});
|
|
180
|
+
return browserPromise;
|
|
181
|
+
};
|
|
182
|
+
const shot = async (svg, scale) => {
|
|
183
|
+
const { width, height } = browserSvgSize(svg);
|
|
184
|
+
const embedded = await ensureEmbeddedFonts(svg);
|
|
185
|
+
const page = await (await getBrowser()).newPage({
|
|
186
|
+
viewport: {
|
|
187
|
+
width,
|
|
188
|
+
height
|
|
189
|
+
},
|
|
190
|
+
deviceScaleFactor: Math.max(1, scale)
|
|
191
|
+
});
|
|
192
|
+
try {
|
|
193
|
+
await page.setContent(browserHtml(embedded, width, height), { waitUntil: "load" });
|
|
194
|
+
await page.evaluate(() => document.fonts.ready);
|
|
195
|
+
const buf = await page.screenshot({
|
|
196
|
+
type: "png",
|
|
197
|
+
omitBackground: false
|
|
198
|
+
});
|
|
199
|
+
return {
|
|
200
|
+
png: new Uint8Array(buf),
|
|
201
|
+
width: Math.round(width * Math.max(1, scale)),
|
|
202
|
+
height: Math.round(height * Math.max(1, scale))
|
|
203
|
+
};
|
|
204
|
+
} finally {
|
|
205
|
+
await page.close();
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
kind: "browser",
|
|
210
|
+
async rasterize(svg, scale) {
|
|
211
|
+
const { png, width, height } = await shot(svg, scale);
|
|
212
|
+
const UPNG = await import("./UPNG-Bo33r8rA.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
|
|
213
|
+
const ab = png.buffer.slice(png.byteOffset, png.byteOffset + png.byteLength);
|
|
214
|
+
const decoded = UPNG.decode(ab);
|
|
215
|
+
const rgbaFrames = UPNG.toRGBA8(decoded);
|
|
216
|
+
return {
|
|
217
|
+
pixels: new Uint8Array(rgbaFrames[0] ?? /* @__PURE__ */ new ArrayBuffer(0)),
|
|
218
|
+
width: decoded.width,
|
|
219
|
+
height: decoded.height
|
|
220
|
+
};
|
|
221
|
+
},
|
|
222
|
+
async toPng(svg, scale) {
|
|
223
|
+
return (await shot(svg, scale)).png;
|
|
224
|
+
},
|
|
225
|
+
async dispose() {
|
|
226
|
+
if (!browserPromise) return;
|
|
227
|
+
const pending = browserPromise;
|
|
228
|
+
browserPromise = null;
|
|
229
|
+
try {
|
|
230
|
+
await (await pending).close();
|
|
231
|
+
} catch {}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Resolve a {@link RendererKind} to a concrete {@link Rasterizer}.
|
|
237
|
+
*
|
|
238
|
+
* - `canvas` — `@napi-rs/canvas`; throws a clear error if the binding is absent.
|
|
239
|
+
* - `resvg` — `@resvg/resvg-js`; throws a clear error if it is absent.
|
|
240
|
+
* - `browser` — headless Chromium via the optional `playwright` package;
|
|
241
|
+
* throws a clear install hint if absent. Never reached from `auto`.
|
|
242
|
+
* - `auto` — `canvas` when its binding loads, else `resvg`.
|
|
243
|
+
*/
|
|
244
|
+
async function selectRasterizer(kind = "auto") {
|
|
245
|
+
if (kind === "canvas") try {
|
|
246
|
+
return createCanvasRasterizer(await loadCanvas());
|
|
247
|
+
} catch {
|
|
248
|
+
throw new Error("--renderer canvas requires @napi-rs/canvas. Install it:\n bun add @napi-rs/canvas");
|
|
249
|
+
}
|
|
250
|
+
if (kind === "resvg") try {
|
|
251
|
+
return createResvgRasterizer(await loadResvg());
|
|
252
|
+
} catch {
|
|
253
|
+
throw new Error("--renderer resvg requires @resvg/resvg-js. Install it:\n bun add @resvg/resvg-js");
|
|
254
|
+
}
|
|
255
|
+
if (kind === "swash") try {
|
|
256
|
+
return await createSwashRasterizer(await loadSwash());
|
|
257
|
+
} catch (e) {
|
|
258
|
+
throw new Error(`--renderer swash requires the @termless/swash-render native binding. Build it:
|
|
259
|
+
cd packages/swash-render && bun run build:prebuild
|
|
260
|
+
\nOriginal error: ${e instanceof Error ? e.message : String(e)}`);
|
|
261
|
+
}
|
|
262
|
+
if (kind === "browser") try {
|
|
263
|
+
return createBrowserRasterizer(await loadPlaywright());
|
|
264
|
+
} catch (e) {
|
|
265
|
+
throw new Error(`--renderer browser requires the optional playwright package. Install it:
|
|
266
|
+
bun add -d playwright
|
|
267
|
+
npx playwright install chromium
|
|
268
|
+
\nOriginal error: ${e instanceof Error ? e.message : String(e)}`);
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
return await createSwashRasterizer(await loadSwash());
|
|
272
|
+
} catch {
|
|
273
|
+
try {
|
|
274
|
+
return createResvgRasterizer(await loadResvg());
|
|
275
|
+
} catch {
|
|
276
|
+
try {
|
|
277
|
+
return createCanvasRasterizer(await loadCanvas());
|
|
278
|
+
} catch {
|
|
279
|
+
throw new Error("createGif/screenshot requires a renderer. Install one:\n @termless/swash-render (swash — cell-native, highest fidelity)\n bun add @resvg/resvg-js (resvg — cross-platform fallback)\n bun add @napi-rs/canvas (canvas — last-resort fallback)");
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
var resvgModule, canvasModule, swashModule, SWASH_EDGE_PADDING, playwrightModule;
|
|
285
|
+
var init_rasterizer = __esmMin((() => {
|
|
286
|
+
init_fonts();
|
|
287
|
+
resvgModule = null;
|
|
288
|
+
canvasModule = null;
|
|
289
|
+
swashModule = null;
|
|
290
|
+
SWASH_EDGE_PADDING = 4;
|
|
291
|
+
playwrightModule = null;
|
|
292
|
+
}));
|
|
293
|
+
//#endregion
|
|
294
|
+
export { selectRasterizer as n, init_rasterizer as t };
|
|
295
|
+
|
|
296
|
+
//# sourceMappingURL=rasterizer-CpEhJvdR.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rasterizer-CpEhJvdR.mjs","names":[],"sources":["../../termless/src/view/rasterizer.ts"],"sourcesContent":["/**\n * The **renderer** — how termless turns an SVG terminal frame into pixels.\n *\n * A *renderer* rasterizes the vector SVG produced by `screenshotSvg` into an\n * RGBA bitmap. termless ships two:\n *\n * - **`canvas`** — `@napi-rs/canvas` (Skia). High fidelity — truecolor, real\n * glyph shaping. Needs the native `@napi-rs/canvas` binding.\n * - **`resvg`** — `@resvg/resvg-js`. Cross-platform, no native binding,\n * lower fidelity.\n * - **`swash`** — `@termless/swash-render` (pure-Rust swash via napi-rs).\n * Browser-grade text + **color emoji** (sbix / CBDT / COLR), ~1.3 MB native\n * binding. swash rasterizes the *cell grid* directly (no SVG round-trip), so\n * its `Rasterizer` exposes {@link Rasterizer.rasterizeCells}; the SVG-input\n * methods delegate to `resvg` (swash does not parse SVG). The cells path is\n * the fidelity win — `Terminal.screenshot({ renderer: \"swash\" })` uses it.\n * - **`browser`** — headless Chromium via Playwright. The **highest-fidelity**\n * path: a real browser text engine gives Chrome-identical shaping, font\n * fallback, ligatures, and color emoji with zero per-glyph work. Playwright\n * is an **optional** dependency — `browser` throws a clear install hint when\n * it is absent, and is **never** the `auto` default (Chromium is a heavy,\n * hundreds-of-MB dependency). Opt-in only, for marketing assets and as a\n * fidelity oracle against `canvas`.\n *\n * `auto` (the default) uses `canvas` when its native binding loads, and falls\n * back to `resvg` otherwise. `--renderer` is a *force* override — the common\n * case never touches it. `browser` is opt-in only and never reached by `auto`.\n *\n * Raster outputs (`.png` / `.gif` / `.apng`) consult the renderer. `.svg` is\n * vector and `.html` defers to the browser canvas — neither rasterizes\n * server-side, so neither involves a renderer.\n */\n\nimport { bundledFontFiles, bundledFontsDir, BUNDLED_FONTS } from \"../render/fonts.ts\"\nimport { existsSync } from \"node:fs\"\nimport { join } from \"node:path\"\n\n/** How a raster frame is rasterized from SVG. */\nexport type RendererKind = \"canvas\" | \"resvg\" | \"swash\" | \"browser\" | \"auto\"\n\n/** A terminal whose cell grid a {@link Rasterizer} can read directly. */\nexport type CellGridSource = import(\"../terminal/types.ts\").TerminalReadable\n\n/** A rasterized RGBA bitmap. */\nexport interface RasterBitmap {\n /** RGBA pixel bytes, row-major, 4 bytes per pixel. */\n pixels: Uint8Array\n /** Bitmap width in pixels. */\n width: number\n /** Bitmap height in pixels. */\n height: number\n}\n\n/** A renderer: rasterizes one SVG frame into an RGBA bitmap. */\nexport interface Rasterizer {\n /** The concrete renderer this resolved to (`auto` collapses to one of these). */\n readonly kind: \"canvas\" | \"resvg\" | \"swash\" | \"browser\"\n /** Rasterize `svg` at `scale`× into an RGBA bitmap. */\n rasterize(svg: string, scale: number): Promise<RasterBitmap>\n /** Rasterize `svg` at `scale`× directly into PNG bytes. */\n toPng(svg: string, scale: number): Promise<Uint8Array>\n /**\n * Rasterize a terminal's cell grid directly into an RGBA bitmap — no SVG\n * round-trip. Present only on the `swash` renderer, whose fidelity edge\n * (color emoji) depends on consuming cells, not a flattened SVG.\n */\n rasterizeCells?(terminal: CellGridSource, scale: number): Promise<RasterBitmap>\n /** Rasterize a terminal's cell grid directly into PNG bytes. */\n cellsToPng?(terminal: CellGridSource, scale: number): Promise<Uint8Array>\n /**\n * Release any long-lived resources the renderer holds. Present only on the\n * `browser` renderer, which keeps a headless-Chromium instance alive across\n * frames — callers that finish a batch (`createGif`, `createApng`) should\n * `await rasterizer.dispose?.()` so Chromium is closed. The pure-native\n * renderers hold nothing and omit it.\n */\n dispose?(): Promise<void>\n}\n\n// =============================================================================\n// resvg renderer\n// =============================================================================\n\nlet resvgModule: { Resvg: any } | null = null\n\nasync function loadResvg(): Promise<{ Resvg: any }> {\n if (resvgModule) return resvgModule\n resvgModule = (await import(\"@resvg/resvg-js\")) as { Resvg: any }\n return resvgModule\n}\n\nfunction createResvgRasterizer(Resvg: { Resvg: any }): Rasterizer {\n const fontFiles = bundledFontFiles()\n const build = (svg: string, scale: number) =>\n new Resvg.Resvg(svg, {\n fitTo: { mode: \"zoom\" as const, value: scale },\n // Bundled emoji + symbol fallback faces — without these, resvg renders\n // emoji / rarer symbol code points as `.notdef` tofu.\n font: { loadSystemFonts: true, defaultFontFamily: \"Menlo\", fontFiles },\n })\n return {\n kind: \"resvg\",\n async rasterize(svg, scale) {\n const rendered = build(svg, scale).render()\n return { pixels: new Uint8Array(rendered.pixels), width: rendered.width, height: rendered.height }\n },\n async toPng(svg, scale) {\n return build(svg, scale).render().asPng()\n },\n }\n}\n\n// =============================================================================\n// canvas renderer (@napi-rs/canvas — Skia)\n// =============================================================================\n\nlet canvasModule: { createCanvas: any; Image: any } | null = null\n\nasync function loadCanvas(): Promise<{ createCanvas: any; Image: any }> {\n if (canvasModule) return canvasModule\n const mod = (await import(\"@napi-rs/canvas\")) as {\n createCanvas: any\n Image: any\n GlobalFonts: { registerFromPath(path: string, family: string): void }\n }\n // Register the bundled fallback faces process-wide BEFORE any SVG with text\n // is rasterized — without these, `@napi-rs/canvas` falls back to system\n // fonts and every symbol / emoji / box-drawing glyph renders as tofu. The\n // `resvg` rasterizer wires the same faces via `font.fontFiles`; this is the\n // canvas-side equivalent. Idempotent (registerFromPath is keyed by family).\n const dir = bundledFontsDir()\n for (const { file, family } of BUNDLED_FONTS) {\n const path = join(dir, file)\n if (existsSync(path)) mod.GlobalFonts.registerFromPath(path, family)\n }\n canvasModule = mod\n return canvasModule\n}\n\n/** Read the intrinsic `width`/`height` from an SVG's root attributes. */\nfunction svgIntrinsicSize(svg: string): { width: number; height: number } {\n const w = svg.match(/<svg[^>]*\\bwidth=\"([\\d.]+)\"/)?.[1]\n const h = svg.match(/<svg[^>]*\\bheight=\"([\\d.]+)\"/)?.[1]\n return { width: w ? Math.round(Number(w)) : 0, height: h ? Math.round(Number(h)) : 0 }\n}\n\nfunction createCanvasRasterizer(mod: { createCanvas: any; Image: any }): Rasterizer {\n const draw = (svg: string, scale: number) => {\n const img = new mod.Image()\n img.src = Buffer.from(svg)\n const intrinsic = svgIntrinsicSize(svg)\n const width = Math.max(1, Math.round((img.width || intrinsic.width) * scale))\n const height = Math.max(1, Math.round((img.height || intrinsic.height) * scale))\n const canvas = mod.createCanvas(width, height)\n const ctx = canvas.getContext(\"2d\")\n ctx.drawImage(img, 0, 0, width, height)\n return { canvas, width, height }\n }\n return {\n kind: \"canvas\",\n async rasterize(svg, scale) {\n const { canvas, width, height } = draw(svg, scale)\n const data = canvas.getContext(\"2d\").getImageData(0, 0, width, height).data\n return { pixels: new Uint8Array(data), width, height }\n },\n async toPng(svg, scale) {\n const { canvas } = draw(svg, scale)\n return new Uint8Array(canvas.toBuffer(\"image/png\"))\n },\n }\n}\n\n// =============================================================================\n// swash renderer (@termless/swash-render — pure-Rust swash via napi-rs)\n// =============================================================================\n\ntype SwashModule = typeof import(\"../../packages/swash-render/src/index.ts\")\n\nlet swashModule: SwashModule | null = null\n\nasync function loadSwash(): Promise<SwashModule> {\n if (swashModule) return swashModule\n const mod = (await import(\"../../packages/swash-render/src/index.ts\")) as SwashModule\n // Probe the native binding now so `selectRasterizer` can fail fast.\n if (!mod.swashRenderAvailable()) {\n throw new Error(\"@termless/swash-render native binding is not built\")\n }\n swashModule = mod\n return mod\n}\n\n/**\n * The swash rasterizer. Its fidelity edge — color emoji — lives in the\n * **cells path** ({@link Rasterizer.rasterizeCells}); swash does not parse\n * SVG, so the SVG-input methods delegate to `resvg` as a faithful fallback\n * for the SVG-based gif / apng pipelines.\n */\nconst SWASH_EDGE_PADDING = 4\n\nasync function createSwashRasterizer(mod: SwashModule): Promise<Rasterizer> {\n // resvg is the SVG-input fallback — swash only consumes the cell grid.\n const svgFallback = createResvgRasterizer(await loadResvg())\n const toBitmap = (terminal: CellGridSource, scale: number): RasterBitmap => {\n const s = Math.max(1, scale)\n const bmp = mod.renderCells(terminal, {\n cellWidth: 9.6 * s,\n cellHeight: 20 * s,\n padding: Math.round(SWASH_EDGE_PADDING * s),\n })\n return { pixels: new Uint8Array(bmp.pixels), width: bmp.width, height: bmp.height }\n }\n return {\n kind: \"swash\",\n rasterize: svgFallback.rasterize,\n toPng: svgFallback.toPng,\n async rasterizeCells(terminal, scale) {\n return toBitmap(terminal, scale)\n },\n async cellsToPng(terminal, scale) {\n const { pixels, width, height } = toBitmap(terminal, scale)\n const UPNG = (await import(\"upng-js\")) as typeof import(\"upng-js\")\n const ab = pixels.buffer.slice(pixels.byteOffset, pixels.byteOffset + pixels.byteLength) as ArrayBuffer\n return new Uint8Array(UPNG.encode([ab], width, height, 0))\n },\n }\n}\n\n// =============================================================================\n// browser renderer (headless Chromium via Playwright)\n// =============================================================================\n\n/**\n * The shape of the `playwright` module's `chromium` namespace we depend on —\n * a structural subset, so the import does not require Playwright's types to\n * be installed (Playwright is an optional dependency).\n */\ninterface PlaywrightChromium {\n launch(opts?: { headless?: boolean; args?: string[] }): Promise<PlaywrightBrowser>\n}\ninterface PlaywrightBrowser {\n newPage(opts?: { viewport?: { width: number; height: number }; deviceScaleFactor?: number }): Promise<PlaywrightPage>\n close(): Promise<void>\n}\ninterface PlaywrightPage {\n setContent(html: string, opts?: { waitUntil?: string }): Promise<void>\n evaluate(fn: () => unknown): Promise<unknown>\n screenshot(opts?: { type?: \"png\"; omitBackground?: boolean }): Promise<Buffer>\n close(): Promise<void>\n}\n\nlet playwrightModule: { chromium: PlaywrightChromium } | null = null\n\n/**\n * Lazily import `playwright`. Playwright is an **optional** dependency — a\n * default `bun install` omits it. Throws a clear, actionable error if absent,\n * naming both the package install and the browser-download step.\n */\nasync function loadPlaywright(): Promise<{ chromium: PlaywrightChromium }> {\n if (playwrightModule) return playwrightModule\n // Indirect specifier so bundlers / static analysers do not treat `playwright`\n // as a hard dependency to resolve at build time.\n const specifier = \"playwright\"\n playwrightModule = (await import(specifier)) as { chromium: PlaywrightChromium }\n return playwrightModule\n}\n\n/** Read intrinsic width/height from an SVG root — reused for the viewport. */\nfunction browserSvgSize(svg: string): { width: number; height: number } {\n const { width, height } = svgIntrinsicSize(svg)\n return { width: Math.max(1, width), height: Math.max(1, height) }\n}\n\n/**\n * Inline the bundled `@font-face` faces into an SVG if it does not already\n * carry them. The `record` GIF/APNG pipeline produces frame SVGs without\n * `embedFonts`, so Chromium would otherwise fall back to host fonts (or tofu\n * for symbol / emoji code points). Embedding makes the page deterministic.\n */\nasync function ensureEmbeddedFonts(svg: string): Promise<string> {\n if (svg.includes(\"@font-face\")) return svg\n const { embeddedFontFaceDefs } = await import(\"../render/svg.ts\")\n const defs = embeddedFontFaceDefs()\n if (!defs) return svg\n // Insert the <defs> block immediately after the opening <svg ...> tag.\n return svg.replace(/(<svg[^>]*>)/, `$1${defs}`)\n}\n\n/** Wrap an SVG in a minimal full-bleed HTML document for Chromium. */\nfunction browserHtml(svg: string, width: number, height: number): string {\n return (\n `<!doctype html><html><head><meta charset=\"utf-8\"><style>` +\n `html,body{margin:0;padding:0;width:${width}px;height:${height}px;overflow:hidden;background:transparent}` +\n `svg{display:block}` +\n `</style></head><body>${svg}</body></html>`\n )\n}\n\n/**\n * The `browser` rasterizer — rasterizes SVG frames via headless Chromium.\n *\n * The browser instance is launched once and reused across every `rasterize` /\n * `toPng` call; `dispose()` closes it. This is the absolute-max-fidelity path\n * (Chrome-grade shaping, fallback, ligatures, color emoji) — opt-in only.\n */\nfunction createBrowserRasterizer(playwright: { chromium: PlaywrightChromium }): Rasterizer {\n let browserPromise: Promise<PlaywrightBrowser> | null = null\n\n const getBrowser = (): Promise<PlaywrightBrowser> => {\n if (!browserPromise) {\n browserPromise = playwright.chromium\n .launch({ headless: true, args: [\"--force-color-profile=srgb\"] })\n .catch((cause: unknown) => {\n browserPromise = null\n throw new Error(\n \"--renderer browser could not launch Chromium. Install the browser binary:\\n\" +\n \" npx playwright install chromium\",\n { cause },\n )\n })\n }\n return browserPromise\n }\n\n const shot = async (svg: string, scale: number): Promise<{ png: Uint8Array; width: number; height: number }> => {\n const { width, height } = browserSvgSize(svg)\n const embedded = await ensureEmbeddedFonts(svg)\n const browser = await getBrowser()\n const page = await browser.newPage({\n viewport: { width, height },\n deviceScaleFactor: Math.max(1, scale),\n })\n try {\n await page.setContent(browserHtml(embedded, width, height), { waitUntil: \"load\" })\n // Block until embedded @font-face faces have finished loading — without\n // this, the first frames screenshot mid-font-swap (FOUT) and glyphs\n // render in a fallback face.\n await page.evaluate(() => (document as { fonts: { ready: Promise<unknown> } }).fonts.ready)\n const buf = await page.screenshot({ type: \"png\", omitBackground: false })\n return {\n png: new Uint8Array(buf),\n width: Math.round(width * Math.max(1, scale)),\n height: Math.round(height * Math.max(1, scale)),\n }\n } finally {\n await page.close()\n }\n }\n\n return {\n kind: \"browser\",\n async rasterize(svg, scale) {\n // Decode the PNG back to RGBA via upng — the GIF/APNG encoders consume\n // raw pixels, not PNG bytes.\n const { png, width, height } = await shot(svg, scale)\n const UPNG = (await import(\"upng-js\")) as typeof import(\"upng-js\")\n const ab = png.buffer.slice(png.byteOffset, png.byteOffset + png.byteLength) as ArrayBuffer\n const decoded = UPNG.decode(ab)\n const rgbaFrames = UPNG.toRGBA8(decoded)\n return {\n pixels: new Uint8Array(rgbaFrames[0] ?? new ArrayBuffer(0)),\n width: decoded.width,\n height: decoded.height,\n }\n },\n async toPng(svg, scale) {\n return (await shot(svg, scale)).png\n },\n async dispose() {\n if (!browserPromise) return\n const pending = browserPromise\n browserPromise = null\n try {\n await (await pending).close()\n } catch {\n // Browser may have already exited — nothing to release.\n }\n },\n }\n}\n\n// =============================================================================\n// Selection\n// =============================================================================\n\n/**\n * Resolve a {@link RendererKind} to a concrete {@link Rasterizer}.\n *\n * - `canvas` — `@napi-rs/canvas`; throws a clear error if the binding is absent.\n * - `resvg` — `@resvg/resvg-js`; throws a clear error if it is absent.\n * - `browser` — headless Chromium via the optional `playwright` package;\n * throws a clear install hint if absent. Never reached from `auto`.\n * - `auto` — `canvas` when its binding loads, else `resvg`.\n */\nexport async function selectRasterizer(kind: RendererKind = \"auto\"): Promise<Rasterizer> {\n if (kind === \"canvas\") {\n try {\n return createCanvasRasterizer(await loadCanvas())\n } catch {\n throw new Error(\"--renderer canvas requires @napi-rs/canvas. Install it:\\n bun add @napi-rs/canvas\")\n }\n }\n if (kind === \"resvg\") {\n try {\n return createResvgRasterizer(await loadResvg())\n } catch {\n throw new Error(\"--renderer resvg requires @resvg/resvg-js. Install it:\\n bun add @resvg/resvg-js\")\n }\n }\n if (kind === \"swash\") {\n try {\n return await createSwashRasterizer(await loadSwash())\n } catch (e) {\n throw new Error(\n \"--renderer swash requires the @termless/swash-render native binding. Build it:\\n\" +\n \" cd packages/swash-render && bun run build:prebuild\\n\" +\n `\\nOriginal error: ${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }\n if (kind === \"browser\") {\n try {\n return createBrowserRasterizer(await loadPlaywright())\n } catch (e) {\n throw new Error(\n \"--renderer browser requires the optional playwright package. Install it:\\n\" +\n \" bun add -d playwright\\n\" +\n \" npx playwright install chromium\\n\" +\n `\\nOriginal error: ${e instanceof Error ? e.message : String(e)}`,\n )\n }\n }\n // auto — prefer swash, fall back to resvg, then canvas.\n // `browser` is opt-in only: never reached from `auto` (Chromium is heavy).\n //\n // swash is the highest-fidelity renderer: it consumes the cell grid\n // directly (no SVG round-trip) and composites full-colour emoji from their\n // native colour tables. Its bundled font chain — JetBrains Mono, Noto Sans\n // Symbols 2, Symbols Nerd Font, Noto Emoji — covers box-drawing, sigil\n // icons, Nerd Font glyphs and emoji, so a km board renders cleanly\n // (verified). For SVG-only inputs swash's `rasterize` delegates to resvg,\n // so `auto`→swash is never worse than resvg and strictly better whenever a\n // frame carries a cell snapshot (the `record` GIF path).\n //\n // resvg is the cross-platform fallback (no native build): handed the same\n // bundled fonts via `font.fontFiles`, it renders the SVG path correctly.\n // The `canvas` rasterizer is last — its `drawImage` SVG path ignores\n // registered fonts, so glyphs the system font lacks render as tofu.\n try {\n return await createSwashRasterizer(await loadSwash())\n } catch {\n try {\n return createResvgRasterizer(await loadResvg())\n } catch {\n try {\n return createCanvasRasterizer(await loadCanvas())\n } catch {\n throw new Error(\n \"createGif/screenshot requires a renderer. Install one:\\n\" +\n \" @termless/swash-render (swash — cell-native, highest fidelity)\\n\" +\n \" bun add @resvg/resvg-js (resvg — cross-platform fallback)\\n\" +\n \" bun add @napi-rs/canvas (canvas — last-resort fallback)\",\n )\n }\n }\n }\n}\n"],"mappings":"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"}
|