From d21db88693c897ce0fc82f5b670c1001b86cd9b2 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Sun, 2 Mar 2025 21:54:19 -0500 Subject: [PATCH 01/78] get core working with Renderer 3 --- package.json | 2 +- pnpm-lock.yaml | 12 ++++---- src/elementNode.ts | 72 ++++++++++++++++++++++++++----------------- src/intrinsicTypes.ts | 26 ++++++---------- 4 files changed, 60 insertions(+), 52 deletions(-) diff --git a/package.json b/package.json index 50ac974..a32f3e8 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "author": "Chris Lorenzo", "license": "Apache-2.0", "peerDependencies": { - "@lightningjs/renderer": "^2.13.0" + "@lightningjs/renderer": "^3.0.0-beta3" }, "devDependencies": { "@eslint/js": "^9.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70fa777..1b9a6b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@lightningjs/renderer': - specifier: ^2.6.2 - version: 2.6.2 + specifier: ^3.0.0-beta3 + version: 3.0.0-beta3 devDependencies: '@eslint/js': specifier: ^9.15.0 @@ -267,9 +267,9 @@ packages: resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} engines: {node: '>=18'} - '@lightningjs/renderer@2.6.2': - resolution: {integrity: sha512-agu9jV8hhbF/Ld8BJSogOJIQZBoSUzow+3BtP4NptqC3l1aV6RQr1rwpXQF12ITYjUi3CrfA7eV76GIiLAkOAA==} - engines: {node: '>= 20.9.0', npm: '>= 10.0.0', pnpm: '>= 8.9.2'} + '@lightningjs/renderer@3.0.0-beta3': + resolution: {integrity: sha512-zesySXNd4TVVegR2qd3Ps8KPjN93NUo035AXji0p4pPmPLJHr/ZDD/S1cmxbRMW33FI9FpDzGIzoWo8pcwpuPA==} + engines: {node: '>= 18.0.0', npm: '>= 10.0.0', pnpm: '>= 8.9.2'} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -2035,7 +2035,7 @@ snapshots: '@inquirer/figures@1.0.7': {} - '@lightningjs/renderer@2.6.2': {} + '@lightningjs/renderer@3.0.0-beta3': {} '@nodelib/fs.scandir@2.1.5': dependencies: diff --git a/src/elementNode.ts b/src/elementNode.ts index b53a6bb..01bcfee 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -34,13 +34,11 @@ import type { INode, INodeAnimateProps, INodeProps, - LinearGradientEffectProps, ITextNodeProps, IAnimationController, - EffectDescUnion, - ShaderController, - RadialGradientEffectProps, - RadialProgressEffectProps, + LinearGradientProps, + RadialGradientProps, + ITextNode, } from '@lightningjs/renderer'; import { assertTruthy } from '@lightningjs/renderer/utils'; import { NodeType } from './nodeTypes.js'; @@ -57,18 +55,40 @@ function runLayout() { } } -function convertEffectsToShader( - node: ElementNode, - styleEffects: StyleEffects, -): ShaderController<'DynamicShader'> { - const effects: EffectDescUnion[] = []; - for (const type in styleEffects) { - // @ts-ignore getting the right type info is hard - effects.push(renderer.createEffect(type, styleEffects[type], type)); - } - return renderer.createShader('DynamicShader', { effects }); +type ShaderProps = Record; +function convertEffectsToShader(_node: ElementNode, v: StyleEffects): any { + const { border, shadow, rounded, radius } = v; + const props: ShaderProps = {}; + + const parseAndAssignProps = (prefix: string, obj: Record) => { + Object.entries(obj).forEach(([key, value]) => { + props[`${prefix}-${key}`] = value; + }); + }; + + if (border) parseAndAssignProps('border', border); + if (shadow) parseAndAssignProps('shadow', shadow); + if (rounded) Object.assign(props, rounded, radius); + + const typeParts = ['rounded']; + if (border) typeParts.push('WithBorder'); + if (shadow) typeParts.push('WithShadow'); + + return renderer.createShader(typeParts.join(''), props); } +// function convertEffectsToShader( +// _node: ElementNode, +// styleEffects: StyleEffects, +// ): CoreShaderNode { +// const effects: CoreShaderNode[] = []; +// for (const type in styleEffects) { +// // @ts-ignore getting the right type info is hard +// effects.push(renderer.createEffect(type, styleEffects[type], type)); +// } +// return renderer.createShader('DynamicShader', { effects }); +// } + function updateShaderEffects( node: ElementNode, styleEffects: StyleEffects, @@ -207,7 +227,7 @@ export interface ElementNode extends RendererNode { | number | ((this: ElementNode, elm: ElementNode) => boolean | void); forwardStates?: boolean; - lng: INode | Partial; + lng: INode | Partial | ITextNode; ref?: ElementNode | ((node: ElementNode) => void) | undefined; rendered: boolean; renderer?: RendererMain; @@ -241,9 +261,8 @@ export interface ElementNode extends RendererNode { | 'center' | 'spaceBetween' | 'spaceEvenly'; - linearGradient?: LinearGradientEffectProps; - radialGradient?: RadialGradientEffectProps; - radialProgress?: RadialProgressEffectProps; + linearGradient?: LinearGradientProps; + radialGradient?: RadialGradientProps; marginBottom?: number; marginLeft?: number; marginRight?: number; @@ -1008,25 +1027,20 @@ Object.defineProperties(ElementNode.prototype, { borderRight: borderAccessor('Right'), borderTop: borderAccessor('Top'), borderBottom: borderAccessor('Bottom'), - linearGradient: - createEffectAccessor('linearGradient'), - radialGradient: - createEffectAccessor('radialGradient'), - radialProgress: createEffectAccessor( - 'radialProgressGradient', - ), + linearGradient: createEffectAccessor('linearGradient'), + radialGradient: createEffectAccessor('radialGradient'), borderRadius: { set(this: ElementNode, radius: BorderRadius) { this.effects = this.effects ? { ...this.effects, - radius: { radius }, + rounded: { radius }, } - : { radius: { radius } }; + : { rounded: { radius } }; }, get(this: ElementNode): BorderRadius | undefined { - return this.effects?.radius?.radius; + return this.effects?.rounded?.radius; }, }, }); diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index f505d06..a6d3843 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -1,14 +1,11 @@ import { - type FadeOutEffectProps, - type GlitchEffectProps, - type GrayscaleEffectProps, type AnimationSettings as RendererAnimationSettings, - type LinearGradientEffectProps, - type RadialGradientEffectProps, - type RadialProgressEffectProps, + type LinearGradientProps, + type RadialGradientProps, type ITextNodeProps, - type HolePunchEffectProps, + type HolePunchProps, type IAnimationController, + type ShadowProps, NodeLoadedPayload, NodeFailedPayload, } from '@lightningjs/renderer'; @@ -31,19 +28,16 @@ export type BorderStyle = number | BorderStyleObject; export type BorderRadius = number | number[]; export interface Effects { - fadeOut?: FadeOutEffectProps; - linearGradient?: LinearGradientEffectProps; - radialGradient?: RadialGradientEffectProps; - radialProgressGradient?: RadialProgressEffectProps; - grayscale?: GrayscaleEffectProps; - glitch?: GlitchEffectProps; - radialProgress?: RadialProgressEffectProps; - holePunch?: HolePunchEffectProps; + linearGradient?: LinearGradientProps; + radialGradient?: RadialGradientProps; + holePunch?: HolePunchProps; + shadow?: ShadowProps; } export interface BorderEffects { radius?: { radius: BorderRadius }; - border?: BorderStyle; + rounded?: { radius: BorderRadius }; + border?: BorderStyleObject; borderTop?: BorderStyle; borderRight?: BorderStyle; borderBottom?: BorderStyle; From 96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 19 Mar 2025 17:19:18 -0400 Subject: [PATCH 02/78] :rocket: bump version v3.0.0-0 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe8cf12..89054d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,16 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.7.6...v3.0.0-0) + +- get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) + #### [v2.7.6](https://github.com/lightning-tv/core/compare/v2.7.6-0...v2.7.6) +> 19 March 2025 + - add flexwrap and alignSelf [`13e6236`](https://github.com/lightning-tv/core/commit/13e6236c083c294be8823c2aa7b7fad7a74691c1) +- :rocket: bump version v2.7.6 [`6a342e4`](https://github.com/lightning-tv/core/commit/6a342e45855ebe84e21a82552a6f114b3182f7bb) #### [v2.7.6-0](https://github.com/lightning-tv/core/compare/v2.7.5...v2.7.6-0) diff --git a/package.json b/package.json index a32f3e8..7ef0383 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "2.7.6", + "version": "3.0.0-0", "description": "Lightning TV Core for Universal Renderers", "type": "module", "main": "./dist/src/index.js", From 60b0fd1a3cd66cfdf4f8f0cc0f02fba943c78e92 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 27 Mar 2025 02:23:16 +0100 Subject: [PATCH 03/78] =?UTF-8?q?Remove=20radius=20effect=E2=80=94replaced?= =?UTF-8?q?=20with=20rounded=20(#37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/elementNode.ts | 4 ++-- src/intrinsicTypes.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 01bcfee..1ddd0f5 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -57,7 +57,7 @@ function runLayout() { type ShaderProps = Record; function convertEffectsToShader(_node: ElementNode, v: StyleEffects): any { - const { border, shadow, rounded, radius } = v; + const { border, shadow, rounded } = v; const props: ShaderProps = {}; const parseAndAssignProps = (prefix: string, obj: Record) => { @@ -68,7 +68,7 @@ function convertEffectsToShader(_node: ElementNode, v: StyleEffects): any { if (border) parseAndAssignProps('border', border); if (shadow) parseAndAssignProps('shadow', shadow); - if (rounded) Object.assign(props, rounded, radius); + if (rounded) Object.assign(props, rounded); const typeParts = ['rounded']; if (border) typeParts.push('WithBorder'); diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index a6d3843..989269f 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -35,7 +35,6 @@ export interface Effects { } export interface BorderEffects { - radius?: { radius: BorderRadius }; rounded?: { radius: BorderRadius }; border?: BorderStyleObject; borderTop?: BorderStyle; From 374b15c210147c77892a25b0ade7fbe7312128dc Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 26 Mar 2025 23:06:28 -0400 Subject: [PATCH 04/78] update renderer --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 7ef0383..6dfc388 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "author": "Chris Lorenzo", "license": "Apache-2.0", "peerDependencies": { - "@lightningjs/renderer": "^3.0.0-beta3" + "@lightningjs/renderer": "^3.0.0-beta4" }, "devDependencies": { "@eslint/js": "^9.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b9a6b6..18c9e0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@lightningjs/renderer': - specifier: ^3.0.0-beta3 - version: 3.0.0-beta3 + specifier: ^3.0.0-beta4 + version: 3.0.0-beta4 devDependencies: '@eslint/js': specifier: ^9.15.0 @@ -267,8 +267,8 @@ packages: resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} engines: {node: '>=18'} - '@lightningjs/renderer@3.0.0-beta3': - resolution: {integrity: sha512-zesySXNd4TVVegR2qd3Ps8KPjN93NUo035AXji0p4pPmPLJHr/ZDD/S1cmxbRMW33FI9FpDzGIzoWo8pcwpuPA==} + '@lightningjs/renderer@3.0.0-beta4': + resolution: {integrity: sha512-a1YM99eiTHyok1Vp29G5+526JTML4XOxInfiChFjSV4byJ02FoAt1ou76B/gCMIWpOKg07lhDk5c+I/RamugFQ==} engines: {node: '>= 18.0.0', npm: '>= 10.0.0', pnpm: '>= 8.9.2'} '@nodelib/fs.scandir@2.1.5': @@ -2035,7 +2035,7 @@ snapshots: '@inquirer/figures@1.0.7': {} - '@lightningjs/renderer@3.0.0-beta3': {} + '@lightningjs/renderer@3.0.0-beta4': {} '@nodelib/fs.scandir@2.1.5': dependencies: From f550053e751193495e866c8c67628c3def5430bc Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Fri, 28 Mar 2025 12:58:34 +0100 Subject: [PATCH 05/78] Experimental DOM Renderer (#34) * Init work on dom renderer * Intercept node setters * Add a proxy DOMNode class * Animate more props * Cleanup * Update DOM property setters to not ignore zero values * Fix line-height prop * Apply font settings from config * Add DOMText class proxy * Handle destroy * Call node destroy method in DOMNode destroy * Handle border and radius shaders * Support image masks * Support maxLines * Correct background-size to strech * Support textures * Set default line height to 1 * Refactor updating node styles to imperative approach Support vertical and horizontal gradients * Animate color * Handle animation repeat * Fix applying gradients * Support diagonal gradients * Use overflow: hidden for text with contain * Improve gradients * Add empty core renderer to avoid rendering to the canvas * Add a noop text rendering engine * Measure text rect with dom elements * get core working with Renderer 3 * Add domRendering option to configuration for conditional rendering * Update to lightning 3 * Fix rendering of new shaders * Remove CanvasShaderNode * Fix measuring text elements * Implement lng.RendererMain interface instead of extending it * Replace instanciating CoreShaderNode with plain object. * Always use the dom engines * Remove any usage of lightning internals and custom rendering engines implementations * Remove usage of internal lightning types * Update animation handling to modify styles after completion instead of using 'forwards' fill * Replace unhandled getters with undefined * Implement a custom animation system to replace waapi * Derive paused state from pausedTime field * Fix interpolating colors * Add renderer interfaces to abstract the part of lightning interface implemented by dom renderer * Correct IRendererTextNode inferface * Add absX and absY to Node and Text interfaces * Typecast RendererMain to IRendererMain The type errors are too deep, complex and stupid to handle * Format * Use stage root el as dom root * Calculate height and width of text nodes for flex layout * Fix setting the background image when src is an empty string * Use transform-translate instead of top/left for positioning to improve performance * remove wordBreak property from DOMText class * Revert "remove wordBreak property from DOMText class" This reverts commit 9eab664e41094be67ee9975728b145386eee3706. * Mock shader manager * Correct calculating element's size --------- Co-authored-by: Chris Lorenzo --- src/config.ts | 2 + src/domRenderer.ts | 1209 ++++++++++++++++++++++++++++++++++++++++++ src/elementNode.ts | 34 +- src/index.ts | 1 + src/lightningInit.ts | 144 +++-- 5 files changed, 1345 insertions(+), 45 deletions(-) create mode 100644 src/domRenderer.ts diff --git a/src/config.ts b/src/config.ts index 0a8d753..71b098d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -16,6 +16,7 @@ export interface Config { rendererOptions?: Partial; setActiveElement: (elm: ElementNode) => void; focusStateKey: DollarString; + domRendering: boolean; lockStyles?: boolean; } @@ -44,4 +45,5 @@ export const Config: Config = { }, setActiveElement: () => {}, focusStateKey: '$focus', + domRendering: false, }; diff --git a/src/domRenderer.ts b/src/domRenderer.ts new file mode 100644 index 0000000..5df1dab --- /dev/null +++ b/src/domRenderer.ts @@ -0,0 +1,1209 @@ +/* + +Experimental DOM renderer + +*/ + +import * as lng from '@lightningjs/renderer'; + +import { Config } from './config.js'; +import { + IRendererShader, + IRendererStage, + IRendererShaderProps, + IRendererTextureProps, + IRendererTexture, + IRendererMain, + IRendererNode, + IRendererNodeProps, + IRendererTextNode, + IRendererTextNodeProps, +} from './lightningInit.js'; +import { EventEmitter } from '@lightningjs/renderer/utils'; + +const colorToRgba = (c: number) => + `rgba(${(c >> 24) & 0xff},${(c >> 16) & 0xff},${(c >> 8) & 0xff},${(c & 0xff) / 255})`; + +/* + Animations +*/ +type AnimationTask = { + node: DOMNode; + propsStart: Record; + propsEnd: Record; + timeStart: number; + timeEnd: number; + settings: Required; + iteration: number; + pausedTime: number | null; +}; + +let animationTasks: AnimationTask[] = []; +let animationFrameRequested = false; + +function requestAnimationUpdate() { + if (!animationFrameRequested && animationTasks.length > 0) { + animationFrameRequested = true; + requestAnimationFrame(updateAnimations); + } +} + +function updateAnimations(time: number) { + animationFrameRequested = false; + + /* + tasks are iterated in insertion order + so that the later task will override the earlier ones + */ + for (let i = 0; i < animationTasks.length; i++) { + let task = animationTasks[i]!; + if (task.pausedTime != null) continue; + + let elapsed = time - task.timeStart; + + // Still in delay period + if (elapsed < task.settings.delay) { + requestAnimationUpdate(); + continue; + } + + let activeTime = elapsed - task.settings.delay; + + if (activeTime >= task.settings.duration) { + // Start next iteration + if (task.settings.loop || task.iteration < task.settings.repeat - 1) { + task.iteration++; + task.timeStart = time - task.settings.delay; + if (task.settings.repeatDelay > 0) { + task.timeStart += task.settings.repeatDelay; + } + requestAnimationUpdate(); + } + // Animation complete + else { + Object.assign(task.node.props, task.propsEnd); + updateNodeStyles(task.node); + animationTasks.splice(i, 1); + i--; + } + continue; + } + + /* + Update props and styles + */ + let t = applyEasing( + activeTime / task.settings.duration, + task.settings.easing, + ); + + for (let prop in task.propsEnd) { + let fn = prop.startsWith('color') ? interpolateColor : interpolate; + (task.node.props as any)[prop] = fn( + task.propsStart[prop]!, + task.propsEnd[prop]!, + t, + ); + } + + updateNodeStyles(task.node); + } + + requestAnimationUpdate(); +} + +function applyEasing(progress: number, easing: string): number { + switch (easing) { + case 'linear': + default: + return progress; + case 'ease-in': + return progress * progress; + case 'ease-out': + return progress * (2 - progress); + case 'ease-in-out': + return progress < 0.5 + ? 2 * progress * progress + : -1 + (4 - 2 * progress) * progress; + } +} + +function interpolate(start: number, end: number, t: number): number { + return start + (end - start) * t; +} + +function interpolateColor(start: number, end: number, t: number): number { + return ( + (interpolate((start >> 24) & 0xff, (end >> 24) & 0xff, t) << 24) | + (interpolate((start >> 16) & 0xff, (end >> 16) & 0xff, t) << 16) | + (interpolate((start >> 8) & 0xff, (end >> 8) & 0xff, t) << 8) | + interpolate(start & 0xff, end & 0xff, t) + ); +} + +class AnimationController implements lng.IAnimationController { + state: lng.AnimationControllerState = 'paused'; + + constructor(public task: AnimationTask) {} + + start() { + if (this.task.pausedTime != null) { + this.task.timeStart += performance.now() - this.task.pausedTime; + this.task.pausedTime = null; + } else { + this.task.timeStart = performance.now(); + } + requestAnimationUpdate(); + return this; + } + pause() { + this.task.pausedTime = performance.now(); + return this; + } + stop() { + let index = animationTasks.indexOf(this.task); + if (index !== -1) { + animationTasks.splice(index, 1); + } + return this; + } + + restore() { + return this; + } + waitUntilStopped() { + return Promise.resolve(); + } + on() { + return this; + } + once() { + return this; + } + off() { + return this; + } + emit() { + return this; + } +} + +function animate( + this: DOMNode, + props: Partial>, + settings: Partial, +): lng.IAnimationController { + let fullSettings: Required = { + duration: settings.duration ?? 300, + delay: settings.delay ?? 0, + easing: settings.easing ?? 'linear', + loop: settings.loop ?? false, + repeat: settings.repeat ?? 1, + repeatDelay: settings.repeatDelay ?? 0, + stopMethod: false, + }; + + let now = performance.now(); + + // Create the animation task + let task: AnimationTask = { + node: this, + propsStart: {}, + propsEnd: {}, + timeStart: now, + timeEnd: now + fullSettings.delay + fullSettings.duration, + settings: fullSettings, + iteration: 0, + pausedTime: null, + }; + + for (let [prop, value] of Object.entries(props)) { + if (value != null && typeof value === 'number') { + task.propsStart[prop] = (this.props as any)[prop]; + task.propsEnd[prop] = value; + } + } + + animationTasks.push(task); + + return new AnimationController(task); +} + +let elMap = new WeakMap(); + +function updateNodeParent(node: DOMNode | DOMText) { + if (node.parent != null) { + elMap.get(node.parent as DOMNode)!.appendChild(node.el); + } else { + document.body.appendChild(node.el); + } +} + +function getNodeStyles(node: Readonly): string { + let style = 'position: absolute;'; + + if (node.alpha !== 1) style += `opacity: ${node.alpha};`; + + if (node.width !== 0) style += `width: ${node.width}px;`; + + if (node.height !== 0) style += `height: ${node.height}px;`; + + if (node.zIndex !== 0) { + style += `z-index: ${node.zIndex};`; + } + + if (node.clipping) { + style += `overflow: hidden;`; + } + + // Transform + { + let transform = ''; + + let { x, y } = node; + + if (node.mountX != null) { + x -= (node.width ?? 0) * node.mountX; + } + + if (node.mountY != null) { + y -= (node.height ?? 0) * node.mountY; + } + + if (x !== 0) transform += `translateX(${x}px)`; + + if (y !== 0) transform += `translateY(${y}px)`; + + if (node.rotation !== 0) transform += `rotate(${node.rotation}rad)`; + + if (node.scale !== 1) transform += `scale(${node.scale})`; + else { + if (node.scaleX !== 1) transform += `scaleX(${node.scaleX})`; + if (node.scaleY !== 1) transform += `scaleY(${node.scaleY})`; + } + + if (transform.length > 0) { + style += `transform: ${transform};`; + } + } + + // + if (node instanceof DOMText) { + if (node.color != null && node.color !== 0) { + style += `color: ${colorToRgba(node.color)};`; + } + + if (node.fontFamily) style += `font-family: ${node.fontFamily};`; + if (node.fontSize) style += `font-size: ${node.fontSize}px;`; + if (node.fontStyle !== 'normal') style += `font-style: ${node.fontStyle};`; + if (node.fontWeight !== 'normal') + style += `font-weight: ${node.fontWeight};`; + if (node.fontStretch !== 'normal') + style += `font-stretch: ${node.fontStretch};`; + if (node.lineHeight != null) style += `line-height: ${node.lineHeight}px;`; + if (node.letterSpacing) style += `letter-spacing: ${node.letterSpacing}px;`; + if (node.textAlign !== 'left') style += `text-align: ${node.textAlign};`; + // if (node.overflowSuffix) style += `overflow-suffix: ${node.overflowSuffix};` + if (node.maxLines > 0) { + // https://stackoverflow.com/a/13924997 + style += `display: -webkit-box; + overflow: hidden; + -webkit-line-clamp: ${node.maxLines}; + line-clamp: ${node.maxLines}; + -webkit-box-orient: vertical;`; + } + if (node.contain !== 'none') { + style += `overflow: hidden;`; + } + // if (node.verticalAlign) style += `vertical-align: ${node.verticalAlign};` + } + // + else { + let bgImg: string[] = []; + let bgPos: null | { x: number; y: number } = null; + + if (node.colorBottom !== node.colorTop) { + bgImg.push( + `linear-gradient(${colorToRgba(node.colorTop)}, ${colorToRgba(node.colorBottom)})`, + ); + } + if (node.colorLeft !== node.colorRight) { + bgImg.push( + `linear-gradient(to right, ${colorToRgba(node.colorLeft)}, ${colorToRgba(node.colorRight)})`, + ); + } + + if ( + node.texture != null && + node.texture.type === lng.TextureType.subTexture + ) { + bgPos = (node.texture as any).props; + bgImg.push(`url(${(node.texture as any).props.texture.props.src})`); + } else if (node.src) { + bgImg.push(`url(${node.src})`); + } + + if (bgImg.length > 0) { + style += `background-image: ${bgImg.join(',')}; background-blend-mode: multiply;`; + if (bgPos !== null) { + style += `background-position: -${bgPos.x}px -${bgPos.y}px;`; + } else { + style += 'background-size: 100% 100%;'; + } + + if (node.color !== 0xffffffff && node.color !== 0) { + style += `background-color: ${colorToRgba(node.color)};`; + style += `mask-image: ${bgImg.join(',')};`; + if (bgPos !== null) { + style += `mask-position: -${bgPos.x}px -${bgPos.y}px;`; + } else { + style += `mask-size: 100% 100%;`; + } + } + } else if (node.color !== 0) { + style += `background-color: ${colorToRgba(node.color)};`; + } + + if (node.shader != null) { + let shader = node.shader.props; + if (shader != null) { + // Border + if ( + typeof shader['border-width'] === 'number' && + shader['border-width'] > 0 && + typeof shader['border-color'] === 'number' && + shader['border-color'] > 0 + ) { + // css border impacts the element's box size when box-shadow doesn't + style += `box-shadow: inset 0px 0px 0px ${shader['border-width']}px ${colorToRgba(shader['border-color'])};`; + } + // Rounded + if (typeof shader['radius'] === 'number' && shader['radius'] > 0) { + style += `border-radius: ${shader['radius']}px;`; + } + } + } + } + + return style; +} + +function updateNodeStyles(node: DOMNode | DOMText) { + node.el.setAttribute('style', getNodeStyles(node)); + + if (node instanceof DOMText) { + scheduleUpdateTextNodeMeasurement(node); + } +} + +/* + Text nodes with contain 'width' or 'none' + need to have their height or width calculated. + And then cause the flex layout to be recalculated. +*/ + +const textNodesToMeasure = new Set(); + +type Size = { width: number; height: number }; + +function getElSize(el: Element): Size { + let rect = el.getBoundingClientRect(); + rect.height = Math.ceil(rect.height); + rect.width = Math.ceil(rect.width); + return rect; +} + +function updateTextNodeMeasurements() { + for (let node of textNodesToMeasure) { + let size: Size; + switch (node.contain) { + case 'width': + size = getElSize(node.el); + if (node.props.height !== size.height) { + node.props.height = size.height; + node.emit('loaded'); + } + case 'none': + size = getElSize(node.el); + if ( + node.props.height !== size.height || + node.props.width !== size.width + ) { + node.props.width = size.width; + node.props.height = size.height; + node.emit('loaded'); + } + } + } + textNodesToMeasure.clear(); +} + +function scheduleUpdateTextNodeMeasurement(node: DOMText) { + if (textNodesToMeasure.size === 0) { + setTimeout(updateTextNodeMeasurements); + } + + textNodesToMeasure.add(node); +} + +function updateNodeData(node: DOMNode | DOMText) { + for (let key in node.data) { + let keyValue: unknown = node.data[key]; + if (keyValue === undefined) { + node.el.removeAttribute('data-' + key); + } else { + node.el.setAttribute('data-' + key, String(keyValue)); + } + } +} + +function resolveNodeDefaults( + props: Partial, +): IRendererNodeProps { + const color = props.color ?? 0xffffffff; + + return { + x: props.x ?? 0, + y: props.y ?? 0, + width: props.width ?? 0, + height: props.height ?? 0, + alpha: props.alpha ?? 1, + autosize: props.autosize ?? false, + boundsMargin: props.boundsMargin ?? null, + clipping: props.clipping ?? false, + color, + colorTop: props.colorTop ?? color, + colorBottom: props.colorBottom ?? color, + colorLeft: props.colorLeft ?? color, + colorRight: props.colorRight ?? color, + colorBl: props.colorBl ?? props.colorBottom ?? props.colorLeft ?? color, + colorBr: props.colorBr ?? props.colorBottom ?? props.colorRight ?? color, + colorTl: props.colorTl ?? props.colorTop ?? props.colorLeft ?? color, + colorTr: props.colorTr ?? props.colorTop ?? props.colorRight ?? color, + zIndex: props.zIndex ?? 0, + zIndexLocked: props.zIndexLocked ?? 0, + parent: props.parent ?? null, + texture: props.texture ?? null, + textureOptions: props.textureOptions ?? {}, + shader: props.shader ?? defaultShader, + // Since setting the `src` will trigger a texture load, we need to set it after + // we set the texture. Otherwise, problems happen. + src: props.src ?? null, + srcHeight: props.srcHeight, + srcWidth: props.srcWidth, + srcX: props.srcX, + srcY: props.srcY, + scale: props.scale ?? null, + scaleX: props.scaleX ?? props.scale ?? 1, + scaleY: props.scaleY ?? props.scale ?? 1, + mount: props.mount ?? 0, + mountX: props.mountX ?? props.mount ?? 0, + mountY: props.mountY ?? props.mount ?? 0, + pivot: props.pivot ?? 0.5, + pivotX: props.pivotX ?? props.pivot ?? 0.5, + pivotY: props.pivotY ?? props.pivot ?? 0.5, + rotation: props.rotation ?? 0, + rtt: props.rtt ?? false, + data: {}, + preventCleanup: props.preventCleanup ?? false, + imageType: props.imageType, + strictBounds: props.strictBounds ?? false, + }; +} + +function resolveTextNodeDefaults( + props: Partial, +): IRendererTextNodeProps { + return { + ...resolveNodeDefaults(props), + text: props.text ?? '', + textRendererOverride: props.textRendererOverride ?? null, + fontSize: props.fontSize ?? 16, + fontFamily: props.fontFamily ?? 'sans-serif', + fontStyle: props.fontStyle ?? 'normal', + fontWeight: props.fontWeight ?? 'normal', + fontStretch: props.fontStretch ?? 'normal', + textAlign: props.textAlign ?? 'left', + contain: props.contain ?? 'none', + scrollable: props.scrollable ?? false, + scrollY: props.scrollY ?? 0, + offsetY: props.offsetY ?? 0, + letterSpacing: props.letterSpacing ?? 0, + lineHeight: props.lineHeight, // `undefined` is a valid value + maxLines: props.maxLines ?? 0, + textBaseline: props.textBaseline ?? 'alphabetic', + verticalAlign: props.verticalAlign ?? 'middle', + overflowSuffix: props.overflowSuffix ?? '...', + wordBreak: props.wordBreak ?? 'normal', + debug: props.debug ?? {}, + }; +} + +const defaultShader: IRendererShader = { + shaderType: '', + props: undefined, +}; + +let lastNodeId = 0; + +class DOMNode extends EventEmitter implements IRendererNode { + el = document.createElement('div'); + id = ++lastNodeId; + + constructor( + public stage: IRendererStage, + public props: IRendererNodeProps, + ) { + super(); + + // @ts-ignore + this.el._node = this; + this.el.setAttribute('data-id', String(this.id)); + elMap.set(this, this.el); + + updateNodeParent(this); + updateNodeStyles(this); + updateNodeData(this); + } + + destroy(): void { + elMap.delete(this); + this.el.parentNode!.removeChild(this.el); + } + + get parent() { + return this.props.parent; + } + set parent(value: IRendererNode | null) { + this.props.parent = value; + updateNodeParent(this); + } + + animate = animate; + + get x() { + return this.props.x; + } + set x(v) { + this.props.x = v; + updateNodeStyles(this); + } + get y() { + return this.props.y; + } + set y(v) { + this.props.y = v; + updateNodeStyles(this); + } + get width() { + return this.props.width; + } + set width(v) { + this.props.width = v; + updateNodeStyles(this); + } + get height() { + return this.props.height; + } + set height(v) { + this.props.height = v; + updateNodeStyles(this); + } + get alpha() { + return this.props.alpha; + } + set alpha(v) { + this.props.alpha = v; + updateNodeStyles(this); + } + get autosize() { + return this.props.autosize; + } + set autosize(v) { + this.props.autosize = v; + updateNodeStyles(this); + } + get clipping() { + return this.props.clipping; + } + set clipping(v) { + this.props.clipping = v; + updateNodeStyles(this); + } + get color() { + return this.props.color; + } + set color(v) { + this.props.color = v; + updateNodeStyles(this); + } + get colorTop() { + return this.props.colorTop; + } + set colorTop(v) { + this.props.colorTop = v; + updateNodeStyles(this); + } + get colorBottom() { + return this.props.colorBottom; + } + set colorBottom(v) { + this.props.colorBottom = v; + updateNodeStyles(this); + } + get colorLeft() { + return this.props.colorLeft; + } + set colorLeft(v) { + this.props.colorLeft = v; + updateNodeStyles(this); + } + get colorRight() { + return this.props.colorRight; + } + set colorRight(v) { + this.props.colorRight = v; + updateNodeStyles(this); + } + get colorTl() { + return this.props.colorTl; + } + set colorTl(v) { + this.props.colorTl = v; + updateNodeStyles(this); + } + get colorTr() { + return this.props.colorTr; + } + set colorTr(v) { + this.props.colorTr = v; + updateNodeStyles(this); + } + get colorBr() { + return this.props.colorBr; + } + set colorBr(v) { + this.props.colorBr = v; + updateNodeStyles(this); + } + get colorBl() { + return this.props.colorBl; + } + set colorBl(v) { + this.props.colorBl = v; + updateNodeStyles(this); + } + get zIndex() { + return this.props.zIndex; + } + set zIndex(v) { + this.props.zIndex = v; + updateNodeStyles(this); + } + get texture() { + return this.props.texture; + } + set texture(v) { + this.props.texture = v; + updateNodeStyles(this); + } + get preventCleanup() { + return this.props.preventCleanup; + } + set preventCleanup(v) { + this.props.preventCleanup = v; + updateNodeStyles(this); + } + get textureOptions(): IRendererNode['textureOptions'] { + return this.props.textureOptions; + } + set textureOptions(v) { + this.props.textureOptions = v; + updateNodeStyles(this); + } + get src() { + return this.props.src; + } + set src(v) { + this.props.src = v; + updateNodeStyles(this); + } + get zIndexLocked() { + return this.props.zIndexLocked; + } + set zIndexLocked(v) { + this.props.zIndexLocked = v; + updateNodeStyles(this); + } + get scale() { + return this.props.scale ?? 1; + } + set scale(v) { + this.props.scale = v; + updateNodeStyles(this); + } + get scaleX() { + return this.props.scaleX; + } + set scaleX(v) { + this.props.scaleX = v; + updateNodeStyles(this); + } + get scaleY() { + return this.props.scaleY; + } + set scaleY(v) { + this.props.scaleY = v; + updateNodeStyles(this); + } + get mount() { + return this.props.mount; + } + set mount(v) { + this.props.mount = v; + updateNodeStyles(this); + } + get mountX() { + return this.props.mountX; + } + set mountX(v) { + this.props.mountX = v; + updateNodeStyles(this); + } + get mountY() { + return this.props.mountY; + } + set mountY(v) { + this.props.mountY = v; + updateNodeStyles(this); + } + get pivot() { + return this.props.pivot; + } + set pivot(v) { + this.props.pivot = v; + updateNodeStyles(this); + } + get pivotX() { + return this.props.pivotX; + } + set pivotX(v) { + this.props.pivotX = v; + updateNodeStyles(this); + } + get pivotY() { + return this.props.pivotY; + } + set pivotY(v) { + this.props.pivotY = v; + updateNodeStyles(this); + } + get rotation() { + return this.props.rotation; + } + set rotation(v) { + this.props.rotation = v; + updateNodeStyles(this); + } + get rtt() { + return this.props.rtt; + } + set rtt(v) { + this.props.rtt = v; + updateNodeStyles(this); + } + get shader() { + return this.props.shader; + } + set shader(v) { + this.props.shader = v; + updateNodeStyles(this); + } + get strictBounds() { + return this.props.strictBounds; + } + set strictBounds(v) { + this.props.strictBounds = v; + updateNodeStyles(this); + } + + get data(): IRendererNode['data'] { + return this.props.data; + } + set data(v) { + this.props.data = v; + updateNodeData(this); + } + + get imageType() { + return this.props.imageType; + } + set imageType(v) { + this.props.imageType = v; + } + get srcWidth() { + return this.props.srcWidth; + } + set srcWidth(v) { + this.props.srcWidth = v; + } + get srcHeight() { + return this.props.srcHeight; + } + set srcHeight(v) { + this.props.srcHeight = v; + } + get srcX() { + return this.props.srcX; + } + set srcX(v) { + this.props.srcX = v; + } + get srcY() { + return this.props.srcY; + } + set srcY(v) { + this.props.srcY = v; + } + + get boundsMargin(): number | [number, number, number, number] | null { + return this.props.boundsMargin; + } + set boundsMargin(value: number | [number, number, number, number] | null) { + this.props.boundsMargin = value; + } + + get absX(): number { + return this.x + -this.width * this.mountX + (this.parent?.absX ?? 0); + } + get absY(): number { + return this.y + -this.height * this.mountY + (this.parent?.absY ?? 0); + } +} + +class DOMText extends DOMNode { + constructor( + stage: IRendererStage, + public override props: IRendererTextNodeProps, + ) { + super(stage, props); + this.el.innerText = props.text; + } + + get text() { + return this.props.text; + } + set text(v) { + this.props.text = v; + this.el.innerText = v; + scheduleUpdateTextNodeMeasurement(this); + } + get fontFamily() { + return this.props.fontFamily; + } + set fontFamily(v) { + this.props.fontFamily = v; + updateNodeStyles(this); + } + get fontSize() { + return this.props.fontSize; + } + set fontSize(v) { + this.props.fontSize = v; + updateNodeStyles(this); + } + get fontStyle() { + return this.props.fontStyle; + } + set fontStyle(v) { + this.props.fontStyle = v; + updateNodeStyles(this); + } + get fontWeight() { + return this.props.fontWeight; + } + set fontWeight(v) { + this.props.fontWeight = v; + updateNodeStyles(this); + } + get fontStretch() { + return this.props.fontStretch; + } + set fontStretch(v) { + this.props.fontStretch = v; + updateNodeStyles(this); + } + get lineHeight() { + return this.props.lineHeight; + } + set lineHeight(v) { + this.props.lineHeight = v; + updateNodeStyles(this); + } + get letterSpacing() { + return this.props.letterSpacing; + } + set letterSpacing(v) { + this.props.letterSpacing = v; + updateNodeStyles(this); + } + get textAlign() { + return this.props.textAlign; + } + set textAlign(v) { + this.props.textAlign = v; + updateNodeStyles(this); + } + get overflowSuffix() { + return this.props.overflowSuffix; + } + set overflowSuffix(v) { + this.props.overflowSuffix = v; + updateNodeStyles(this); + } + get maxLines() { + return this.props.maxLines; + } + set maxLines(v) { + this.props.maxLines = v; + updateNodeStyles(this); + } + get contain() { + return this.props.contain; + } + set contain(v) { + this.props.contain = v; + updateNodeStyles(this); + } + get verticalAlign() { + return this.props.verticalAlign; + } + set verticalAlign(v) { + this.props.verticalAlign = v; + updateNodeStyles(this); + } + get textBaseline() { + return this.props.textBaseline; + } + set textBaseline(v) { + this.props.textBaseline = v; + updateNodeStyles(this); + } + get textRendererOverride() { + return this.props.textRendererOverride; + } + set textRendererOverride(v) { + this.props.textRendererOverride = v; + updateNodeStyles(this); + } + get scrollable() { + return this.props.scrollable; + } + set scrollable(v) { + this.props.scrollable = v; + updateNodeStyles(this); + } + get scrollY() { + return this.props.scrollY; + } + set scrollY(v) { + this.props.scrollY = v; + updateNodeStyles(this); + } + get offsetY() { + return this.props.offsetY; + } + set offsetY(v) { + this.props.offsetY = v; + updateNodeStyles(this); + } + get wordBreak() { + return this.props.wordBreak; + } + set wordBreak(v) { + this.props.wordBreak = v; + updateNodeStyles(this); + } + get debug() { + return this.props.debug; + } + set debug(v) { + this.props.debug = v; + updateNodeStyles(this); + } +} + +function updateRootPosition(this: DOMRendererMain) { + let { canvas, settings } = this; + + let rect = canvas.getBoundingClientRect(); + let top = document.documentElement.scrollTop + rect.top; + let left = document.documentElement.scrollLeft + rect.left; + + let height = Math.ceil( + settings.appHeight ?? 1080 / (settings.deviceLogicalPixelRatio ?? 1), + ); + let width = Math.ceil( + settings.appWidth ?? 1920 / (settings.deviceLogicalPixelRatio ?? 1), + ); + + let scaleX = settings.deviceLogicalPixelRatio ?? 1; + let scaleY = settings.deviceLogicalPixelRatio ?? 1; + + this.root.el.style.left = `${left}px`; + this.root.el.style.top = `${top}px`; + this.root.el.style.width = `${width}px`; + this.root.el.style.height = `${height}px`; + this.root.el.style.position = 'absolute'; + this.root.el.style.transformOrigin = '0 0 0'; + this.root.el.style.transform = `scale(${scaleX}, ${scaleY})`; + this.root.el.style.overflow = 'hidden'; + this.root.el.style.zIndex = '65534'; +} + +export class DOMRendererMain implements IRendererMain { + root: DOMNode; + canvas: HTMLCanvasElement; + + stage: IRendererStage; + + constructor( + public settings: lng.RendererMainSettings, + public target: string | HTMLElement, + ) { + let canvas = document.body.appendChild(document.createElement('canvas')); + canvas.style.position = 'absolute'; + canvas.style.top = '0'; + canvas.style.left = '0'; + canvas.style.width = '100vw'; + canvas.style.height = '100vh'; + + this.canvas = canvas; + + this.stage = { + root: null!, + renderer: { + mode: 'canvas', + }, + fontManager: { + addFontFace: () => {}, + }, + shManager: { + registerShaderType() {}, + }, + }; + + this.root = new DOMNode(this.stage, { + x: 0, + y: 0, + width: settings.appWidth ?? 1920, + height: settings.appWidth ?? 1080, + alpha: 1, + autosize: false, + boundsMargin: null, + clipping: false, + color: 0x00000000, + colorTop: 0x00000000, + colorBottom: 0x00000000, + colorLeft: 0x00000000, + colorRight: 0x00000000, + colorTl: 0x00000000, + colorTr: 0x00000000, + colorBl: 0x00000000, + colorBr: 0x00000000, + zIndex: 0, + zIndexLocked: 0, + scaleX: 1, + scaleY: 1, + mountX: 0, + mountY: 0, + mount: 0, + pivot: 0.5, + pivotX: 0.5, + pivotY: 0.5, + rotation: 0, + parent: null, + texture: null, + textureOptions: {}, + shader: defaultShader, + rtt: false, + src: null, + scale: 1, + preventCleanup: false, + strictBounds: false, + }); + this.stage.root = this.root; + + if (Config.fontSettings.fontFamily != null) { + this.root.el.style.setProperty( + 'font-family', + Config.fontSettings.fontFamily, + ); + } + if (Config.fontSettings.fontSize != null) { + this.root.el.style.setProperty( + 'font-size', + Config.fontSettings.fontSize + 'px', + ); + } + + this.root.el.style.setProperty( + 'line-height', + Config.fontSettings.lineHeight + ? Config.fontSettings.lineHeight + 'px' + : '1', // 1 = same as font size + ); + + updateRootPosition.call(this); + + new MutationObserver(updateRootPosition.bind(this)).observe(this.canvas, { + attributes: true, + }); + new ResizeObserver(updateRootPosition.bind(this)).observe(this.canvas); + window.addEventListener('resize', updateRootPosition.bind(this)); + } + + createNode(props: Partial): IRendererNode { + return new DOMNode(this.stage, resolveNodeDefaults(props)); + } + + createTextNode(props: Partial): IRendererTextNode { + return new DOMText(this.stage, resolveTextNodeDefaults(props)); + } + + createShader( + shaderType: string, + props?: IRendererShaderProps, + ): IRendererShader { + return { shaderType, props }; + } + + createTexture( + textureType: keyof lng.TextureMap, + props: IRendererTextureProps, + ): IRendererTexture { + let type = lng.TextureType.generic; + switch (textureType) { + case 'SubTexture': + type = lng.TextureType.subTexture; + break; + case 'ImageTexture': + type = lng.TextureType.image; + break; + case 'ColorTexture': + type = lng.TextureType.color; + break; + case 'NoiseTexture': + type = lng.TextureType.noise; + break; + case 'RenderTexture': + type = lng.TextureType.renderToTexture; + break; + } + return { type, props }; + } + + on(name: string, callback: (target: any, data: any) => void) { + console.log('on', name, callback); + } +} diff --git a/src/elementNode.ts b/src/elementNode.ts index 6394d1a..726ee1c 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -1,4 +1,9 @@ -import { renderer } from './lightningInit.js'; +import { + IRendererNode, + IRendererNodeProps, + IRendererTextNode, + renderer, +} from './lightningInit.js'; import { type BorderRadius, type BorderStyle, @@ -33,12 +38,11 @@ import type { RendererMain, INode, INodeAnimateProps, - INodeProps, ITextNodeProps, IAnimationController, LinearGradientProps, RadialGradientProps, - ITextNode, + CoreShaderNode, } from '@lightningjs/renderer'; import { assertTruthy } from '@lightningjs/renderer/utils'; import { NodeType } from './nodeTypes.js'; @@ -199,7 +203,7 @@ export interface ElementNode extends RendererNode { // Properties _animationQueue?: | Array<{ - props: Partial; + props: Partial>; animationSettings?: AnimationSettings; }> | undefined; @@ -227,7 +231,7 @@ export interface ElementNode extends RendererNode { | number | ((this: ElementNode, elm: ElementNode) => boolean | void); forwardStates?: boolean; - lng: INode | Partial | ITextNode; + lng: Partial | IRendererNode | IRendererTextNode; ref?: ElementNode | ((node: ElementNode) => void) | undefined; rendered: boolean; renderer?: RendererMain; @@ -361,7 +365,7 @@ export class ElementNode extends Object { set parent(p) { this._parent = p; if (this.rendered) { - this.lng.parent = (p?.lng as INode) ?? null; + this.lng.parent = (p?.lng as IRendererNode) ?? null; } } @@ -408,7 +412,7 @@ export class ElementNode extends Object { set shader( shaderProps: | Parameters - | ReturnType, + | ReturnType, ) { let shProps = shaderProps; if (isArray(shaderProps)) { @@ -452,23 +456,23 @@ export class ElementNode extends Object { return animationController.start(); } - (this.lng[name as keyof INode] as number | string) = value; + (this.lng[name as keyof IRendererNodeProps] as number | string) = value; } animate( - props: Partial, + props: Partial>, animationSettings?: AnimationSettings, ): IAnimationController { isDev && assertTruthy(this.rendered, 'Node must be rendered before animating'); - return (this.lng as INode).animate( + return (this.lng as IRendererNode).animate( props, animationSettings || this.animationSettings || {}, ); } chain( - props: Partial, + props: Partial>, animationSettings?: AnimationSettings, ) { if (this._animationRunning) { @@ -544,7 +548,7 @@ export class ElementNode extends Object { } _layoutOnLoad() { - (this.lng as INode).on('loaded', () => { + (this.lng as IRendererNode).on('loaded', () => { this.parent!.updateLayout(); }); } @@ -794,7 +798,7 @@ export class ElementNode extends Object { render(topNode?: boolean) { // Elements are inserted from the inside out, then rendered from the outside in. - // Render starts when an element is insertered with a parent that is already renderered. + // Render starts when an element is inserted with a parent that is already renderered. const node = this; const parent = this.parent; @@ -825,7 +829,7 @@ export class ElementNode extends Object { const props = node.lng; props.x = props.x || 0; props.y = props.y || 0; - props.parent = parent.lng as INode; + props.parent = parent.lng as IRendererNode; if (this.right || this.right === 0) { props.x = (parent.width || 0) - this.right; @@ -929,7 +933,7 @@ export class ElementNode extends Object { } isDev && log('Rendering: ', this, props); - node.lng = renderer.createNode(props as INodeProps); + node.lng = renderer.createNode(props as IRendererNodeProps); } node.rendered = true; diff --git a/src/index.ts b/src/index.ts index bfec25b..66a49d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,3 +10,4 @@ export { type AnimationSettings } from './intrinsicTypes.js'; import { assertTruthy, deg2Rad } from '@lightningjs/renderer/utils'; export { assertTruthy, deg2Rad }; // export type * from '@lightningjs/renderer/utils'; +export * from './domRenderer.js'; diff --git a/src/lightningInit.ts b/src/lightningInit.ts index f81825c..9366379 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -1,47 +1,131 @@ -import type { - RendererMainSettings, - SdfTrFontFaceOptions, - WebTrFontFaceOptions, -} from '@lightningjs/renderer'; -import { - SdfTrFontFace, - WebTrFontFace, - RendererMain, -} from '@lightningjs/renderer'; -type SdfFontType = 'ssdf' | 'msdf'; - -export let renderer: RendererMain; +import * as lng from '@lightningjs/renderer'; +import { DOMRendererMain } from './domRenderer.js'; +import { Config } from './config.js'; + +export type SdfFontType = 'ssdf' | 'msdf'; + +/** Based on {@link lng.CoreRenderer} */ +export interface IRendererCoreRenderer { + mode: 'canvas' | 'webgl' | undefined; +} +/** Based on {@link lng.TrFontManager} */ +export interface IRendererFontManager { + addFontFace: (...a: any[]) => void; +} +/** Based on {@link lng.Stage} */ +export interface IRendererStage { + root: IRendererNode; + renderer: IRendererCoreRenderer; + fontManager: IRendererFontManager; + shManager: IRendererShaderManager; +} + +/** Based on {@link lng.CoreShaderManager} */ +export interface IRendererShaderManager { + registerShaderType: (name: string, shader: any) => void; +} + +/** Based on {@link lng.CoreShaderNode} */ +export interface IRendererShader { + shaderType: IRendererShaderType; + props: IRendererShaderProps | undefined; +} +/** Based on {@link lng.CoreShaderType} */ +export interface IRendererShaderType {} +export type IRendererShaderProps = Record; + +/** Based on {@link lng.Texture} */ +export interface IRendererTexture { + props: IRendererTextureProps; + type: lng.TextureType; +} +export interface IRendererTextureProps {} + +export interface IRendererNodeShaded { + stage: IRendererStage; + id: number; + animate: ( + props: Partial>, + settings: Partial, + ) => lng.IAnimationController; + on: (e: string, cb: (...a: any[]) => void) => void; + get absX(): number; + get absY(): number; +} + +/** Based on {@link lng.INodeProps} */ +export interface IRendererNodeProps + extends Omit, 'shader' | 'parent'> { + shader: IRendererShader | null; + parent: IRendererNode | null; +} +/** Based on {@link lng.INode} */ +export interface IRendererNode extends IRendererNodeShaded, IRendererNodeProps { + props: IRendererNodeProps; +} + +/** Based on {@link lng.ITextNodeProps} */ +export interface IRendererTextNodeProps + extends Omit { + shader: IRendererShader | null; + parent: IRendererNode | null; +} +/** Based on {@link lng.ITextNode} */ +export interface IRendererTextNode + extends IRendererNodeShaded, + IRendererTextNodeProps { + props: IRendererTextNodeProps; +} + +/** Based on {@link lng.RendererMain} */ +export interface IRendererMain { + stage: IRendererStage; + createTextNode: (props: Partial) => IRendererTextNode; + createNode: (props: Partial) => IRendererNode; + createShader: (kind: string, props: IRendererShaderProps) => IRendererShader; + createTexture: ( + kind: keyof lng.TextureMap, + props: IRendererTextureProps, + ) => IRendererTexture; +} + +export let renderer: IRendererMain; export const getRenderer = () => renderer; export function startLightningRenderer( - options: RendererMainSettings, + options: lng.RendererMainSettings, rootId: string | HTMLElement = 'app', ) { - renderer = new RendererMain(options, rootId); + renderer = Config.domRendering + ? new DOMRendererMain(options, rootId) + : (new lng.RendererMain(options, rootId) as any as IRendererMain); return renderer; } export function loadFonts( fonts: ( - | WebTrFontFaceOptions - | (Partial & { type: SdfFontType }) + | lng.WebTrFontFaceOptions + | (Partial & { type: SdfFontType }) )[], ) { - const stage = renderer.stage; - for (const font of fonts) { - if ('type' in font && (font.type === 'msdf' || font.type === 'ssdf')) { - if (renderer.stage.renderer.mode === 'webgl') { - stage.fontManager.addFontFace( - new SdfTrFontFace(font.type, { - ...font, - stage, - } as SdfTrFontFaceOptions), - ); - } - } else if ('fontUrl' in font) { - stage.fontManager.addFontFace(new WebTrFontFace(font)); + // WebGL — SDF + if ( + renderer.stage.renderer.mode === 'webgl' && + 'type' in font && + (font.type === 'msdf' || font.type === 'ssdf') + ) { + renderer.stage.fontManager.addFontFace( + new lng.SdfTrFontFace(font.type, { + ...font, + stage: renderer.stage as lng.Stage, + } as lng.SdfTrFontFaceOptions), + ); + } + // Canvas — Web + else if ('fontUrl' in font) { + renderer.stage.fontManager.addFontFace(new lng.WebTrFontFace(font)); } } } From a0f6f49fff8a3de2af01fc99f78d5dea132cdea6 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 28 Mar 2025 08:01:08 -0400 Subject: [PATCH 06/78] :rocket: bump version v3.0.0-1 --- CHANGELOG.md | 16 ++++++++++++++-- package.json | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c9ef25..e3cc9a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,18 +4,30 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [v2.7.7](https://github.com/lightning-tv/core/compare/v3.0.0-0...v2.7.7) +#### [v3.0.0-1](https://github.com/lightning-tv/core/compare/v3.0.0-0...v3.0.0-1) +- Experimental DOM Renderer [`#34`](https://github.com/lightning-tv/core/pull/34) +- Remove radius effect—replaced with rounded [`#37`](https://github.com/lightning-tv/core/pull/37) - Add @lightningtv/source export condition [`#35`](https://github.com/lightning-tv/core/pull/35) +- :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) +- update renderer [`374b15c`](https://github.com/lightning-tv/core/commit/374b15c210147c77892a25b0ade7fbe7312128dc) -#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.7.6...v3.0.0-0) +#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.7.7...v3.0.0-0) > 19 March 2025 - get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) - :rocket: bump version v3.0.0-0 [`96d54bf`](https://github.com/lightning-tv/core/commit/96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f) +#### [v2.7.7](https://github.com/lightning-tv/core/compare/v2.7.6...v2.7.7) + +> 25 March 2025 + +- Add @lightningtv/source export condition [`#35`](https://github.com/lightning-tv/core/pull/35) +- :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) +- add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) + #### [v2.7.6](https://github.com/lightning-tv/core/compare/v2.7.6-0...v2.7.6) > 19 March 2025 diff --git a/package.json b/package.json index d451422..4e3b36a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-0", + "version": "3.0.0-1", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 8af7f7e41d78d8ba2dddb8c92b400fa36725b44f Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 3 Apr 2025 23:25:31 +0200 Subject: [PATCH 07/78] Cleanup updateRootPosition and use appHeight for root.height (#38) --- src/domRenderer.ts | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index 5df1dab..666608f 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -1040,15 +1040,10 @@ function updateRootPosition(this: DOMRendererMain) { let top = document.documentElement.scrollTop + rect.top; let left = document.documentElement.scrollLeft + rect.left; - let height = Math.ceil( - settings.appHeight ?? 1080 / (settings.deviceLogicalPixelRatio ?? 1), - ); - let width = Math.ceil( - settings.appWidth ?? 1920 / (settings.deviceLogicalPixelRatio ?? 1), - ); + let dpr = settings.deviceLogicalPixelRatio ?? 1; - let scaleX = settings.deviceLogicalPixelRatio ?? 1; - let scaleY = settings.deviceLogicalPixelRatio ?? 1; + let height = Math.ceil(settings.appHeight ?? 1080 / dpr); + let width = Math.ceil(settings.appWidth ?? 1920 / dpr); this.root.el.style.left = `${left}px`; this.root.el.style.top = `${top}px`; @@ -1056,7 +1051,7 @@ function updateRootPosition(this: DOMRendererMain) { this.root.el.style.height = `${height}px`; this.root.el.style.position = 'absolute'; this.root.el.style.transformOrigin = '0 0 0'; - this.root.el.style.transform = `scale(${scaleX}, ${scaleY})`; + this.root.el.style.transform = `scale(${dpr}, ${dpr})`; this.root.el.style.overflow = 'hidden'; this.root.el.style.zIndex = '65534'; } @@ -1097,7 +1092,7 @@ export class DOMRendererMain implements IRendererMain { x: 0, y: 0, width: settings.appWidth ?? 1920, - height: settings.appWidth ?? 1080, + height: settings.appHeight ?? 1080, alpha: 1, autosize: false, boundsMargin: null, From f3347aeb05e230c6d8872c5a24b39778374ce00b Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 3 Apr 2025 23:26:29 +0200 Subject: [PATCH 08/78] Add missing fields to renderer interfaces required by lightning/solid (#39) --- src/domRenderer.ts | 2 ++ src/lightningInit.ts | 12 +++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index 666608f..d5475a0 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -550,6 +550,8 @@ class DOMNode extends EventEmitter implements IRendererNode { el = document.createElement('div'); id = ++lastNodeId; + renderState: lng.CoreNodeRenderState = 0 /* Init */; + constructor( public stage: IRendererStage, public props: IRendererNodeProps, diff --git a/src/lightningInit.ts b/src/lightningInit.ts index 9366379..33bacc7 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -41,14 +41,17 @@ export interface IRendererTexture { } export interface IRendererTextureProps {} -export interface IRendererNodeShaded { +export interface IEventEmitter { + on: (e: string, cb: (...a: any[]) => void) => void; +} + +export interface IRendererNodeShaded extends IEventEmitter { stage: IRendererStage; id: number; animate: ( props: Partial>, settings: Partial, ) => lng.IAnimationController; - on: (e: string, cb: (...a: any[]) => void) => void; get absX(): number; get absY(): number; } @@ -62,6 +65,7 @@ export interface IRendererNodeProps /** Based on {@link lng.INode} */ export interface IRendererNode extends IRendererNodeShaded, IRendererNodeProps { props: IRendererNodeProps; + renderState: lng.CoreNodeRenderState; } /** Based on {@link lng.ITextNodeProps} */ @@ -75,11 +79,13 @@ export interface IRendererTextNode extends IRendererNodeShaded, IRendererTextNodeProps { props: IRendererTextNodeProps; + renderState: lng.CoreNodeRenderState; } /** Based on {@link lng.RendererMain} */ -export interface IRendererMain { +export interface IRendererMain extends IEventEmitter { stage: IRendererStage; + root: IRendererNode; createTextNode: (props: Partial) => IRendererTextNode; createNode: (props: Partial) => IRendererNode; createShader: (kind: string, props: IRendererShaderProps) => IRendererShader; From 76c6076bec009f5b9f4c83bfa5f08529404577e4 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 3 Apr 2025 23:46:40 +0200 Subject: [PATCH 09/78] Remove _queueDelete field from core (#40) --- src/elementNode.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 726ee1c..4b7d261 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -212,7 +212,6 @@ export interface ElementNode extends RendererNode { _animationSettings?: AnimationSettings; _effects?: StyleEffects; _id: string | undefined; - _queueDelete?: boolean; _parent: ElementNode | undefined; _rendererProps?: any; _states?: States; @@ -578,7 +577,7 @@ export class ElementNode extends Object { } _destroy() { - if (this._queueDelete && isINode(this.lng)) { + if (isINode(this.lng)) { this.lng.destroy(); if (this.parent?.requiresLayout()) { this.parent.updateLayout(); From 7c9abd8822aa02b21ff27fc8d39b10e4ad873367 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Thu, 3 Apr 2025 19:51:10 -0400 Subject: [PATCH 10/78] :rocket: bump version v3.0.0-2 --- CHANGELOG.md | 10 +++++++++- package.json | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3cc9a0..162d4ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-2](https://github.com/lightning-tv/core/compare/v3.0.0-1...v3.0.0-2) + +- Remove \_queueDelete field from core [`#40`](https://github.com/lightning-tv/core/pull/40) +- Add missing fields to renderer interfaces required by lightning/solid [`#39`](https://github.com/lightning-tv/core/pull/39) +- Cleanup updateRootPosition and use appHeight for root.height [`#38`](https://github.com/lightning-tv/core/pull/38) + #### [v3.0.0-1](https://github.com/lightning-tv/core/compare/v3.0.0-0...v3.0.0-1) +> 28 March 2025 + - Experimental DOM Renderer [`#34`](https://github.com/lightning-tv/core/pull/34) - Remove radius effect—replaced with rounded [`#37`](https://github.com/lightning-tv/core/pull/37) - Add @lightningtv/source export condition [`#35`](https://github.com/lightning-tv/core/pull/35) +- :rocket: bump version v3.0.0-1 [`a0f6f49`](https://github.com/lightning-tv/core/commit/a0f6f49fff8a3de2af01fc99f78d5dea132cdea6) - :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) -- update renderer [`374b15c`](https://github.com/lightning-tv/core/commit/374b15c210147c77892a25b0ade7fbe7312128dc) #### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.7.7...v3.0.0-0) diff --git a/package.json b/package.json index 4e3b36a..5e8f02c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-1", + "version": "3.0.0-2", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From a11f6aa04517daab1c09c75d9bb7fdf7c6cc6c06 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Tue, 8 Apr 2025 13:54:50 +0200 Subject: [PATCH 11/78] dom renderer fixes (#41) * Remove background-blend-mode from getNodeStyles function * Update getNodeStyles to always include z-index * Add missing break statements * Correct el size by dpr * use props directly in getNodeStyles * Remove default line-height assignment in getNodeStyles function * Add background-blend-mode back * Remove rounding of dimensions from getElSize --- src/domRenderer.ts | 190 ++++++++++++++++++--------------------------- 1 file changed, 75 insertions(+), 115 deletions(-) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index d5475a0..b5cd132 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -240,19 +240,17 @@ function updateNodeParent(node: DOMNode | DOMText) { } function getNodeStyles(node: Readonly): string { - let style = 'position: absolute;'; + let { props } = node; - if (node.alpha !== 1) style += `opacity: ${node.alpha};`; + let style = `position: absolute; z-index: ${props.zIndex};`; - if (node.width !== 0) style += `width: ${node.width}px;`; + if (props.alpha !== 1) style += `opacity: ${props.alpha};`; - if (node.height !== 0) style += `height: ${node.height}px;`; + if (props.width !== 0) style += `width: ${props.width}px;`; - if (node.zIndex !== 0) { - style += `z-index: ${node.zIndex};`; - } + if (props.height !== 0) style += `height: ${props.height}px;`; - if (node.clipping) { + if (props.clipping) { style += `overflow: hidden;`; } @@ -260,26 +258,27 @@ function getNodeStyles(node: Readonly): string { { let transform = ''; - let { x, y } = node; + let { x, y } = props; - if (node.mountX != null) { - x -= (node.width ?? 0) * node.mountX; + if (props.mountX != null) { + x -= (props.width ?? 0) * props.mountX; } - if (node.mountY != null) { - y -= (node.height ?? 0) * node.mountY; + if (props.mountY != null) { + y -= (props.height ?? 0) * props.mountY; } if (x !== 0) transform += `translateX(${x}px)`; if (y !== 0) transform += `translateY(${y}px)`; - if (node.rotation !== 0) transform += `rotate(${node.rotation}rad)`; + if (props.rotation !== 0) transform += `rotate(${props.rotation}rad)`; - if (node.scale !== 1) transform += `scale(${node.scale})`; + if (props.scale !== 1 && props.scale != null) + transform += `scale(${props.scale})`; else { - if (node.scaleX !== 1) transform += `scaleX(${node.scaleX})`; - if (node.scaleY !== 1) transform += `scaleY(${node.scaleY})`; + if (props.scaleX !== 1) transform += `scaleX(${props.scaleX})`; + if (props.scaleY !== 1) transform += `scaleY(${props.scaleY})`; } if (transform.length > 0) { @@ -289,30 +288,36 @@ function getNodeStyles(node: Readonly): string { // if (node instanceof DOMText) { - if (node.color != null && node.color !== 0) { - style += `color: ${colorToRgba(node.color)};`; + let textProps = node.props; + + if (textProps.color != null && textProps.color !== 0) { + style += `color: ${colorToRgba(textProps.color)};`; } - if (node.fontFamily) style += `font-family: ${node.fontFamily};`; - if (node.fontSize) style += `font-size: ${node.fontSize}px;`; - if (node.fontStyle !== 'normal') style += `font-style: ${node.fontStyle};`; - if (node.fontWeight !== 'normal') - style += `font-weight: ${node.fontWeight};`; - if (node.fontStretch !== 'normal') - style += `font-stretch: ${node.fontStretch};`; - if (node.lineHeight != null) style += `line-height: ${node.lineHeight}px;`; - if (node.letterSpacing) style += `letter-spacing: ${node.letterSpacing}px;`; - if (node.textAlign !== 'left') style += `text-align: ${node.textAlign};`; + if (textProps.fontFamily) style += `font-family: ${textProps.fontFamily};`; + if (textProps.fontSize) style += `font-size: ${textProps.fontSize}px;`; + if (textProps.fontStyle !== 'normal') + style += `font-style: ${textProps.fontStyle};`; + if (textProps.fontWeight !== 'normal') + style += `font-weight: ${textProps.fontWeight};`; + if (textProps.fontStretch !== 'normal') + style += `font-stretch: ${textProps.fontStretch};`; + if (textProps.lineHeight != null) + style += `line-height: ${textProps.lineHeight}px;`; + if (textProps.letterSpacing) + style += `letter-spacing: ${textProps.letterSpacing}px;`; + if (textProps.textAlign !== 'left') + style += `text-align: ${textProps.textAlign};`; // if (node.overflowSuffix) style += `overflow-suffix: ${node.overflowSuffix};` - if (node.maxLines > 0) { + if (textProps.maxLines > 0) { // https://stackoverflow.com/a/13924997 style += `display: -webkit-box; overflow: hidden; - -webkit-line-clamp: ${node.maxLines}; - line-clamp: ${node.maxLines}; + -webkit-line-clamp: ${textProps.maxLines}; + line-clamp: ${textProps.maxLines}; -webkit-box-orient: vertical;`; } - if (node.contain !== 'none') { + if (textProps.contain !== 'none') { style += `overflow: hidden;`; } // if (node.verticalAlign) style += `vertical-align: ${node.verticalAlign};` @@ -322,25 +327,25 @@ function getNodeStyles(node: Readonly): string { let bgImg: string[] = []; let bgPos: null | { x: number; y: number } = null; - if (node.colorBottom !== node.colorTop) { + if (props.colorBottom !== props.colorTop) { bgImg.push( - `linear-gradient(${colorToRgba(node.colorTop)}, ${colorToRgba(node.colorBottom)})`, + `linear-gradient(${colorToRgba(props.colorTop)}, ${colorToRgba(props.colorBottom)})`, ); } - if (node.colorLeft !== node.colorRight) { + if (props.colorLeft !== props.colorRight) { bgImg.push( - `linear-gradient(to right, ${colorToRgba(node.colorLeft)}, ${colorToRgba(node.colorRight)})`, + `linear-gradient(to right, ${colorToRgba(props.colorLeft)}, ${colorToRgba(props.colorRight)})`, ); } if ( - node.texture != null && - node.texture.type === lng.TextureType.subTexture + props.texture != null && + props.texture.type === lng.TextureType.subTexture ) { - bgPos = (node.texture as any).props; - bgImg.push(`url(${(node.texture as any).props.texture.props.src})`); - } else if (node.src) { - bgImg.push(`url(${node.src})`); + bgPos = (props.texture as any).props; + bgImg.push(`url(${(props.texture as any).props.texture.props.src})`); + } else if (props.src) { + bgImg.push(`url(${props.src})`); } if (bgImg.length > 0) { @@ -351,8 +356,8 @@ function getNodeStyles(node: Readonly): string { style += 'background-size: 100% 100%;'; } - if (node.color !== 0xffffffff && node.color !== 0) { - style += `background-color: ${colorToRgba(node.color)};`; + if (props.color !== 0xffffffff && props.color !== 0) { + style += `background-color: ${colorToRgba(props.color)};`; style += `mask-image: ${bgImg.join(',')};`; if (bgPos !== null) { style += `mask-position: -${bgPos.x}px -${bgPos.y}px;`; @@ -360,12 +365,12 @@ function getNodeStyles(node: Readonly): string { style += `mask-size: 100% 100%;`; } } - } else if (node.color !== 0) { - style += `background-color: ${colorToRgba(node.color)};`; + } else if (props.color !== 0) { + style += `background-color: ${colorToRgba(props.color)};`; } - if (node.shader != null) { - let shader = node.shader.props; + if (props.shader != null) { + let shader = props.shader.props; if (shader != null) { // Border if ( @@ -406,10 +411,11 @@ const textNodesToMeasure = new Set(); type Size = { width: number; height: number }; -function getElSize(el: Element): Size { - let rect = el.getBoundingClientRect(); - rect.height = Math.ceil(rect.height); - rect.width = Math.ceil(rect.width); +function getElSize(node: DOMNode): Size { + let rect = node.el.getBoundingClientRect(); + let dpr = Config.rendererOptions?.deviceLogicalPixelRatio ?? 1; + rect.height = rect.height / dpr; + rect.width = rect.width / dpr; return rect; } @@ -418,13 +424,14 @@ function updateTextNodeMeasurements() { let size: Size; switch (node.contain) { case 'width': - size = getElSize(node.el); + size = getElSize(node); if (node.props.height !== size.height) { node.props.height = size.height; node.emit('loaded'); } + break; case 'none': - size = getElSize(node.el); + size = getElSize(node); if ( node.props.height !== size.height || node.props.width !== size.width @@ -433,6 +440,7 @@ function updateTextNodeMeasurements() { node.props.height = size.height; node.emit('loaded'); } + break; } } textNodesToMeasure.clear(); @@ -1055,7 +1063,6 @@ function updateRootPosition(this: DOMRendererMain) { this.root.el.style.transformOrigin = '0 0 0'; this.root.el.style.transform = `scale(${dpr}, ${dpr})`; this.root.el.style.overflow = 'hidden'; - this.root.el.style.zIndex = '65534'; } export class DOMRendererMain implements IRendererMain { @@ -1090,66 +1097,19 @@ export class DOMRendererMain implements IRendererMain { }, }; - this.root = new DOMNode(this.stage, { - x: 0, - y: 0, - width: settings.appWidth ?? 1920, - height: settings.appHeight ?? 1080, - alpha: 1, - autosize: false, - boundsMargin: null, - clipping: false, - color: 0x00000000, - colorTop: 0x00000000, - colorBottom: 0x00000000, - colorLeft: 0x00000000, - colorRight: 0x00000000, - colorTl: 0x00000000, - colorTr: 0x00000000, - colorBl: 0x00000000, - colorBr: 0x00000000, - zIndex: 0, - zIndexLocked: 0, - scaleX: 1, - scaleY: 1, - mountX: 0, - mountY: 0, - mount: 0, - pivot: 0.5, - pivotX: 0.5, - pivotY: 0.5, - rotation: 0, - parent: null, - texture: null, - textureOptions: {}, - shader: defaultShader, - rtt: false, - src: null, - scale: 1, - preventCleanup: false, - strictBounds: false, - }); - this.stage.root = this.root; - - if (Config.fontSettings.fontFamily != null) { - this.root.el.style.setProperty( - 'font-family', - Config.fontSettings.fontFamily, - ); - } - if (Config.fontSettings.fontSize != null) { - this.root.el.style.setProperty( - 'font-size', - Config.fontSettings.fontSize + 'px', - ); - } - - this.root.el.style.setProperty( - 'line-height', - Config.fontSettings.lineHeight - ? Config.fontSettings.lineHeight + 'px' - : '1', // 1 = same as font size + this.root = new DOMNode( + this.stage, + resolveTextNodeDefaults({ + width: settings.appWidth ?? 1920, + height: settings.appHeight ?? 1080, + fontFamily: Config.fontSettings.fontFamily, + fontSize: Config.fontSettings.fontSize, + lineHeight: Config.fontSettings.lineHeight, + shader: defaultShader, + zIndex: 65534, + }), ); + this.stage.root = this.root; updateRootPosition.call(this); From 12dfac9b4f5b26e8ef27782e290643147f117c7a Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Tue, 8 Apr 2025 07:56:00 -0400 Subject: [PATCH 12/78] update renderer --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 5e8f02c..944ed61 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "author": "Chris Lorenzo", "license": "Apache-2.0", "peerDependencies": { - "@lightningjs/renderer": "^3.0.0-beta4" + "@lightningjs/renderer": "^3.0.0-beta6" }, "devDependencies": { "@eslint/js": "^9.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 18c9e0e..85caeed 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@lightningjs/renderer': - specifier: ^3.0.0-beta4 - version: 3.0.0-beta4 + specifier: ^3.0.0-beta6 + version: 3.0.0-beta6 devDependencies: '@eslint/js': specifier: ^9.15.0 @@ -267,8 +267,8 @@ packages: resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} engines: {node: '>=18'} - '@lightningjs/renderer@3.0.0-beta4': - resolution: {integrity: sha512-a1YM99eiTHyok1Vp29G5+526JTML4XOxInfiChFjSV4byJ02FoAt1ou76B/gCMIWpOKg07lhDk5c+I/RamugFQ==} + '@lightningjs/renderer@3.0.0-beta6': + resolution: {integrity: sha512-1spHTpZ8CLgL9ZZNtvKdaJYazn+SM4neIOj5wb8jJ6aFBbj4CyAeigyNmhYylo0lUr9VxfmXoJ4n6efUm2p8Vg==} engines: {node: '>= 18.0.0', npm: '>= 10.0.0', pnpm: '>= 8.9.2'} '@nodelib/fs.scandir@2.1.5': @@ -2035,7 +2035,7 @@ snapshots: '@inquirer/figures@1.0.7': {} - '@lightningjs/renderer@3.0.0-beta4': {} + '@lightningjs/renderer@3.0.0-beta6': {} '@nodelib/fs.scandir@2.1.5': dependencies: From 551cbadf1118aed7a4a16a80115d9d88ff1a891e Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Tue, 8 Apr 2025 08:22:18 -0400 Subject: [PATCH 13/78] new packagemanager line --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 944ed61..03b4ccd 100644 --- a/package.json +++ b/package.json @@ -82,5 +82,6 @@ "LICENSE", "NOTICE", "README.md" - ] + ], + "packageManager": "pnpm@9.4.0+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a" } From c23ad51ae31924d986016849a2a06d0dead4c56f Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Tue, 8 Apr 2025 08:24:05 -0400 Subject: [PATCH 14/78] update release it script --- package.json | 2 +- pnpm-lock.yaml | 912 +++++++++++++++++++++++++++++-------------------- 2 files changed, 537 insertions(+), 377 deletions(-) diff --git a/package.json b/package.json index 03b4ccd..aac5455 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "husky": "^9.1.6", "lint-staged": "^15.2.10", "prettier": "^3.3.3", - "release-it": "^17.10.0", + "release-it": "^18.1.2", "typescript": "^5.6.3", "typescript-eslint": "^8.14.0", "vite": "^5.4.11" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 85caeed..bf7ea8d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,8 +46,8 @@ importers: specifier: ^3.3.3 version: 3.3.3 release-it: - specifier: ^17.10.0 - version: 17.10.0(typescript@5.6.3) + specifier: ^18.1.2 + version: 18.1.2(@types/node@22.14.0)(typescript@5.6.3) typescript: specifier: ^5.6.3 version: 5.6.3 @@ -56,7 +56,7 @@ importers: version: 8.16.0(eslint@9.15.0)(typescript@5.6.3) vite: specifier: ^5.4.11 - version: 5.4.11 + version: 5.4.11(@types/node@22.14.0) packages: @@ -263,9 +263,126 @@ packages: '@iarna/toml@2.2.5': resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} - '@inquirer/figures@1.0.7': - resolution: {integrity: sha512-m+Trk77mp54Zma6xLkLuY+mvanPxlE4A7yNKs2HBiyZ4UkVs28Mv5c/pgWrHeInx+USHeX/WEPzjrWrcJiQgjw==} + '@inquirer/checkbox@4.1.5': + resolution: {integrity: sha512-swPczVU+at65xa5uPfNP9u3qx/alNwiaykiI/ExpsmMSQW55trmZcwhYWzw/7fj+n6Q8z1eENvR7vFfq9oPSAQ==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.9': + resolution: {integrity: sha512-NgQCnHqFTjF7Ys2fsqK2WtnA8X1kHyInyG+nMIuHowVTIgIuS10T4AznI/PvbqSpJqjCUqNBlKGh1v3bwLFL4w==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@10.1.10': + resolution: {integrity: sha512-roDaKeY1PYY0aCqhRmXihrHjoSW2A00pV3Ke5fTpMCkzcGF64R8e0lw3dK+eLEHwS4vB5RnW1wuQmvzoRul8Mw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.10': + resolution: {integrity: sha512-5GVWJ+qeI6BzR6TIInLP9SXhWCEcvgFQYmcRG6d6RIlhFjM5TyG18paTGBgRYyEouvCmzeco47x9zX9tQEofkw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.12': + resolution: {integrity: sha512-jV8QoZE1fC0vPe6TnsOfig+qwu7Iza1pkXoUJ3SroRagrt2hxiL+RbM432YAihNR7m7XnU0HWl/WQ35RIGmXHw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@1.0.11': + resolution: {integrity: sha512-eOg92lvrn/aRUqbxRyvpEWnrvRuTYRifixHkYVpJiygTgVSBIHDqLh0SrMQXkafvULg3ck11V7xvR+zcgvpHFw==} + engines: {node: '>=18'} + + '@inquirer/input@4.1.9': + resolution: {integrity: sha512-mshNG24Ij5KqsQtOZMgj5TwEjIf+F2HOESk6bjMwGWgcH5UBe8UoljwzNFHqdMbGYbgAf6v2wU/X9CAdKJzgOA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.12': + resolution: {integrity: sha512-7HRFHxbPCA4e4jMxTQglHJwP+v/kpFsCf2szzfBHy98Wlc3L08HL76UDiA87TOdX5fwj2HMOLWqRWv9Pnn+Z5Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/password@4.0.12': + resolution: {integrity: sha512-FlOB0zvuELPEbnBYiPaOdJIaDzb2PmJ7ghi/SVwIHDDSQ2K4opGBkF+5kXOg6ucrtSUQdLhVVY5tycH0j0l+0g==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/prompts@7.4.1': + resolution: {integrity: sha512-UlmM5FVOZF0gpoe1PT/jN4vk8JmpIWBlMvTL8M+hlvPmzN89K6z03+IFmyeu/oFCenwdwHDr2gky7nIGSEVvlA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.0.12': + resolution: {integrity: sha512-wNPJZy8Oc7RyGISPxp9/MpTOqX8lr0r+lCCWm7hQra+MDtYRgINv1hxw7R+vKP71Bu/3LszabxOodfV/uTfsaA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/search@3.0.12': + resolution: {integrity: sha512-H/kDJA3kNlnNIjB8YsaXoQI0Qccgf0Na14K1h8ExWhNmUg2E941dyFPrZeugihEa9AZNW5NdsD/NcvUME83OPQ==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/select@4.1.1': + resolution: {integrity: sha512-IUXzzTKVdiVNMA+2yUvPxWsSgOG4kfX93jOM4Zb5FgujeInotv5SPIJVeXQ+fO4xu7tW8VowFhdG5JRmmCyQ1Q==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.6': + resolution: {integrity: sha512-/mKVCtVpyBu3IDarv0G+59KC4stsD5mDsGpYh+GKs1NZT88Jh52+cuoA1AtLk2Q0r/quNl+1cSUyLRHBFeD0XA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true '@lightningjs/renderer@3.0.0-beta6': resolution: {integrity: sha512-1spHTpZ8CLgL9ZZNtvKdaJYazn+SM4neIOj5wb8jJ6aFBbj4CyAeigyNmhYylo0lUr9VxfmXoJ4n6efUm2p8Vg==} @@ -283,36 +400,39 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@octokit/auth-token@4.0.0': - resolution: {integrity: sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==} + '@octokit/auth-token@5.1.2': + resolution: {integrity: sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==} engines: {node: '>= 18'} - '@octokit/core@5.2.0': - resolution: {integrity: sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==} + '@octokit/core@6.1.4': + resolution: {integrity: sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==} engines: {node: '>= 18'} - '@octokit/endpoint@9.0.5': - resolution: {integrity: sha512-ekqR4/+PCLkEBF6qgj8WqJfvDq65RH85OAgrtnVp1mSxaXF03u2xW/hUdweGS5654IlC0wkNYC18Z50tSYTAFw==} + '@octokit/endpoint@10.1.3': + resolution: {integrity: sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==} engines: {node: '>= 18'} - '@octokit/graphql@7.1.0': - resolution: {integrity: sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==} + '@octokit/graphql@8.2.1': + resolution: {integrity: sha512-n57hXtOoHrhwTWdvhVkdJHdhTv0JstjDbDRhJfwIRNfFqmSo1DaK/mD2syoNUoLCyqSjBpGAKOG0BuwF392slw==} engines: {node: '>= 18'} '@octokit/openapi-types@22.2.0': resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==} + '@octokit/openapi-types@24.2.0': + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} + '@octokit/plugin-paginate-rest@11.3.1': resolution: {integrity: sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==} engines: {node: '>= 18'} peerDependencies: '@octokit/core': '5' - '@octokit/plugin-request-log@4.0.1': - resolution: {integrity: sha512-GihNqNpGHorUrO7Qa9JbAl0dbLnqJVrV8OXe2Zm5/Y4wFkZQDfTreBzVmiRfJVfE4mClXdihHnbpyyO9FSX4HA==} + '@octokit/plugin-request-log@5.3.1': + resolution: {integrity: sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==} engines: {node: '>= 18'} peerDependencies: - '@octokit/core': '5' + '@octokit/core': '>=6' '@octokit/plugin-rest-endpoint-methods@13.2.2': resolution: {integrity: sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==} @@ -320,18 +440,21 @@ packages: peerDependencies: '@octokit/core': ^5 - '@octokit/request-error@5.1.0': - resolution: {integrity: sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==} + '@octokit/request-error@6.1.7': + resolution: {integrity: sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==} engines: {node: '>= 18'} - '@octokit/request@8.4.0': - resolution: {integrity: sha512-9Bb014e+m2TgBeEJGEbdplMVWwPmL1FPtggHQRkV+WVsMggPtEkLKPlcVYm/o8xKLkpJ7B+6N8WfQMtDLX2Dpw==} + '@octokit/request@9.2.2': + resolution: {integrity: sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==} engines: {node: '>= 18'} - '@octokit/rest@20.1.1': - resolution: {integrity: sha512-MB4AYDsM5jhIHro/dq4ix1iWTLGToIGk6cWF5L6vanFaMble5jTX/UBQyiv05HsWnwUtY8JrfHy2LWfKwihqMw==} + '@octokit/rest@21.0.2': + resolution: {integrity: sha512-+CiLisCoyWmYicH25y1cDfCrv41kRSvTq6pPWtRroRJzhsCZWZyCqGyI8foJT5LmScADSwRAnr/xo+eewL04wQ==} engines: {node: '>= 18'} + '@octokit/types@13.10.0': + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} + '@octokit/types@13.6.1': resolution: {integrity: sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==} @@ -441,10 +564,17 @@ packages: cpu: [x64] os: [win32] + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} @@ -460,6 +590,12 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node@22.14.0': + resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==} + + '@types/parse-path@7.0.3': + resolution: {integrity: sha512-LriObC2+KYZD3FzCrgWGv/qufdUy4eXrxcLgQMfYXgPbLIecKIsVBaQgUPmxSSLcjmYbDTQbMgr6qr6l/eb7Bg==} + '@typescript-eslint/eslint-plugin@8.14.0': resolution: {integrity: sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -589,8 +725,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - agent-base@7.1.1: - resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} ajv@6.12.6: @@ -639,18 +775,12 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - basic-ftp@5.0.5: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} - before-after-hook@2.2.3: - resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + before-after-hook@3.0.2: + resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} boxen@8.0.1: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} @@ -666,9 +796,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -689,21 +816,21 @@ packages: resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - ci-info@4.0.0: - resolution: {integrity: sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==} + ci-info@4.2.0: + resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} engines: {node: '>=8'} cli-boxes@3.0.0: resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} engines: {node: '>=10'} - cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} - cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} @@ -720,10 +847,6 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -793,9 +916,6 @@ packages: resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} engines: {node: '>=18'} - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - define-lazy-prop@3.0.0: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} @@ -804,9 +924,6 @@ packages: resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} engines: {node: '>= 14'} - deprecation@2.3.1: - resolution: {integrity: sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==} - dot-prop@9.0.0: resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} engines: {node: '>=18'} @@ -916,22 +1033,21 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - execa@5.1.1: - resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} - engines: {node: '>=10'} - - execa@8.0.0: - resolution: {integrity: sha512-CTNS0BcKBcoOsawKBlpcKNmK4Kjuyz5jVLhf+PUsHGMqiKMVTa4cN3U7r7bRY8KTpfOGpXMo27fdy0dYVg2pqA==} - engines: {node: '>=16.17'} - execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + execa@9.5.2: + resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} + engines: {node: ^18.19.0 || >=20.5.0} + external-editor@3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} + fast-content-type-parse@2.0.1: + resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -951,6 +1067,10 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -989,23 +1109,23 @@ packages: resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} engines: {node: '>=18'} - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-uri@6.0.3: resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} engines: {node: '>= 14'} - git-up@7.0.0: - resolution: {integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==} + git-up@8.1.0: + resolution: {integrity: sha512-cT2f5ERrhFDMPS5wLHURcjRiacC8HonX0zIAWBTwHv1fS6HheP902l6pefOX/H9lNmvCHDwomw0VeN7nhg5bxg==} - git-url-parse@14.0.0: - resolution: {integrity: sha512-NnLweV+2A4nCvn4U/m2AoYu0pPKlsmhK9cknG7IMwsjFY1S2jxM+mAhsDxyxfCIGfGaD+dozsyX4b6vkYc83yQ==} + git-url-parse@16.0.0: + resolution: {integrity: sha512-Y8iAF0AmCaqXc6a5GYgPQW9ESbncNLOL+CeQAJRhmWUOmnPkKpBYeWYp4mFd3LA5j53CdGDdslzX12yEBVHQQg==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -1056,18 +1176,18 @@ packages: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} - human-signals@2.1.0: - resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} - engines: {node: '>=10.17.0'} - human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + husky@9.1.6: resolution: {integrity: sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==} engines: {node: '>=18'} @@ -1077,9 +1197,6 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1106,9 +1223,11 @@ packages: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - inquirer@9.3.2: - resolution: {integrity: sha512-+ynEbhWKhyomnaX0n2aLIMSkgSlGB5RrWbNXnEqj6mdaIydu6y40MdBjL38SAB0JcdmOaIaMua1azdjLEr3sdw==} + inquirer@12.3.0: + resolution: {integrity: sha512-3NixUXq+hM8ezj2wc7wC37b32/rHq1MwNZDYdvx+d6jokOD+r+i8Q4Pkylh9tISYP114A128LCX8RKhopC5RfQ==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' interpret@1.4.0: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} @@ -1164,10 +1283,6 @@ packages: resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} engines: {node: '>=18'} - is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - is-interactive@2.0.0: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} @@ -1184,20 +1299,20 @@ packages: resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} engines: {node: '>=12'} + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + is-ssh@1.4.0: resolution: {integrity: sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==} - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} is-unicode-supported@1.3.0: resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} @@ -1299,10 +1414,6 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - log-symbols@6.0.0: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} @@ -1338,10 +1449,6 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} @@ -1363,9 +1470,9 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} @@ -1383,21 +1490,17 @@ packages: resolution: {integrity: sha512-NHDDGYudnvRutt/VhKFlX26IotXe1w0cmkDm6JGquh5bz/bDTw0LufSmH/GxTjEdpHEO+bVKFTwdrcGa/9XlKQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - npm-run-path@4.0.1: - resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} - engines: {node: '>=8'} - npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} @@ -1414,17 +1517,13 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} - - ora@8.1.0: - resolution: {integrity: sha512-GQEkNkH/GHOhPFXcqZs3IDahXEQcQxsSjEkK4KvEEST4t7eNzoMjxTzef+EZ+JluDEV+Raoi3WQ2CflnRdSVnQ==} + ora@8.1.1: + resolution: {integrity: sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==} engines: {node: '>=18'} - os-name@5.1.0: - resolution: {integrity: sha512-YEIoAnM6zFmzw3PQ201gCVCIWbXNyKObGlVvpAVvraAeOHnlYVKFssbA/riRX5R40WA6kKrZ7Dr7dWzO3nKSeQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + os-name@6.0.0: + resolution: {integrity: sha512-bv608E0UX86atYi2GMGjDe0vF/X1TJjemNS8oEW6z22YW1Rc3QykSYoGfkQbX0zZX9H0ZB6CQP/3GTf1I5hURg==} + engines: {node: '>=18'} os-tmpdir@1.0.2: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} @@ -1438,8 +1537,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - pac-proxy-agent@7.0.2: - resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} + pac-proxy-agent@7.2.0: + resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} engines: {node: '>= 14'} pac-resolver@7.0.1: @@ -1458,11 +1557,16 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + parse-path@7.0.0: resolution: {integrity: sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==} - parse-url@8.1.0: - resolution: {integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==} + parse-url@9.2.0: + resolution: {integrity: sha512-bCgsFI+GeGWPAvAiUv63ZorMeif3/U0zaXABGJbOWt5OH2KCaPHF6S+0ok4aqM9RuIPGyZdx9tR9l13PsW4AYQ==} + engines: {node: '>=14.13.0'} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -1487,9 +1591,6 @@ packages: resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} engines: {node: '>=12'} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1519,14 +1620,18 @@ packages: engines: {node: '>=14'} hasBin: true + pretty-ms@9.2.0: + resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + engines: {node: '>=18'} + proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} protocols@2.0.1: resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==} - proxy-agent@6.4.0: - resolution: {integrity: sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==} + proxy-agent@6.5.0: + resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} engines: {node: '>= 14'} proxy-from-env@1.1.0: @@ -1547,10 +1652,6 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - rechoir@0.6.2: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} @@ -1563,9 +1664,9 @@ packages: resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} engines: {node: '>=12'} - release-it@17.10.0: - resolution: {integrity: sha512-00cXYEl7RFD5NnjXpwaH9JFjpwe8w3NcfUd4XPxrKQkszp1xppPo42zK9eSbxStKyPA5CVk2KmKPDPDiAKVJTA==} - engines: {node: ^18.18.0 || ^20.9.0 || ^22.0.0} + release-it@18.1.2: + resolution: {integrity: sha512-HOVRcicehCgoCsPFOu0iCBlEC8GDOoKS5s6ICkWmqomGEoZtRQ88D3RCsI5MciSU8vAQU+aWZW2z57NQNNb74w==} + engines: {node: ^20.9.0 || >=22.0.0} hasBin: true resolve-from@4.0.0: @@ -1576,10 +1677,6 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true - restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} - restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} @@ -1614,9 +1711,6 @@ packages: rxjs@7.8.1: resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -1638,9 +1732,6 @@ packages: engines: {node: '>=4'} hasBin: true - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -1661,8 +1752,8 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - socks-proxy-agent@8.0.4: - resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} socks@2.8.3: @@ -1696,9 +1787,6 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -1707,14 +1795,14 @@ packages: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} - strip-final-newline@2.0.0: - resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} - engines: {node: '>=6'} - strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} @@ -1786,12 +1874,23 @@ packages: engines: {node: '>=14.17'} hasBin: true + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@6.21.1: + resolution: {integrity: sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==} + engines: {node: '>=18.17'} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} - universal-user-agent@6.0.1: - resolution: {integrity: sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==} + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + universal-user-agent@7.0.2: + resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==} universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} @@ -1808,9 +1907,6 @@ packages: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vite@5.4.11: resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1842,9 +1938,6 @@ packages: terser: optional: true - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - when-exit@2.1.3: resolution: {integrity: sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw==} @@ -1857,12 +1950,12 @@ packages: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} - wildcard-match@5.1.3: - resolution: {integrity: sha512-a95hPUk+BNzSGLntNXYxsjz2Hooi5oL7xOfJR6CKwSsSALh7vUNuTlzsrZowtYy38JNduYFRVhFv19ocqNOZlg==} + wildcard-match@5.1.4: + resolution: {integrity: sha512-wldeCaczs8XXq7hj+5d/F38JE2r7EXgb6WQDM84RVwxy81T/sxB5e9+uZLK9Q9oNz1mlvjut+QtvgaOQFPVq/g==} - windows-release@5.1.1: - resolution: {integrity: sha512-NMD00arvqcq2nwqc5Q6KtrSRHK+fVD31erE5FEMahAw5PmVCgD7MUXodq3pdZSUkqA9Cda2iWx6s1XYwiJWRmw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + windows-release@6.0.1: + resolution: {integrity: sha512-MS3BzG8QK33dAyqwxfYJCJ03arkwKaddUOvvnnlFdXLudflsQF6I8yAxrLBeQk4yO8wjdH/+ax0YzxJEDrOftg==} + engines: {node: '>=18'} word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} @@ -1900,13 +1993,17 @@ packages: resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} engines: {node: '>=18'} + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + snapshots: '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 js-tokens: 4.0.0 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/helper-validator-identifier@7.25.9': {} @@ -2033,7 +2130,121 @@ snapshots: '@iarna/toml@2.2.5': {} - '@inquirer/figures@1.0.7': {} + '@inquirer/checkbox@4.1.5(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/figures': 1.0.11 + '@inquirer/type': 3.0.6(@types/node@22.14.0) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/confirm@5.1.9(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/type': 3.0.6(@types/node@22.14.0) + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/core@10.1.10(@types/node@22.14.0)': + dependencies: + '@inquirer/figures': 1.0.11 + '@inquirer/type': 3.0.6(@types/node@22.14.0) + ansi-escapes: 4.3.2 + cli-width: 4.1.0 + mute-stream: 2.0.0 + signal-exit: 4.1.0 + wrap-ansi: 6.2.0 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/editor@4.2.10(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/type': 3.0.6(@types/node@22.14.0) + external-editor: 3.1.0 + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/expand@4.0.12(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/type': 3.0.6(@types/node@22.14.0) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/figures@1.0.11': {} + + '@inquirer/input@4.1.9(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/type': 3.0.6(@types/node@22.14.0) + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/number@3.0.12(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/type': 3.0.6(@types/node@22.14.0) + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/password@4.0.12(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/type': 3.0.6(@types/node@22.14.0) + ansi-escapes: 4.3.2 + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/prompts@7.4.1(@types/node@22.14.0)': + dependencies: + '@inquirer/checkbox': 4.1.5(@types/node@22.14.0) + '@inquirer/confirm': 5.1.9(@types/node@22.14.0) + '@inquirer/editor': 4.2.10(@types/node@22.14.0) + '@inquirer/expand': 4.0.12(@types/node@22.14.0) + '@inquirer/input': 4.1.9(@types/node@22.14.0) + '@inquirer/number': 3.0.12(@types/node@22.14.0) + '@inquirer/password': 4.0.12(@types/node@22.14.0) + '@inquirer/rawlist': 4.0.12(@types/node@22.14.0) + '@inquirer/search': 3.0.12(@types/node@22.14.0) + '@inquirer/select': 4.1.1(@types/node@22.14.0) + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/rawlist@4.0.12(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/type': 3.0.6(@types/node@22.14.0) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/search@3.0.12(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/figures': 1.0.11 + '@inquirer/type': 3.0.6(@types/node@22.14.0) + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/select@4.1.1(@types/node@22.14.0)': + dependencies: + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/figures': 1.0.11 + '@inquirer/type': 3.0.6(@types/node@22.14.0) + ansi-escapes: 4.3.2 + yoctocolors-cjs: 2.1.2 + optionalDependencies: + '@types/node': 22.14.0 + + '@inquirer/type@3.0.6(@types/node@22.14.0)': + optionalDependencies: + '@types/node': 22.14.0 '@lightningjs/renderer@3.0.0-beta6': {} @@ -2049,64 +2260,69 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - '@octokit/auth-token@4.0.0': {} + '@octokit/auth-token@5.1.2': {} - '@octokit/core@5.2.0': + '@octokit/core@6.1.4': dependencies: - '@octokit/auth-token': 4.0.0 - '@octokit/graphql': 7.1.0 - '@octokit/request': 8.4.0 - '@octokit/request-error': 5.1.0 - '@octokit/types': 13.6.1 - before-after-hook: 2.2.3 - universal-user-agent: 6.0.1 + '@octokit/auth-token': 5.1.2 + '@octokit/graphql': 8.2.1 + '@octokit/request': 9.2.2 + '@octokit/request-error': 6.1.7 + '@octokit/types': 13.10.0 + before-after-hook: 3.0.2 + universal-user-agent: 7.0.2 - '@octokit/endpoint@9.0.5': + '@octokit/endpoint@10.1.3': dependencies: - '@octokit/types': 13.6.1 - universal-user-agent: 6.0.1 + '@octokit/types': 13.10.0 + universal-user-agent: 7.0.2 - '@octokit/graphql@7.1.0': + '@octokit/graphql@8.2.1': dependencies: - '@octokit/request': 8.4.0 - '@octokit/types': 13.6.1 - universal-user-agent: 6.0.1 + '@octokit/request': 9.2.2 + '@octokit/types': 13.10.0 + universal-user-agent: 7.0.2 '@octokit/openapi-types@22.2.0': {} - '@octokit/plugin-paginate-rest@11.3.1(@octokit/core@5.2.0)': + '@octokit/openapi-types@24.2.0': {} + + '@octokit/plugin-paginate-rest@11.3.1(@octokit/core@6.1.4)': dependencies: - '@octokit/core': 5.2.0 + '@octokit/core': 6.1.4 '@octokit/types': 13.6.1 - '@octokit/plugin-request-log@4.0.1(@octokit/core@5.2.0)': + '@octokit/plugin-request-log@5.3.1(@octokit/core@6.1.4)': dependencies: - '@octokit/core': 5.2.0 + '@octokit/core': 6.1.4 - '@octokit/plugin-rest-endpoint-methods@13.2.2(@octokit/core@5.2.0)': + '@octokit/plugin-rest-endpoint-methods@13.2.2(@octokit/core@6.1.4)': dependencies: - '@octokit/core': 5.2.0 + '@octokit/core': 6.1.4 '@octokit/types': 13.6.1 - '@octokit/request-error@5.1.0': + '@octokit/request-error@6.1.7': dependencies: - '@octokit/types': 13.6.1 - deprecation: 2.3.1 - once: 1.4.0 + '@octokit/types': 13.10.0 - '@octokit/request@8.4.0': + '@octokit/request@9.2.2': dependencies: - '@octokit/endpoint': 9.0.5 - '@octokit/request-error': 5.1.0 - '@octokit/types': 13.6.1 - universal-user-agent: 6.0.1 + '@octokit/endpoint': 10.1.3 + '@octokit/request-error': 6.1.7 + '@octokit/types': 13.10.0 + fast-content-type-parse: 2.0.1 + universal-user-agent: 7.0.2 + + '@octokit/rest@21.0.2': + dependencies: + '@octokit/core': 6.1.4 + '@octokit/plugin-paginate-rest': 11.3.1(@octokit/core@6.1.4) + '@octokit/plugin-request-log': 5.3.1(@octokit/core@6.1.4) + '@octokit/plugin-rest-endpoint-methods': 13.2.2(@octokit/core@6.1.4) - '@octokit/rest@20.1.1': + '@octokit/types@13.10.0': dependencies: - '@octokit/core': 5.2.0 - '@octokit/plugin-paginate-rest': 11.3.1(@octokit/core@5.2.0) - '@octokit/plugin-request-log': 4.0.1(@octokit/core@5.2.0) - '@octokit/plugin-rest-endpoint-methods': 13.2.2(@octokit/core@5.2.0) + '@octokit/openapi-types': 24.2.0 '@octokit/types@13.6.1': dependencies: @@ -2180,8 +2396,12 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.27.2': optional: true + '@sec-ant/readable-stream@0.4.1': {} + '@sindresorhus/merge-streams@2.3.0': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@tootallnate/quickjs-emscripten@0.23.0': {} '@types/eslint@9.6.1': @@ -2197,6 +2417,12 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/node@22.14.0': + dependencies: + undici-types: 6.21.0 + + '@types/parse-path@7.0.3': {} + '@typescript-eslint/eslint-plugin@8.14.0(@typescript-eslint/parser@8.14.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -2366,11 +2592,7 @@ snapshots: acorn@8.14.0: {} - agent-base@7.1.1: - dependencies: - debug: 4.3.7 - transitivePeerDependencies: - - supports-color + agent-base@7.1.3: {} ajv@6.12.6: dependencies: @@ -2418,23 +2640,15 @@ snapshots: balanced-match@1.0.2: {} - base64-js@1.5.1: {} - basic-ftp@5.0.5: {} - before-after-hook@2.2.3: {} - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 + before-after-hook@3.0.2: {} boxen@8.0.1: dependencies: ansi-align: 3.0.1 camelcase: 8.0.0 - chalk: 5.3.0 + chalk: 5.4.1 cli-boxes: 3.0.0 string-width: 7.2.0 type-fest: 4.26.1 @@ -2454,11 +2668,6 @@ snapshots: dependencies: fill-range: 7.1.1 - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - bundle-name@4.1.0: dependencies: run-applescript: 7.0.0 @@ -2474,16 +2683,14 @@ snapshots: chalk@5.3.0: {} + chalk@5.4.1: {} + chardet@0.7.0: {} - ci-info@4.0.0: {} + ci-info@4.2.0: {} cli-boxes@3.0.0: {} - cli-cursor@3.1.0: - dependencies: - restore-cursor: 3.1.0 - cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 @@ -2497,8 +2704,6 @@ snapshots: cli-width@4.1.0: {} - clone@1.0.4: {} - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -2561,10 +2766,6 @@ snapshots: bundle-name: 4.1.0 default-browser-id: 5.0.0 - defaults@1.0.4: - dependencies: - clone: 1.0.4 - define-lazy-prop@3.0.0: {} degenerator@5.0.1: @@ -2573,8 +2774,6 @@ snapshots: escodegen: 2.1.0 esprima: 4.0.1 - deprecation@2.3.1: {} - dot-prop@9.0.0: dependencies: type-fest: 4.26.1 @@ -2713,19 +2912,7 @@ snapshots: eventemitter3@5.0.1: {} - execa@5.1.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 2.1.0 - is-stream: 2.0.1 - merge-stream: 2.0.0 - npm-run-path: 4.0.1 - onetime: 5.1.2 - signal-exit: 3.0.7 - strip-final-newline: 2.0.0 - - execa@8.0.0: + execa@8.0.1: dependencies: cross-spawn: 7.0.3 get-stream: 8.0.1 @@ -2737,17 +2924,20 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 - execa@8.0.1: + execa@9.5.2: dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.2.0 signal-exit: 4.1.0 - strip-final-newline: 3.0.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.1 external-editor@3.1.0: dependencies: @@ -2755,6 +2945,8 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 + fast-content-type-parse@2.0.1: {} + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -2775,6 +2967,10 @@ snapshots: dependencies: reusify: 1.0.4 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -2810,10 +3006,13 @@ snapshots: get-east-asian-width@1.2.0: {} - get-stream@6.0.1: {} - get-stream@8.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-uri@6.0.3: dependencies: basic-ftp: 5.0.5 @@ -2823,14 +3022,14 @@ snapshots: transitivePeerDependencies: - supports-color - git-up@7.0.0: + git-up@8.1.0: dependencies: is-ssh: 1.4.0 - parse-url: 8.1.0 + parse-url: 9.2.0 - git-url-parse@14.0.0: + git-url-parse@16.0.0: dependencies: - git-up: 7.0.0 + git-up: 8.1.0 glob-parent@5.1.2: dependencies: @@ -2880,30 +3079,28 @@ snapshots: http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.1 + agent-base: 7.1.3 debug: 4.3.7 transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.5: + https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.1 + agent-base: 7.1.3 debug: 4.3.7 transitivePeerDependencies: - supports-color - human-signals@2.1.0: {} - human-signals@5.0.0: {} + human-signals@8.0.1: {} + husky@9.1.6: {} iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - ieee754@1.2.1: {} - ignore@5.3.2: {} import-fresh@3.3.0: @@ -2924,20 +3121,16 @@ snapshots: ini@4.1.1: {} - inquirer@9.3.2: + inquirer@12.3.0(@types/node@22.14.0): dependencies: - '@inquirer/figures': 1.0.7 + '@inquirer/core': 10.1.10(@types/node@22.14.0) + '@inquirer/prompts': 7.4.1(@types/node@22.14.0) + '@inquirer/type': 3.0.6(@types/node@22.14.0) + '@types/node': 22.14.0 ansi-escapes: 4.3.2 - cli-width: 4.1.0 - external-editor: 3.1.0 - mute-stream: 1.0.0 - ora: 5.4.1 + mute-stream: 2.0.0 run-async: 3.0.0 rxjs: 7.8.1 - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.2 interpret@1.4.0: {} @@ -2979,8 +3172,6 @@ snapshots: global-directory: 4.0.1 is-path-inside: 4.0.0 - is-interactive@1.0.0: {} - is-interactive@2.0.0: {} is-npm@6.0.0: {} @@ -2989,15 +3180,15 @@ snapshots: is-path-inside@4.0.0: {} + is-plain-obj@4.1.0: {} + is-ssh@1.4.0: dependencies: protocols: 2.0.1 - is-stream@2.0.1: {} - is-stream@3.0.0: {} - is-unicode-supported@0.1.0: {} + is-stream@4.0.1: {} is-unicode-supported@1.3.0: {} @@ -3100,14 +3291,9 @@ snapshots: lodash@4.17.21: {} - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - log-symbols@6.0.0: dependencies: - chalk: 5.3.0 + chalk: 5.4.1 is-unicode-supported: 1.3.0 log-update@6.1.0: @@ -3137,8 +3323,6 @@ snapshots: dependencies: mime-db: 1.52.0 - mimic-fn@2.1.0: {} - mimic-fn@4.0.0: {} mimic-function@5.0.1: {} @@ -3155,7 +3339,7 @@ snapshots: ms@2.1.3: {} - mute-stream@1.0.0: {} + mute-stream@2.0.0: {} nanoid@3.3.7: {} @@ -3167,22 +3351,19 @@ snapshots: dependencies: type-fest: 2.19.0 - npm-run-path@4.0.1: + npm-run-path@5.3.0: dependencies: - path-key: 3.1.1 + path-key: 4.0.0 - npm-run-path@5.3.0: + npm-run-path@6.0.0: dependencies: path-key: 4.0.0 + unicorn-magic: 0.3.0 once@1.4.0: dependencies: wrappy: 1.0.2 - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - onetime@6.0.0: dependencies: mimic-fn: 4.0.0 @@ -3207,21 +3388,9 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - ora@5.4.1: - dependencies: - bl: 4.1.0 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-spinners: 2.9.2 - is-interactive: 1.0.0 - is-unicode-supported: 0.1.0 - log-symbols: 4.1.0 - strip-ansi: 6.0.1 - wcwidth: 1.0.1 - - ora@8.1.0: + ora@8.1.1: dependencies: - chalk: 5.3.0 + chalk: 5.4.1 cli-cursor: 5.0.0 cli-spinners: 2.9.2 is-interactive: 2.0.0 @@ -3231,10 +3400,10 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.0 - os-name@5.1.0: + os-name@6.0.0: dependencies: macos-release: 3.3.0 - windows-release: 5.1.1 + windows-release: 6.0.1 os-tmpdir@1.0.2: {} @@ -3246,16 +3415,16 @@ snapshots: dependencies: p-limit: 3.1.0 - pac-proxy-agent@7.0.2: + pac-proxy-agent@7.2.0: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.1 + agent-base: 7.1.3 debug: 4.3.7 get-uri: 6.0.3 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 + https-proxy-agent: 7.0.6 pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.4 + socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -3282,12 +3451,15 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse-ms@4.0.0: {} + parse-path@7.0.0: dependencies: protocols: 2.0.1 - parse-url@8.1.0: + parse-url@9.2.0: dependencies: + '@types/parse-path': 7.0.3 parse-path: 7.0.0 path-exists@4.0.0: {} @@ -3302,8 +3474,6 @@ snapshots: path-type@5.0.0: {} - picocolors@1.1.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3324,20 +3494,24 @@ snapshots: prettier@3.3.3: {} + pretty-ms@9.2.0: + dependencies: + parse-ms: 4.0.0 + proto-list@1.2.4: {} protocols@2.0.1: {} - proxy-agent@6.4.0: + proxy-agent@6.5.0: dependencies: - agent-base: 7.1.1 + agent-base: 7.1.3 debug: 4.3.7 http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 + https-proxy-agent: 7.0.6 lru-cache: 7.18.3 - pac-proxy-agent: 7.0.2 + pac-proxy-agent: 7.2.0 proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.4 + socks-proxy-agent: 8.0.5 transitivePeerDependencies: - supports-color @@ -3358,12 +3532,6 @@ snapshots: minimist: 1.2.8 strip-json-comments: 2.0.1 - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - rechoir@0.6.2: dependencies: resolve: 1.22.8 @@ -3376,33 +3544,35 @@ snapshots: dependencies: rc: 1.2.8 - release-it@17.10.0(typescript@5.6.3): + release-it@18.1.2(@types/node@22.14.0)(typescript@5.6.3): dependencies: '@iarna/toml': 2.2.5 - '@octokit/rest': 20.1.1 + '@octokit/rest': 21.0.2 async-retry: 1.3.3 - chalk: 5.3.0 - ci-info: 4.0.0 + chalk: 5.4.1 + ci-info: 4.2.0 cosmiconfig: 9.0.0(typescript@5.6.3) - execa: 8.0.0 - git-url-parse: 14.0.0 + execa: 9.5.2 + git-url-parse: 16.0.0 globby: 14.0.2 - inquirer: 9.3.2 + inquirer: 12.3.0(@types/node@22.14.0) issue-parser: 7.0.1 lodash: 4.17.21 mime-types: 2.1.35 new-github-release-url: 2.0.0 open: 10.1.0 - ora: 8.1.0 - os-name: 5.1.0 - proxy-agent: 6.4.0 + ora: 8.1.1 + os-name: 6.0.0 + proxy-agent: 6.5.0 semver: 7.6.3 shelljs: 0.8.5 + undici: 6.21.1 update-notifier: 7.3.1 url-join: 5.0.0 - wildcard-match: 5.1.3 + wildcard-match: 5.1.4 yargs-parser: 21.1.1 transitivePeerDependencies: + - '@types/node' - supports-color - typescript @@ -3414,11 +3584,6 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - restore-cursor@3.1.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - restore-cursor@5.1.0: dependencies: onetime: 7.0.0 @@ -3466,8 +3631,6 @@ snapshots: dependencies: tslib: 2.8.1 - safe-buffer@5.2.1: {} - safer-buffer@2.1.2: {} semver@7.6.3: {} @@ -3484,8 +3647,6 @@ snapshots: interpret: 1.4.0 rechoir: 0.6.2 - signal-exit@3.0.7: {} - signal-exit@4.1.0: {} slash@5.1.0: {} @@ -3502,9 +3663,9 @@ snapshots: smart-buffer@4.2.0: {} - socks-proxy-agent@8.0.4: + socks-proxy-agent@8.0.5: dependencies: - agent-base: 7.1.1 + agent-base: 7.1.3 debug: 4.3.7 socks: 2.8.3 transitivePeerDependencies: @@ -3538,10 +3699,6 @@ snapshots: get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -3550,10 +3707,10 @@ snapshots: dependencies: ansi-regex: 6.1.0 - strip-final-newline@2.0.0: {} - strip-final-newline@3.0.0: {} + strip-final-newline@4.0.0: {} + strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} @@ -3608,16 +3765,22 @@ snapshots: typescript@5.6.3: {} + undici-types@6.21.0: {} + + undici@6.21.1: {} + unicorn-magic@0.1.0: {} - universal-user-agent@6.0.1: {} + unicorn-magic@0.3.0: {} + + universal-user-agent@7.0.2: {} universalify@2.0.1: {} update-notifier@7.3.1: dependencies: boxen: 8.0.1 - chalk: 5.3.0 + chalk: 5.4.1 configstore: 7.0.0 is-in-ci: 1.0.0 is-installed-globally: 1.0.0 @@ -3633,20 +3796,15 @@ snapshots: url-join@5.0.0: {} - util-deprecate@1.0.2: {} - - vite@5.4.11: + vite@5.4.11(@types/node@22.14.0): dependencies: esbuild: 0.21.5 postcss: 8.4.49 rollup: 4.27.2 optionalDependencies: + '@types/node': 22.14.0 fsevents: 2.3.3 - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - when-exit@2.1.3: {} which@2.0.2: @@ -3657,11 +3815,11 @@ snapshots: dependencies: string-width: 7.2.0 - wildcard-match@5.1.3: {} + wildcard-match@5.1.4: {} - windows-release@5.1.1: + windows-release@6.0.1: dependencies: - execa: 5.1.1 + execa: 8.0.1 word-wrap@1.2.5: {} @@ -3688,3 +3846,5 @@ snapshots: yocto-queue@0.1.0: {} yoctocolors-cjs@2.1.2: {} + + yoctocolors@2.1.1: {} From e54b6c1a9ea41d7dff2ec68d944a4223f761eb15 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Tue, 8 Apr 2025 14:43:26 +0200 Subject: [PATCH 15/78] remove preventCleanup property from DOMNode class (#42) --- src/domRenderer.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index b5cd132..852547c 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -513,7 +513,6 @@ function resolveNodeDefaults( rotation: props.rotation ?? 0, rtt: props.rtt ?? false, data: {}, - preventCleanup: props.preventCleanup ?? false, imageType: props.imageType, strictBounds: props.strictBounds ?? false, }; @@ -717,13 +716,6 @@ class DOMNode extends EventEmitter implements IRendererNode { this.props.texture = v; updateNodeStyles(this); } - get preventCleanup() { - return this.props.preventCleanup; - } - set preventCleanup(v) { - this.props.preventCleanup = v; - updateNodeStyles(this); - } get textureOptions(): IRendererNode['textureOptions'] { return this.props.textureOptions; } From ef05a08d54d3453250441f50c04b80548e8b9037 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Tue, 8 Apr 2025 08:48:42 -0400 Subject: [PATCH 16/78] :rocket: bump version v3.0.0-3 --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 162d4ef..c1ddf6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,22 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-3](https://github.com/lightning-tv/core/compare/v3.0.0-2...v3.0.0-3) + +- remove preventCleanup property from DOMNode class [`#42`](https://github.com/lightning-tv/core/pull/42) +- dom renderer fixes [`#41`](https://github.com/lightning-tv/core/pull/41) +- update release it script [`c23ad51`](https://github.com/lightning-tv/core/commit/c23ad51ae31924d986016849a2a06d0dead4c56f) +- update renderer [`12dfac9`](https://github.com/lightning-tv/core/commit/12dfac9b4f5b26e8ef27782e290643147f117c7a) +- new packagemanager line [`551cbad`](https://github.com/lightning-tv/core/commit/551cbadf1118aed7a4a16a80115d9d88ff1a891e) + #### [v3.0.0-2](https://github.com/lightning-tv/core/compare/v3.0.0-1...v3.0.0-2) +> 3 April 2025 + - Remove \_queueDelete field from core [`#40`](https://github.com/lightning-tv/core/pull/40) - Add missing fields to renderer interfaces required by lightning/solid [`#39`](https://github.com/lightning-tv/core/pull/39) - Cleanup updateRootPosition and use appHeight for root.height [`#38`](https://github.com/lightning-tv/core/pull/38) +- :rocket: bump version v3.0.0-2 [`7c9abd8`](https://github.com/lightning-tv/core/commit/7c9abd8822aa02b21ff27fc8d39b10e4ad873367) #### [v3.0.0-1](https://github.com/lightning-tv/core/compare/v3.0.0-0...v3.0.0-1) diff --git a/package.json b/package.json index aac5455..65a3b59 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-2", + "version": "3.0.0-3", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 10acbb30cdaa310cdb22fc94c25b60a580528f1e Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Sat, 12 Apr 2025 05:28:50 +0200 Subject: [PATCH 17/78] Update pnpm to 10.8.0 (#44) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65a3b59..cae4ec6 100644 --- a/package.json +++ b/package.json @@ -83,5 +83,5 @@ "NOTICE", "README.md" ], - "packageManager": "pnpm@9.4.0+sha512.f549b8a52c9d2b8536762f99c0722205efc5af913e77835dbccc3b0b0b2ca9e7dc8022b78062c17291c48e88749c70ce88eb5a74f1fa8c4bf5e18bb46c8bd83a" + "packageManager": "pnpm@10.8.0" } From 60c3ab526fc733ceb9034ba135a099cee411d7d3 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 24 Apr 2025 14:41:10 +0200 Subject: [PATCH 18/78] Update layout when child is removed, or parent is swapped (#45) --- src/elementNode.ts | 21 ++++++++++----------- src/utils.ts | 13 +++++++++++++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 4b7d261..b0e4b3c 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -32,6 +32,7 @@ import { isElementText, logRenderTree, isFunction, + spliceItem, } from './utils.js'; import { Config, isDev, SHADERS_ENABLED } from './config.js'; import type { @@ -372,26 +373,27 @@ export class ElementNode extends Object { node: ElementNode | ElementText | TextNode, beforeNode?: ElementNode | ElementText | TextNode | null, ) { + if (node.parent && node.parent !== this) { + node.parent.removeChild(node); + } + node.parent = this; if (beforeNode) { // SolidJS can move nodes around in the children array. // We need to insert following DOM insertBefore which moves elements. - this.removeChild(node); - const index = this.children.indexOf(beforeNode as ElementNode); - if (index >= 0) { - this.children.splice(index, 0, node as ElementNode); + spliceItem(this.children, node as ElementNode, 1); + if (spliceItem(this.children, beforeNode as ElementNode, 0, node) > -1) { return; } } + this.children.push(node as ElementNode); } removeChild(node: ElementNode | ElementText | TextNode) { - const nodeIndexToRemove = this.children.indexOf(node as ElementNode); - if (nodeIndexToRemove >= 0) { - this.children.splice(nodeIndexToRemove, 1); - } + spliceItem(this.children, node as ElementNode, 1); + this.updateLayout(); } get selectedNode(): ElementNode | undefined { @@ -579,9 +581,6 @@ export class ElementNode extends Object { _destroy() { if (isINode(this.lng)) { this.lng.destroy(); - if (this.parent?.requiresLayout()) { - this.parent.updateLayout(); - } } } diff --git a/src/utils.ts b/src/utils.ts index ca87ab4..c441ea9 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -80,6 +80,19 @@ export function keyExists( return false; } +export function spliceItem( + arr: T[], + item: T, + deleteCount: number, + ...insert: T[] +): number { + const index = arr.indexOf(item); + if (index > -1) { + arr.splice(index, deleteCount, ...insert); + } + return index; +} + export function flattenStyles( obj: Styles | undefined | (Styles | undefined)[], result: Styles = {}, From a641e5bd511c14ba1e536b7669697924db2a2abc Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Fri, 2 May 2025 16:32:13 +0200 Subject: [PATCH 19/78] Fix issues in DOM renderer (#46) * Some fixes to dom renderer * Make sure the font is loaded before measuring * Correct handling text contain in dom renderer * Update text node styles when the measurement changes * Support contain textureOptions.resizeMode * Separate border and background to differnet divs when needed * Rename 'el' field to 'div' to match lightning inspector --- src/domRenderer.ts | 292 +++++++++++++++++++++++++++---------------- src/elementNode.ts | 9 +- src/lightningInit.ts | 2 + 3 files changed, 191 insertions(+), 112 deletions(-) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index 852547c..99bf0dc 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -233,23 +233,17 @@ let elMap = new WeakMap(); function updateNodeParent(node: DOMNode | DOMText) { if (node.parent != null) { - elMap.get(node.parent as DOMNode)!.appendChild(node.el); - } else { - document.body.appendChild(node.el); + elMap.get(node.parent as DOMNode)!.appendChild(node.div); } } -function getNodeStyles(node: Readonly): string { +function updateNodeStyles(node: DOMNode | DOMText) { let { props } = node; let style = `position: absolute; z-index: ${props.zIndex};`; if (props.alpha !== 1) style += `opacity: ${props.alpha};`; - if (props.width !== 0) style += `width: ${props.width}px;`; - - if (props.height !== 0) style += `height: ${props.height}px;`; - if (props.clipping) { style += `overflow: hidden;`; } @@ -274,12 +268,8 @@ function getNodeStyles(node: Readonly): string { if (props.rotation !== 0) transform += `rotate(${props.rotation}rad)`; - if (props.scale !== 1 && props.scale != null) - transform += `scale(${props.scale})`; - else { - if (props.scaleX !== 1) transform += `scaleX(${props.scaleX})`; - if (props.scaleY !== 1) transform += `scaleY(${props.scaleY})`; - } + if (props.scaleX !== 1) transform += `scaleX(${props.scaleX})`; + if (props.scaleY !== 1) transform += `scaleY(${props.scaleY})`; if (transform.length > 0) { style += `transform: ${transform};`; @@ -317,138 +307,199 @@ function getNodeStyles(node: Readonly): string { line-clamp: ${textProps.maxLines}; -webkit-box-orient: vertical;`; } - if (textProps.contain !== 'none') { - style += `overflow: hidden;`; + switch (textProps.contain) { + case 'width': + style += `width: ${textProps.width}px; overflow: hidden;`; + break; + case 'both': + style += `width: ${textProps.width}px; height: ${textProps.height}px; overflow: hidden;`; + break; + case 'none': + break; } // if (node.verticalAlign) style += `vertical-align: ${node.verticalAlign};` + + scheduleUpdateDOMTextMeasurement(node); } // else { - let bgImg: string[] = []; - let bgPos: null | { x: number; y: number } = null; + if (props.width !== 0) style += `width: ${props.width}px;`; + if (props.height !== 0) style += `height: ${props.height}px;`; - if (props.colorBottom !== props.colorTop) { - bgImg.push( - `linear-gradient(${colorToRgba(props.colorTop)}, ${colorToRgba(props.colorBottom)})`, - ); - } - if (props.colorLeft !== props.colorRight) { - bgImg.push( - `linear-gradient(to right, ${colorToRgba(props.colorLeft)}, ${colorToRgba(props.colorRight)})`, - ); - } + let vGradient = + props.colorBottom !== props.colorTop + ? `linear-gradient(to bottom, ${colorToRgba(props.colorTop)}, ${colorToRgba(props.colorBottom)})` + : null; + + let hGradient = + props.colorLeft !== props.colorRight + ? `linear-gradient(to right, ${colorToRgba(props.colorLeft)}, ${colorToRgba(props.colorRight)})` + : null; + + let gradient = + vGradient && hGradient + ? `${vGradient}, ${hGradient}` + : vGradient || hGradient; + + let srcImg: string | null = null; + let srcPos: null | { x: number; y: number } = null; if ( props.texture != null && props.texture.type === lng.TextureType.subTexture ) { - bgPos = (props.texture as any).props; - bgImg.push(`url(${(props.texture as any).props.texture.props.src})`); + srcPos = (props.texture as any).props; + srcImg = `url(${(props.texture as any).props.texture.props.src})`; } else if (props.src) { - bgImg.push(`url(${props.src})`); + srcImg = `url(${props.src})`; } - if (bgImg.length > 0) { - style += `background-image: ${bgImg.join(',')}; background-blend-mode: multiply;`; - if (bgPos !== null) { - style += `background-position: -${bgPos.x}px -${bgPos.y}px;`; + let bgStyle = ''; + let borderStyle = ''; + + if (srcImg) { + bgStyle += `background-image: ${srcImg};`; + bgStyle += `background-repeat: no-repeat;`; + + if (props.textureOptions.resizeMode?.type === 'contain') { + bgStyle += `background-size: contain; background-position: center;`; + } else if (srcPos !== null) { + bgStyle += `background-position: -${srcPos.x}px -${srcPos.y}px;`; } else { - style += 'background-size: 100% 100%;'; + bgStyle += 'background-size: 100% 100%;'; } - if (props.color !== 0xffffffff && props.color !== 0) { - style += `background-color: ${colorToRgba(props.color)};`; - style += `mask-image: ${bgImg.join(',')};`; - if (bgPos !== null) { - style += `mask-position: -${bgPos.x}px -${bgPos.y}px;`; - } else { - style += `mask-size: 100% 100%;`; + if (gradient) { + // use gradient as a mask + bgStyle += `mask-image: ${gradient};`; + // separate layers are needed for the mask + if (node.divBg == null) { + node.div.appendChild((node.divBg = document.createElement('div'))); + node.div.appendChild( + (node.divBorder = document.createElement('div')), + ); } } + } else if (gradient) { + bgStyle += `background-image: ${gradient};`; + bgStyle += `background-repeat: no-repeat;`; + bgStyle += `background-size: 100% 100%;`; } else if (props.color !== 0) { - style += `background-color: ${colorToRgba(props.color)};`; + bgStyle += `background-color: ${colorToRgba(props.color)};`; } if (props.shader != null) { let shader = props.shader.props; if (shader != null) { + const borderWidth = shader['border-width'] as number | undefined; + const borderColor = shader['border-color'] as number | undefined; + const radius = shader['radius'] as + | number + | [number, number, number, number] + | undefined; + // Border if ( - typeof shader['border-width'] === 'number' && - shader['border-width'] > 0 && - typeof shader['border-color'] === 'number' && - shader['border-color'] > 0 + typeof borderWidth === 'number' && + borderWidth !== 0 && + typeof borderColor === 'number' && + borderColor !== 0 ) { // css border impacts the element's box size when box-shadow doesn't - style += `box-shadow: inset 0px 0px 0px ${shader['border-width']}px ${colorToRgba(shader['border-color'])};`; + borderStyle += `box-shadow: inset 0px 0px 0px ${borderWidth}px ${colorToRgba(borderColor)};`; } // Rounded - if (typeof shader['radius'] === 'number' && shader['radius'] > 0) { - style += `border-radius: ${shader['radius']}px;`; + if (typeof radius === 'number' && radius > 0) { + borderStyle += `border-radius: ${radius}px;`; + } else if (Array.isArray(radius) && radius.length === 4) { + borderStyle += `border-radius: ${radius[0]}px ${radius[1]}px ${radius[2]}px ${radius[3]}px;`; } } } - } - return style; -} - -function updateNodeStyles(node: DOMNode | DOMText) { - node.el.setAttribute('style', getNodeStyles(node)); + style += borderStyle; + bgStyle += borderStyle; - if (node instanceof DOMText) { - scheduleUpdateTextNodeMeasurement(node); + if (node.divBg == null) { + style += bgStyle; + } else { + bgStyle += 'position: absolute; inset: 0; z-index: -1;'; + node.divBg.setAttribute('style', bgStyle); + } + if (node.divBorder != null) { + borderStyle += 'position: absolute; inset: 0; z-index: -1;'; + node.divBorder.setAttribute('style', borderStyle); + } } + + node.div.setAttribute('style', style); } -/* - Text nodes with contain 'width' or 'none' - need to have their height or width calculated. - And then cause the flex layout to be recalculated. -*/ +const fontFamiliesToLoad = new Set(); const textNodesToMeasure = new Set(); type Size = { width: number; height: number }; function getElSize(node: DOMNode): Size { - let rect = node.el.getBoundingClientRect(); + let rect = node.div.getBoundingClientRect(); let dpr = Config.rendererOptions?.deviceLogicalPixelRatio ?? 1; - rect.height = rect.height / dpr; - rect.width = rect.width / dpr; + rect.height = rect.height / node.scaleY / dpr; + rect.width = rect.width / node.scaleX / dpr; return rect; } -function updateTextNodeMeasurements() { - for (let node of textNodesToMeasure) { - let size: Size; - switch (node.contain) { - case 'width': - size = getElSize(node); - if (node.props.height !== size.height) { - node.props.height = size.height; - node.emit('loaded'); - } - break; - case 'none': - size = getElSize(node); - if ( - node.props.height !== size.height || - node.props.width !== size.width - ) { - node.props.width = size.width; - node.props.height = size.height; - node.emit('loaded'); - } - break; - } +/* + Text nodes with contain 'width' or 'none' + need to have their height or width calculated. + And then cause the flex layout to be recalculated. +*/ +function updateDOMTextSize(node: DOMText): void { + let size: Size; + switch (node.contain) { + case 'width': + size = getElSize(node); + if (node.props.height !== size.height) { + node.props.height = size.height; + updateNodeStyles(node); + node.emit('loaded'); + } + break; + case 'none': + size = getElSize(node); + if ( + node.props.height !== size.height || + node.props.width !== size.width + ) { + node.props.width = size.width; + node.props.height = size.height; + updateNodeStyles(node); + node.emit('loaded'); + } + break; } +} + +function updateDOMTextMeasurements() { + textNodesToMeasure.forEach(updateDOMTextSize); textNodesToMeasure.clear(); } -function scheduleUpdateTextNodeMeasurement(node: DOMText) { +function scheduleUpdateDOMTextMeasurement(node: DOMText) { + /* + Make sure the font is loaded before measuring + */ + if (node.fontFamily && !fontFamiliesToLoad.has(node.fontFamily)) { + fontFamiliesToLoad.add(node.fontFamily); + document.fonts.load(`16px ${node.fontFamily}`); + } + if (textNodesToMeasure.size === 0) { - setTimeout(updateTextNodeMeasurements); + if (document.fonts.status === 'loaded') { + setTimeout(updateDOMTextMeasurements); + } else { + document.fonts.ready.then(updateDOMTextMeasurements); + } } textNodesToMeasure.add(node); @@ -458,9 +509,9 @@ function updateNodeData(node: DOMNode | DOMText) { for (let key in node.data) { let keyValue: unknown = node.data[key]; if (keyValue === undefined) { - node.el.removeAttribute('data-' + key); + node.div.removeAttribute('data-' + key); } else { - node.el.setAttribute('data-' + key, String(keyValue)); + node.div.setAttribute('data-' + key, String(keyValue)); } } } @@ -554,7 +605,10 @@ const defaultShader: IRendererShader = { let lastNodeId = 0; class DOMNode extends EventEmitter implements IRendererNode { - el = document.createElement('div'); + div = document.createElement('div'); + divBg: HTMLElement | undefined; + divBorder: HTMLElement | undefined; + id = ++lastNodeId; renderState: lng.CoreNodeRenderState = 0 /* Init */; @@ -566,9 +620,9 @@ class DOMNode extends EventEmitter implements IRendererNode { super(); // @ts-ignore - this.el._node = this; - this.el.setAttribute('data-id', String(this.id)); - elMap.set(this, this.el); + this.div._node = this; + this.div.setAttribute('data-id', String(this.id)); + elMap.set(this, this.div); updateNodeParent(this); updateNodeStyles(this); @@ -577,7 +631,7 @@ class DOMNode extends EventEmitter implements IRendererNode { destroy(): void { elMap.delete(this); - this.el.parentNode!.removeChild(this.el); + this.div.parentNode!.removeChild(this.div); } get parent() { @@ -889,7 +943,7 @@ class DOMText extends DOMNode { public override props: IRendererTextNodeProps, ) { super(stage, props); - this.el.innerText = props.text; + this.div.innerText = props.text; } get text() { @@ -897,8 +951,8 @@ class DOMText extends DOMNode { } set text(v) { this.props.text = v; - this.el.innerText = v; - scheduleUpdateTextNodeMeasurement(this); + this.div.innerText = v; + scheduleUpdateDOMTextMeasurement(this); } get fontFamily() { return this.props.fontFamily; @@ -1047,14 +1101,14 @@ function updateRootPosition(this: DOMRendererMain) { let height = Math.ceil(settings.appHeight ?? 1080 / dpr); let width = Math.ceil(settings.appWidth ?? 1920 / dpr); - this.root.el.style.left = `${left}px`; - this.root.el.style.top = `${top}px`; - this.root.el.style.width = `${width}px`; - this.root.el.style.height = `${height}px`; - this.root.el.style.position = 'absolute'; - this.root.el.style.transformOrigin = '0 0 0'; - this.root.el.style.transform = `scale(${dpr}, ${dpr})`; - this.root.el.style.overflow = 'hidden'; + this.root.div.style.left = `${left}px`; + this.root.div.style.top = `${top}px`; + this.root.div.style.width = `${width}px`; + this.root.div.style.height = `${height}px`; + this.root.div.style.position = 'absolute'; + this.root.div.style.transformOrigin = '0 0 0'; + this.root.div.style.transform = `scale(${dpr}, ${dpr})`; + this.root.div.style.overflow = 'hidden'; } export class DOMRendererMain implements IRendererMain { @@ -1102,6 +1156,24 @@ export class DOMRendererMain implements IRendererMain { }), ); this.stage.root = this.root; + document.body.appendChild(this.root.div); + + if (Config.fontSettings.fontFamily) { + this.root.div.style.fontFamily = Config.fontSettings.fontFamily; + } + if (Config.fontSettings.fontSize) { + this.root.div.style.fontSize = Config.fontSettings.fontSize + 'px'; + } + if (Config.fontSettings.lineHeight) { + this.root.div.style.lineHeight = Config.fontSettings.lineHeight + 'px'; + } + if (Config.fontSettings.fontWeight) { + if (typeof Config.fontSettings.fontWeight === 'number') { + this.root.div.style.fontWeight = Config.fontSettings.fontWeight + 'px'; + } else { + this.root.div.style.fontWeight = Config.fontSettings.fontWeight; + } + } updateRootPosition.call(this); diff --git a/src/elementNode.ts b/src/elementNode.ts index b0e4b3c..8eeac17 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -195,6 +195,13 @@ const LightningRendererNonAnimatingProps = [ 'wordWrap', ]; +declare global { + interface HTMLElement { + /** Assigned for development, to quickly get ElementNode from selected HTMLElement */ + element?: ElementNode; + } +} + export type RendererNode = AddColorString< Partial> >; @@ -953,9 +960,7 @@ export class ElementNode extends Object { } // L3 Inspector adds div to the lng object - //@ts-expect-error - div is not in the typings if (node.lng?.div) { - //@ts-expect-error - div is not in the typings node.lng.div.element = node; } diff --git a/src/lightningInit.ts b/src/lightningInit.ts index 33bacc7..98534c8 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -64,6 +64,7 @@ export interface IRendererNodeProps } /** Based on {@link lng.INode} */ export interface IRendererNode extends IRendererNodeShaded, IRendererNodeProps { + div?: HTMLElement; props: IRendererNodeProps; renderState: lng.CoreNodeRenderState; } @@ -78,6 +79,7 @@ export interface IRendererTextNodeProps export interface IRendererTextNode extends IRendererNodeShaded, IRendererTextNodeProps { + div?: HTMLElement; props: IRendererTextNodeProps; renderState: lng.CoreNodeRenderState; } From 6b225e4caa9aed4a7886acdaec91b3b275031116 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 2 May 2025 13:04:09 -0400 Subject: [PATCH 20/78] :rocket: bump version v3.0.0-4 --- CHANGELOG.md | 10 +++++++++- package.json | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1ddf6c..e19c45b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,21 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-4](https://github.com/lightning-tv/core/compare/v3.0.0-3...v3.0.0-4) + +- Fix issues in DOM renderer [`#46`](https://github.com/lightning-tv/core/pull/46) +- Update layout when child is removed, or parent is swapped [`#45`](https://github.com/lightning-tv/core/pull/45) +- Update pnpm to 10.8.0 [`#44`](https://github.com/lightning-tv/core/pull/44) + #### [v3.0.0-3](https://github.com/lightning-tv/core/compare/v3.0.0-2...v3.0.0-3) +> 8 April 2025 + - remove preventCleanup property from DOMNode class [`#42`](https://github.com/lightning-tv/core/pull/42) - dom renderer fixes [`#41`](https://github.com/lightning-tv/core/pull/41) - update release it script [`c23ad51`](https://github.com/lightning-tv/core/commit/c23ad51ae31924d986016849a2a06d0dead4c56f) +- :rocket: bump version v3.0.0-3 [`ef05a08`](https://github.com/lightning-tv/core/commit/ef05a08d54d3453250441f50c04b80548e8b9037) - update renderer [`12dfac9`](https://github.com/lightning-tv/core/commit/12dfac9b4f5b26e8ef27782e290643147f117c7a) -- new packagemanager line [`551cbad`](https://github.com/lightning-tv/core/commit/551cbadf1118aed7a4a16a80115d9d88ff1a891e) #### [v3.0.0-2](https://github.com/lightning-tv/core/compare/v3.0.0-1...v3.0.0-2) diff --git a/package.json b/package.json index cae4ec6..e5e736c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-3", + "version": "3.0.0-4", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From eb3273f45276cbba5be536ae58031fe121c222b2 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Tue, 6 May 2025 14:26:28 +0200 Subject: [PATCH 21/78] Dom renderer improvements (#47) * Implement animation.waitUntilStopped() * Cleanup AnimationController * Improve clipping text over limited height * Cleanup dom renderer * Handle resizeMode.type=cover * Add max-content width style for contain=none * Attach root to target element instead of body --- src/domRenderer.ts | 292 ++++++++++++++++++++++++++------------------- 1 file changed, 171 insertions(+), 121 deletions(-) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index 99bf0dc..898dc04 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -24,21 +24,51 @@ import { EventEmitter } from '@lightningjs/renderer/utils'; const colorToRgba = (c: number) => `rgba(${(c >> 24) & 0xff},${(c >> 16) & 0xff},${(c >> 8) & 0xff},${(c & 0xff) / 255})`; +function applyEasing(easing: string, progress: number): number { + switch (easing) { + case 'linear': + default: + return progress; + case 'ease-in': + return progress * progress; + case 'ease-out': + return progress * (2 - progress); + case 'ease-in-out': + return progress < 0.5 + ? 2 * progress * progress + : -1 + (4 - 2 * progress) * progress; + } +} + +function interpolate(start: number, end: number, t: number): number { + return start + (end - start) * t; +} + +function interpolateColor(start: number, end: number, t: number): number { + return ( + (interpolate((start >> 24) & 0xff, (end >> 24) & 0xff, t) << 24) | + (interpolate((start >> 16) & 0xff, (end >> 16) & 0xff, t) << 16) | + (interpolate((start >> 8) & 0xff, (end >> 8) & 0xff, t) << 8) | + interpolate(start & 0xff, end & 0xff, t) + ); +} + +function interpolateProp( + name: string, + start: number, + end: number, + t: number, +): number { + return name.startsWith('color') + ? interpolateColor(start, end, t) + : interpolate(start, end, t); +} + /* Animations */ -type AnimationTask = { - node: DOMNode; - propsStart: Record; - propsEnd: Record; - timeStart: number; - timeEnd: number; - settings: Required; - iteration: number; - pausedTime: number | null; -}; -let animationTasks: AnimationTask[] = []; +let animationTasks: AnimationController[] = []; let animationFrameRequested = false; function requestAnimationUpdate() { @@ -83,7 +113,8 @@ function updateAnimations(time: number) { else { Object.assign(task.node.props, task.propsEnd); updateNodeStyles(task.node); - animationTasks.splice(i, 1); + + task.stop(); i--; } continue; @@ -92,18 +123,13 @@ function updateAnimations(time: number) { /* Update props and styles */ - let t = applyEasing( - activeTime / task.settings.duration, - task.settings.easing, - ); + let t = activeTime / task.settings.duration; + t = applyEasing(task.settings.easing, t); for (let prop in task.propsEnd) { - let fn = prop.startsWith('color') ? interpolateColor : interpolate; - (task.node.props as any)[prop] = fn( - task.propsStart[prop]!, - task.propsEnd[prop]!, - t, - ); + let start = task.propsStart[prop]!; + let end = task.propsEnd[prop]!; + (task.node.props as any)[prop] = interpolateProp(prop, start, end, t); } updateNodeStyles(task.node); @@ -112,67 +138,85 @@ function updateAnimations(time: number) { requestAnimationUpdate(); } -function applyEasing(progress: number, easing: string): number { - switch (easing) { - case 'linear': - default: - return progress; - case 'ease-in': - return progress * progress; - case 'ease-out': - return progress * (2 - progress); - case 'ease-in-out': - return progress < 0.5 - ? 2 * progress * progress - : -1 + (4 - 2 * progress) * progress; - } -} +class AnimationController implements lng.IAnimationController { + state: lng.AnimationControllerState = 'paused'; -function interpolate(start: number, end: number, t: number): number { - return start + (end - start) * t; -} + stopPromise: Promise | null = null; + stopResolve: (() => void) | null = null; -function interpolateColor(start: number, end: number, t: number): number { - return ( - (interpolate((start >> 24) & 0xff, (end >> 24) & 0xff, t) << 24) | - (interpolate((start >> 16) & 0xff, (end >> 16) & 0xff, t) << 16) | - (interpolate((start >> 8) & 0xff, (end >> 8) & 0xff, t) << 8) | - interpolate(start & 0xff, end & 0xff, t) - ); -} + propsStart: Record = {}; + propsEnd: Record = {}; + timeStart: number = performance.now(); + timeEnd: number; + settings: Required; + iteration: number = 0; + pausedTime: number | null = null; -class AnimationController implements lng.IAnimationController { - state: lng.AnimationControllerState = 'paused'; + constructor( + public node: DOMNode, + props: Partial>, + rawSettings: Partial, + ) { + this.settings = { + duration: rawSettings.duration ?? 300, + delay: rawSettings.delay ?? 0, + easing: rawSettings.easing ?? 'linear', + loop: rawSettings.loop ?? false, + repeat: rawSettings.repeat ?? 1, + repeatDelay: rawSettings.repeatDelay ?? 0, + stopMethod: false, + }; - constructor(public task: AnimationTask) {} + this.timeEnd = + this.timeStart + this.settings.delay + this.settings.duration; + + for (let [prop, value] of Object.entries(props)) { + if (value != null && typeof value === 'number') { + this.propsStart[prop] = (node.props as any)[prop]; + this.propsEnd[prop] = value; + } + } + + animationTasks.push(this); + } start() { - if (this.task.pausedTime != null) { - this.task.timeStart += performance.now() - this.task.pausedTime; - this.task.pausedTime = null; + if (this.pausedTime != null) { + this.timeStart += performance.now() - this.pausedTime; + this.pausedTime = null; } else { - this.task.timeStart = performance.now(); + this.timeStart = performance.now(); } + this.state = 'running'; requestAnimationUpdate(); return this; } pause() { - this.task.pausedTime = performance.now(); + this.pausedTime = performance.now(); + this.state = 'paused'; return this; } stop() { - let index = animationTasks.indexOf(this.task); + let index = animationTasks.indexOf(this); if (index !== -1) { animationTasks.splice(index, 1); } + this.state = 'stopped'; + if (this.stopResolve) { + this.stopResolve(); + this.stopResolve = null; + this.stopPromise = null; + } return this; } - restore() { return this; } waitUntilStopped() { - return Promise.resolve(); + this.stopPromise ??= new Promise((resolve) => { + this.stopResolve = resolve; + }); + return this.stopPromise; } on() { return this; @@ -193,42 +237,13 @@ function animate( props: Partial>, settings: Partial, ): lng.IAnimationController { - let fullSettings: Required = { - duration: settings.duration ?? 300, - delay: settings.delay ?? 0, - easing: settings.easing ?? 'linear', - loop: settings.loop ?? false, - repeat: settings.repeat ?? 1, - repeatDelay: settings.repeatDelay ?? 0, - stopMethod: false, - }; - - let now = performance.now(); - - // Create the animation task - let task: AnimationTask = { - node: this, - propsStart: {}, - propsEnd: {}, - timeStart: now, - timeEnd: now + fullSettings.delay + fullSettings.duration, - settings: fullSettings, - iteration: 0, - pausedTime: null, - }; - - for (let [prop, value] of Object.entries(props)) { - if (value != null && typeof value === 'number') { - task.propsStart[prop] = (this.props as any)[prop]; - task.propsEnd[prop] = value; - } - } - - animationTasks.push(task); - - return new AnimationController(task); + return new AnimationController(this, props, settings); } +/* + Node Properties +*/ + let elMap = new WeakMap(); function updateNodeParent(node: DOMNode | DOMText) { @@ -237,6 +252,12 @@ function updateNodeParent(node: DOMNode | DOMText) { } } +function getNodeLineHeight(props: IRendererTextNodeProps): number { + return ( + props.lineHeight ?? Config.fontSettings.lineHeight ?? 1.2 * props.fontSize + ); +} + function updateNodeStyles(node: DOMNode | DOMText) { let { props } = node; @@ -283,40 +304,58 @@ function updateNodeStyles(node: DOMNode | DOMText) { if (textProps.color != null && textProps.color !== 0) { style += `color: ${colorToRgba(textProps.color)};`; } - - if (textProps.fontFamily) style += `font-family: ${textProps.fontFamily};`; - if (textProps.fontSize) style += `font-size: ${textProps.fontSize}px;`; - if (textProps.fontStyle !== 'normal') + if (textProps.fontFamily) { + style += `font-family: ${textProps.fontFamily};`; + } + if (textProps.fontSize) { + style += `font-size: ${textProps.fontSize}px;`; + } + if (textProps.fontStyle !== 'normal') { style += `font-style: ${textProps.fontStyle};`; - if (textProps.fontWeight !== 'normal') + } + if (textProps.fontWeight !== 'normal') { style += `font-weight: ${textProps.fontWeight};`; - if (textProps.fontStretch !== 'normal') + } + if (textProps.fontStretch !== 'normal') { style += `font-stretch: ${textProps.fontStretch};`; - if (textProps.lineHeight != null) + } + if (textProps.lineHeight != null) { style += `line-height: ${textProps.lineHeight}px;`; - if (textProps.letterSpacing) + } + if (textProps.letterSpacing) { style += `letter-spacing: ${textProps.letterSpacing}px;`; - if (textProps.textAlign !== 'left') + } + if (textProps.textAlign !== 'left') { style += `text-align: ${textProps.textAlign};`; - // if (node.overflowSuffix) style += `overflow-suffix: ${node.overflowSuffix};` - if (textProps.maxLines > 0) { - // https://stackoverflow.com/a/13924997 - style += `display: -webkit-box; - overflow: hidden; - -webkit-line-clamp: ${textProps.maxLines}; - line-clamp: ${textProps.maxLines}; - -webkit-box-orient: vertical;`; } + + let maxLines = textProps.maxLines || Infinity; switch (textProps.contain) { case 'width': - style += `width: ${textProps.width}px; overflow: hidden;`; + style += `width: ${props.width}px; overflow: hidden;`; break; - case 'both': - style += `width: ${textProps.width}px; height: ${textProps.height}px; overflow: hidden;`; + case 'both': { + let lineHeight = getNodeLineHeight(textProps); + maxLines = Math.min(maxLines, Math.floor(props.height / lineHeight)); + let height = maxLines * lineHeight; + style += `width: ${props.width}px; height: ${height}px; overflow: hidden;`; break; + } case 'none': + style += `width: max-content;`; break; } + + if (maxLines !== Infinity) { + // https://stackoverflow.com/a/13924997 + style += `display: -webkit-box; + overflow: hidden; + -webkit-line-clamp: ${maxLines}; + line-clamp: ${maxLines}; + -webkit-box-orient: vertical;`; + } + + // if (node.overflowSuffix) style += `overflow-suffix: ${node.overflowSuffix};` // if (node.verticalAlign) style += `vertical-align: ${node.verticalAlign};` scheduleUpdateDOMTextMeasurement(node); @@ -361,8 +400,8 @@ function updateNodeStyles(node: DOMNode | DOMText) { bgStyle += `background-image: ${srcImg};`; bgStyle += `background-repeat: no-repeat;`; - if (props.textureOptions.resizeMode?.type === 'contain') { - bgStyle += `background-size: contain; background-position: center;`; + if (props.textureOptions.resizeMode?.type) { + bgStyle += `background-size: ${props.textureOptions.resizeMode.type}; background-position: center;`; } else if (srcPos !== null) { bgStyle += `background-position: -${srcPos.x}px -${srcPos.y}px;`; } else { @@ -1119,8 +1158,20 @@ export class DOMRendererMain implements IRendererMain { constructor( public settings: lng.RendererMainSettings, - public target: string | HTMLElement, + rawTarget: string | HTMLElement, ) { + let target: HTMLElement; + if (typeof rawTarget === 'string') { + let result = document.getElementById(rawTarget); + if (result instanceof HTMLElement) { + target = result; + } else { + throw new Error(`Target #${rawTarget} not found`); + } + } else { + target = rawTarget; + } + let canvas = document.body.appendChild(document.createElement('canvas')); canvas.style.position = 'absolute'; canvas.style.top = '0'; @@ -1145,18 +1196,15 @@ export class DOMRendererMain implements IRendererMain { this.root = new DOMNode( this.stage, - resolveTextNodeDefaults({ + resolveNodeDefaults({ width: settings.appWidth ?? 1920, height: settings.appHeight ?? 1080, - fontFamily: Config.fontSettings.fontFamily, - fontSize: Config.fontSettings.fontSize, - lineHeight: Config.fontSettings.lineHeight, shader: defaultShader, zIndex: 65534, }), ); this.stage.root = this.root; - document.body.appendChild(this.root.div); + target.appendChild(this.root.div); if (Config.fontSettings.fontFamily) { this.root.div.style.fontFamily = Config.fontSettings.fontFamily; @@ -1166,6 +1214,8 @@ export class DOMRendererMain implements IRendererMain { } if (Config.fontSettings.lineHeight) { this.root.div.style.lineHeight = Config.fontSettings.lineHeight + 'px'; + } else { + this.root.div.style.lineHeight = '1.2'; } if (Config.fontSettings.fontWeight) { if (typeof Config.fontSettings.fontWeight === 'number') { From eab6085f37ba209c363b4716c256ad0d023a0ea5 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Tue, 6 May 2025 09:07:52 -0400 Subject: [PATCH 22/78] :rocket: bump version v3.0.0-5 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e19c45b..cb7bd87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,18 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-5](https://github.com/lightning-tv/core/compare/v3.0.0-4...v3.0.0-5) + +- Dom renderer improvements [`#47`](https://github.com/lightning-tv/core/pull/47) + #### [v3.0.0-4](https://github.com/lightning-tv/core/compare/v3.0.0-3...v3.0.0-4) +> 2 May 2025 + - Fix issues in DOM renderer [`#46`](https://github.com/lightning-tv/core/pull/46) - Update layout when child is removed, or parent is swapped [`#45`](https://github.com/lightning-tv/core/pull/45) - Update pnpm to 10.8.0 [`#44`](https://github.com/lightning-tv/core/pull/44) +- :rocket: bump version v3.0.0-4 [`6b225e4`](https://github.com/lightning-tv/core/commit/6b225e4caa9aed4a7886acdaec91b3b275031116) #### [v3.0.0-3](https://github.com/lightning-tv/core/compare/v3.0.0-2...v3.0.0-3) diff --git a/package.json b/package.json index e5e736c..7909458 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-4", + "version": "3.0.0-5", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From e22d467fd1f9497c3a3c8032ca5e7c56bd99a496 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Thu, 8 May 2025 20:02:01 -0400 Subject: [PATCH 23/78] set lockStyles to true by default --- src/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config.ts b/src/config.ts index 71b098d..ee844a9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -46,4 +46,5 @@ export const Config: Config = { setActiveElement: () => {}, focusStateKey: '$focus', domRendering: false, + lockStyles: true, }; From fcc005046fd7e0112d506d01614612b1b349a1c5 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Thu, 8 May 2025 20:02:29 -0400 Subject: [PATCH 24/78] refactor shaders, remove border* and add shader animations --- src/elementNode.ts | 229 +++++++++++++++++++----------------------- src/intrinsicTypes.ts | 13 +-- 2 files changed, 107 insertions(+), 135 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 8eeac17..c67f807 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -1,6 +1,7 @@ import { IRendererNode, IRendererNodeProps, + IRendererShaderProps, IRendererTextNode, renderer, } from './lightningInit.js'; @@ -43,6 +44,7 @@ import type { IAnimationController, LinearGradientProps, RadialGradientProps, + ShadowProps, CoreShaderNode, } from '@lightningjs/renderer'; import { assertTruthy } from '@lightningjs/renderer/utils'; @@ -60,71 +62,25 @@ function runLayout() { } } -type ShaderProps = Record; -function convertEffectsToShader(_node: ElementNode, v: StyleEffects): any { - const { border, shadow, rounded } = v; - const props: ShaderProps = {}; - - const parseAndAssignProps = (prefix: string, obj: Record) => { - Object.entries(obj).forEach(([key, value]) => { - props[`${prefix}-${key}`] = value; - }); - }; +const parseAndAssignShaderProps = ( + prefix: string, + obj: Record, + props: Record = {}, +) => { + props[prefix] = obj; + Object.entries(obj).forEach(([key, value]) => { + props[`${prefix}-${key}`] = value; + }); +}; - if (border) parseAndAssignProps('border', border); - if (shadow) parseAndAssignProps('shadow', shadow); - if (rounded) Object.assign(props, rounded); +function convertToShader(_node: ElementNode, v: StyleEffects): any { + const { border, shadow } = v; const typeParts = ['rounded']; if (border) typeParts.push('WithBorder'); if (shadow) typeParts.push('WithShadow'); - return renderer.createShader(typeParts.join(''), props); -} - -// function convertEffectsToShader( -// _node: ElementNode, -// styleEffects: StyleEffects, -// ): CoreShaderNode { -// const effects: CoreShaderNode[] = []; -// for (const type in styleEffects) { -// // @ts-ignore getting the right type info is hard -// effects.push(renderer.createEffect(type, styleEffects[type], type)); -// } -// return renderer.createShader('DynamicShader', { effects }); -// } - -function updateShaderEffects( - node: ElementNode, - styleEffects: StyleEffects, -): void { - const shader = node.lng.shader; - for (const type in styleEffects) { - // @ts-ignore getting the right type info is hard - Object.assign(shader.props[type], styleEffects[type]); - } -} - -function borderAccessor( - direction: '' | 'Top' | 'Right' | 'Bottom' | 'Left' = '', -) { - return { - set(this: ElementNode, value: BorderStyle) { - // Format: width || { width, color } - if (isNumber(value)) { - value = { width: value, color: 0x000000ff }; - } - this.effects = this.effects - ? { - ...(this.effects || {}), - ...{ [`border${direction}`]: value }, - } - : { [`border${direction}`]: value }; - }, - get(this: ElementNode): BorderStyle | undefined { - return this.effects?.[`border${direction}`]; - }, - }; + return renderer.createShader(typeParts.join(''), v as IRendererShaderProps); } const LightningRendererNumberProps = [ @@ -238,7 +194,10 @@ export interface ElementNode extends RendererNode { | number | ((this: ElementNode, elm: ElementNode) => boolean | void); forwardStates?: boolean; - lng: Partial | IRendererNode | IRendererTextNode; + lng: + | Partial + | IRendererNode + | (IRendererTextNode & { shader: any }); ref?: ElementNode | ((node: ElementNode) => void) | undefined; rendered: boolean; renderer?: RendererMain; @@ -250,11 +209,7 @@ export interface ElementNode extends RendererNode { alignItems?: 'flexStart' | 'flexEnd' | 'center'; alignSelf?: 'flexStart' | 'flexEnd' | 'center'; border?: BorderStyle; - borderBottom?: BorderStyle; - borderLeft?: BorderStyle; borderRadius?: BorderRadius; - borderRight?: BorderStyle; - borderTop?: BorderStyle; center?: boolean; centerX?: boolean; centerY?: boolean; @@ -340,18 +295,21 @@ export class ElementNode extends Object { } get effects(): StyleEffects | undefined { - return this._effects; + return this.lng.shader; } set effects(v: StyleEffects) { - this._effects = v; - if (SHADERS_ENABLED && this.rendered) { - this.lng.shader = convertEffectsToShader(this, v); - // if (this.lng.shader) { - // updateShaderEffects(this, v); - // } else { - // } - } + if (!SHADERS_ENABLED) return; + console.warn( + '`effects` is deprecated. Use shortcuts like `border`, `shadow`, `borderRadius` instead.', + ); + this.lng.shader = this.lng.shader || {}; + const target = this.lng.shader?.program + ? this.lng.shader.props + : this.lng.shader; + if (v.rounded) target.radius = v.rounded; + if (v.border) parseAndAssignShaderProps('border', v.border, target); + if (v.shadow) parseAndAssignShaderProps('shadow', v.shadow, target); } set id(id: string) { @@ -446,21 +404,6 @@ export class ElementNode extends Object { animationSettings, ); - if (this.onAnimation) { - const animationEvents = Object.keys( - this.onAnimation, - ) as AnimationEvents[]; - for (const event of animationEvents) { - const handler = this.onAnimation[event]; - animationController.on( - event, - (controller: IAnimationController, props?: any) => { - handler!.call(this, controller, name, value, props); - }, - ); - } - } - return animationController.start(); } @@ -473,10 +416,27 @@ export class ElementNode extends Object { ): IAnimationController { isDev && assertTruthy(this.rendered, 'Node must be rendered before animating'); - return (this.lng as IRendererNode).animate( + const animationController = (this.lng as IRendererNode).animate( props, animationSettings || this.animationSettings || {}, ); + + if (this.onAnimation) { + const animationEvents = Object.keys( + this.onAnimation, + ) as AnimationEvents[]; + for (const event of animationEvents) { + const handler = this.onAnimation[event]; + animationController.on( + event, + (controller: IAnimationController, props?: any) => { + handler!.call(this, controller, props); + }, + ); + } + } + + return animationController; } chain( @@ -897,8 +857,8 @@ export class ElementNode extends Object { } // Can you put effects on Text nodes? Need to confirm... - if (SHADERS_ENABLED && node._effects) { - props.shader = convertEffectsToShader(node, node._effects); + if (SHADERS_ENABLED && props.shader) { + props.shader = convertToShader(node, props.shader); } isDev && log('Rendering: ', this, props); @@ -933,8 +893,8 @@ export class ElementNode extends Object { } } - if (SHADERS_ENABLED && node._effects) { - props.shader = convertEffectsToShader(node, node._effects); + if (SHADERS_ENABLED && props.shader) { + props.shader = convertToShader(node, props.shader); } isDev && log('Rendering: ', this, props); @@ -1005,20 +965,55 @@ for (const key of LightningRendererNonAnimatingProps) { }); } -// Add Border Helpers -function createEffectAccessor(key: keyof StyleEffects) { +function createRawShaderAccessor(key: keyof StyleEffects) { return { set(this: ElementNode, value: T) { - this.effects = this.effects - ? { - ...this.effects, - [key]: value, - } - : { [key]: value }; + this.shader = [key, value as unknown as IRendererShaderProps]; }, - get(this: ElementNode): T | undefined { - return this.effects?.[key] as T | undefined; + get(this: ElementNode) { + return this.shader; + }, + }; +} + +function shaderAccessor | number>( + key: 'border' | 'shadow' | 'rounded', +) { + return { + set(this: ElementNode, value: T) { + this.lng.shader = this.lng.shader || {}; + let target = this.lng.shader; + let animationSettings: AnimationSettings | undefined; + if (this.lng.shader.program) { + target = this.lng.shader.props; + const transitionKey = key === 'rounded' ? 'borderRadius' : key; + if ( + this.transition && + (this.transition === true || this.transition[transitionKey]) + ) { + target = {}; + animationSettings = + this.transition === true || this.transition[transitionKey] === true + ? undefined + : (this.transition[transitionKey] as + | undefined + | AnimationSettings); + } + } + + if (key === 'rounded' || typeof value === 'number') { + target.radius = value; + } else { + parseAndAssignShaderProps(key, value, target); + } + + if (animationSettings) { + this.animate({ shaderProps: target }, animationSettings).start(); + } + }, + get(this: ElementNode) { + return this.effects?.[key]; }, }; } @@ -1030,25 +1025,11 @@ if (isDev) { } Object.defineProperties(ElementNode.prototype, { - border: borderAccessor(), - borderLeft: borderAccessor('Left'), - borderRight: borderAccessor('Right'), - borderTop: borderAccessor('Top'), - borderBottom: borderAccessor('Bottom'), - linearGradient: createEffectAccessor('linearGradient'), - radialGradient: createEffectAccessor('radialGradient'), - borderRadius: { - set(this: ElementNode, radius: BorderRadius) { - this.effects = this.effects - ? { - ...this.effects, - rounded: { radius }, - } - : { rounded: { radius } }; - }, - - get(this: ElementNode): BorderRadius | undefined { - return this.effects?.rounded?.radius; - }, - }, + border: shaderAccessor('border'), + shadow: shaderAccessor('shadow'), + borderRadius: shaderAccessor('rounded'), + linearGradient: + createRawShaderAccessor('linearGradient'), + radialGradient: + createRawShaderAccessor('radialGradient'), }); diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index 989269f..1fc9f1f 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -24,7 +24,7 @@ export interface BorderStyleObject { } export type DollarString = `$${string}`; -export type BorderStyle = number | BorderStyleObject; +export type BorderStyle = BorderStyleObject; export type BorderRadius = number | number[]; export interface Effects { @@ -32,18 +32,11 @@ export interface Effects { radialGradient?: RadialGradientProps; holePunch?: HolePunchProps; shadow?: ShadowProps; -} - -export interface BorderEffects { rounded?: { radius: BorderRadius }; border?: BorderStyleObject; - borderTop?: BorderStyle; - borderRight?: BorderStyle; - borderBottom?: BorderStyle; - borderLeft?: BorderStyle; } -export type StyleEffects = Effects & BorderEffects; +export type StyleEffects = Effects; // Renderer should export EffectDesc export type ShaderEffectDesc = { @@ -174,8 +167,6 @@ export interface IntrinsicTextNodeStyleProps extends TextStyles {} export type AnimationEvents = 'animating' | 'tick' | 'stopped'; export type AnimationEventHandler = ( controller: IAnimationController, - name: string, - endValue: number, props?: any, ) => void; From 43d036da4390ac3a3a6095901d6f8c59a1008f77 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Thu, 8 May 2025 20:03:50 -0400 Subject: [PATCH 25/78] :rocket: bump version v3.0.0-6 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb7bd87..9fca22a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,17 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-6](https://github.com/lightning-tv/core/compare/v3.0.0-5...v3.0.0-6) + +- refactor shaders, remove border\* and add shader animations [`fcc0050`](https://github.com/lightning-tv/core/commit/fcc005046fd7e0112d506d01614612b1b349a1c5) +- set lockStyles to true by default [`e22d467`](https://github.com/lightning-tv/core/commit/e22d467fd1f9497c3a3c8032ca5e7c56bd99a496) + #### [v3.0.0-5](https://github.com/lightning-tv/core/compare/v3.0.0-4...v3.0.0-5) +> 6 May 2025 + - Dom renderer improvements [`#47`](https://github.com/lightning-tv/core/pull/47) +- :rocket: bump version v3.0.0-5 [`eab6085`](https://github.com/lightning-tv/core/commit/eab6085f37ba209c363b4716c256ad0d023a0ea5) #### [v3.0.0-4](https://github.com/lightning-tv/core/compare/v3.0.0-3...v3.0.0-4) diff --git a/package.json b/package.json index 7909458..6386a5c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-5", + "version": "3.0.0-6", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 8889ce0fa69e48ad8b1a21b630040fb1c2590cb4 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Sat, 10 May 2025 14:30:08 -0400 Subject: [PATCH 26/78] optimize access to parent dimentions --- src/elementNode.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index c67f807..c4f49e1 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -792,17 +792,20 @@ export class ElementNode extends Object { } const props = node.lng; + const parentWidth = parent.width || 0; + const parentHeight = parent.height || 0; + props.x = props.x || 0; props.y = props.y || 0; props.parent = parent.lng as IRendererNode; if (this.right || this.right === 0) { - props.x = (parent.width || 0) - this.right; + props.x = parentWidth - this.right; props.mountX = 1; } if (this.bottom || this.bottom === 0) { - props.y = (parent.height || 0) - this.bottom; + props.y = parentHeight - this.bottom; props.mountY = 1; } @@ -811,12 +814,12 @@ export class ElementNode extends Object { } if (this.centerX) { - props.x += (parent.width || 0) / 2; + props.x += parentWidth / 2; props.mountX = 0.5; } if (this.centerY) { - props.y += (parent.height || 0) / 2; + props.y += parentHeight / 2; props.mountY = 0.5; } @@ -839,7 +842,7 @@ export class ElementNode extends Object { if (textProps.contain) { if (!textProps.width) { textProps.width = - (parent.width || 0) - textProps.x! - (textProps.marginRight || 0); + parentWidth - textProps.x! - (textProps.marginRight || 0); } if ( @@ -848,7 +851,7 @@ export class ElementNode extends Object { !textProps.maxLines ) { textProps.height = - (parent.height || 0) - textProps.y! - (textProps.marginBottom || 0); + parentHeight - textProps.y! - (textProps.marginBottom || 0); } else if (textProps.maxLines === 1) { textProps.height = (textProps.height || textProps.lineHeight || @@ -873,12 +876,12 @@ export class ElementNode extends Object { if (!props.texture) { // Set width and height to parent less offset if (isNaN(props.width as number)) { - props.width = (parent.width || 0) - props.x; + props.width = parentWidth - props.x; node._calcWidth = true; } if (isNaN(props.height as number)) { - props.height = (parent.height || 0) - props.y; + props.height = parentHeight - props.y; node._calcHeight = true; } From 2ff344868259a9186022ec43b50b46930c71c646 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Fri, 16 May 2025 14:10:11 +0200 Subject: [PATCH 27/78] Improve src+color and scale in dom renderer (#48) --- src/domRenderer.ts | 65 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index 898dc04..a666cd1 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -289,8 +289,12 @@ function updateNodeStyles(node: DOMNode | DOMText) { if (props.rotation !== 0) transform += `rotate(${props.rotation}rad)`; - if (props.scaleX !== 1) transform += `scaleX(${props.scaleX})`; - if (props.scaleY !== 1) transform += `scaleY(${props.scaleY})`; + if (props.scale !== 1 && props.scale != null) { + transform += `scale(${props.scale})`; + } else { + if (props.scaleX !== 1) transform += `scaleX(${props.scaleX})`; + if (props.scaleY !== 1) transform += `scaleY(${props.scaleY})`; + } if (transform.length > 0) { style += `transform: ${transform};`; @@ -395,8 +399,24 @@ function updateNodeStyles(node: DOMNode | DOMText) { let bgStyle = ''; let borderStyle = ''; + let radiusStyle = ''; + let maskStyle = ''; if (srcImg) { + if (props.color !== 0xffffffff && props.color !== 0x00000000) { + // use image as a mask + bgStyle += `background-color: ${colorToRgba(props.color)}; background-blend-mode: multiply;`; + maskStyle += `mask-image: ${srcImg};`; + if (srcPos !== null) { + maskStyle += `mask-position: -${srcPos.x}px -${srcPos.y}px;`; + } else { + maskStyle += `mask-size: 100% 100%;`; + } + } else if (gradient) { + // use gradient as a mask + maskStyle += `mask-image: ${gradient};`; + } + bgStyle += `background-image: ${srcImg};`; bgStyle += `background-repeat: no-repeat;`; @@ -408,16 +428,13 @@ function updateNodeStyles(node: DOMNode | DOMText) { bgStyle += 'background-size: 100% 100%;'; } - if (gradient) { - // use gradient as a mask - bgStyle += `mask-image: ${gradient};`; - // separate layers are needed for the mask - if (node.divBg == null) { - node.div.appendChild((node.divBg = document.createElement('div'))); - node.div.appendChild( - (node.divBorder = document.createElement('div')), - ); - } + if (maskStyle !== '') { + bgStyle += maskStyle; + } + // separate layers are needed for the mask + if (maskStyle !== '' && node.divBg == null) { + node.div.appendChild((node.divBg = document.createElement('div'))); + node.div.appendChild((node.divBorder = document.createElement('div'))); } } else if (gradient) { bgStyle += `background-image: ${gradient};`; @@ -449,15 +466,16 @@ function updateNodeStyles(node: DOMNode | DOMText) { } // Rounded if (typeof radius === 'number' && radius > 0) { - borderStyle += `border-radius: ${radius}px;`; + radiusStyle += `border-radius: ${radius}px;`; } else if (Array.isArray(radius) && radius.length === 4) { - borderStyle += `border-radius: ${radius[0]}px ${radius[1]}px ${radius[2]}px ${radius[3]}px;`; + radiusStyle += `border-radius: ${radius[0]}px ${radius[1]}px ${radius[2]}px ${radius[3]}px;`; } } } - style += borderStyle; - bgStyle += borderStyle; + style += radiusStyle; + bgStyle += radiusStyle; + borderStyle += radiusStyle; if (node.divBg == null) { style += bgStyle; @@ -465,7 +483,9 @@ function updateNodeStyles(node: DOMNode | DOMText) { bgStyle += 'position: absolute; inset: 0; z-index: -1;'; node.divBg.setAttribute('style', bgStyle); } - if (node.divBorder != null) { + if (node.divBorder == null) { + style += borderStyle; + } else { borderStyle += 'position: absolute; inset: 0; z-index: -1;'; node.divBorder.setAttribute('style', borderStyle); } @@ -483,8 +503,15 @@ type Size = { width: number; height: number }; function getElSize(node: DOMNode): Size { let rect = node.div.getBoundingClientRect(); let dpr = Config.rendererOptions?.deviceLogicalPixelRatio ?? 1; - rect.height = rect.height / node.scaleY / dpr; - rect.width = rect.width / node.scaleX / dpr; + rect.height /= dpr; + rect.width /= dpr; + if (node.props.scale != null && node.props.scale !== 1) { + rect.height /= node.props.scale; + rect.width /= node.props.scale; + } else { + rect.height /= node.props.scaleY; + rect.width /= node.props.scaleX; + } return rect; } From f03fd8ac05af3aa66bce318895a06ad2b2572c35 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Sun, 11 May 2025 00:08:58 -0400 Subject: [PATCH 28/78] start adding some flex tests with vitest --- package.json | 7 +- pnpm-lock.yaml | 391 +++++++++++++ src/tests/flex-perf-results.json | 495 +++++++++++++++++ src/tests/flex.performance.spec.ts | 352 ++++++++++++ src/tests/flex.spec.ts | 848 +++++++++++++++++++++++++++++ tsconfig.json | 2 +- vitest.config.ts | 7 + 7 files changed, 2099 insertions(+), 3 deletions(-) create mode 100644 src/tests/flex-perf-results.json create mode 100644 src/tests/flex.performance.spec.ts create mode 100644 src/tests/flex.spec.ts create mode 100644 vitest.config.ts diff --git a/package.json b/package.json index 6386a5c..1fb1a49 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "sideEffects": false, "scripts": { "start": "npm run watch", - "test": "echo \"Error: no test specified\" && exit 1", "lint": "npm run lint:prettier && npm run lint:eslint", "lint:fix": "npm run lint:fix:prettier && npm run lint:fix:eslint", "lint:prettier": "prettier --check \"**/*.{ts,js,cjs,md}\"", @@ -32,6 +31,8 @@ "build": "npm run tsc", "tsc": "tsc", "watch": "tsc -w", + "test": "vitest", + "test:perf": "vitest run flex.performance.spec.ts", "prepare": "husky", "prepack": "npm run build", "release": "release-it --only-version" @@ -53,6 +54,7 @@ "@types/eslint__js": "^8.42.3", "@typescript-eslint/eslint-plugin": "^8.14.0", "@typescript-eslint/parser": "^8.14.0", + "@vitest/ui": "^3.1.3", "eslint": "^9.15.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.2.1", @@ -63,7 +65,8 @@ "release-it": "^18.1.2", "typescript": "^5.6.3", "typescript-eslint": "^8.14.0", - "vite": "^5.4.11" + "vite": "^5.4.11", + "vitest": "^3.1.3" }, "lint-staged": { "*.ts": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bf7ea8d..60481f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,6 +24,9 @@ importers: '@typescript-eslint/parser': specifier: ^8.14.0 version: 8.14.0(eslint@9.15.0)(typescript@5.6.3) + '@vitest/ui': + specifier: ^3.1.3 + version: 3.1.3(vitest@3.1.3) eslint: specifier: ^9.15.0 version: 9.15.0 @@ -57,6 +60,9 @@ importers: vite: specifier: ^5.4.11 version: 5.4.11(@types/node@22.14.0) + vitest: + specifier: ^3.1.3 + version: 3.1.3(@types/node@22.14.0)(@vitest/ui@3.1.3) packages: @@ -384,6 +390,9 @@ packages: '@types/node': optional: true + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@lightningjs/renderer@3.0.0-beta6': resolution: {integrity: sha512-1spHTpZ8CLgL9ZZNtvKdaJYazn+SM4neIOj5wb8jJ6aFBbj4CyAeigyNmhYylo0lUr9VxfmXoJ4n6efUm2p8Vg==} engines: {node: '>= 18.0.0', npm: '>= 10.0.0', pnpm: '>= 8.9.2'} @@ -474,6 +483,9 @@ packages: resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} engines: {node: '>=12'} + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@rollup/rollup-android-arm-eabi@4.27.2': resolution: {integrity: sha512-Tj+j7Pyzd15wAdSJswvs5CJzJNV+qqSUcr/aCD+jpQSBtXvGnV0pnrjoc8zFTe9fcKCatkpFpOO7yAzpO998HA==} cpu: [arm] @@ -715,6 +727,40 @@ packages: resolution: {integrity: sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/expect@3.1.3': + resolution: {integrity: sha512-7FTQQuuLKmN1Ig/h+h/GO+44Q1IlglPlR2es4ab7Yvfx+Uk5xsv+Ykk+MEt/M2Yn/xGmzaLKxGw2lgy2bwuYqg==} + + '@vitest/mocker@3.1.3': + resolution: {integrity: sha512-PJbLjonJK82uCWHjzgBJZuR7zmAOrSvKk1QBxrennDIgtH4uK0TB1PvYmc0XBCigxxtiAVPfWtAdy4lpz8SQGQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.1.3': + resolution: {integrity: sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==} + + '@vitest/runner@3.1.3': + resolution: {integrity: sha512-Tae+ogtlNfFei5DggOsSUvkIaSuVywujMj6HzR97AHK6XK8i3BuVyIifWAm/sE3a15lF5RH9yQIrbXYuo0IFyA==} + + '@vitest/snapshot@3.1.3': + resolution: {integrity: sha512-XVa5OPNTYUsyqG9skuUkFzAeFnEzDp8hQu7kZ0N25B1+6KjGm4hWLtURyBbsIAOekfWQ7Wuz/N/XXzgYO3deWQ==} + + '@vitest/spy@3.1.3': + resolution: {integrity: sha512-x6w+ctOEmEXdWaa6TO4ilb7l9DxPR5bwEb6hILKuxfU1NqWT2mpJD9NJN7t3OTfxmVlOMrvtoFJGdgyzZ605lQ==} + + '@vitest/ui@3.1.3': + resolution: {integrity: sha512-IipSzX+8DptUdXN/GWq3hq5z18MwnpphYdOMm0WndkRGYELzfq7NDP8dMpZT7JGW1uXFrIGxOW2D0Xi++ulByg==} + peerDependencies: + vitest: 3.1.3 + + '@vitest/utils@3.1.3': + resolution: {integrity: sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -762,6 +808,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-types@0.13.4: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} @@ -800,6 +850,10 @@ packages: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -808,6 +862,10 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -823,6 +881,10 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + ci-info@4.2.0: resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} engines: {node: '>=8'} @@ -901,6 +963,19 @@ packages: supports-color: optional: true + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -945,6 +1020,9 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -1026,6 +1104,9 @@ packages: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} @@ -1041,6 +1122,10 @@ packages: resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} engines: {node: ^18.19.0 || >=20.5.0} + expect-type@1.2.1: + resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} + engines: {node: '>=12.0.0'} + external-editor@3.1.0: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} @@ -1067,6 +1152,17 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -1090,6 +1186,9 @@ packages: flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + fs-extra@11.2.0: resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} engines: {node: '>=14.14'} @@ -1422,6 +1521,9 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} @@ -1430,6 +1532,9 @@ packages: resolution: {integrity: sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1467,6 +1572,10 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1591,6 +1700,13 @@ packages: resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} engines: {node: '>=12'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1598,6 +1714,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pidtree@0.6.0: resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} engines: {node: '>=0.10'} @@ -1732,10 +1852,17 @@ packages: engines: {node: '>=4'} hasBin: true + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + sirv@3.0.1: + resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + engines: {node: '>=18'} + slash@5.1.0: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} @@ -1771,6 +1898,12 @@ packages: sprintf-js@1.1.3: resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -1826,6 +1959,28 @@ packages: resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} engines: {node: ^14.18.0 || >=16.0.0} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -1834,6 +1989,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + ts-api-utils@1.4.0: resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==} engines: {node: '>=16'} @@ -1907,6 +2066,11 @@ packages: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + vite-node@3.1.3: + resolution: {integrity: sha512-uHV4plJ2IxCl4u1up1FQRrqclylKAogbtBfOTwcuJ28xFi+89PZ57BRh+naIRvH70HPwxy5QHYzg1OrEaC7AbA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + vite@5.4.11: resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1938,6 +2102,34 @@ packages: terser: optional: true + vitest@3.1.3: + resolution: {integrity: sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.1.3 + '@vitest/ui': 3.1.3 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + when-exit@2.1.3: resolution: {integrity: sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw==} @@ -1946,6 +2138,11 @@ packages: engines: {node: '>= 8'} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + widest-line@5.0.0: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} @@ -2246,6 +2443,8 @@ snapshots: optionalDependencies: '@types/node': 22.14.0 + '@jridgewell/sourcemap-codec@1.5.0': {} + '@lightningjs/renderer@3.0.0-beta6': {} '@nodelib/fs.scandir@2.1.5': @@ -2342,6 +2541,8 @@ snapshots: '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 + '@polka/url@1.0.0-next.29': {} + '@rollup/rollup-android-arm-eabi@4.27.2': optional: true @@ -2586,6 +2787,57 @@ snapshots: '@typescript-eslint/types': 8.16.0 eslint-visitor-keys: 4.2.0 + '@vitest/expect@3.1.3': + dependencies: + '@vitest/spy': 3.1.3 + '@vitest/utils': 3.1.3 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.1.3(vite@5.4.11(@types/node@22.14.0))': + dependencies: + '@vitest/spy': 3.1.3 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 5.4.11(@types/node@22.14.0) + + '@vitest/pretty-format@3.1.3': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.1.3': + dependencies: + '@vitest/utils': 3.1.3 + pathe: 2.0.3 + + '@vitest/snapshot@3.1.3': + dependencies: + '@vitest/pretty-format': 3.1.3 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.1.3': + dependencies: + tinyspy: 3.0.2 + + '@vitest/ui@3.1.3(vitest@3.1.3)': + dependencies: + '@vitest/utils': 3.1.3 + fflate: 0.8.2 + flatted: 3.3.3 + pathe: 2.0.3 + sirv: 3.0.1 + tinyglobby: 0.2.13 + tinyrainbow: 2.0.0 + vitest: 3.1.3(@types/node@22.14.0)(@vitest/ui@3.1.3) + + '@vitest/utils@3.1.3': + dependencies: + '@vitest/pretty-format': 3.1.3 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + acorn-jsx@5.3.2(acorn@8.14.0): dependencies: acorn: 8.14.0 @@ -2625,6 +2877,8 @@ snapshots: argparse@2.0.1: {} + assertion-error@2.0.1: {} + ast-types@0.13.4: dependencies: tslib: 2.8.1 @@ -2672,10 +2926,20 @@ snapshots: dependencies: run-applescript: 7.0.0 + cac@6.7.14: {} + callsites@3.1.0: {} camelcase@8.0.0: {} + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -2687,6 +2951,8 @@ snapshots: chardet@0.7.0: {} + check-error@2.1.1: {} + ci-info@4.2.0: {} cli-boxes@3.0.0: {} @@ -2755,6 +3021,12 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.0: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + deep-extend@0.6.0: {} deep-is@0.1.4: {} @@ -2790,6 +3062,8 @@ snapshots: dependencies: is-arrayish: 0.2.1 + es-module-lexer@1.7.0: {} + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -2908,6 +3182,10 @@ snapshots: estraverse@5.3.0: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.6 + esutils@2.0.3: {} eventemitter3@5.0.1: {} @@ -2939,6 +3217,8 @@ snapshots: strip-final-newline: 4.0.0 yoctocolors: 2.1.1 + expect-type@1.2.1: {} + external-editor@3.1.0: dependencies: chardet: 0.7.0 @@ -2967,6 +3247,12 @@ snapshots: dependencies: reusify: 1.0.4 + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fflate@0.8.2: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -2991,6 +3277,8 @@ snapshots: flatted@3.3.1: {} + flatted@3.3.3: {} + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 @@ -3304,10 +3592,16 @@ snapshots: strip-ansi: 7.1.0 wrap-ansi: 9.0.0 + loupe@3.1.3: {} + lru-cache@7.18.3: {} macos-release@3.3.0: {} + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -3337,6 +3631,8 @@ snapshots: minimist@1.2.8: {} + mrmime@2.0.1: {} + ms@2.1.3: {} mute-stream@2.0.0: {} @@ -3474,10 +3770,16 @@ snapshots: path-type@5.0.0: {} + pathe@2.0.3: {} + + pathval@2.0.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.2: {} + pidtree@0.6.0: {} postcss@8.4.49: @@ -3647,8 +3949,16 @@ snapshots: interpret: 1.4.0 rechoir: 0.6.2 + siginfo@2.0.0: {} + signal-exit@4.1.0: {} + sirv@3.0.1: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + slash@5.1.0: {} slice-ansi@5.0.0: @@ -3683,6 +3993,10 @@ snapshots: sprintf-js@1.1.3: {} + stackback@0.0.2: {} + + std-env@3.9.0: {} + stdin-discarder@0.2.2: {} string-argv@0.3.2: {} @@ -3728,6 +4042,21 @@ snapshots: '@pkgr/core': 0.1.1 tslib: 2.8.1 + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.0.2: {} + + tinyrainbow@2.0.0: {} + + tinyspy@3.0.2: {} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -3736,6 +4065,8 @@ snapshots: dependencies: is-number: 7.0.0 + totalist@3.0.1: {} + ts-api-utils@1.4.0(typescript@5.6.3): dependencies: typescript: 5.6.3 @@ -3796,6 +4127,24 @@ snapshots: url-join@5.0.0: {} + vite-node@3.1.3(@types/node@22.14.0): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 5.4.11(@types/node@22.14.0) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + vite@5.4.11(@types/node@22.14.0): dependencies: esbuild: 0.21.5 @@ -3805,12 +4154,54 @@ snapshots: '@types/node': 22.14.0 fsevents: 2.3.3 + vitest@3.1.3(@types/node@22.14.0)(@vitest/ui@3.1.3): + dependencies: + '@vitest/expect': 3.1.3 + '@vitest/mocker': 3.1.3(vite@5.4.11(@types/node@22.14.0)) + '@vitest/pretty-format': 3.1.3 + '@vitest/runner': 3.1.3 + '@vitest/snapshot': 3.1.3 + '@vitest/spy': 3.1.3 + '@vitest/utils': 3.1.3 + chai: 5.2.0 + debug: 4.4.0 + expect-type: 1.2.1 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.13 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 5.4.11(@types/node@22.14.0) + vite-node: 3.1.3(@types/node@22.14.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.14.0 + '@vitest/ui': 3.1.3(vitest@3.1.3) + transitivePeerDependencies: + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + when-exit@2.1.3: {} which@2.0.2: dependencies: isexe: 2.0.0 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + widest-line@5.0.0: dependencies: string-width: 7.2.0 diff --git a/src/tests/flex-perf-results.json b/src/tests/flex-perf-results.json new file mode 100644 index 0000000..0483675 --- /dev/null +++ b/src/tests/flex-perf-results.json @@ -0,0 +1,495 @@ +{ + "timestamp": "2025-05-11T04:01:59.305Z", + "results": [ + { + "id": "Row, FlexStart, NoGrow-3", + "scenarioName": "Row, FlexStart, NoGrow", + "numChildren": 3, + "durationMs": 0.08027800000000222, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, NoGrow-5", + "scenarioName": "Row, FlexStart, NoGrow", + "numChildren": 5, + "durationMs": 0.08926400000001422, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, NoGrow-10", + "scenarioName": "Row, FlexStart, NoGrow", + "numChildren": 10, + "durationMs": 0.012763999999985268, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, NoGrow-15", + "scenarioName": "Row, FlexStart, NoGrow", + "numChildren": 15, + "durationMs": 0.0073886666666377705, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, NoGrow-20", + "scenarioName": "Row, FlexStart, NoGrow", + "numChildren": 20, + "durationMs": 0.012499999999988631, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, NoGrow-50", + "scenarioName": "Row, FlexStart, NoGrow", + "numChildren": 50, + "durationMs": 0.020833333333352282, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, NoGrow-100", + "scenarioName": "Row, FlexStart, NoGrow", + "numChildren": 100, + "durationMs": 0.06497200000001158, + "containerUpdated": false + }, + { + "id": "Column, Center, WithGrow-3", + "scenarioName": "Column, Center, WithGrow", + "numChildren": 3, + "durationMs": 0.012291666666669698, + "containerUpdated": false + }, + { + "id": "Column, Center, WithGrow-5", + "scenarioName": "Column, Center, WithGrow", + "numChildren": 5, + "durationMs": 0.003361000000002908, + "containerUpdated": false + }, + { + "id": "Column, Center, WithGrow-10", + "scenarioName": "Column, Center, WithGrow", + "numChildren": 10, + "durationMs": 0.006138999999999821, + "containerUpdated": false + }, + { + "id": "Column, Center, WithGrow-15", + "scenarioName": "Column, Center, WithGrow", + "numChildren": 15, + "durationMs": 0.007652666666672303, + "containerUpdated": false + }, + { + "id": "Column, Center, WithGrow-20", + "scenarioName": "Column, Center, WithGrow", + "numChildren": 20, + "durationMs": 0.013944333333351247, + "containerUpdated": false + }, + { + "id": "Column, Center, WithGrow-50", + "scenarioName": "Column, Center, WithGrow", + "numChildren": 50, + "durationMs": 0.024777333333323288, + "containerUpdated": false + }, + { + "id": "Column, Center, WithGrow-100", + "scenarioName": "Column, Center, WithGrow", + "numChildren": 100, + "durationMs": 0.051693999999997686, + "containerUpdated": false + }, + { + "id": "Row, SpaceBetween, MixedGrow-3", + "scenarioName": "Row, SpaceBetween, MixedGrow", + "numChildren": 3, + "durationMs": 0.004541666666663484, + "containerUpdated": false + }, + { + "id": "Row, SpaceBetween, MixedGrow-5", + "scenarioName": "Row, SpaceBetween, MixedGrow", + "numChildren": 5, + "durationMs": 0.005874999999984236, + "containerUpdated": false + }, + { + "id": "Row, SpaceBetween, MixedGrow-10", + "scenarioName": "Row, SpaceBetween, MixedGrow", + "numChildren": 10, + "durationMs": 0.005541333333326293, + "containerUpdated": false + }, + { + "id": "Row, SpaceBetween, MixedGrow-15", + "scenarioName": "Row, SpaceBetween, MixedGrow", + "numChildren": 15, + "durationMs": 0.019763999999990272, + "containerUpdated": false + }, + { + "id": "Row, SpaceBetween, MixedGrow-20", + "scenarioName": "Row, SpaceBetween, MixedGrow", + "numChildren": 20, + "durationMs": 0.012430333333346274, + "containerUpdated": false + }, + { + "id": "Row, SpaceBetween, MixedGrow-50", + "scenarioName": "Row, SpaceBetween, MixedGrow", + "numChildren": 50, + "durationMs": 0.05880533333333915, + "containerUpdated": false + }, + { + "id": "Row, SpaceBetween, MixedGrow-100", + "scenarioName": "Row, SpaceBetween, MixedGrow", + "numChildren": 100, + "durationMs": 0.06331966666664357, + "containerUpdated": false + }, + { + "id": "Row, Wrap, FlexStart-3", + "scenarioName": "Row, Wrap, FlexStart", + "numChildren": 3, + "durationMs": 0.009208666666665977, + "containerUpdated": false + }, + { + "id": "Row, Wrap, FlexStart-5", + "scenarioName": "Row, Wrap, FlexStart", + "numChildren": 5, + "durationMs": 0.009875000000003334, + "containerUpdated": true + }, + { + "id": "Row, Wrap, FlexStart-10", + "scenarioName": "Row, Wrap, FlexStart", + "numChildren": 10, + "durationMs": 0.005416666666690162, + "containerUpdated": true + }, + { + "id": "Row, Wrap, FlexStart-15", + "scenarioName": "Row, Wrap, FlexStart", + "numChildren": 15, + "durationMs": 0.007500000000031075, + "containerUpdated": true + }, + { + "id": "Row, Wrap, FlexStart-20", + "scenarioName": "Row, Wrap, FlexStart", + "numChildren": 20, + "durationMs": 0.010069333333357614, + "containerUpdated": true + }, + { + "id": "Row, Wrap, FlexStart-50", + "scenarioName": "Row, Wrap, FlexStart", + "numChildren": 50, + "durationMs": 0.022972333333333, + "containerUpdated": true + }, + { + "id": "Row, Wrap, FlexStart-100", + "scenarioName": "Row, Wrap, FlexStart", + "numChildren": 100, + "durationMs": 0.04563866666664277, + "containerUpdated": true + }, + { + "id": "Row, RTL, FlexEnd-3", + "scenarioName": "Row, RTL, FlexEnd", + "numChildren": 3, + "durationMs": 0.003569333333321841, + "containerUpdated": false + }, + { + "id": "Row, RTL, FlexEnd-5", + "scenarioName": "Row, RTL, FlexEnd", + "numChildren": 5, + "durationMs": 0.0024169999999988554, + "containerUpdated": false + }, + { + "id": "Row, RTL, FlexEnd-10", + "scenarioName": "Row, RTL, FlexEnd", + "numChildren": 10, + "durationMs": 0.004069333333328966, + "containerUpdated": false + }, + { + "id": "Row, RTL, FlexEnd-15", + "scenarioName": "Row, RTL, FlexEnd", + "numChildren": 15, + "durationMs": 0.006166666666672427, + "containerUpdated": false + }, + { + "id": "Row, RTL, FlexEnd-20", + "scenarioName": "Row, RTL, FlexEnd", + "numChildren": 20, + "durationMs": 0.010221999999979895, + "containerUpdated": false + }, + { + "id": "Row, RTL, FlexEnd-50", + "scenarioName": "Row, RTL, FlexEnd", + "numChildren": 50, + "durationMs": 0.02020833333331969, + "containerUpdated": false + }, + { + "id": "Row, RTL, FlexEnd-100", + "scenarioName": "Row, RTL, FlexEnd", + "numChildren": 100, + "durationMs": 0.03984733333332239, + "containerUpdated": false + }, + { + "id": "Row, WithOrder-3", + "scenarioName": "Row, WithOrder", + "numChildren": 3, + "durationMs": 0.017374666666664023, + "containerUpdated": false + }, + { + "id": "Row, WithOrder-5", + "scenarioName": "Row, WithOrder", + "numChildren": 5, + "durationMs": 0.0036526666666721517, + "containerUpdated": false + }, + { + "id": "Row, WithOrder-10", + "scenarioName": "Row, WithOrder", + "numChildren": 10, + "durationMs": 0.0070140000000075515, + "containerUpdated": false + }, + { + "id": "Row, WithOrder-15", + "scenarioName": "Row, WithOrder", + "numChildren": 15, + "durationMs": 0.007875333333345225, + "containerUpdated": false + }, + { + "id": "Row, WithOrder-20", + "scenarioName": "Row, WithOrder", + "numChildren": 20, + "durationMs": 0.011902999999980087, + "containerUpdated": false + }, + { + "id": "Row, WithOrder-50", + "scenarioName": "Row, WithOrder", + "numChildren": 50, + "durationMs": 0.02779166666666318, + "containerUpdated": false + }, + { + "id": "Row, WithOrder-100", + "scenarioName": "Row, WithOrder", + "numChildren": 100, + "durationMs": 0.05297199999999217, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithMargins-3", + "scenarioName": "Row, FlexStart, WithMargins", + "numChildren": 3, + "durationMs": 0.008083333333312718, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithMargins-5", + "scenarioName": "Row, FlexStart, WithMargins", + "numChildren": 5, + "durationMs": 0.0025696666666590318, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithMargins-10", + "scenarioName": "Row, FlexStart, WithMargins", + "numChildren": 10, + "durationMs": 0.004666666666651054, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithMargins-15", + "scenarioName": "Row, FlexStart, WithMargins", + "numChildren": 15, + "durationMs": 0.00658333333332924, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithMargins-20", + "scenarioName": "Row, FlexStart, WithMargins", + "numChildren": 20, + "durationMs": 0.00836066666666587, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithMargins-50", + "scenarioName": "Row, FlexStart, WithMargins", + "numChildren": 50, + "durationMs": 0.019486333333342525, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithMargins-100", + "scenarioName": "Row, FlexStart, WithMargins", + "numChildren": 100, + "durationMs": 0.04315299999999903, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithGap-3", + "scenarioName": "Row, FlexStart, WithGap", + "numChildren": 3, + "durationMs": 0.004999666666681908, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithGap-5", + "scenarioName": "Row, FlexStart, WithGap", + "numChildren": 5, + "durationMs": 0.0024859999999762294, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithGap-10", + "scenarioName": "Row, FlexStart, WithGap", + "numChildren": 10, + "durationMs": 0.0045419999999959755, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithGap-15", + "scenarioName": "Row, FlexStart, WithGap", + "numChildren": 15, + "durationMs": 0.006499666666665386, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithGap-20", + "scenarioName": "Row, FlexStart, WithGap", + "numChildren": 20, + "durationMs": 0.008138666666657931, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithGap-50", + "scenarioName": "Row, FlexStart, WithGap", + "numChildren": 50, + "durationMs": 0.23718033333333702, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, WithGap-100", + "scenarioName": "Row, FlexStart, WithGap", + "numChildren": 100, + "durationMs": 0.04145866666668022, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignCenter (Cross Axis)-3", + "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", + "numChildren": 3, + "durationMs": 0.005291333333351152, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignCenter (Cross Axis)-5", + "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", + "numChildren": 5, + "durationMs": 0.002888999999981934, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignCenter (Cross Axis)-10", + "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", + "numChildren": 10, + "durationMs": 0.004986333333325395, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignCenter (Cross Axis)-15", + "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", + "numChildren": 15, + "durationMs": 0.00709666666667393, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignCenter (Cross Axis)-20", + "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", + "numChildren": 20, + "durationMs": 0.009277666666681247, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignCenter (Cross Axis)-50", + "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", + "numChildren": 50, + "durationMs": 0.02187466666665235, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignCenter (Cross Axis)-100", + "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", + "numChildren": 100, + "durationMs": 0.10990299999999327, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignSelf (Cross Axis)-3", + "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", + "numChildren": 3, + "durationMs": 0.016055666666659363, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignSelf (Cross Axis)-5", + "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", + "numChildren": 5, + "durationMs": 0.0033476666666463948, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignSelf (Cross Axis)-10", + "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", + "numChildren": 10, + "durationMs": 0.02041666666667652, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignSelf (Cross Axis)-15", + "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", + "numChildren": 15, + "durationMs": 0.01143066666664557, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignSelf (Cross Axis)-20", + "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", + "numChildren": 20, + "durationMs": 0.009430666666654966, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignSelf (Cross Axis)-50", + "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", + "numChildren": 50, + "durationMs": 0.021652999999995853, + "containerUpdated": false + }, + { + "id": "Row, FlexStart, AlignSelf (Cross Axis)-100", + "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", + "numChildren": 100, + "durationMs": 0.06109733333331254, + "containerUpdated": false + } + ] +} \ No newline at end of file diff --git a/src/tests/flex.performance.spec.ts b/src/tests/flex.performance.spec.ts new file mode 100644 index 0000000..5702d72 --- /dev/null +++ b/src/tests/flex.performance.spec.ts @@ -0,0 +1,352 @@ +import { ElementNode } from '../elementNode'; +import calculateFlex from '../flex'; +import { + describe, + it, + vi, + beforeEach, + afterEach, + expect, + afterAll, +} from 'vitest'; +import fs from 'fs'; +import path from 'path'; +import { isElementNode } from '../utils'; // Assuming isElementText and isTextNode are not directly needed for createTestElement logic here + +// Helper to create a basic ElementNode for flex testing +function createTestElement( + initialProps: Partial & { + nodeType?: 'element' | 'textNode'; + } = {}, +): ElementNode { + const nodeTypeName = + initialProps.nodeType === 'textNode' ? 'text' : 'element'; + const node = new ElementNode(nodeTypeName); + + // Assign all properties. The ElementNode's own setters will handle lng delegation. + for (const key in initialProps) { + if (Object.prototype.hasOwnProperty.call(initialProps, key)) { + if (key === 'children') continue; // Handled below + if (key === 'nodeType') continue; // Handled above + (node as any)[key] = (initialProps as any)[key]; + } + } + + // Ensure essential dimension properties are initialized on lng via setters if not explicitly set + // This ensures their getters return a number, as flex calculations expect. + node.x = node.x || 0; + node.y = node.y || 0; + node.width = node.width || 0; + node.height = node.height || 0; + + if (initialProps.children) { + node.children = initialProps.children as Array; // ElementText is a type of ElementNode + node.children.forEach((child) => { + if (isElementNode(child)) { + child.parent = node; + child.rendered = true; + // Ensure children also have their dimensions initialized + child.width = child.width || 0; + child.height = child.height || 0; + child.x = child.x || 0; + child.y = child.y || 0; + } + }); + } else { + node.children = []; + } + + node.rendered = true; // Assume rendered for layout calculations + return node; +} + +// Mock console.warn to avoid noise during tests (e.g., for negative growFactor) +// Use vi.fn() for Vitest +let originalConsoleWarn: (...data: any[]) => void; +beforeEach(() => { + originalConsoleWarn = console.warn; + console.warn = vi.fn(); +}); +afterEach(() => { + console.warn = originalConsoleWarn; +}); + +const RESULTS_FILE_PATH = path.join(__dirname, 'flex-perf-results.json'); +const REGRESSION_THRESHOLD_PERCENTAGE = 10; // 10% slowdown is a regression +const NUM_TEST_RUNS = 3; // Number of times to run each test for averaging + +interface PerformanceResult { + id: string; // Unique ID for a test case: scenarioName + numChildren + scenarioName: string; + numChildren: number; + durationMs: number; + containerUpdated: boolean; +} + +interface PerformanceRunData { + timestamp: string; + results: PerformanceResult[]; +} + +const currentRunResults: PerformanceResult[] = []; +let previousRunData: PerformanceRunData | null = null; + +// Check if fs module is available (for Node.js environment) +const isFsAvailable = + typeof fs !== 'undefined' && + typeof fs.existsSync === 'function' && + typeof fs.readFileSync === 'function' && + typeof fs.writeFileSync === 'function'; + +describe('Flexbox Performance Tests (calculateFlex)', () => { + const scenarios = [ + { + name: 'Row, FlexStart, NoGrow', + // To ensure the test setup is minimal and doesn't rely on renderer for basic ElementNode creation + // we'll assume the `startLightningRenderer` call in a `beforeAll` is for more complex scenarios + // or if ElementNode setters have deep renderer interactions not covered by simple prop assignment. + parentProps: { flexDirection: 'row', justifyContent: 'flexStart' }, + childProps: { width: 50, height: 50 }, + }, + { + name: 'Column, Center, WithGrow', + parentProps: { flexDirection: 'column', justifyContent: 'center' }, + childProps: { width: 50, height: 50, flexGrow: 1 }, + }, + { + name: 'Row, SpaceBetween, MixedGrow', + parentProps: { flexDirection: 'row', justifyContent: 'spaceBetween' }, + childPropsFn: (i: number) => ({ + width: 50, + height: 50, + flexGrow: i % 2, + }), + }, + { + name: 'Row, Wrap, FlexStart', + parentProps: { + flexDirection: 'row', + justifyContent: 'flexStart', + flexWrap: 'wrap', + width: 300, // Container width that forces wrapping + }, + childProps: { width: 100, height: 50 }, + }, + { + name: 'Row, RTL, FlexEnd', + parentProps: { + flexDirection: 'row', + justifyContent: 'flexEnd', + direction: 'rtl', + }, + childProps: { width: 50, height: 50 }, + }, + { + name: 'Row, WithOrder', + parentProps: { flexDirection: 'row', justifyContent: 'flexStart' }, + childPropsFn: (i: number, total: number) => ({ + width: 50, + height: 50, + flexOrder: total - i, + }), + }, + { + name: 'Row, FlexStart, WithMargins', + parentProps: { flexDirection: 'row', justifyContent: 'flexStart' }, + childProps: { + width: 50, + height: 50, + marginLeft: 5, + marginRight: 5, + marginTop: 2, + marginBottom: 2, + }, + }, + { + name: 'Row, FlexStart, WithGap', + parentProps: { + flexDirection: 'row', + justifyContent: 'flexStart', + gap: 10, + }, + childProps: { width: 50, height: 50 }, + }, + { + name: 'Row, FlexStart, AlignCenter (Cross Axis)', + parentProps: { + flexDirection: 'row', + justifyContent: 'flexStart', + alignItems: 'center', + height: 200, // Parent height for alignment context + }, + childProps: { width: 50, height: 50 }, + }, + { + name: 'Row, FlexStart, AlignSelf (Cross Axis)', + parentProps: { + flexDirection: 'row', + justifyContent: 'flexStart', + alignItems: 'flexStart', // Default alignment + height: 200, + }, + childPropsFn: (i: number) => ({ + width: 50, + height: 50, + alignSelf: + i % 3 === 0 ? 'center' : i % 3 === 1 ? 'flexEnd' : 'flexStart', + }), + }, + ]; + + const childCounts = [3, 5, 10, 15, 20, 50, 100]; // Number of children to test + + // Try to load previous results before tests run + if (isFsAvailable) { + if (fs.existsSync(RESULTS_FILE_PATH)) { + previousRunData = JSON.parse(fs.readFileSync(RESULTS_FILE_PATH, 'utf-8')); + } + } + + scenarios.forEach((scenario) => { + describe(`Scenario: ${scenario.name}`, () => { + childCounts.forEach((numChildren) => { + // Test names need to be unique if you use `it.each` or similar advanced Jest features + it(`should perform layout for ${numChildren} children`, () => { + const parentNode = createTestElement({ + display: 'flex', + width: scenario.parentProps.width ?? 600, // Default parent width + height: scenario.parentProps.height ?? 600, // Default parent height + children: [], // Will be populated + ...scenario.parentProps, + // @ts-ignore - Type 'string' is not assignable to type 'FlexDirection'. + flexDirection: scenario.parentProps.flexDirection, + }); + + for (let i = 0; i < numChildren; i++) { + let currentChildProps = {}; + if (scenario.childPropsFn) { + currentChildProps = scenario.childPropsFn(i, numChildren); + } else { + currentChildProps = scenario.childProps || {}; + } + const childNode = createTestElement({ + // parent will be set by createTestElement if passed in children, + // or manually if children are pushed one by one. + // Here, we push to parentNode.children AFTER creating child. + ...currentChildProps, + }); + childNode.parent = parentNode; // Explicitly set parent + parentNode.children.push(childNode); + } + + let totalDuration = 0; + let lastContainerUpdatedStatus = false; + + for (let run = 0; run < NUM_TEST_RUNS; run++) { + const startTime = performance.now(); + lastContainerUpdatedStatus = calculateFlex(parentNode); + const endTime = performance.now(); + totalDuration += endTime - startTime; + } + + const averageDuration = totalDuration / NUM_TEST_RUNS; + + const resultId = `${scenario.name}-${numChildren}`; + currentRunResults.push({ + id: resultId, + scenarioName: scenario.name, + numChildren: numChildren, + durationMs: averageDuration, + containerUpdated: lastContainerUpdatedStatus, // Use status from the last run + }); + + // Outputting to console. In a CI/benchmark setup, you'd log this to a file or service. + // Using a template literal to make it easy to copy-paste into a spreadsheet. + console.log( + `FlexPerf Immediate; ${scenario.name}; ${numChildren}; ${averageDuration.toFixed(3)};`, + ); + + // Basic assertion: layout should generally take less than a few milliseconds for these counts. + // This threshold is arbitrary and should be adjusted based on your performance targets. + expect(averageDuration).toBeLessThan(10); // e.g., 10ms, check average + + // Optional: Add functional assertions to ensure layout is somewhat correct + if (numChildren > 0 && parentNode.children[0]) { + const firstChild = parentNode.children[0] as ElementNode; + if (parentNode.flexDirection === 'column') { + expect(firstChild.y).toBeDefined(); + } else { + // Default or 'row' + expect(firstChild.x).toBeDefined(); + } + } + }); + }); + }); + }); + + afterAll(() => { + // Save current results + const currentRunData: PerformanceRunData = { + timestamp: new Date().toISOString(), + results: currentRunResults, + }; + if (isFsAvailable) { + fs.writeFileSync( + RESULTS_FILE_PATH, + JSON.stringify(currentRunData, null, 2), + ); + console.log(`Performance results saved to ${RESULTS_FILE_PATH}`); + } else { + console.log( + 'File system not available. Performance results not saved to disk.', + ); + } + + // Compare with previous results if available + if (previousRunData) { + console.log('\n--- Performance Comparison ---'); + console.log(`Previous run: ${previousRunData.timestamp}`); + console.log(`Current run: ${currentRunData.timestamp}`); + + const previousResultsMap = new Map( + previousRunData.results.map((r) => [r.id, r]), + ); + + currentRunResults.forEach((currentResult) => { + const prevResult = previousResultsMap.get(currentResult.id); + if (prevResult) { + const diff = currentResult.durationMs - prevResult.durationMs; + const percentageChange = + prevResult.durationMs === 0 + ? currentResult.durationMs > 0 + ? Infinity + : 0 + : (diff / prevResult.durationMs) * 100; + + let logMessage = ` ${currentResult.scenarioName} (${currentResult.numChildren} children): ${currentResult.durationMs.toFixed(3)}ms (prev: ${prevResult.durationMs.toFixed(3)}ms, change: ${diff.toFixed(3)}ms, ${percentageChange.toFixed(2)}%)`; + + if (percentageChange > REGRESSION_THRESHOLD_PERCENTAGE) { + logMessage += ' - REGRESSION DETECTED'; + // You could add an expect here to fail the build if desired: + // expect(percentageChange).toBeLessThanOrEqual(REGRESSION_THRESHOLD_PERCENTAGE); + } else if (percentageChange < -REGRESSION_THRESHOLD_PERCENTAGE) { + logMessage += ' - IMPROVEMENT DETECTED'; + } + console.log(logMessage); + previousResultsMap.delete(currentResult.id); // Mark as processed + } else { + console.log( + ` ${currentResult.scenarioName} (${currentResult.numChildren} children): ${currentResult.durationMs.toFixed(3)}ms (New test)`, + ); + } + }); + + previousResultsMap.forEach((removedTest) => { + console.log( + ` ${removedTest.scenarioName} (${removedTest.numChildren} children): (Test removed or not run in current suite)`, + ); + }); + } + }); +}); diff --git a/src/tests/flex.spec.ts b/src/tests/flex.spec.ts new file mode 100644 index 0000000..02731da --- /dev/null +++ b/src/tests/flex.spec.ts @@ -0,0 +1,848 @@ +import { ElementNode } from '../elementNode'; +import calculateFlex from '../flex'; +import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { isElementNode } from '../utils'; +import { NodeType } from '../nodeTypes'; + +// Helper to create a basic ElementNode for flex testing +// (Adapted from flex.performance.spec.ts) +function createTestElement( + initialProps: Partial & { + nodeType?: 'element' | 'textNode' | 'text'; // Added 'text' for true TextNode + children?: Array; // Allow TextNode in children + } = {}, +): ElementNode | TextNode { + if (initialProps.nodeType === 'text') { + // Create a simple TextNode (not ElementText which is a type of ElementNode) + const textNodeInstance: TextNode = { + _type: NodeType.Text, + text: (initialProps as any).text || '', + // Add other properties if TextNode has them and they are relevant for testing + }; + return textNodeInstance; + } + + const nodeTypeName = + initialProps.nodeType === 'textNode' ? 'text' : 'element'; + const node = new ElementNode(nodeTypeName); + + for (const key in initialProps) { + if (Object.prototype.hasOwnProperty.call(initialProps, key)) { + if (key === 'children') continue; + if (key === 'nodeType') continue; + (node as any)[key] = (initialProps as any)[key]; + } + } + + node.x = node.x || 0; + node.y = node.y || 0; + node.width = node.width || 0; + node.height = node.height || 0; + + if (initialProps.children) { + node.children = initialProps.children as Array; + node.children.forEach((child) => { + if (isElementNode(child)) { + child.parent = node; + child.rendered = true; + child.width = child.width || 0; + child.height = child.height || 0; + child.x = child.x || 0; + child.y = child.y || 0; + } else { + // If it's a simple TextNode, it might need a parent reference if flex logic ever inspects it + // For now, flex logic filters out TextNode instances early. + (child as TextNode).parent = node as unknown as ElementText; // Cast for parent type + } + }); + } else { + node.children = []; + } + + node.rendered = true; + return node; +} + +// Mock console.warn +let originalConsoleWarn: (...data: any[]) => void; +beforeEach(() => { + originalConsoleWarn = console.warn; + console.warn = vi.fn(); +}); +afterEach(() => { + console.warn = originalConsoleWarn; +}); + +describe('Flexbox Layout (calculateFlex)', () => { + describe('Basic Row Layout', () => { + it('should layout children in a row with flexStart', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + justifyContent: 'flexStart', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + + expect(child1.x).toBe(0); + expect(child1.y).toBe(0); // Default y + expect(child2.x).toBe(50); // child1.width + expect(child2.y).toBe(0); + }); + + it('should layout children in a row with flexEnd', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + justifyContent: 'flexEnd', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + // total width = 50 + 60 = 110. Parent width = 200. Space = 90 + // child2 is at 200 - 60 = 140 + // child1 is at 140 - 50 = 90 + expect(child1.x).toBe(200 - 110); + expect(child2.x).toBe(200 - 60); + }); + + it('should layout children in a row with center', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + justifyContent: 'center', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + // total width = 110. Parent width = 200. Space = 90. Start = 90 / 2 = 45. + expect(child1.x).toBe(45); + expect(child2.x).toBe(45 + 50); + }); + + it('should layout children in a row with spaceBetween', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + }) as ElementNode; + const child3 = createTestElement({ + width: 40, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 300, // total child width = 50+60+40 = 150 + height: 100, + flexDirection: 'row', + justifyContent: 'spaceBetween', + children: [child1, child2, child3], + }) as ElementNode; + + calculateFlex(parent); + // Remaining space = 300 - 150 = 150. Gaps = 2. Space per gap = 150 / 2 = 75 + expect(child1.x).toBe(0); + expect(child2.x).toBe(50 + 75); // child1.width + space + expect(child3.x).toBe(50 + 75 + 60 + 75); // child1.width + space + child2.width + space + }); + + it('should layout children in a row with spaceBetween and padding', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, // total child width = 110 + height: 100, + padding: 10, // available space for items = 200 - 10 - 10 = 180 + flexDirection: 'row', + justifyContent: 'spaceBetween', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + // Item space = 180. Total child width = 110. Remaining space = 70. + expect(child1.x).toBe(10); // parent.padding + expect(child2.x).toBe(10 + 50 + 70); // parent.padding + child1.width + space + }); + + it('should layout children in a row with spaceEvenly', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, // total child width = 110 + height: 100, + flexDirection: 'row', + justifyContent: 'spaceEvenly', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + // Remaining space = 200 - 110 = 90. Spaces = 3 (before first, between, after last). Space per gap = 90 / 3 = 30 + expect(child1.x).toBe(30); + expect(child2.x).toBe(30 + 50 + 30); // space + child1.width + space + }); + }); + + describe('Basic Column Layout', () => { + it('should layout children in a column with flexStart', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 50, + height: 60, + }) as ElementNode; + const parent = createTestElement({ + width: 100, + height: 200, + flexDirection: 'column', + justifyContent: 'flexStart', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + + expect(child1.x).toBe(0); + expect(child1.y).toBe(0); + expect(child2.x).toBe(0); + expect(child2.y).toBe(50); // child1.height + }); + }); + + describe('Flex Grow', () => { + it('should distribute remaining space among flexGrow items in a row', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + flexGrow: 1, + }) as ElementNode; + const child2 = createTestElement({ + width: 50, + height: 50, + flexGrow: 1, + }) as ElementNode; + const parent = createTestElement({ + width: 300, // Initial total child width = 100. Remaining space = 200. + height: 100, + flexDirection: 'row', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + // Each child gets 200 / 2 = 100 extra width. + expect(child1.width).toBe(50 + 100); + expect(child2.width).toBe(50 + 100); + expect(child1.x).toBe(0); + expect(child2.x).toBe(150); + }); + + it('should distribute remaining space proportionally to flexGrow values', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + flexGrow: 1, + }) as ElementNode; + const child2 = createTestElement({ + width: 50, + height: 50, + flexGrow: 3, + }) as ElementNode; // Total flexGrow = 4 + const parent = createTestElement({ + width: 300, // Initial total child width = 100. Remaining space = 200. + height: 100, + flexDirection: 'row', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + // child1 gets 200 * (1/4) = 50 extra. child2 gets 200 * (3/4) = 150 extra. + expect(child1.width).toBe(50 + 50); + expect(child2.width).toBe(50 + 150); + expect(child1.x).toBe(0); + expect(child2.x).toBe(100); + }); + + it('should not apply flexGrow if growFactor is negative', () => { + const child1 = createTestElement({ + width: 150, + height: 50, + flexGrow: 1, + }) as ElementNode; + const child2 = createTestElement({ + width: 150, + height: 50, + flexGrow: 1, + }) as ElementNode; + const parent = createTestElement({ + width: 200, // Children already wider than parent + height: 100, + flexDirection: 'row', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.width).toBe(150); // Unchanged + expect(child2.width).toBe(150); // Unchanged + expect(console.warn).toHaveBeenCalledWith( + 'Negative growFactor, flexGrow not applied', + ); + }); + + it('should not apply flexGrow if only one child', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + flexGrow: 1, + }) as ElementNode; + const parent = createTestElement({ + width: 300, + height: 100, + flexDirection: 'row', + children: [child1], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.width).toBe(50); // Unchanged + }); + }); + + describe('Align Items (Cross Axis)', () => { + it('should align items to flexStart in a row', () => { + const child1 = createTestElement({ + width: 50, + height: 30, + }) as ElementNode; + const child2 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + alignItems: 'flexStart', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.y).toBe(0); + expect(child2.y).toBe(0); + }); + + it('should align items to center in a row', () => { + const child1 = createTestElement({ + width: 50, + height: 30, + }) as ElementNode; + const child2 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + alignItems: 'center', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.y).toBe((100 - 30) / 2); // (parent.height - child.height) / 2 + expect(child2.y).toBe((100 - 50) / 2); + }); + + it('should align items to flexEnd in a row', () => { + const child1 = createTestElement({ + width: 50, + height: 30, + }) as ElementNode; + const child2 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + alignItems: 'flexEnd', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.y).toBe(100 - 30); // parent.height - child.height + expect(child2.y).toBe(100 - 50); + }); + + it('should respect alignSelf property', () => { + const child1 = createTestElement({ + width: 50, + height: 30, + alignSelf: 'flexEnd', + }) as ElementNode; + const child2 = createTestElement({ + width: 50, + height: 50, + alignSelf: 'center', + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + alignItems: 'flexStart', // Default for parent + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.y).toBe(100 - 30); + expect(child2.y).toBe((100 - 50) / 2); + }); + }); + + describe('Gap Property', () => { + it('should apply gap between items in a row', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + gap: 10, + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.x).toBe(0); + expect(child2.x).toBe(50 + 10); // child1.width + gap + }); + + it('should apply rowGap and columnGap in a wrapped row', () => { + const child1 = createTestElement({ + width: 80, + height: 30, + }) as ElementNode; + const child2 = createTestElement({ + width: 80, + height: 30, + }) as ElementNode; + const child3 = createTestElement({ + width: 80, + height: 30, + }) as ElementNode; + const parent = createTestElement({ + width: 150, // child1 and child2 fit, child3 wraps + height: 200, + flexDirection: 'row', + flexWrap: 'wrap', + columnGap: 10, // Gap between columns (horizontal) + rowGap: 5, // Gap between rows (vertical) + alignItems: 'flexStart', // To make cross axis predictable + children: [child1, child2, child3], + }) as ElementNode; + parent.containerCrossSize = 30; // Simulate a known cross size for wrapping calculations + + calculateFlex(parent); + + expect(child1.x).toBe(0); + expect(child1.y).toBe(0); + + expect(child2.x).toBe(80 + 10); // child1.width + columnGap + expect(child2.y).toBe(0); // Still on the first row + + expect(child3.x).toBe(0); // Wrapped to new line + expect(child3.y).toBe(30 + 5); // parent.containerCrossSize (height of first row items) + rowGap + }); + }); + + describe('Margins', () => { + it('should respect margins in a row', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + marginLeft: 5, + marginRight: 10, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + marginLeft: 15, + }) as ElementNode; + const parent = createTestElement({ + width: 300, + height: 100, + flexDirection: 'row', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.x).toBe(5); // marginLeft + expect(child2.x).toBe(5 + 50 + 10 + 15); // child1.marginLeft + child1.width + child1.marginRight + child2.marginLeft + }); + }); + + describe('Padding', () => { + it('should respect parent padding for flexStart', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + padding: 20, + flexDirection: 'row', + justifyContent: 'flexStart', + children: [child1], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.x).toBe(20); + }); + + it('should respect parent padding for flexEnd', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + padding: 20, + flexDirection: 'row', + justifyContent: 'flexEnd', + children: [child1], + }) as ElementNode; + + calculateFlex(parent); + expect(child1.x).toBe(200 - 20 - 50); // parent.width - parent.padding - child1.width + }); + }); + + describe('Flex Wrap', () => { + it('should wrap children in a row when they exceed container width', () => { + const child1 = createTestElement({ + width: 100, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 100, + height: 50, + }) as ElementNode; + const child3 = createTestElement({ + width: 100, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 250, // Can fit two children per row + height: 200, + flexDirection: 'row', + flexWrap: 'wrap', + gap: 10, + alignItems: 'flexStart', // for predictable cross-axis + children: [child1, child2, child3], + }) as ElementNode; + // Manually set containerCrossSize as it's normally determined by children or explicit parent height + // For this test, assume children determine the row height. + parent.containerCrossSize = 50; + + const updated = calculateFlex(parent); + + expect(child1.x).toBe(0); + expect(child1.y).toBe(0); + expect(child2.x).toBe(100 + 10); // child1.width + gap + expect(child2.y).toBe(0); + expect(child3.x).toBe(0); // Wrapped + expect(child3.y).toBe(50 + 10); // parent.containerCrossSize + gap (acting as rowGap) + expect(updated).toBe(true); // Container cross size should have been updated + expect(parent.height).toBe(50 + 10 + 50); // row1_height + gap + row2_height + }); + }); + + describe('Direction RTL', () => { + it('should layout children in reverse order for row with direction RTL and flexStart', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + justifyContent: 'flexStart', // Still flexStart, but items are reversed first + direction: 'rtl', + children: [child1, child2], + }) as ElementNode; + + calculateFlex(parent); + // Children are [child2, child1] effectively for layout + expect(child2.x).toBe(0); + expect(child1.x).toBe(60); // child2.width + }); + }); + + describe('Flex Order', () => { + it('should respect flexOrder property', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + flexOrder: 2, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + flexOrder: 1, + }) as ElementNode; + const child3 = createTestElement({ + width: 70, + height: 50, + }) as ElementNode; // default order 0 + const parent = createTestElement({ + width: 300, + height: 100, + flexDirection: 'row', + children: [child1, child2, child3], + }) as ElementNode; + + calculateFlex(parent); + // Order: child3 (0), child2 (1), child1 (2) + expect(child3.x).toBe(0); + expect(child2.x).toBe(70); // child3.width + expect(child1.x).toBe(70 + 60); // child3.width + child2.width + }); + }); + + describe('Edge Cases and Filtering', () => { + it('should return false if no processable children', () => { + const parent = createTestElement({ + width: 100, + height: 100, + flexDirection: 'row', + children: [], + }) as ElementNode; + expect(calculateFlex(parent)).toBe(false); + + const textChild = createTestElement({ + nodeType: 'text', + text: 'hello', + }) as TextNode; + const parentWithTextChild = createTestElement({ + width: 100, + height: 100, + flexDirection: 'row', + children: [textChild], + }) as ElementNode; + expect(calculateFlex(parentWithTextChild)).toBe(false); + }); + + it('should filter out non-flex items (flexItem: false)', () => { + const child1 = createTestElement({ + width: 50, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 60, + height: 50, + flexItem: false, + }) as ElementNode; + const child3 = createTestElement({ + width: 70, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + children: [child1, child2, child3], + }) as ElementNode; + + calculateFlex(parent); + // child2 is ignored + expect(child1.x).toBe(0); + expect(child3.x).toBe(50); // child1.width + // child2 position should remain unchanged by flex + expect(child2.x).toBe(0); + expect(child2.y).toBe(0); + }); + + it('should return false if an ElementText child has text but no dimensions', () => { + // ElementText is an ElementNode with _type = NodeType.TextNode + const textChild = createTestElement({ + nodeType: 'textNode', + text: 'Hello', + width: 0, + height: 0, + }) as ElementNode; + const parent = createTestElement({ + width: 100, + height: 100, + children: [textChild], + }) as ElementNode; + expect(calculateFlex(parent)).toBe(false); + }); + }); + + describe('Container Size Update (flexBoundary)', () => { + it('should update parent width if flexBoundary is not fixed and children exceed width (row)', () => { + const child1 = createTestElement({ + width: 100, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 120, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, // Initial width, children total 220 + height: 100, + flexDirection: 'row', + // flexBoundary default is not 'fixed' + children: [child1, child2], + }) as ElementNode; + + const updated = calculateFlex(parent); + expect(updated).toBe(true); + expect(parent.width).toBe(220); + expect(parent.preFlexwidth).toBe(200); + }); + + it('should NOT update parent width if flexBoundary is "fixed"', () => { + const child1 = createTestElement({ + width: 100, + height: 50, + }) as ElementNode; + const child2 = createTestElement({ + width: 120, + height: 50, + }) as ElementNode; + const parent = createTestElement({ + width: 200, + height: 100, + flexDirection: 'row', + flexBoundary: 'fixed', + children: [child1, child2], + }) as ElementNode; + + const updated = calculateFlex(parent); + expect(updated).toBe(false); + expect(parent.width).toBe(200); // Unchanged + }); + + it('should update parent height if flexBoundary is not fixed and children exceed height (column)', () => { + const child1 = createTestElement({ + width: 50, + height: 100, + }) as ElementNode; + const child2 = createTestElement({ + width: 50, + height: 120, + }) as ElementNode; + const parent = createTestElement({ + width: 100, + height: 200, // Initial height, children total 220 + flexDirection: 'column', + children: [child1, child2], + }) as ElementNode; + + const updated = calculateFlex(parent); + expect(updated).toBe(true); + expect(parent.height).toBe(220); + expect(parent.preFlexheight).toBe(200); + }); + }); + + describe('Container Cross Size Update (flexWrap)', () => { + it('should update parent cross dimension (height for row) when flexWrap is active', () => { + const child1 = createTestElement({ + width: 100, + height: 30, + }) as ElementNode; + const child2 = createTestElement({ + width: 100, + height: 40, + }) as ElementNode; // Taller child + const child3 = createTestElement({ + width: 100, + height: 35, + }) as ElementNode; + const parent = createTestElement({ + width: 150, // Forces child2 and child3 to wrap + height: 50, // Initial height, might be overridden by content + flexDirection: 'row', + flexWrap: 'wrap', + gap: 5, + alignItems: 'flexStart', + children: [child1, child2, child3], + }) as ElementNode; + parent.containerCrossSize = 40; // Tallest item in first "virtual" row before actual layout + + const containerUpdated = calculateFlex(parent); + + expect(containerUpdated).toBe(true); + // Expected height: + // Row 1 height is determined by tallest item: child1 (30) -> so 30 (or parent.containerCrossSize if set) + // For this test, let's assume the first row's effective height is based on its content. + // If child1 is alone on first row, its height is 30. + // child2 and child3 wrap. child2 is 40, child3 is 35. Second row height is 40. + // Total height = height_row1 (30) + gap (5) + height_row2 (40) = 75 + // However, the logic uses parent.containerCrossSize for each row's height before summing. + // So, if parent.containerCrossSize is 40 (from child2, the tallest overall before this specific layout pass): + // Row 1 uses 40. Row 2 uses 40. + // Total height = 40 (row1) + 5 (gap) + 40 (row2) = 85 + expect(parent.height).toBe( + parent.containerCrossSize + 5 + parent.containerCrossSize, + ); // 40 + 5 + 40 = 85 + expect(parent.preFlexheight).toBe(50); // Original height + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 86a9b94..2f469fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,7 @@ "allowSyntheticDefaultImports": true, "resolveJsonModule": true, "composite": true, - "types": ["vite/client"] + "types": ["vite/client", "vitest/globals"] }, "include": ["src/*"], "exclude": ["node_modules", "dist"] diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..7382f40 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + }, +}); From b8354bd7f8a8d0ed1bfebebf90dbe895922c74b2 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 23 May 2025 07:41:14 -0400 Subject: [PATCH 29/78] Refactored flex (#49) * start adding some flex tests with vitest * refactor flex with some tests, change flex grow calculations * update flex and tests - one child dont apply flexgrow * :rocket: bump version v3.0.0-7 * fix up rounded in effects --- CHANGELOG.md | 10 ++ package.json | 2 +- src/elementNode.ts | 16 +- src/flex.ts | 282 ++++++++++++++++++------------- src/intrinsicTypes.ts | 1 + src/tests/flex-perf-results.json | 142 ++++++++-------- src/tests/flex.spec.ts | 60 +++---- 7 files changed, 289 insertions(+), 224 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fca22a..3d675ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,19 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-7](https://github.com/lightning-tv/core/compare/v3.0.0-6...v3.0.0-7) + +- Improve src+color and scale in dom renderer [`#48`](https://github.com/lightning-tv/core/pull/48) +- start adding some flex tests with vitest [`f03fd8a`](https://github.com/lightning-tv/core/commit/f03fd8ac05af3aa66bce318895a06ad2b2572c35) +- start adding some flex tests with vitest [`7e506ef`](https://github.com/lightning-tv/core/commit/7e506ef0d31538049c3e79a9328846edc06123c3) +- refactor flex with some tests, change flex grow calculations [`7bdc69b`](https://github.com/lightning-tv/core/commit/7bdc69b5e726406c89d43139c4d23db8387eeb34) + #### [v3.0.0-6](https://github.com/lightning-tv/core/compare/v3.0.0-5...v3.0.0-6) +> 8 May 2025 + - refactor shaders, remove border\* and add shader animations [`fcc0050`](https://github.com/lightning-tv/core/commit/fcc005046fd7e0112d506d01614612b1b349a1c5) +- :rocket: bump version v3.0.0-6 [`43d036d`](https://github.com/lightning-tv/core/commit/43d036da4390ac3a3a6095901d6f8c59a1008f77) - set lockStyles to true by default [`e22d467`](https://github.com/lightning-tv/core/commit/e22d467fd1f9497c3a3c8032ca5e7c56bd99a496) #### [v3.0.0-5](https://github.com/lightning-tv/core/compare/v3.0.0-4...v3.0.0-5) diff --git a/package.json b/package.json index 1fb1a49..aee67e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-6", + "version": "3.0.0-7", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { diff --git a/src/elementNode.ts b/src/elementNode.ts index c4f49e1..35a2df4 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -300,14 +300,12 @@ export class ElementNode extends Object { set effects(v: StyleEffects) { if (!SHADERS_ENABLED) return; - console.warn( - '`effects` is deprecated. Use shortcuts like `border`, `shadow`, `borderRadius` instead.', - ); this.lng.shader = this.lng.shader || {}; const target = this.lng.shader?.program ? this.lng.shader.props : this.lng.shader; - if (v.rounded) target.radius = v.rounded; + if (v.rounded) target.radius = v.rounded.radius; + if (v.borderRadius) target.radius = v.borderRadius; if (v.border) parseAndAssignShaderProps('border', v.border, target); if (v.shadow) parseAndAssignShaderProps('shadow', v.shadow, target); } @@ -876,7 +874,7 @@ export class ElementNode extends Object { if (!props.texture) { // Set width and height to parent less offset if (isNaN(props.width as number)) { - props.width = parentWidth - props.x; + props.width = node.flexGrow ? 0 : parentWidth - props.x; node._calcWidth = true; } @@ -932,10 +930,8 @@ export class ElementNode extends Object { const numChildren = node.children.length; for (let i = 0; i < numChildren; i++) { const c = node.children[i]; - isDev && assertTruthy(c, 'Child is undefined'); - if (isElementNode(c)) { - c.render(); - } + isDev && assertTruthy(isElementNode(c), 'Child is an elementNode'); + c!.render(); } } if (topNode) { @@ -1030,6 +1026,8 @@ if (isDev) { Object.defineProperties(ElementNode.prototype, { border: shaderAccessor('border'), shadow: shaderAccessor('shadow'), + rounded: shaderAccessor('rounded'), + // Alias for rounded borderRadius: shaderAccessor('rounded'), linearGradient: createRawShaderAccessor('linearGradient'), diff --git a/src/flex.ts b/src/flex.ts index ce925b4..c3cdf73 100644 --- a/src/flex.ts +++ b/src/flex.ts @@ -1,10 +1,34 @@ import { type ElementNode } from './elementNode.js'; import { isTextNode, isElementText } from './utils.js'; +interface ProcessedChild { + node: ElementNode; + mainSize: number; + marginStart: number; + marginEnd: number; + totalMainSizeOnAxis: number; + isGrowItem: boolean; + flexGrowValue: number; + flexOrder: number; + crossSize: number; + crossMarginStart: number; + crossMarginEnd: number; +} + export default function (node: ElementNode): boolean { - const children: ElementNode[] = []; + const direction = node.flexDirection || 'row'; + const isRow = direction === 'row'; + const dimension = isRow ? 'width' : 'height'; + const crossDimension = isRow ? 'height' : 'width'; + const marginOne = isRow ? 'marginLeft' : 'marginTop'; + const crossMarginOne = isRow ? 'marginTop' : 'marginLeft'; + const marginTwo = isRow ? 'marginRight' : 'marginBottom'; + const crossMarginTwo = isRow ? 'marginBottom' : 'marginRight'; + + const processedChildren: ProcessedChild[] = []; let hasOrder = false; - let growSize = 0; + let totalFlexGrow = 0; + for (let i = 0; i < node.children.length; i++) { const c = node.children[i]!; @@ -12,147 +36,179 @@ export default function (node: ElementNode): boolean { return false; } - // Filter empty text nodes which are place holders for and elements missing dimensions if (isTextNode(c) || c.flexItem === false) { continue; } - if (c.flexOrder !== undefined) { + const flexOrder = c.flexOrder; + if (flexOrder !== undefined) { hasOrder = true; } - if (c.flexGrow !== undefined) { - growSize += c.flexGrow; + const flexGrow = c.flexGrow; + const isGrowItem = flexGrow !== undefined && flexGrow >= 0; + if (isGrowItem) { + totalFlexGrow += flexGrow!; } - children.push(c as ElementNode); + const mainSize = c[dimension] || 0; + const currentMarginStart = c[marginOne] || 0; + const currentMarginEnd = c[marginTwo] || 0; + + processedChildren.push({ + node: c as ElementNode, + mainSize: mainSize, + marginStart: currentMarginStart, + marginEnd: currentMarginEnd, + totalMainSizeOnAxis: mainSize + currentMarginStart + currentMarginEnd, + isGrowItem: isGrowItem, + flexGrowValue: isGrowItem ? flexGrow! : 0, + flexOrder: flexOrder || 0, + crossSize: c[crossDimension] || 0, + crossMarginStart: c[crossMarginOne] || 0, + crossMarginEnd: c[crossMarginTwo] || 0, + }); } if (hasOrder) { - children.sort((a, b) => (a.flexOrder || 0) - (b.flexOrder || 0)); + processedChildren.sort((a, b) => a.flexOrder - b.flexOrder); } else if (node.direction === 'rtl') { - children.reverse(); + processedChildren.reverse(); + } + + const numProcessedChildren = processedChildren.length; + if (numProcessedChildren === 0) { + return false; // No layout changes if no processable children } - const numChildren = children.length; - const direction = node.flexDirection || 'row'; - const isRow = direction === 'row'; - const dimension = isRow ? 'width' : 'height'; - const crossDimension = isRow ? 'height' : 'width'; - const marginOne = isRow ? 'marginLeft' : 'marginTop'; - const crossMarginOne = isRow ? 'marginTop' : 'marginLeft'; - const marginTwo = isRow ? 'marginRight' : 'marginBottom'; - const crossMarginTwo = isRow ? 'marginBottom' : 'marginRight'; const prop = isRow ? 'x' : 'y'; const crossProp = isRow ? 'y' : 'x'; const containerSize = node[dimension] || 0; let containerCrossSize = node[crossDimension] || 0; const gap = node.gap || 0; const justify = node.justifyContent || 'flexStart'; - const align = node.alignItems || (node.flexWrap ? 'flexStart' : undefined); let containerUpdated = false; - // if there is only 1 child by default it inherits the parent size so we can skip - if (growSize && numChildren > 1) { - node.flexBoundary = node.flexBoundary || 'fixed'; // cant change size of flex container - const flexBasis = children.reduce( - (prev, c) => - prev + - (c.flexGrow != null && c.flexGrow >= 0 ? 0 : c[dimension] || 0) + - (c[marginOne] || 0) + - (c[marginTwo] || 0), - 0, - ); - const growFactor = - (containerSize - flexBasis - gap * (numChildren - 1)) / growSize; - - if (growFactor >= 0) { - for (let i = 0; i < numChildren; i++) { - const c = children[i]!; - if (c.flexGrow != null && c.flexGrow >= 0) { - c[dimension] = c.flexGrow * growFactor; + if (totalFlexGrow > 0 && numProcessedChildren > 1) { + // When flex-grow is used, the container's size is considered fixed for this calculation pass, + // unless flexBoundary is explicitly set to allow container resizing based on content. + node.flexBoundary = node.flexBoundary || 'fixed'; + + // Determine the sum of the flex base sizes of all items. + // The flex base size is the item's mainSize before flex-grow is applied. + let sumOfFlexBaseSizesWithMargins = 0; + for (const pc of processedChildren) { + sumOfFlexBaseSizesWithMargins += + pc.mainSize + pc.marginStart + pc.marginEnd; + } + + // Calculate the total space occupied by gaps between items. + const totalGapSpace = + numProcessedChildren > 0 ? gap * (numProcessedChildren - 1) : 0; + + // Calculate the available space for flex items to grow into. + const availableSpace = + containerSize - sumOfFlexBaseSizesWithMargins - totalGapSpace; + + if (availableSpace > 0) { + for (const pc of processedChildren) { + if (pc.isGrowItem && pc.flexGrowValue > 0) { + const shareOfSpace = + (pc.flexGrowValue / totalFlexGrow) * availableSpace; + const newMainSize = pc.mainSize + shareOfSpace; + pc.node[dimension] = newMainSize; + pc.mainSize = newMainSize; + pc.totalMainSizeOnAxis = newMainSize + pc.marginStart + pc.marginEnd; } } } else { - console.warn('Negative growFactor, flexGrow not applied'); + // No positive space available for items to grow, or items overflow. + // flex-grow has no effect in this case. + console.warn( + 'No available space for flex-grow items to expand, or items overflow.', + ); } } - let itemSize = 0; + let totalItemSize = 0; if ( justify === 'center' || justify === 'spaceBetween' || justify === 'spaceEvenly' ) { - itemSize = children.reduce( - (prev, c) => - prev + (c[dimension] || 0) + (c[marginOne] || 0) + (c[marginTwo] || 0), - 0, - ); + for (const pc of processedChildren) { + totalItemSize += pc.totalMainSizeOnAxis; + } } - const crossAlignChild = containerCrossSize - ? (c: ElementNode, crossStart: number = 0) => { - const alignSelf = c.alignSelf || align; + const align = node.alignItems || (node.flexWrap ? 'flexStart' : undefined); + const doCrossAlign = containerCrossSize + ? (pc: ProcessedChild, crossCurrentPos: number = 0) => { + const alignSelf = pc.node.alignSelf || align; if (!alignSelf) { return; - } else if (alignSelf === 'flexStart') { - c[crossProp] = crossStart + (c[crossMarginOne] || 0); + } + if (alignSelf === 'flexStart') { + pc.node[crossProp] = crossCurrentPos + pc.crossMarginStart; } else if (alignSelf === 'center') { - c[crossProp] = - crossStart + - (containerCrossSize - (c[crossDimension] || 0)) / 2 + - (c[crossMarginOne] || 0); + pc.node[crossProp] = + crossCurrentPos + + (containerCrossSize - pc.crossSize) / 2 + + pc.crossMarginStart; } else if (alignSelf === 'flexEnd') { - c[crossProp] = - crossStart + + pc.node[crossProp] = + crossCurrentPos + containerCrossSize - - (c[crossDimension] || 0) - - (c[crossMarginTwo] || 0); + pc.crossSize - + pc.crossMarginEnd; } } - : (c: ElementNode) => c; + : (_pc: ProcessedChild, _crossCurrentPos: number = 0) => { + /* no-op */ + }; if (isRow && node._calcHeight && !node.flexCrossBoundary) { - // Assuming all the children have the same height - const newHeight = children[0]?.height || node.height; + const firstChildNode = processedChildren[0]?.node; + const newHeight = firstChildNode?.height || node.height; if (newHeight !== node.height) { containerUpdated = true; node.height = containerCrossSize = newHeight; } } + let currentPos = node.padding || 0; if (justify === 'flexStart') { - let start = node.padding || 0; if (node.flexWrap === 'wrap') { - let crossStart = 0; + let crossCurrentPos = 0; const crossGap = isRow ? (node.columnGap ?? gap) : (node.rowGap ?? gap); - for (let i = 0; i < children.length; i++) { - const c = children[i]!; - const nextSize = - (c[dimension] || 0) + (c[marginOne] || 0) + (c[marginTwo] || 0); - if (start + nextSize > containerSize) { - start = node.padding || 0; - crossStart += containerCrossSize + crossGap; + for (const pc of processedChildren) { + if ( + currentPos + pc.totalMainSizeOnAxis > containerSize && + currentPos > (node.padding || 0) + ) { + currentPos = node.padding || 0; + crossCurrentPos += containerCrossSize + crossGap; } - c[prop] = start + (c[marginOne] || 0); - start += nextSize + gap; - crossAlignChild(c, crossStart); + pc.node[prop] = currentPos + pc.marginStart; + currentPos += pc.totalMainSizeOnAxis + gap; + doCrossAlign(pc, crossCurrentPos); + } + const finalCrossSize = crossCurrentPos + containerCrossSize; + if (node[crossDimension] !== finalCrossSize) { + node[`preFlex${crossDimension}`] = node[crossDimension]; + node[crossDimension] = finalCrossSize; + containerUpdated = true; } - node[`preFlex${crossDimension}`] = containerCrossSize; - node[crossDimension] = crossStart + containerCrossSize; } else { - for (let i = 0; i < children.length; i++) { - const c = children[i]!; - c[prop] = start + (c[marginOne] || 0); - start += - (c[dimension] || 0) + gap + (c[marginOne] || 0) + (c[marginTwo] || 0); - crossAlignChild(c); + for (const pc of processedChildren) { + pc.node[prop] = currentPos + pc.marginStart; + currentPos += pc.totalMainSizeOnAxis + gap; + doCrossAlign(pc); } } // Update container size if (node.flexBoundary !== 'fixed' && node.flexWrap !== 'wrap') { - const calculatedSize = start - gap + (node.padding || 0); + const calculatedSize = currentPos - gap + (node.padding || 0); if (calculatedSize !== containerSize) { // store the original size for Row & Column node[`preFlex${dimension}`] = containerSize; @@ -161,45 +217,45 @@ export default function (node: ElementNode): boolean { } } } else if (justify === 'flexEnd') { - let start = containerSize; - for (let i = numChildren - 1; i >= 0; i--) { - const c = children[i]!; - c[prop] = start - (c[dimension] || 0) - (c[marginTwo] || 0); - start -= - (c[dimension] || 0) + gap + (c[marginOne] || 0) + (c[marginTwo] || 0); - crossAlignChild(c); + currentPos = containerSize - (node.padding || 0); + for (let i = numProcessedChildren - 1; i >= 0; i--) { + const pc = processedChildren[i]!; + pc.node[prop] = currentPos - pc.mainSize - pc.marginEnd; + currentPos -= pc.totalMainSizeOnAxis + gap; + doCrossAlign(pc); } } else if (justify === 'center') { - let start = (containerSize - (itemSize + gap * (numChildren - 1))) / 2; - for (let i = 0; i < children.length; i++) { - const c = children[i]!; - c[prop] = start + (c[marginOne] || 0); - start += - (c[dimension] || 0) + gap + (c[marginOne] || 0) + (c[marginTwo] || 0); - crossAlignChild(c); + currentPos = + (containerSize - (totalItemSize + gap * (numProcessedChildren - 1))) / 2 + + (node.padding || 0); + for (const pc of processedChildren) { + pc.node[prop] = currentPos + pc.marginStart; + currentPos += pc.totalMainSizeOnAxis + gap; + doCrossAlign(pc); } } else if (justify === 'spaceBetween') { - const toPad = (containerSize - itemSize) / (numChildren - 1); - let start = 0; - for (let i = 0; i < children.length; i++) { - const c = children[i]!; - c[prop] = start + (c[marginOne] || 0); - start += - (c[dimension] || 0) + toPad + (c[marginOne] || 0) + (c[marginTwo] || 0); - crossAlignChild(c); + const spaceBetween = + numProcessedChildren > 1 + ? (containerSize - totalItemSize - (node.padding || 0) * 2) / + (numProcessedChildren - 1) + : 0; + currentPos = node.padding || 0; + for (const pc of processedChildren) { + pc.node[prop] = currentPos + pc.marginStart; + currentPos += pc.totalMainSizeOnAxis + spaceBetween; + doCrossAlign(pc); } } else if (justify === 'spaceEvenly') { - const toPad = (containerSize - itemSize) / (numChildren + 1); - let start = toPad; - for (let i = 0; i < children.length; i++) { - const c = children[i]!; - c[prop] = start + (c[marginOne] || 0); - start += - (c[dimension] || 0) + toPad + (c[marginOne] || 0) + (c[marginTwo] || 0); - crossAlignChild(c); + const spaceAround = + (containerSize - totalItemSize - (node.padding || 0) * 2) / + (numProcessedChildren + 1); + currentPos = spaceAround + (node.padding || 0); + for (const pc of processedChildren) { + pc.node[prop] = currentPos + pc.marginStart; + currentPos += pc.totalMainSizeOnAxis + spaceAround; + doCrossAlign(pc); } } - // Container was not updated return containerUpdated; } diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index 1fc9f1f..7b7d797 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -33,6 +33,7 @@ export interface Effects { holePunch?: HolePunchProps; shadow?: ShadowProps; rounded?: { radius: BorderRadius }; + borderRadius?: { radius: BorderRadius }; border?: BorderStyleObject; } diff --git a/src/tests/flex-perf-results.json b/src/tests/flex-perf-results.json index 0483675..85534cc 100644 --- a/src/tests/flex-perf-results.json +++ b/src/tests/flex-perf-results.json @@ -1,494 +1,494 @@ { - "timestamp": "2025-05-11T04:01:59.305Z", + "timestamp": "2025-05-22T19:54:55.779Z", "results": [ { "id": "Row, FlexStart, NoGrow-3", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 3, - "durationMs": 0.08027800000000222, + "durationMs": 0.08379166666666531, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-5", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 5, - "durationMs": 0.08926400000001422, + "durationMs": 0.08915300000001025, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-10", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 10, - "durationMs": 0.012763999999985268, + "durationMs": 0.013138666666653384, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-15", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 15, - "durationMs": 0.0073886666666377705, + "durationMs": 0.008166333333349485, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-20", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 20, - "durationMs": 0.012499999999988631, + "durationMs": 0.011069333333352915, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-50", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 50, - "durationMs": 0.020833333333352282, + "durationMs": 0.026860999999996693, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-100", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 100, - "durationMs": 0.06497200000001158, + "durationMs": 0.04136166666667881, "containerUpdated": false }, { "id": "Column, Center, WithGrow-3", "scenarioName": "Column, Center, WithGrow", "numChildren": 3, - "durationMs": 0.012291666666669698, + "durationMs": 0.04645833333332424, "containerUpdated": false }, { "id": "Column, Center, WithGrow-5", "scenarioName": "Column, Center, WithGrow", "numChildren": 5, - "durationMs": 0.003361000000002908, + "durationMs": 0.005611333333339037, "containerUpdated": false }, { "id": "Column, Center, WithGrow-10", "scenarioName": "Column, Center, WithGrow", "numChildren": 10, - "durationMs": 0.006138999999999821, + "durationMs": 0.007097333333319966, "containerUpdated": false }, { "id": "Column, Center, WithGrow-15", "scenarioName": "Column, Center, WithGrow", "numChildren": 15, - "durationMs": 0.007652666666672303, + "durationMs": 0.02734733333333376, "containerUpdated": false }, { "id": "Column, Center, WithGrow-20", "scenarioName": "Column, Center, WithGrow", "numChildren": 20, - "durationMs": 0.013944333333351247, + "durationMs": 0.016305666666672398, "containerUpdated": false }, { "id": "Column, Center, WithGrow-50", "scenarioName": "Column, Center, WithGrow", "numChildren": 50, - "durationMs": 0.024777333333323288, + "durationMs": 0.028180333333333845, "containerUpdated": false }, { "id": "Column, Center, WithGrow-100", "scenarioName": "Column, Center, WithGrow", "numChildren": 100, - "durationMs": 0.051693999999997686, + "durationMs": 0.07701433333335217, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-3", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 3, - "durationMs": 0.004541666666663484, + "durationMs": 0.011569333333322144, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-5", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 5, - "durationMs": 0.005874999999984236, + "durationMs": 0.006166333333339935, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-10", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 10, - "durationMs": 0.005541333333326293, + "durationMs": 0.006277666666676396, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-15", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 15, - "durationMs": 0.019763999999990272, + "durationMs": 0.008903000000013131, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-20", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 20, - "durationMs": 0.012430333333346274, + "durationMs": 0.011333333333330605, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-50", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 50, - "durationMs": 0.05880533333333915, + "durationMs": 0.025222000000004147, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-100", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 100, - "durationMs": 0.06331966666664357, + "durationMs": 0.04809733333333573, "containerUpdated": false }, { "id": "Row, Wrap, FlexStart-3", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 3, - "durationMs": 0.009208666666665977, + "durationMs": 0.00681933333332078, "containerUpdated": false }, { "id": "Row, Wrap, FlexStart-5", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 5, - "durationMs": 0.009875000000003334, + "durationMs": 0.010555666666656785, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-10", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 10, - "durationMs": 0.005416666666690162, + "durationMs": 0.007750333333338706, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-15", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 15, - "durationMs": 0.007500000000031075, + "durationMs": 0.007888999999996335, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-20", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 20, - "durationMs": 0.010069333333357614, + "durationMs": 0.01034700000000536, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-50", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 50, - "durationMs": 0.022972333333333, + "durationMs": 0.02636133333332206, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-100", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 100, - "durationMs": 0.04563866666664277, + "durationMs": 0.04866666666669062, "containerUpdated": true }, { "id": "Row, RTL, FlexEnd-3", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 3, - "durationMs": 0.003569333333321841, + "durationMs": 0.005472333333329971, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-5", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 5, - "durationMs": 0.0024169999999988554, + "durationMs": 0.002847333333344674, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-10", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 10, - "durationMs": 0.004069333333328966, + "durationMs": 0.004333333333325602, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-15", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 15, - "durationMs": 0.006166666666672427, + "durationMs": 0.006250000000022737, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-20", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 20, - "durationMs": 0.010221999999979895, + "durationMs": 0.0077086666666446035, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-50", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 50, - "durationMs": 0.02020833333331969, + "durationMs": 0.019180666666689678, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-100", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 100, - "durationMs": 0.03984733333332239, + "durationMs": 0.04169433333333927, "containerUpdated": false }, { "id": "Row, WithOrder-3", "scenarioName": "Row, WithOrder", "numChildren": 3, - "durationMs": 0.017374666666664023, + "durationMs": 0.016944666666669644, "containerUpdated": false }, { "id": "Row, WithOrder-5", "scenarioName": "Row, WithOrder", "numChildren": 5, - "durationMs": 0.0036526666666721517, + "durationMs": 0.0035553333333382398, "containerUpdated": false }, { "id": "Row, WithOrder-10", "scenarioName": "Row, WithOrder", "numChildren": 10, - "durationMs": 0.0070140000000075515, + "durationMs": 0.005069333333343214, "containerUpdated": false }, { "id": "Row, WithOrder-15", "scenarioName": "Row, WithOrder", "numChildren": 15, - "durationMs": 0.007875333333345225, + "durationMs": 0.007180666666670277, "containerUpdated": false }, { "id": "Row, WithOrder-20", "scenarioName": "Row, WithOrder", "numChildren": 20, - "durationMs": 0.011902999999980087, + "durationMs": 0.009222000000003542, "containerUpdated": false }, { "id": "Row, WithOrder-50", "scenarioName": "Row, WithOrder", "numChildren": 50, - "durationMs": 0.02779166666666318, + "durationMs": 0.021930666666662546, "containerUpdated": false }, { "id": "Row, WithOrder-100", "scenarioName": "Row, WithOrder", "numChildren": 100, - "durationMs": 0.05297199999999217, + "durationMs": 0.05390299999999115, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-3", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 3, - "durationMs": 0.008083333333312718, + "durationMs": 0.0069443333333651935, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-5", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 5, - "durationMs": 0.0025696666666590318, + "durationMs": 0.002694333333333058, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-10", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 10, - "durationMs": 0.004666666666651054, + "durationMs": 0.004527999999974479, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-15", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 15, - "durationMs": 0.00658333333332924, + "durationMs": 0.006305666666662546, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-20", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 20, - "durationMs": 0.00836066666666587, + "durationMs": 0.008514000000009977, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-50", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 50, - "durationMs": 0.019486333333342525, + "durationMs": 0.019458333333337425, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-100", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 100, - "durationMs": 0.04315299999999903, + "durationMs": 0.0455696666666654, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-3", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 3, - "durationMs": 0.004999666666681908, + "durationMs": 0.0049303333333530945, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-5", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 5, - "durationMs": 0.0024859999999762294, + "durationMs": 0.002652999999971447, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-10", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 10, - "durationMs": 0.0045419999999959755, + "durationMs": 0.004347333333328152, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-15", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 15, - "durationMs": 0.006499666666665386, + "durationMs": 0.008861333333356924, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-20", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 20, - "durationMs": 0.008138666666657931, + "durationMs": 0.008624666666662506, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-50", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 50, - "durationMs": 0.23718033333333702, + "durationMs": 0.019944333333323055, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-100", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 100, - "durationMs": 0.04145866666668022, + "durationMs": 0.040569333333337454, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-3", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 3, - "durationMs": 0.005291333333351152, + "durationMs": 0.0049026666666804886, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-5", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 5, - "durationMs": 0.002888999999981934, + "durationMs": 0.002847666666658218, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-10", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 10, - "durationMs": 0.004986333333325395, + "durationMs": 0.005431000000006255, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-15", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 15, - "durationMs": 0.00709666666667393, + "durationMs": 0.007361333333316604, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-20", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 20, - "durationMs": 0.009277666666681247, + "durationMs": 0.009624999999971351, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-50", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 50, - "durationMs": 0.02187466666665235, + "durationMs": 0.023430666666646022, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-100", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 100, - "durationMs": 0.10990299999999327, + "durationMs": 0.04922200000000506, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-3", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 3, - "durationMs": 0.016055666666659363, + "durationMs": 0.009972333333318298, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-5", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 5, - "durationMs": 0.0033476666666463948, + "durationMs": 0.0030553333333311152, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-10", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 10, - "durationMs": 0.02041666666667652, + "durationMs": 0.008166666666663028, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-15", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 15, - "durationMs": 0.01143066666664557, + "durationMs": 0.009555666666661486, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-20", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 20, - "durationMs": 0.009430666666654966, + "durationMs": 0.009902666666656993, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-50", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 50, - "durationMs": 0.021652999999995853, + "durationMs": 0.024944666666651, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-100", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 100, - "durationMs": 0.06109733333331254, + "durationMs": 0.053347333333325274, "containerUpdated": false } ] diff --git a/src/tests/flex.spec.ts b/src/tests/flex.spec.ts index 02731da..a0f4367 100644 --- a/src/tests/flex.spec.ts +++ b/src/tests/flex.spec.ts @@ -326,11 +326,12 @@ describe('Flexbox Layout (calculateFlex)', () => { expect(child1.width).toBe(150); // Unchanged expect(child2.width).toBe(150); // Unchanged expect(console.warn).toHaveBeenCalledWith( - 'Negative growFactor, flexGrow not applied', + 'No available space for flex-grow items to expand, or items overflow.', ); }); it('should not apply flexGrow if only one child', () => { + // If you want this to be 300, dont specify a width for one child const child1 = createTestElement({ width: 50, height: 50, @@ -344,7 +345,7 @@ describe('Flexbox Layout (calculateFlex)', () => { }) as ElementNode; calculateFlex(parent); - expect(child1.width).toBe(50); // Unchanged + expect(child1.width).toBe(50); }); }); @@ -477,8 +478,8 @@ describe('Flexbox Layout (calculateFlex)', () => { height: 30, }) as ElementNode; const parent = createTestElement({ - width: 150, // child1 and child2 fit, child3 wraps - height: 200, + width: 150, // child1 (80) fits. child2 (80) wraps. child3 (80) wraps. + height: 30, // Initial height, will be overridden by content if flexCrossBoundary is not 'fixed' flexDirection: 'row', flexWrap: 'wrap', columnGap: 10, // Gap between columns (horizontal) @@ -486,18 +487,22 @@ describe('Flexbox Layout (calculateFlex)', () => { alignItems: 'flexStart', // To make cross axis predictable children: [child1, child2, child3], }) as ElementNode; - parent.containerCrossSize = 30; // Simulate a known cross size for wrapping calculations calculateFlex(parent); expect(child1.x).toBe(0); expect(child1.y).toBe(0); - expect(child2.x).toBe(80 + 10); // child1.width + columnGap - expect(child2.y).toBe(0); // Still on the first row + // child2 wraps to the next line + expect(child2.x).toBe(0); + expect(child2.y).toBe(30 + 10); // line1_height (30) + columnGap (10) - expect(child3.x).toBe(0); // Wrapped to new line - expect(child3.y).toBe(30 + 5); // parent.containerCrossSize (height of first row items) + rowGap + // child3 wraps to the line after child2 + expect(child3.x).toBe(0); + expect(child3.y).toBe(30 + 10 + 30 + 10); // line1_height + columnGap + line2_height + columnGap + + // Final parent height: line1(30) + columnGap(10) + line2(30) + columnGap(10) + line3(30) = 110 + expect(parent.height).toBe(110); }); }); @@ -580,17 +585,14 @@ describe('Flexbox Layout (calculateFlex)', () => { height: 50, }) as ElementNode; const parent = createTestElement({ - width: 250, // Can fit two children per row - height: 200, + width: 250, // Can fit two children (100 + 10 + 100 = 210) + height: 50, // This will be used as the containerCrossSize for line calculations flexDirection: 'row', flexWrap: 'wrap', gap: 10, alignItems: 'flexStart', // for predictable cross-axis children: [child1, child2, child3], }) as ElementNode; - // Manually set containerCrossSize as it's normally determined by children or explicit parent height - // For this test, assume children determine the row height. - parent.containerCrossSize = 50; const updated = calculateFlex(parent); @@ -600,7 +602,8 @@ describe('Flexbox Layout (calculateFlex)', () => { expect(child2.y).toBe(0); expect(child3.x).toBe(0); // Wrapped expect(child3.y).toBe(50 + 10); // parent.containerCrossSize + gap (acting as rowGap) - expect(updated).toBe(true); // Container cross size should have been updated + expect(updated).toBe(true); // Container cross size (height) should have been updated + // Expected height: line1_height (50) + gap (10) + line2_height (50) = 110 expect(parent.height).toBe(50 + 10 + 50); // row1_height + gap + row2_height }); }); @@ -817,32 +820,29 @@ describe('Flexbox Layout (calculateFlex)', () => { }) as ElementNode; const parent = createTestElement({ width: 150, // Forces child2 and child3 to wrap - height: 50, // Initial height, might be overridden by content + height: 40, // Set initial height to what's intended as containerCrossSize for lines flexDirection: 'row', flexWrap: 'wrap', gap: 5, alignItems: 'flexStart', children: [child1, child2, child3], }) as ElementNode; - parent.containerCrossSize = 40; // Tallest item in first "virtual" row before actual layout const containerUpdated = calculateFlex(parent); expect(containerUpdated).toBe(true); // Expected height: - // Row 1 height is determined by tallest item: child1 (30) -> so 30 (or parent.containerCrossSize if set) - // For this test, let's assume the first row's effective height is based on its content. - // If child1 is alone on first row, its height is 30. - // child2 and child3 wrap. child2 is 40, child3 is 35. Second row height is 40. - // Total height = height_row1 (30) + gap (5) + height_row2 (40) = 75 - // However, the logic uses parent.containerCrossSize for each row's height before summing. - // So, if parent.containerCrossSize is 40 (from child2, the tallest overall before this specific layout pass): - // Row 1 uses 40. Row 2 uses 40. - // Total height = 40 (row1) + 5 (gap) + 40 (row2) = 85 - expect(parent.height).toBe( - parent.containerCrossSize + 5 + parent.containerCrossSize, - ); // 40 + 5 + 40 = 85 - expect(parent.preFlexheight).toBe(50); // Original height + // Each child (100w) wraps onto a new line in a 150w container. 3 lines. + // Line height for calculation is initial parent.height = 40. + // Gap = 5. + // Line 1: child1 (height 40 for calc) + // Gap: 5 + // Line 2: child2 (height 40 for calc) + // Gap: 5 + // Line 3: child3 (height 40 for calc) + // Total height = 40 + 5 + 40 + 5 + 40 = 130. + expect(parent.height).toBe(130); + expect(parent.preFlexheight).toBe(40); // Original height }); }); }); From 6b02da48dd8c508188253c06371d5a3f096de547 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 23 May 2025 18:37:52 -0400 Subject: [PATCH 30/78] fix only render element nodes --- src/elementNode.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 35a2df4..e3d717a 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -930,8 +930,10 @@ export class ElementNode extends Object { const numChildren = node.children.length; for (let i = 0; i < numChildren; i++) { const c = node.children[i]; - isDev && assertTruthy(isElementNode(c), 'Child is an elementNode'); - c!.render(); + isDev && assertTruthy(c, 'Child is undefined'); + if (isElementNode(c)) { + c.render(); + } } } if (topNode) { From 1559bba1ab7adcf92151d5a4ed4b06774a6537bc Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 23 May 2025 18:44:04 -0400 Subject: [PATCH 31/78] add comment for later --- src/elementNode.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/elementNode.ts b/src/elementNode.ts index e3d717a..cc3ab27 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -931,6 +931,7 @@ export class ElementNode extends Object { for (let i = 0; i < numChildren; i++) { const c = node.children[i]; isDev && assertTruthy(c, 'Child is undefined'); + // Text elements sneak in from Solid creating tracked nodes if (isElementNode(c)) { c.render(); } From 4b8c5fca4cd2869900fcc975275a9de4209ed75d Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 23 May 2025 21:14:35 -0400 Subject: [PATCH 32/78] fix up double creation of shaders for linearGradient --- src/elementNode.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index cc3ab27..05ad7c6 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -858,7 +858,7 @@ export class ElementNode extends Object { } // Can you put effects on Text nodes? Need to confirm... - if (SHADERS_ENABLED && props.shader) { + if (SHADERS_ENABLED && props.shader && !props.shader.program) { props.shader = convertToShader(node, props.shader); } @@ -894,7 +894,7 @@ export class ElementNode extends Object { } } - if (SHADERS_ENABLED && props.shader) { + if (SHADERS_ENABLED && props.shader && !props.shader.program) { props.shader = convertToShader(node, props.shader); } @@ -984,10 +984,10 @@ function shaderAccessor | number>( ) { return { set(this: ElementNode, value: T) { - this.lng.shader = this.lng.shader || {}; - let target = this.lng.shader; + let target = this.lng.shader || {}; + let animationSettings: AnimationSettings | undefined; - if (this.lng.shader.program) { + if (this.lng.shader?.program) { target = this.lng.shader.props; const transitionKey = key === 'rounded' ? 'borderRadius' : key; if ( @@ -1010,6 +1010,14 @@ function shaderAccessor | number>( parseAndAssignShaderProps(key, value, target); } + if (this.rendered) { + if (!this.lng.shader) { + this.lng.shader = convertToShader(this, target); + } + } else { + this.lng.shader = target; + } + if (animationSettings) { this.animate({ shaderProps: target }, animationSettings).start(); } From 288689fb28bcf80bd3c3f0aa3cf11b23bbfa050e Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 23 May 2025 21:15:37 -0400 Subject: [PATCH 33/78] :rocket: bump version v3.0.0-8 --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d675ee..9b22198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,17 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-8](https://github.com/lightning-tv/core/compare/v3.0.0-7...v3.0.0-8) + +- Refactored flex [`#49`](https://github.com/lightning-tv/core/pull/49) +- fix up double creation of shaders for linearGradient [`4b8c5fc`](https://github.com/lightning-tv/core/commit/4b8c5fca4cd2869900fcc975275a9de4209ed75d) +- fix only render element nodes [`6b02da4`](https://github.com/lightning-tv/core/commit/6b02da48dd8c508188253c06371d5a3f096de547) +- add comment for later [`1559bba`](https://github.com/lightning-tv/core/commit/1559bba1ab7adcf92151d5a4ed4b06774a6537bc) + #### [v3.0.0-7](https://github.com/lightning-tv/core/compare/v3.0.0-6...v3.0.0-7) +> 22 May 2025 + - Improve src+color and scale in dom renderer [`#48`](https://github.com/lightning-tv/core/pull/48) - start adding some flex tests with vitest [`f03fd8a`](https://github.com/lightning-tv/core/commit/f03fd8ac05af3aa66bce318895a06ad2b2572c35) - start adding some flex tests with vitest [`7e506ef`](https://github.com/lightning-tv/core/commit/7e506ef0d31538049c3e79a9328846edc06123c3) diff --git a/package.json b/package.json index aee67e4..382d005 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-7", + "version": "3.0.0-8", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From acd4dba380822d70f70245309e73c2d49068e238 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 28 May 2025 15:12:30 -0400 Subject: [PATCH 34/78] update capture key to support mappedEvent and pass as last arg --- src/focusManager.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/focusManager.ts b/src/focusManager.ts index 6004fb8..689ae96 100644 --- a/src/focusManager.ts +++ b/src/focusManager.ts @@ -151,11 +151,13 @@ const propagateKeyPress = ( for (let i = numItems - 1; i >= 0; i--) { const elm = focusPath[i]!; - const captureKey = `capture${e.key}`; + const captureKey = `capture${mappedEvent || e.key}`; const captureHandler = elm[captureKey] || elm.captureKey; if (isFunction(captureHandler)) { handlerAvailable = elm; - if (captureHandler.call(elm, e, elm, finalFocusElm) === true) { + if ( + captureHandler.call(elm, e, elm, finalFocusElm, mappedEvent) === true + ) { return true; } } From 674b2f83dbbbadc778c2bf9c551044ea8af1cca6 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 28 May 2025 15:13:51 -0400 Subject: [PATCH 35/78] :rocket: bump version v3.0.0-9 --- CHANGELOG.md | 8 +++++++- package.json | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b22198..d19c7db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,18 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-9](https://github.com/lightning-tv/core/compare/v3.0.0-8...v3.0.0-9) + +- update capture key to support mappedEvent and pass as last arg [`acd4dba`](https://github.com/lightning-tv/core/commit/acd4dba380822d70f70245309e73c2d49068e238) + #### [v3.0.0-8](https://github.com/lightning-tv/core/compare/v3.0.0-7...v3.0.0-8) +> 23 May 2025 + - Refactored flex [`#49`](https://github.com/lightning-tv/core/pull/49) - fix up double creation of shaders for linearGradient [`4b8c5fc`](https://github.com/lightning-tv/core/commit/4b8c5fca4cd2869900fcc975275a9de4209ed75d) +- :rocket: bump version v3.0.0-8 [`288689f`](https://github.com/lightning-tv/core/commit/288689fb28bcf80bd3c3f0aa3cf11b23bbfa050e) - fix only render element nodes [`6b02da4`](https://github.com/lightning-tv/core/commit/6b02da48dd8c508188253c06371d5a3f096de547) -- add comment for later [`1559bba`](https://github.com/lightning-tv/core/commit/1559bba1ab7adcf92151d5a4ed4b06774a6537bc) #### [v3.0.0-7](https://github.com/lightning-tv/core/compare/v3.0.0-6...v3.0.0-7) diff --git a/package.json b/package.json index 382d005..e29a60b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-8", + "version": "3.0.0-9", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 1ee683b6a49313db79c1a4f76e02e488d5e1e683 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 28 May 2025 16:13:44 -0400 Subject: [PATCH 36/78] fix creating shaders through effects --- src/elementNode.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 05ad7c6..60a0300 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -300,14 +300,22 @@ export class ElementNode extends Object { set effects(v: StyleEffects) { if (!SHADERS_ENABLED) return; - this.lng.shader = this.lng.shader || {}; - const target = this.lng.shader?.program - ? this.lng.shader.props - : this.lng.shader; + let target = this.lng.shader || {}; + if (this.lng.shader?.program) { + target = this.lng.shader.props; + } if (v.rounded) target.radius = v.rounded.radius; if (v.borderRadius) target.radius = v.borderRadius; if (v.border) parseAndAssignShaderProps('border', v.border, target); if (v.shadow) parseAndAssignShaderProps('shadow', v.shadow, target); + + if (this.rendered) { + if (!this.lng.shader) { + this.lng.shader = convertToShader(this, target); + } + } else { + this.lng.shader = target; + } } set id(id: string) { From bdb81ceac67318033150ff367dd0f77f974d101a Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 28 May 2025 16:14:09 -0400 Subject: [PATCH 37/78] add mappedEvent for capture key --- src/focusKeyTypes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/focusKeyTypes.ts b/src/focusKeyTypes.ts index 27141d5..7e960c6 100644 --- a/src/focusKeyTypes.ts +++ b/src/focusKeyTypes.ts @@ -72,6 +72,7 @@ export type KeyHandler = ( e: KeyboardEvent, target: ElementNode, handlerElm: ElementNode, + mappedEvent?: string, ) => KeyHandlerReturn; export type KeyHoldOptions = { From 10e08df7f3ccd1674f90a458aa528d73757ea5e4 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 28 May 2025 21:28:12 -0400 Subject: [PATCH 38/78] optimize keypress, rename onCaptureKey add onCaptureRelease --- src/focusKeyTypes.ts | 5 +++-- src/focusManager.ts | 45 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/focusKeyTypes.ts b/src/focusKeyTypes.ts index 7e960c6..6ecf8df 100644 --- a/src/focusKeyTypes.ts +++ b/src/focusKeyTypes.ts @@ -58,9 +58,10 @@ export type EventHandlers = { } & { [K in keyof Map as `on${Capitalize}Release`]?: KeyHandler; } & { - [K in keyof Map as `capture${Capitalize}`]?: KeyHandler; + [K in keyof Map as `onCapture${Capitalize}`]?: KeyHandler; } & { - captureKey?: KeyHandler; + onCaptureKey?: KeyHandler; + onCaptureKeyRelease?: KeyHandler; }; export interface KeyHoldMap extends DefaultKeyHoldMap {} diff --git a/src/focusManager.ts b/src/focusManager.ts index 689ae96..add97d9 100644 --- a/src/focusManager.ts +++ b/src/focusManager.ts @@ -148,11 +148,13 @@ const propagateKeyPress = ( let finalFocusElm: ElementNode | undefined; let handlerAvailable: ElementNode | undefined; const numItems = focusPath.length; + const captureEvent = + `onCapture${mappedEvent || e.key}` + isUp ? 'Release' : ''; + const captureKey = isUp ? 'onCaptureKeyRelease' : 'onCaptureKey'; for (let i = numItems - 1; i >= 0; i--) { const elm = focusPath[i]!; - const captureKey = `capture${mappedEvent || e.key}`; - const captureHandler = elm[captureKey] || elm.captureKey; + const captureHandler = elm[captureEvent] || elm[captureKey]; if (isFunction(captureHandler)) { handlerAvailable = elm; if ( @@ -163,15 +165,37 @@ const propagateKeyPress = ( } } + let eventHandlerKey: string | undefined; + let releaseEventHandlerKey: string | undefined; + let fallbackHandlerKey: 'onKeyHold' | 'onKeyPress' | undefined; + + if (mappedEvent) { + eventHandlerKey = `on${mappedEvent}`; + releaseEventHandlerKey = `on${mappedEvent}Release`; + } + + if (!isUp) { + fallbackHandlerKey = isHold ? 'onKeyHold' : 'onKeyPress'; + } + for (let i = 0; i < numItems; i++) { const elm = focusPath[i]!; - if (!finalFocusElm) finalFocusElm = elm; - - if (mappedEvent) { - const eventHandler = isUp - ? elm[`on${mappedEvent}Release`] - : elm[`on${mappedEvent}`]; + if (!finalFocusElm) { + finalFocusElm = elm; + } + // Check for the release event handler if isUp is true and the key is defined + if (isUp && releaseEventHandlerKey) { + const eventHandler = elm[releaseEventHandlerKey]; + if (isFunction(eventHandler)) { + handlerAvailable = elm; + if (eventHandler.call(elm, e, elm, finalFocusElm) === true) { + return true; + } + } + } else if (!isUp && eventHandlerKey) { + // Check for the regular event handler if isUp is false and the key is defined + const eventHandler = elm[eventHandlerKey]; if (isFunction(eventHandler)) { handlerAvailable = elm; if (eventHandler.call(elm, e, elm, finalFocusElm) === true) { @@ -180,8 +204,9 @@ const propagateKeyPress = ( } } - if (!isUp) { - const fallbackHandler = isHold ? elm.onKeyHold : elm.onKeyPress; + // Check for the fallback handler if its key is defined + if (fallbackHandlerKey) { + const fallbackHandler = elm[fallbackHandlerKey]; if (isFunction(fallbackHandler)) { handlerAvailable = elm; if ( From 2e05d3b1264241d7c2ea19527a0a45c7a9b6b8df Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 28 May 2025 21:31:08 -0400 Subject: [PATCH 39/78] :rocket: bump version v3.0.0-10 --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d19c7db..db05684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,17 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-10](https://github.com/lightning-tv/core/compare/v3.0.0-9...v3.0.0-10) + +- optimize keypress, rename onCaptureKey add onCaptureRelease [`10e08df`](https://github.com/lightning-tv/core/commit/10e08df7f3ccd1674f90a458aa528d73757ea5e4) +- fix creating shaders through effects [`1ee683b`](https://github.com/lightning-tv/core/commit/1ee683b6a49313db79c1a4f76e02e488d5e1e683) +- add mappedEvent for capture key [`bdb81ce`](https://github.com/lightning-tv/core/commit/bdb81ceac67318033150ff367dd0f77f974d101a) + #### [v3.0.0-9](https://github.com/lightning-tv/core/compare/v3.0.0-8...v3.0.0-9) +> 28 May 2025 + +- :rocket: bump version v3.0.0-9 [`674b2f8`](https://github.com/lightning-tv/core/commit/674b2f83dbbbadc778c2bf9c551044ea8af1cca6) - update capture key to support mappedEvent and pass as last arg [`acd4dba`](https://github.com/lightning-tv/core/commit/acd4dba380822d70f70245309e73c2d49068e238) #### [v3.0.0-8](https://github.com/lightning-tv/core/compare/v3.0.0-7...v3.0.0-8) diff --git a/package.json b/package.json index e29a60b..d93634a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-9", + "version": "3.0.0-10", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 4016cbba612ead2af40fc46a1e00561b8535f8b3 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Mon, 2 Jun 2025 15:13:08 +0200 Subject: [PATCH 40/78] Correct contain=both for height less than lineheight (#50) --- src/domRenderer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index a666cd1..c75fdb3 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -341,6 +341,7 @@ function updateNodeStyles(node: DOMNode | DOMText) { case 'both': { let lineHeight = getNodeLineHeight(textProps); maxLines = Math.min(maxLines, Math.floor(props.height / lineHeight)); + maxLines = Math.max(1, maxLines); let height = maxLines * lineHeight; style += `width: ${props.width}px; height: ${height}px; overflow: hidden;`; break; From 690e2df299a15a71de2837754af930c7c3f8161e Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Mon, 2 Jun 2025 21:29:22 -0400 Subject: [PATCH 41/78] fix obj undefined, inserting node that isnt rendered --- src/elementNode.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 60a0300..061960e 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -67,6 +67,7 @@ const parseAndAssignShaderProps = ( obj: Record, props: Record = {}, ) => { + if (!obj) return; props[prefix] = obj; Object.entries(obj).forEach(([key, value]) => { props[`${prefix}-${key}`] = value; @@ -335,7 +336,7 @@ export class ElementNode extends Object { set parent(p) { this._parent = p; - if (this.rendered) { + if (this.rendered && p?.rendered) { this.lng.parent = (p?.lng as IRendererNode) ?? null; } } From aa14a859c4511e65469946d2f710d240f3bc652f Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Mon, 2 Jun 2025 21:30:52 -0400 Subject: [PATCH 42/78] :rocket: bump version v3.0.0-11 --- CHANGELOG.md | 9 ++++++++- package.json | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db05684..1352310 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,18 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-11](https://github.com/lightning-tv/core/compare/v3.0.0-10...v3.0.0-11) + +- Correct contain=both for height less than lineheight [`#50`](https://github.com/lightning-tv/core/pull/50) +- fix obj undefined, inserting node that isnt rendered [`690e2df`](https://github.com/lightning-tv/core/commit/690e2df299a15a71de2837754af930c7c3f8161e) + #### [v3.0.0-10](https://github.com/lightning-tv/core/compare/v3.0.0-9...v3.0.0-10) +> 28 May 2025 + - optimize keypress, rename onCaptureKey add onCaptureRelease [`10e08df`](https://github.com/lightning-tv/core/commit/10e08df7f3ccd1674f90a458aa528d73757ea5e4) - fix creating shaders through effects [`1ee683b`](https://github.com/lightning-tv/core/commit/1ee683b6a49313db79c1a4f76e02e488d5e1e683) -- add mappedEvent for capture key [`bdb81ce`](https://github.com/lightning-tv/core/commit/bdb81ceac67318033150ff367dd0f77f974d101a) +- :rocket: bump version v3.0.0-10 [`2e05d3b`](https://github.com/lightning-tv/core/commit/2e05d3b1264241d7c2ea19527a0a45c7a9b6b8df) #### [v3.0.0-9](https://github.com/lightning-tv/core/compare/v3.0.0-8...v3.0.0-9) diff --git a/package.json b/package.json index d93634a..0507301 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-10", + "version": "3.0.0-11", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 3e393299aabe96b84a9f9a09fc143de4a8ed929d Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Tue, 3 Jun 2025 15:41:20 +0200 Subject: [PATCH 43/78] Fix domRenderer.getElSize to use scale of parent els (#52) --- src/domRenderer.ts | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/domRenderer.ts b/src/domRenderer.ts index c75fdb3..a78df06 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -503,16 +503,27 @@ type Size = { width: number; height: number }; function getElSize(node: DOMNode): Size { let rect = node.div.getBoundingClientRect(); + let dpr = Config.rendererOptions?.deviceLogicalPixelRatio ?? 1; rect.height /= dpr; rect.width /= dpr; - if (node.props.scale != null && node.props.scale !== 1) { - rect.height /= node.props.scale; - rect.width /= node.props.scale; - } else { - rect.height /= node.props.scaleY; - rect.width /= node.props.scaleX; + + for (;;) { + if (node.props.scale != null && node.props.scale !== 1) { + rect.height /= node.props.scale; + rect.width /= node.props.scale; + } else { + rect.height /= node.props.scaleY; + rect.width /= node.props.scaleX; + } + + if (node.parent instanceof DOMNode) { + node = node.parent; + } else { + break; + } } + return rect; } From 9e3eaef6d882fdd8096862b0e5785bed2f1f0c4f Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Fri, 13 Jun 2025 01:06:52 +0200 Subject: [PATCH 44/78] Fix borders not changing in dom renderer (#53) * Simplify constants * Add program field to dom renderer shader to update shader.props --- src/config.ts | 12 ++++-------- src/domRenderer.ts | 2 +- src/elementNode.ts | 15 ++++++--------- src/lightningInit.ts | 3 ++- 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/config.ts b/src/config.ts index 3873400..5dcc8be 100644 --- a/src/config.ts +++ b/src/config.ts @@ -19,15 +19,11 @@ export interface Config { lockStyles?: boolean; } -function isDevEnv(): boolean { - return !!(import.meta.env && import.meta.env.DEV); -} -export const isDev = isDevEnv() || false; +export const isDev = !!(import.meta.env && import.meta.env.DEV); -function shadersEnabled(): boolean { - return !(import.meta.env && import.meta.env.VITE_DISABLE_SHADERS === 'true'); -} -export const SHADERS_ENABLED = shadersEnabled(); +export const SHADERS_ENABLED = !( + import.meta.env && import.meta.env.VITE_DISABLE_SHADERS === 'true' +); export const Config: Config = { debug: false, diff --git a/src/domRenderer.ts b/src/domRenderer.ts index a78df06..05fe79b 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -1285,7 +1285,7 @@ export class DOMRendererMain implements IRendererMain { shaderType: string, props?: IRendererShaderProps, ): IRendererShader { - return { shaderType, props }; + return { shaderType, props, program: {} }; } createTexture( diff --git a/src/elementNode.ts b/src/elementNode.ts index bdaf776..f27c161 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -1,6 +1,7 @@ import { IRendererNode, IRendererNodeProps, + IRendererShader, IRendererShaderProps, IRendererTextNode, renderer, @@ -198,7 +199,7 @@ export interface ElementNode extends RendererNode { lng: | Partial | IRendererNode - | (IRendererTextNode & { shader: any }); + | (IRendererTextNode & { shader?: any }); ref?: ElementNode | ((node: ElementNode) => void) | undefined; rendered: boolean; renderer?: RendererMain; @@ -383,15 +384,11 @@ export class ElementNode extends Object { } set shader( - shaderProps: - | Parameters - | ReturnType, + shaderProps: IRendererShader | [kind: string, props: IRendererShaderProps], ) { - let shProps = shaderProps; - if (isArray(shaderProps)) { - shProps = renderer.createShader(...shaderProps); - } - this.lng.shader = shProps; + this.lng.shader = isArray(shaderProps) + ? renderer.createShader(...shaderProps) + : shaderProps; } _sendToLightningAnimatable(name: string, value: number) { diff --git a/src/lightningInit.ts b/src/lightningInit.ts index 98534c8..e3382f6 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -28,7 +28,8 @@ export interface IRendererShaderManager { /** Based on {@link lng.CoreShaderNode} */ export interface IRendererShader { shaderType: IRendererShaderType; - props: IRendererShaderProps | undefined; + props?: IRendererShaderProps; + program?: {}; } /** Based on {@link lng.CoreShaderType} */ export interface IRendererShaderType {} From 8fdc5037e7ebe4117bd9be916ac6f3b954f95117 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Mon, 16 Jun 2025 17:11:09 +0200 Subject: [PATCH 45/78] Declare static configuration using global vars (#54) * Declare static configuration using global vars * Drop the additional underscores in global vars --- src/config.ts | 34 ++++++++++++++++++++++++++++------ src/lightningInit.ts | 4 ++-- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/config.ts b/src/config.ts index 5dcc8be..9d66372 100644 --- a/src/config.ts +++ b/src/config.ts @@ -6,6 +6,34 @@ import type { } from './intrinsicTypes.js'; import { type ElementNode } from './elementNode.js'; +/** + STATIC LIGHTNING CONFIGURATION \ + Replace the values below with in your build system, \ + or set them in the global scope before importing lightning-core. +*/ +declare global { + /** Whether the DOM renderer should be used instead of `@lightningjs/renderer` */ + var LIGHTNING_DOM_RENDERING: boolean | undefined; + /** Whether element shaders should be disabled */ + var LIGHTNING_DISABLE_SHADERS: boolean | undefined; +} + +export const isDev = !!(import.meta.env && import.meta.env.DEV); + +/** Whether the DOM renderer is used instead of `@lightningjs/renderer` */ +export const DOM_RENDERING = + typeof LIGHTNING_DOM_RENDERING === 'boolean' && LIGHTNING_DOM_RENDERING; + +/** Whether element shaders are enabled */ +export const SHADERS_ENABLED = !( + typeof LIGHTNING_DISABLE_SHADERS === 'boolean' && LIGHTNING_DISABLE_SHADERS +); + +/** + RUNTIME LIGHTNING CONFIGURATION \ + This configuration can be set at runtime, but it is recommended to set it + before running any Lightning modules to ensure consistent behavior across the application. +*/ export interface Config { debug: boolean; focusDebug: boolean; @@ -19,12 +47,6 @@ export interface Config { lockStyles?: boolean; } -export const isDev = !!(import.meta.env && import.meta.env.DEV); - -export const SHADERS_ENABLED = !( - import.meta.env && import.meta.env.VITE_DISABLE_SHADERS === 'true' -); - export const Config: Config = { debug: false, focusDebug: false, diff --git a/src/lightningInit.ts b/src/lightningInit.ts index e3382f6..51cbc8f 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -1,6 +1,6 @@ import * as lng from '@lightningjs/renderer'; import { DOMRendererMain } from './domRenderer.js'; -import { Config } from './config.js'; +import { DOM_RENDERING } from './config.js'; export type SdfFontType = 'ssdf' | 'msdf'; @@ -106,7 +106,7 @@ export function startLightningRenderer( options: lng.RendererMainSettings, rootId: string | HTMLElement = 'app', ) { - renderer = Config.domRendering + renderer = DOM_RENDERING ? new DOMRendererMain(options, rootId) : (new lng.RendererMain(options, rootId) as any as IRendererMain); return renderer; From 58515eb9b47f2e02900338369c17e515f4e9f367 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 2 Jul 2025 20:25:16 -0400 Subject: [PATCH 46/78] Merge main to rc3 (#57) --- CHANGELOG.md | 97 ++++++++++++++++ LICENSE | 2 +- src/animation.ts | 166 +++++++++++++++++++++++++++ src/config.ts | 1 + src/elementNode.ts | 106 +++++++++++++---- src/intrinsicTypes.ts | 2 + src/lightningInit.ts | 21 +++- src/timings.ts | 261 ++++++++++++++++++++++++++++++++++++++++++ tsconfig.json | 1 + 9 files changed, 626 insertions(+), 31 deletions(-) create mode 100644 src/animation.ts create mode 100644 src/timings.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 1352310..090a509 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,21 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v2.9.3](https://github.com/lightning-tv/core/compare/v3.0.0-11...v2.9.3) + +- Add onRender and onRemove handlers [`#56`](https://github.com/lightning-tv/core/pull/56) +- Port dom renderer from v3 to v2 [`#55`](https://github.com/lightning-tv/core/pull/55) +- add GitHub action to build and lint the code on PRs [`#43`](https://github.com/lightning-tv/core/pull/43) +- start adding some flex tests with vitest [`22c47ce`](https://github.com/lightning-tv/core/commit/22c47ce56ea98c9938c59d5091f963dafef007c2) +- update release it script [`4c3f04e`](https://github.com/lightning-tv/core/commit/4c3f04e4904a71b4ed0bf544b757f46b51f81979) +- add simple animation manager for transitions [`5980ff9`](https://github.com/lightning-tv/core/commit/5980ff9008b881c6452db5bcd1d50630656e868e) + #### [v3.0.0-11](https://github.com/lightning-tv/core/compare/v3.0.0-10...v3.0.0-11) +> 2 June 2025 + - Correct contain=both for height less than lineheight [`#50`](https://github.com/lightning-tv/core/pull/50) +- :rocket: bump version v3.0.0-11 [`aa14a85`](https://github.com/lightning-tv/core/commit/aa14a859c4511e65469946d2f710d240f3bc652f) - fix obj undefined, inserting node that isnt rendered [`690e2df`](https://github.com/lightning-tv/core/commit/690e2df299a15a71de2837754af930c7c3f8161e) #### [v3.0.0-10](https://github.com/lightning-tv/core/compare/v3.0.0-9...v3.0.0-10) @@ -96,13 +108,98 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) +<<<<<<< HEAD + #### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.7.7...v3.0.0-0) +======= + +#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.9.2...v3.0.0-0) + +> > > > > > > main + > 19 March 2025 - get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) - :rocket: bump version v3.0.0-0 [`96d54bf`](https://github.com/lightning-tv/core/commit/96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f) +# <<<<<<< HEAD + +#### [v2.9.2](https://github.com/lightning-tv/core/compare/v2.9.1...v2.9.2) + +> 1 July 2025 + +- Add onRender and onRemove handlers [`#56`](https://github.com/lightning-tv/core/pull/56) +- :rocket: bump version v2.9.2 [`5653f35`](https://github.com/lightning-tv/core/commit/5653f356528916213453a81c7ee6b2afaa96f39e) + +#### [v2.9.1](https://github.com/lightning-tv/core/compare/v2.9.1-0...v2.9.1) + +> 28 June 2025 + +- queue layout for parents rather than running on each child [`a3b5392`](https://github.com/lightning-tv/core/commit/a3b53924dc5f4e8841b177d711373976be913c26) +- :rocket: bump version v2.9.1 [`57b049b`](https://github.com/lightning-tv/core/commit/57b049b6e066ac88485cf0935256d08aaa94a955) + +#### [v2.9.1-0](https://github.com/lightning-tv/core/compare/v2.9.0...v2.9.1-0) + +> 28 June 2025 + +- :rocket: bump version v2.9.1-0 [`f55d700`](https://github.com/lightning-tv/core/commit/f55d7008ac9852d3ecd8b53169dece0326849f18) +- readd queue for layout reduce duplicate calls [`9ff1f33`](https://github.com/lightning-tv/core/commit/9ff1f330f6ed6f7529bbfd7710994e495efbb84e) +- fix licensing [`b35c030`](https://github.com/lightning-tv/core/commit/b35c030bc32672131e3a556e4742c1f6de3e2582) + +#### [v2.9.0](https://github.com/lightning-tv/core/compare/v2.8.3...v2.9.0) + +> 18 June 2025 + +- Port dom renderer from v3 to v2 [`#55`](https://github.com/lightning-tv/core/pull/55) +- :rocket: bump version v2.9.0 [`1763a33`](https://github.com/lightning-tv/core/commit/1763a33930a5935d9961d3b219c011ba439cf2f8) + +#### [v2.8.3](https://github.com/lightning-tv/core/compare/v2.8.2...v2.8.3) + +> 16 June 2025 + +- :rocket: bump version v2.8.3 [`874ba4b`](https://github.com/lightning-tv/core/commit/874ba4b3cf1ca97ddeb4f2279006ff784fbea56c) +- make sure to re-register animations [`5b2166f`](https://github.com/lightning-tv/core/commit/5b2166f1bef9dd9ee8a393b5bc2ea05ad0b56b87) + +#### [v2.8.2](https://github.com/lightning-tv/core/compare/v2.8.1...v2.8.2) + +> 14 June 2025 + +- :rocket: bump version v2.8.2 [`4a37a5b`](https://github.com/lightning-tv/core/commit/4a37a5b9f8aca566349b2c84463dd40f1ab37749) +- fix default animation settings [`e651e37`](https://github.com/lightning-tv/core/commit/e651e377d75e8353327f8bb323ec1c43bb1cabc3) + +#### [v2.8.1](https://github.com/lightning-tv/core/compare/v2.8.0...v2.8.1) + +> 14 June 2025 + +- add simple animation manager for transitions [`5980ff9`](https://github.com/lightning-tv/core/commit/5980ff9008b881c6452db5bcd1d50630656e868e) +- :rocket: bump version v2.8.1 [`700fe96`](https://github.com/lightning-tv/core/commit/700fe968835818853e06f5085e8fc8e2b94fa122) +- fix lint errors [`c578802`](https://github.com/lightning-tv/core/commit/c578802963fdba5a6dabe9c2ac8aa471f46080d4) + +#### [v2.8.0](https://github.com/lightning-tv/core/compare/v2.8.0-1...v2.8.0) + +> 12 June 2025 + +- :rocket: bump version v2.8.0 [`c11b1ce`](https://github.com/lightning-tv/core/commit/c11b1ce30c1ffbaca14af59b3b9754dbbb7c899c) + +#### [v2.8.0-1](https://github.com/lightning-tv/core/compare/v2.8.0-0...v2.8.0-1) + +> 12 June 2025 + +- :rocket: bump version v2.8.0-1 [`f38b684`](https://github.com/lightning-tv/core/commit/f38b684a7adf35012ce78e30463327e95335c3f9) +- remove queueDelete - managed by Solid [`fc2786c`](https://github.com/lightning-tv/core/commit/fc2786cea22de3481c54d15e76c9b7754a62e64a) + +#### [v2.8.0-0](https://github.com/lightning-tv/core/compare/v2.7.7...v2.8.0-0) + +> 12 June 2025 + +- add GitHub action to build and lint the code on PRs [`#43`](https://github.com/lightning-tv/core/pull/43) +- start adding some flex tests with vitest [`22c47ce`](https://github.com/lightning-tv/core/commit/22c47ce56ea98c9938c59d5091f963dafef007c2) +- update release it script [`4c3f04e`](https://github.com/lightning-tv/core/commit/4c3f04e4904a71b4ed0bf544b757f46b51f81979) +- refactor flex, follow web for flex grow [`c24dcc9`](https://github.com/lightning-tv/core/commit/c24dcc9b6be47a41d4748250c35a84cd616c3472) + +> > > > > > > main + #### [v2.7.7](https://github.com/lightning-tv/core/compare/v2.7.6...v2.7.7) > 25 March 2025 diff --git a/LICENSE b/LICENSE index d645695..3b559d9 100644 --- a/LICENSE +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2024 Chris Lorenzo Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/src/animation.ts b/src/animation.ts new file mode 100644 index 0000000..9b22d0b --- /dev/null +++ b/src/animation.ts @@ -0,0 +1,166 @@ +import { getTimingFunction, mergeColorProgress } from './timings.js'; +import { + type ElementNode, + LightningRendererNumberProps, +} from './elementNode.js'; +import { type IRendererStage } from './lightningInit.js'; + +/** + * Simplified Animation Settings + */ +export interface SimpleAnimationSettings { + duration?: number; + delay?: number; + easing?: string; +} + +/** + * Properties of a Node used by the SimpleAnimation + * (Excludes shaderProps) + */ +export type SimpleAnimationProps = + (typeof LightningRendererNumberProps)[number]; + +/** + * Configuration for a single node within a SimpleAnimation + */ +interface SimpleAnimationNodeConfig { + node: ElementNode; + duration: number; + delay: number; + easing: string; + progress: number; + delayFor: number; + timingFunction: (t: number) => number | undefined; + propName: SimpleAnimationProps; + startValue: number; + targetValue: number; +} + +export class SimpleAnimation { + private nodeConfigs: SimpleAnimationNodeConfig[] = []; + private isRegistered = false; + private stage: IRendererStage | undefined; + + register(stage: IRendererStage) { + if (this.isRegistered) { + return; + } + this.isRegistered = true; + this.stage = stage; + stage.animationManager.registerAnimation(this); + } + + /** + * Adds a node and its animation properties to this animation instance. + * The animation's start values for the specified properties are captured + * from the node's current state when this method is called. + * + * @param node - The CoreNode to animate. + * @param props - The properties to animate and their target values. Only number properties are supported. + * @param settings - Animation settings for this specific node animation. + */ + add( + node: ElementNode, + key: SimpleAnimationProps, + value: number, + settings: SimpleAnimationSettings, + ): void { + const duration = settings.duration ?? 0; + const delay = settings.delay ?? 0; + const easing = settings.easing || 'linear'; + const timingFunction = getTimingFunction(easing); + + const targetValue = value; + const startValue = node[key] as number; + + this.nodeConfigs.push({ + node, + duration, + delay, + easing, + progress: 0, + delayFor: delay, + timingFunction, + propName: key, + startValue, + targetValue, + }); + } + + update(dt: number) { + // Iterate backward to safely remove finished animations + for (let i = this.nodeConfigs.length - 1; i >= 0; i--) { + const nodeConfig = this.nodeConfigs[i] as SimpleAnimationNodeConfig; + const { + node, + duration, + timingFunction, + propName, + startValue, + targetValue, + } = nodeConfig; + let remainingDt = dt; + + // 1. Handle Delay + if (nodeConfig.delayFor > 0) { + nodeConfig.delayFor -= remainingDt; + if (nodeConfig.delayFor >= 0) { + // Still in delay phase for this node, skip applying values this frame + continue; + } else { + // Delay finished this frame, use the remaining time for animation + remainingDt = -nodeConfig.delayFor; + nodeConfig.delayFor = 0; + } + } + + // 2. Update Progress (directly on nodeConfig.progress) + if (duration > 0) { + nodeConfig.progress += remainingDt / duration; + // Clamp progress between 0 and 1 + nodeConfig.progress = Math.max(0, Math.min(1, nodeConfig.progress)); + } else if (duration === 0 && nodeConfig.delayFor <= 0) { + // Duration is 0 and delay is finished or was 0. Animation completes instantly. + nodeConfig.progress = 1; + } + + // 3. Calculate Eased Progress + const easedProgress = + timingFunction(nodeConfig.progress) || nodeConfig.progress; + + // 4. Apply Animated Values to the Node + let interpolatedValue: number; + if (nodeConfig.progress === 1) { + interpolatedValue = targetValue; + } else { + if (propName.includes('color')) { + // Handle color interpolation + interpolatedValue = mergeColorProgress( + startValue, + targetValue, + easedProgress, + ); + } else { + // Handle linear interpolation for other number properties + interpolatedValue = + startValue + (targetValue - startValue) * easedProgress; + } + } + // @typescript-eslint/no-explicit-any + (node.lng as any)[propName] = interpolatedValue; // Cast to any because the properties on CoreNode might have broader types. + + // 5. Remove Node if Progress is 1 + if (nodeConfig.progress === 1) { + this.nodeConfigs.splice(i, 1); + } + if (this.nodeConfigs.length === 0) { + this.stage?.animationManager.unregisterAnimation(this); + this.isRegistered = false; + } + } + } +} + +export const simpleAnimation = new SimpleAnimation(); +export default simpleAnimation; diff --git a/src/config.ts b/src/config.ts index 9d66372..2464cdd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -38,6 +38,7 @@ export interface Config { debug: boolean; focusDebug: boolean; keyDebug: boolean; + simpleAnimationsEnabled?: boolean; animationSettings?: AnimationSettings; animationsEnabled: boolean; fontSettings: Partial; diff --git a/src/elementNode.ts b/src/elementNode.ts index f27c161..599d2c8 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -4,6 +4,7 @@ import { IRendererShader, IRendererShaderProps, IRendererTextNode, + IRendererTextNodeProps, renderer, } from './lightningInit.js'; import { @@ -41,7 +42,6 @@ import type { RendererMain, INode, INodeAnimateProps, - ITextNodeProps, IAnimationController, LinearGradientProps, RadialGradientProps, @@ -51,10 +51,13 @@ import type { import { assertTruthy } from '@lightningjs/renderer/utils'; import { NodeType } from './nodeTypes.js'; import { setActiveElement } from './focusManager.js'; +import simpleAnimation, { SimpleAnimationSettings } from './animation.js'; +let layoutRunQueued = false; const layoutQueue = new Set(); function runLayout() { + layoutRunQueued = false; const queue = [...layoutQueue]; layoutQueue.clear(); for (let i = queue.length - 1; i >= 0; i--) { @@ -85,7 +88,7 @@ function convertToShader(_node: ElementNode, v: StyleEffects): any { return renderer.createShader(typeParts.join(''), v as IRendererShaderProps); } -const LightningRendererNumberProps = [ +export const LightningRendererNumberProps = [ 'alpha', 'color', 'colorTop', @@ -261,8 +264,23 @@ export interface ElementNode extends RendererNode { * @see https://lightning-tv.github.io/solid/#/essentials/transitions?id=animation-callbacks */ onAnimation?: Partial>; - onCreate?: (this: ElementNode, target: ElementNode) => void; - onDestroy?: (this: ElementNode, elm: ElementNode) => Promise | void; + /** + * Optional handler for when the element is created and rendered. + */ + onCreate?: (this: ElementNode, el: ElementNode) => void; + /** + * Optional handler for when the element is destroyed. + * It can return a promise to wait for the cleanup to finish before the element is destroyed. + */ + onDestroy?: (this: ElementNode, el: ElementNode) => Promise | void; + /** + * Optional handlers for when the element is rendered—after creation and when switching parents. + */ + onRender?: (this: ElementNode, el: ElementNode) => void; + /** + * Optional handlers for when the element is removed from a parent element. + */ + onRemove?: (this: ElementNode, el: ElementNode) => void; /** * Listen to Events coming from the renderer * @param NodeEvents @@ -365,8 +383,11 @@ export class ElementNode extends Object { } removeChild(node: ElementNode | ElementText | TextNode) { - spliceItem(this.children, node as ElementNode, 1); - this.updateLayout(); + const nodeIndexToRemove = this.children.indexOf(node as ElementNode); + if (nodeIndexToRemove >= 0) { + this.children.splice(nodeIndexToRemove, 1); + node.onRemove?.call(node, node); + } } get selectedNode(): ElementNode | undefined { @@ -403,15 +424,42 @@ export class ElementNode extends Object { ? undefined : (this.transition[name] as undefined | AnimationSettings); - const animationController = this.animate( - { [name]: value }, - animationSettings, - ); + if (Config.simpleAnimationsEnabled) { + simpleAnimation.add( + this, + name, + value, + animationSettings || + (this.animationSettings as SimpleAnimationSettings), + ); + simpleAnimation.register(renderer.stage); + return; + } else { + const animationController = this.animate( + { [name]: value }, + animationSettings, + ); + + if (this.onAnimation) { + const animationEvents = Object.keys( + this.onAnimation, + ) as AnimationEvents[]; + for (const event of animationEvents) { + const handler = this.onAnimation[event]; + animationController.on( + event, + (controller: IAnimationController, props?: any) => { + handler!.call(this, controller, name, value, props); + }, + ); + } + } - return animationController.start(); + return animationController.start(); + } } - (this.lng[name as keyof IRendererNodeProps] as number | string) = value; + (this.lng[name as keyof IRendererNode] as number | string) = value; } animate( @@ -691,14 +739,17 @@ export class ElementNode extends Object { if (this.hasChildren) { isDev && log('Layout: ', this); - if (this.display === 'flex') { - if (calculateFlex(this)) { - this.parent?.updateLayout(); - } - } + const flexChanged = this.display === 'flex' && calculateFlex(this); + layoutQueue.delete(this); + const onLayoutChanged = + isFunc(this.onLayout) && this.onLayout.call(this, this); - if (isFunc(this.onLayout) && this.onLayout.call(this, this)) { - this.parent?.updateLayout(); + if ((flexChanged || onLayoutChanged) && this.parent) { + layoutQueue.add(this.parent); + if (!layoutRunQueued) { + layoutRunQueued = true; + queueMicrotask(runLayout); + } } } } @@ -788,6 +839,7 @@ export class ElementNode extends Object { if (this.rendered) { // This happens if Array of items is reordered to reuse elements. // We return after layout is queued so the change can trigger layout updates. + this.onRender?.(this); return; } @@ -869,7 +921,9 @@ export class ElementNode extends Object { } isDev && log('Rendering: ', this, props); - node.lng = renderer.createTextNode(props as unknown as ITextNodeProps); + node.lng = renderer.createTextNode( + props as unknown as IRendererTextNodeProps, + ); if (parent.requiresLayout()) { if (!props.width || !props.height) { node._layoutOnLoad(); @@ -918,7 +972,8 @@ export class ElementNode extends Object { node._layoutOnLoad(); } - isFunc(this.onCreate) && this.onCreate.call(this, node); + this.onCreate?.(this); + this.onRender?.(this); if (node.onEvent) { for (const [name, handler] of Object.entries(node.onEvent)) { @@ -943,10 +998,13 @@ export class ElementNode extends Object { } } } - if (topNode) { - //Do one pass of layout, then another with Text completed - runLayout(); + if (topNode && !layoutRunQueued) { + //Do one pass of layout, then another with Text loads + layoutRunQueued = true; + // We use queue because loop will add children one at a time, causing lots of layout + queueMicrotask(runLayout); } + node._autofocus && node.setFocus(); } } diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index 7b7d797..4c98849 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -168,6 +168,8 @@ export interface IntrinsicTextNodeStyleProps extends TextStyles {} export type AnimationEvents = 'animating' | 'tick' | 'stopped'; export type AnimationEventHandler = ( controller: IAnimationController, + name: string, + endValue: number, props?: any, ) => void; diff --git a/src/lightningInit.ts b/src/lightningInit.ts index 51cbc8f..b79296c 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -18,6 +18,10 @@ export interface IRendererStage { renderer: IRendererCoreRenderer; fontManager: IRendererFontManager; shManager: IRendererShaderManager; + animationManager: { + registerAnimation: (anim: any) => void; + unregisterAnimation: (anim: any) => void; + }; } /** Based on {@link lng.CoreShaderManager} */ @@ -89,13 +93,18 @@ export interface IRendererTextNode export interface IRendererMain extends IEventEmitter { stage: IRendererStage; root: IRendererNode; - createTextNode: (props: Partial) => IRendererTextNode; - createNode: (props: Partial) => IRendererNode; - createShader: (kind: string, props: IRendererShaderProps) => IRendererShader; - createTexture: ( + createTextNode(props: Partial): IRendererTextNode; + createNode(props: Partial): IRendererNode; + createShader(kind: string, props: IRendererShaderProps): IRendererShader; + createTexture( kind: keyof lng.TextureMap, props: IRendererTextureProps, - ) => IRendererTexture; + ): IRendererTexture; + createEffect( + kind: keyof lng.EffectMap, + props: Record, + name?: string, + ): lng.EffectDescUnion; } export let renderer: IRendererMain; @@ -128,7 +137,7 @@ export function loadFonts( renderer.stage.fontManager.addFontFace( new lng.SdfTrFontFace(font.type, { ...font, - stage: renderer.stage as lng.Stage, + stage: renderer.stage as any, } as lng.SdfTrFontFaceOptions), ); } diff --git a/src/timings.ts b/src/timings.ts new file mode 100644 index 0000000..3b98176 --- /dev/null +++ b/src/timings.ts @@ -0,0 +1,261 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 Comcast Cable Communications Management, LLC. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Core Utility Functions + * + * @module + */ + +export const EPSILON = 0.000001; +export let ARRAY_TYPE = + typeof Float32Array !== 'undefined' ? Float32Array : Array; +export const RANDOM = Math.random; +export const ANGLE_ORDER = 'zyx'; +const degree = Math.PI / 180; + +export const setMatrixArrayType = ( + type: Float32ArrayConstructor | ArrayConstructor, +) => { + ARRAY_TYPE = type; +}; + +/** + * Merges two colors based on a given progress value. + * + * This function takes two colors (c1 and c2) represented as 32-bit integers + * in RGBA format and blends them based on the provided progress value (p). + * The result is a new color that is a weighted combination of the input colors, + * where the weight is determined by the progress value. + * + * @param {number} c1 - The first color in RGBA format (32-bit integer). + * @param {number} c2 - The second color in RGBA format (32-bit integer). + * @param {number} p - The progress value between 0 and 1. + * @returns {number} The merged color as a 32-bit integer in RGBA format. + */ +export function mergeColorProgress( + rgba1: number, + rgba2: number, + p: number, +): number { + const r1 = Math.trunc(rgba1 >>> 24); + const g1 = Math.trunc((rgba1 >>> 16) & 0xff); + const b1 = Math.trunc((rgba1 >>> 8) & 0xff); + const a1 = Math.trunc(rgba1 & 0xff); + + const r2 = Math.trunc(rgba2 >>> 24); + const g2 = Math.trunc((rgba2 >>> 16) & 0xff); + const b2 = Math.trunc((rgba2 >>> 8) & 0xff); + const a2 = Math.trunc(rgba2 & 0xff); + + const r = Math.round(r2 * p + r1 * (1 - p)); + const g = Math.round(g2 * p + g1 * (1 - p)); + const b = Math.round(b2 * p + b1 * (1 - p)); + const a = Math.round(a2 * p + a1 * (1 - p)); + + return ((r << 24) | (g << 16) | (b << 8) | a) >>> 0; +} + +export const toRadian = (a: number) => { + return a * degree; +}; + +export const equals = (a: number, b: number) => { + return Math.abs(a - b) <= EPSILON * Math.max(1.0, Math.abs(a), Math.abs(b)); +}; + +export const rand = (min: number, max: number) => { + return Math.floor(Math.random() * (max - min + 1) + min); +}; + +export const isPowerOfTwo = (value: number) => { + return value && !(value & (value - 1)); +}; + +const getTimingBezier = ( + a: number, + b: number, + c: number, + d: number, +): ((time: number) => number | undefined) => { + const xc = 3.0 * a; + const xb = 3.0 * (c - a) - xc; + const xa = 1.0 - xc - xb; + const yc = 3.0 * b; + const yb = 3.0 * (d - b) - yc; + const ya = 1.0 - yc - yb; + + return function (time: number): number | undefined { + if (time >= 1.0) { + return 1; + } + if (time <= 0) { + return 0; + } + + let t = 0.5, + cbx, + cbxd, + dx; + + for (let it = 0; it < 20; it++) { + cbx = t * (t * (t * xa + xb) + xc); + dx = time - cbx; + if (dx > -1e-8 && dx < 1e-8) { + return t * (t * (t * ya + yb) + yc); + } + + // Cubic bezier derivative. + cbxd = t * (t * (3 * xa) + 2 * xb) + xc; + + if (cbxd > 1e-10 && cbxd < 1e-10) { + // Problematic. Fall back to binary search method. + break; + } + + t += dx / cbxd; + } + + // Fallback: binary search method. This is more reliable when there are near-0 slopes. + let minT = 0; + let maxT = 1; + for (let it = 0; it < 20; it++) { + t = 0.5 * (minT + maxT); + + cbx = t * (t * (t * xa + xb) + xc); + + dx = time - cbx; + if (dx > -1e-8 && dx < 1e-8) { + // Solution found! + return t * (t * (t * ya + yb) + yc); + } + + if (dx < 0) { + maxT = t; + } else { + minT = t; + } + } + }; +}; + +interface TimingFunctionMap { + [key: string]: (time: number) => number | undefined; +} + +type TimingLookupArray = number[]; +interface TimingLookup { + [key: string]: TimingLookupArray; +} + +const timingMapping: TimingFunctionMap = {}; + +const timingLookup: TimingLookup = { + ease: [0.25, 0.1, 0.25, 1.0], + 'ease-in': [0.42, 0, 1.0, 1.0], + 'ease-out': [0, 0, 0.58, 1.0], + 'ease-in-out': [0.42, 0, 0.58, 1.0], + 'ease-in-sine': [0.12, 0, 0.39, 0], + 'ease-out-sine': [0.12, 0, 0.39, 0], + 'ease-in-out-sine': [0.37, 0, 0.63, 1], + 'ease-in-cubic': [0.32, 0, 0.67, 0], + 'ease-out-cubic': [0.33, 1, 0.68, 1], + 'ease-in-out-cubic': [0.65, 0, 0.35, 1], + 'ease-in-circ': [0.55, 0, 1, 0.45], + 'ease-out-circ': [0, 0.55, 0.45, 1], + 'ease-in-out-circ': [0.85, 0, 0.15, 1], + 'ease-in-back': [0.36, 0, 0.66, -0.56], + 'ease-out-back': [0.34, 1.56, 0.64, 1], + 'ease-in-out-back': [0.68, -0.6, 0.32, 1.6], +}; + +const defaultTiming = (t: number): number => t; + +const parseCubicBezier = (str: string) => { + //cubic-bezier(0.84, 0.52, 0.56, 0.6) + const regex = /-?\d*\.?\d+/g; + const match = str.match(regex); + + if (match) { + const [num1, num2, num3, num4] = match; + const a = parseFloat(num1 || '0.42'); + const b = parseFloat(num2 || '0'); + const c = parseFloat(num3 || '1'); + const d = parseFloat(num4 || '1'); + + const timing = getTimingBezier(a, b, c, d); + timingMapping[str] = timing; + + return timing; + } + + // parse failed, return linear + console.warn('Unknown cubic-bezier timing: ' + str); + return defaultTiming; +}; + +export const getTimingFunction = ( + str: string, +): ((time: number) => number | undefined) => { + if (str === 'linear') { + return defaultTiming; + } + + if (timingMapping[str] !== undefined) { + return timingMapping[str] || defaultTiming; + } + + if (str === 'step-start') { + return () => { + return 1; + }; + } + + if (str === 'step-end') { + return (time: number) => { + return time === 1 ? 1 : 0; + }; + } + + const lookup = timingLookup[str]; + if (lookup !== undefined) { + const [a, b, c, d] = lookup; + // @ts-ignore - TS doesn't understand that we've checked for undefined + const timing = getTimingBezier(a, b, c, d); + timingMapping[str] = timing; + return timing; + } + + if (str.startsWith('cubic-bezier')) { + return parseCubicBezier(str); + } + + console.warn('Unknown timing function: ' + str); + return defaultTiming; +}; + +/** + * Convert bytes to string of megabytes with 2 decimal points + * + * @param bytes + * @returns + */ +export function bytesToMb(bytes: number) { + return (bytes / 1024 / 1024).toFixed(2); +} diff --git a/tsconfig.json b/tsconfig.json index 2f469fc..6ea8fbb 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,6 +16,7 @@ "noUncheckedIndexedAccess": true, "noImplicitOverride": true, "allowSyntheticDefaultImports": true, + "noFallthroughCasesInSwitch": true, "resolveJsonModule": true, "composite": true, "types": ["vite/client", "vitest/globals"] From 3e810f4ccdc4ad3ec504a33dda38af77c9edea41 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 2 Jul 2025 20:03:15 -0400 Subject: [PATCH 47/78] fix up merge conflicts --- CHANGELOG.md | 10 ---------- src/domRenderer.ts | 4 ++++ src/elementNode.ts | 19 +------------------ src/lightningInit.ts | 5 ----- 4 files changed, 5 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 090a509..bac7b18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,23 +108,13 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) -<<<<<<< HEAD - -#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.7.7...v3.0.0-0) - -======= - #### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.9.2...v3.0.0-0) -> > > > > > > main - > 19 March 2025 - get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) - :rocket: bump version v3.0.0-0 [`96d54bf`](https://github.com/lightning-tv/core/commit/96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f) -# <<<<<<< HEAD - #### [v2.9.2](https://github.com/lightning-tv/core/compare/v2.9.1...v2.9.2) > 1 July 2025 diff --git a/src/domRenderer.ts b/src/domRenderer.ts index 05fe79b..e6ee5df 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -1231,6 +1231,10 @@ export class DOMRendererMain implements IRendererMain { shManager: { registerShaderType() {}, }, + animationManager: { + registerAnimation() {}, + unregisterAnimation() {}, + }, }; this.root = new DOMNode( diff --git a/src/elementNode.ts b/src/elementNode.ts index 599d2c8..97836c9 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -468,27 +468,10 @@ export class ElementNode extends Object { ): IAnimationController { isDev && assertTruthy(this.rendered, 'Node must be rendered before animating'); - const animationController = (this.lng as IRendererNode).animate( + return (this.lng as IRendererNode).animate( props, animationSettings || this.animationSettings || {}, ); - - if (this.onAnimation) { - const animationEvents = Object.keys( - this.onAnimation, - ) as AnimationEvents[]; - for (const event of animationEvents) { - const handler = this.onAnimation[event]; - animationController.on( - event, - (controller: IAnimationController, props?: any) => { - handler!.call(this, controller, props); - }, - ); - } - } - - return animationController; } chain( diff --git a/src/lightningInit.ts b/src/lightningInit.ts index b79296c..7d65f59 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -100,11 +100,6 @@ export interface IRendererMain extends IEventEmitter { kind: keyof lng.TextureMap, props: IRendererTextureProps, ): IRendererTexture; - createEffect( - kind: keyof lng.EffectMap, - props: Record, - name?: string, - ): lng.EffectDescUnion; } export let renderer: IRendererMain; From 2972bcf11f29cf2afea5d813c5dc4713627c54d9 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 2 Jul 2025 20:57:53 -0400 Subject: [PATCH 48/78] :rocket: bump version v3.0.0-12 --- CHANGELOG.md | 21 ++++++++++++++------- package.json | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bac7b18..c26408d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,16 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [v2.9.3](https://github.com/lightning-tv/core/compare/v3.0.0-11...v2.9.3) +#### [v3.0.0-12](https://github.com/lightning-tv/core/compare/v3.0.0-11...v3.0.0-12) -- Add onRender and onRemove handlers [`#56`](https://github.com/lightning-tv/core/pull/56) -- Port dom renderer from v3 to v2 [`#55`](https://github.com/lightning-tv/core/pull/55) +- Merge main to rc3 [`#57`](https://github.com/lightning-tv/core/pull/57) +- Declare static configuration using global vars [`#54`](https://github.com/lightning-tv/core/pull/54) +- Fix borders not changing in dom renderer [`#53`](https://github.com/lightning-tv/core/pull/53) +- Fix domRenderer.getElSize to use scale of parent els [`#52`](https://github.com/lightning-tv/core/pull/52) - add GitHub action to build and lint the code on PRs [`#43`](https://github.com/lightning-tv/core/pull/43) - start adding some flex tests with vitest [`22c47ce`](https://github.com/lightning-tv/core/commit/22c47ce56ea98c9938c59d5091f963dafef007c2) - update release it script [`4c3f04e`](https://github.com/lightning-tv/core/commit/4c3f04e4904a71b4ed0bf544b757f46b51f81979) -- add simple animation manager for transitions [`5980ff9`](https://github.com/lightning-tv/core/commit/5980ff9008b881c6452db5bcd1d50630656e868e) +- refactor flex, follow web for flex grow [`c24dcc9`](https://github.com/lightning-tv/core/commit/c24dcc9b6be47a41d4748250c35a84cd616c3472) #### [v3.0.0-11](https://github.com/lightning-tv/core/compare/v3.0.0-10...v3.0.0-11) @@ -108,13 +110,20 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) -#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.9.2...v3.0.0-0) +#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.9.3...v3.0.0-0) > 19 March 2025 - get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) - :rocket: bump version v3.0.0-0 [`96d54bf`](https://github.com/lightning-tv/core/commit/96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f) +#### [v2.9.3](https://github.com/lightning-tv/core/compare/v2.9.2...v2.9.3) + +> 1 July 2025 + +- :rocket: bump version v2.9.3 [`35b1c79`](https://github.com/lightning-tv/core/commit/35b1c79b036fdbd98ac7b83172b417fa6898d045) +- update to latest renderer [`dbdffe9`](https://github.com/lightning-tv/core/commit/dbdffe90ee51659ae70fa311e13e519161697669) + #### [v2.9.2](https://github.com/lightning-tv/core/compare/v2.9.1...v2.9.2) > 1 July 2025 @@ -188,8 +197,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - update release it script [`4c3f04e`](https://github.com/lightning-tv/core/commit/4c3f04e4904a71b4ed0bf544b757f46b51f81979) - refactor flex, follow web for flex grow [`c24dcc9`](https://github.com/lightning-tv/core/commit/c24dcc9b6be47a41d4748250c35a84cd616c3472) -> > > > > > > main - #### [v2.7.7](https://github.com/lightning-tv/core/compare/v2.7.6...v2.7.7) > 25 March 2025 diff --git a/package.json b/package.json index 0507301..1410415 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-11", + "version": "3.0.0-12", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From b9b2285395b77ad6926291935a44fa31bfabd25e Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 4 Jul 2025 10:38:58 -0400 Subject: [PATCH 49/78] fix rendered children being added to unrendered parent --- src/elementNode.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/elementNode.ts b/src/elementNode.ts index 97836c9..8ad5919 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -179,6 +179,7 @@ export interface ElementNode extends RendererNode { _animationQueueSettings?: AnimationSettings; _animationRunning?: boolean; _animationSettings?: AnimationSettings; + _hasRenderedChildren?: boolean; _effects?: StyleEffects; _id: string | undefined; _parent: ElementNode | undefined; @@ -366,6 +367,11 @@ export class ElementNode extends Object { ) { if (node.parent && node.parent !== this) { node.parent.removeChild(node); + + // We're inserting a node thats been rendered into a node that hasn't been + if (!this.rendered) { + this._hasRenderedChildren = true; + } } node.parent = this; @@ -943,6 +949,16 @@ export class ElementNode extends Object { isDev && log('Rendering: ', this, props); node.lng = renderer.createNode(props as IRendererNodeProps); + + if (node._hasRenderedChildren) { + node._hasRenderedChildren = false; + + for (const child of node.children) { + if (isElementNode(child) && isINode(child.lng)) { + child.lng.parent = node.lng as any; + } + } + } } node.rendered = true; From 072805bebda76dc1ec734d5d58d25f67ed2d4152 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 4 Jul 2025 10:40:06 -0400 Subject: [PATCH 50/78] :rocket: bump version v3.0.0-13 --- CHANGELOG.md | 6 ++++++ package.json | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c26408d..2de19a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,14 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-13](https://github.com/lightning-tv/core/compare/v3.0.0-12...v3.0.0-13) + +- fix rendered children being added to unrendered parent [`b9b2285`](https://github.com/lightning-tv/core/commit/b9b2285395b77ad6926291935a44fa31bfabd25e) + #### [v3.0.0-12](https://github.com/lightning-tv/core/compare/v3.0.0-11...v3.0.0-12) +> 2 July 2025 + - Merge main to rc3 [`#57`](https://github.com/lightning-tv/core/pull/57) - Declare static configuration using global vars [`#54`](https://github.com/lightning-tv/core/pull/54) - Fix borders not changing in dom renderer [`#53`](https://github.com/lightning-tv/core/pull/53) diff --git a/package.json b/package.json index 1410415..f4e36ba 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-12", + "version": "3.0.0-13", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 6e6762fcdcc7aa7c21d5cb169836d51550d5d9c2 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Fri, 4 Jul 2025 20:01:43 +0200 Subject: [PATCH 51/78] Add ForwardFocusHandler type and export focus types from main module (#58) --- src/elementNode.ts | 6 ++---- src/focusKeyTypes.ts | 5 +++++ src/index.ts | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 8ad5919..28e1f10 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -50,7 +50,7 @@ import type { } from '@lightningjs/renderer'; import { assertTruthy } from '@lightningjs/renderer/utils'; import { NodeType } from './nodeTypes.js'; -import { setActiveElement } from './focusManager.js'; +import { ForwardFocusHandler, setActiveElement } from './focusManager.js'; import simpleAnimation, { SimpleAnimationSettings } from './animation.js'; let layoutRunQueued = false; @@ -196,9 +196,7 @@ export interface ElementNode extends RendererNode { flexWrap?: 'nowrap' | 'wrap'; flexItem?: boolean; flexOrder?: number; - forwardFocus?: - | number - | ((this: ElementNode, elm: ElementNode) => boolean | void); + forwardFocus?: number | ForwardFocusHandler; forwardStates?: boolean; lng: | Partial diff --git a/src/focusKeyTypes.ts b/src/focusKeyTypes.ts index 6ecf8df..6343621 100644 --- a/src/focusKeyTypes.ts +++ b/src/focusKeyTypes.ts @@ -76,6 +76,11 @@ export type KeyHandler = ( mappedEvent?: string, ) => KeyHandlerReturn; +export type ForwardFocusHandler = ( + this: ElementNode, + elm: ElementNode, +) => boolean | void; + export type KeyHoldOptions = { userKeyHoldMap: Partial; holdThreshold?: number; diff --git a/src/index.ts b/src/index.ts index 66a49d7..289b8de 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ export * from './lightningInit.js'; export * from './nodeTypes.js'; export * from './utils.js'; export * from './intrinsicTypes.js'; +export * from './focusKeyTypes.js'; export * from './config.js'; export type * from '@lightningjs/renderer'; export { type AnimationSettings } from './intrinsicTypes.js'; From 24a871829d89eeba26c8dfc183761e6efcbe036f Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 9 Jul 2025 23:09:16 +0200 Subject: [PATCH 52/78] Improve tsc and build setup (#59) - Remove vite and vitest types from globals - separate tsconfig for tests - move tests out of src - Use `tsc -b` for building --- .gitignore | 1 + package.json | 5 +- src/config.ts | 8 + {src/tests => tests}/flex-perf-results.json | 142 +++++++++--------- {src/tests => tests}/flex.performance.spec.ts | 6 +- {src/tests => tests}/flex.spec.ts | 9 +- tests/tsconfig.json | 8 + tsconfig.json | 7 +- 8 files changed, 102 insertions(+), 84 deletions(-) rename {src/tests => tests}/flex-perf-results.json (79%) rename {src/tests => tests}/flex.performance.spec.ts (98%) rename {src/tests => tests}/flex.spec.ts (99%) create mode 100644 tests/tsconfig.json diff --git a/.gitignore b/.gitignore index 3005afe..182900e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ build releases .tmp .env +*.tsbuildinfo # This project uses `pnpm` for package management package-lock.json diff --git a/package.json b/package.json index f4e36ba..e088dd8 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,8 @@ "lint:fix:prettier": "prettier --write \"**/*.{ts,js,cjs,md}\"", "lint:eslint": "eslint", "lint:fix:eslint": "eslint --fix", - "build": "npm run tsc", - "tsc": "tsc", - "watch": "tsc -w", + "build": "tsc -b", + "watch": "tsc -b -w", "test": "vitest", "test:perf": "vitest run flex.performance.spec.ts", "prepare": "husky", diff --git a/src/config.ts b/src/config.ts index 2464cdd..44b6b7f 100644 --- a/src/config.ts +++ b/src/config.ts @@ -16,6 +16,14 @@ declare global { var LIGHTNING_DOM_RENDERING: boolean | undefined; /** Whether element shaders should be disabled */ var LIGHTNING_DISABLE_SHADERS: boolean | undefined; + + /** Could be set by vite or other bundler */ + interface ImportMetaEnv { + DEV?: unknown; + } + interface ImportMeta { + env?: ImportMetaEnv; + } } export const isDev = !!(import.meta.env && import.meta.env.DEV); diff --git a/src/tests/flex-perf-results.json b/tests/flex-perf-results.json similarity index 79% rename from src/tests/flex-perf-results.json rename to tests/flex-perf-results.json index 6e458c0..5b6ce1e 100644 --- a/src/tests/flex-perf-results.json +++ b/tests/flex-perf-results.json @@ -1,494 +1,494 @@ { - "timestamp": "2025-06-12T03:37:49.593Z", + "timestamp": "2025-07-04T17:14:36.328Z", "results": [ { "id": "Row, FlexStart, NoGrow-3", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 3, - "durationMs": 0.10381966666667115, + "durationMs": 0.24479766666668942, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-5", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 5, - "durationMs": 0.09461133333333767, + "durationMs": 0.09870733333332282, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-10", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 10, - "durationMs": 0.012638666666665207, + "durationMs": 0.03500033333337645, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-15", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 15, - "durationMs": 0.006847000000012334, + "durationMs": 0.01494566666663862, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-20", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 20, - "durationMs": 0.011180666666651481, + "durationMs": 0.016064666666693483, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-50", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 50, - "durationMs": 0.01970799999999902, + "durationMs": 0.03625333333335826, "containerUpdated": false }, { "id": "Row, FlexStart, NoGrow-100", "scenarioName": "Row, FlexStart, NoGrow", "numChildren": 100, - "durationMs": 0.038736000000028525, + "durationMs": 0.0696376666666841, "containerUpdated": false }, { "id": "Column, Center, WithGrow-3", "scenarioName": "Column, Center, WithGrow", "numChildren": 3, - "durationMs": 0.04319433333332275, + "durationMs": 0.08456633333336565, "containerUpdated": false }, { "id": "Column, Center, WithGrow-5", "scenarioName": "Column, Center, WithGrow", "numChildren": 5, - "durationMs": 0.005819666666676919, + "durationMs": 0.014154333333256849, "containerUpdated": false }, { "id": "Column, Center, WithGrow-10", "scenarioName": "Column, Center, WithGrow", "numChildren": 10, - "durationMs": 0.007555666666670883, + "durationMs": 0.019897999999974065, "containerUpdated": false }, { "id": "Column, Center, WithGrow-15", "scenarioName": "Column, Center, WithGrow", "numChildren": 15, - "durationMs": 0.026486333333347527, + "durationMs": 0.03914366666663227, "containerUpdated": false }, { "id": "Column, Center, WithGrow-20", "scenarioName": "Column, Center, WithGrow", "numChildren": 20, - "durationMs": 0.02476366666667218, + "durationMs": 0.031068333333299353, "containerUpdated": false }, { "id": "Column, Center, WithGrow-50", "scenarioName": "Column, Center, WithGrow", "numChildren": 50, - "durationMs": 0.02755533333333915, + "durationMs": 0.08357366666666621, "containerUpdated": false }, { "id": "Column, Center, WithGrow-100", "scenarioName": "Column, Center, WithGrow", "numChildren": 100, - "durationMs": 0.06358333333332666, + "durationMs": 0.1421340000000555, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-3", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 3, - "durationMs": 0.010333000000002812, + "durationMs": 0.02381999999996272, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-5", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 5, - "durationMs": 0.0077776666666788214, + "durationMs": 0.011734666666673851, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-10", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 10, - "durationMs": 0.007027666666658661, + "durationMs": 0.022639666666653586, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-15", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 15, - "durationMs": 0.00897233333330405, + "durationMs": 0.05160399999999754, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-20", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 20, - "durationMs": 0.010750000000011065, + "durationMs": 0.05060033333336378, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-50", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 50, - "durationMs": 0.026194333333326842, + "durationMs": 0.11945166666665348, "containerUpdated": false }, { "id": "Row, SpaceBetween, MixedGrow-100", "scenarioName": "Row, SpaceBetween, MixedGrow", "numChildren": 100, - "durationMs": 0.05126433333335475, + "durationMs": 0.2521686666666862, "containerUpdated": false }, { "id": "Row, Wrap, FlexStart-3", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 3, - "durationMs": 0.006264000000006338, + "durationMs": 0.05415066666663885, "containerUpdated": false }, { "id": "Row, Wrap, FlexStart-5", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 5, - "durationMs": 0.010347333333318906, + "durationMs": 0.022094333333332845, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-10", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 10, - "durationMs": 0.005999999999990753, + "durationMs": 0.01997066666668464, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-15", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 15, - "durationMs": 0.008305333333358552, + "durationMs": 0.018759666666634683, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-20", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 20, - "durationMs": 0.009874666666651896, + "durationMs": 0.025255333333348062, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-50", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 50, - "durationMs": 0.029777666666651232, + "durationMs": 0.05415866666673234, "containerUpdated": true }, { "id": "Row, Wrap, FlexStart-100", "scenarioName": "Row, Wrap, FlexStart", "numChildren": 100, - "durationMs": 0.05005566666665118, + "durationMs": 0.10836366666664314, "containerUpdated": true }, { "id": "Row, RTL, FlexEnd-3", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 3, - "durationMs": 0.003611333333329488, + "durationMs": 0.007669666666629382, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-5", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 5, - "durationMs": 0.002361000000007607, + "durationMs": 0.00670966666662783, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-10", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 10, - "durationMs": 0.004152666666679276, + "durationMs": 0.010900000000030255, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-15", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 15, - "durationMs": 0.00583366666666052, + "durationMs": 0.016606666666727204, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-20", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 20, - "durationMs": 0.011360999999984264, + "durationMs": 0.01916666666666818, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-50", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 50, - "durationMs": 0.02074999999998302, + "durationMs": 0.05021533333331263, "containerUpdated": false }, { "id": "Row, RTL, FlexEnd-100", "scenarioName": "Row, RTL, FlexEnd", "numChildren": 100, - "durationMs": 0.04512499999998454, + "durationMs": 0.16159300000000107, "containerUpdated": false }, { "id": "Row, WithOrder-3", "scenarioName": "Row, WithOrder", "numChildren": 3, - "durationMs": 0.016833333333314233, + "durationMs": 0.030483333333336304, "containerUpdated": false }, { "id": "Row, WithOrder-5", "scenarioName": "Row, WithOrder", "numChildren": 5, - "durationMs": 0.003736333333336006, + "durationMs": 0.01072599999997692, "containerUpdated": false }, { "id": "Row, WithOrder-10", "scenarioName": "Row, WithOrder", "numChildren": 10, - "durationMs": 0.005722333333324059, + "durationMs": 0.01293133333338877, "containerUpdated": false }, { "id": "Row, WithOrder-15", "scenarioName": "Row, WithOrder", "numChildren": 15, - "durationMs": 0.009916666666659543, + "durationMs": 0.019508333333305927, "containerUpdated": false }, { "id": "Row, WithOrder-20", "scenarioName": "Row, WithOrder", "numChildren": 20, - "durationMs": 0.010972000000000056, + "durationMs": 0.02293100000004718, "containerUpdated": false }, { "id": "Row, WithOrder-50", "scenarioName": "Row, WithOrder", "numChildren": 50, - "durationMs": 0.024152666666661087, + "durationMs": 0.08106500000000476, "containerUpdated": false }, { "id": "Row, WithOrder-100", "scenarioName": "Row, WithOrder", "numChildren": 100, - "durationMs": 0.06316666666666985, + "durationMs": 0.1687006666666472, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-3", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 3, - "durationMs": 0.00793066666667149, + "durationMs": 0.03431000000000495, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-5", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 5, - "durationMs": 0.0028613333333282753, + "durationMs": 0.016097333333353465, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-10", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 10, - "durationMs": 0.004972333333303898, + "durationMs": 0.008877999999981512, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-15", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 15, - "durationMs": 0.007000000000005002, + "durationMs": 0.013968999999974585, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-20", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 20, - "durationMs": 0.009097333333348464, + "durationMs": 0.015760666666627305, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-50", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 50, - "durationMs": 0.021028000000001157, + "durationMs": 0.036235666666660414, "containerUpdated": false }, { "id": "Row, FlexStart, WithMargins-100", "scenarioName": "Row, FlexStart, WithMargins", "numChildren": 100, - "durationMs": 0.050944999999993947, + "durationMs": 0.07032799999994192, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-3", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 3, - "durationMs": 0.005652999999995245, + "durationMs": 0.009205000000027516, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-5", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 5, - "durationMs": 0.0027639999999943634, + "durationMs": 0.005585999999993874, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-10", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 10, - "durationMs": 0.005194333333349732, + "durationMs": 0.008997999999981706, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-15", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 15, - "durationMs": 0.00826399999999694, + "durationMs": 0.012427000000002408, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-20", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 20, - "durationMs": 0.009402333333317378, + "durationMs": 0.016192000000008495, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-50", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 50, - "durationMs": 0.22224966666666054, + "durationMs": 0.037464666666664925, "containerUpdated": false }, { "id": "Row, FlexStart, WithGap-100", "scenarioName": "Row, FlexStart, WithGap", "numChildren": 100, - "durationMs": 0.05202799999998812, + "durationMs": 0.08811900000000605, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-3", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 3, - "durationMs": 0.005930666666642992, + "durationMs": 0.011233666666650302, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-5", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 5, - "durationMs": 0.002944333333346094, + "durationMs": 0.00605566666664951, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-10", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 10, - "durationMs": 0.004916333333350546, + "durationMs": 0.009934666666633044, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-15", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 15, - "durationMs": 0.007583666666657034, + "durationMs": 0.014033999999962058, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-20", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 20, - "durationMs": 0.0094033333333338, + "durationMs": 0.01789666666669139, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-50", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 50, - "durationMs": 0.022791666666686677, + "durationMs": 0.042219666666672616, "containerUpdated": false }, { "id": "Row, FlexStart, AlignCenter (Cross Axis)-100", "scenarioName": "Row, FlexStart, AlignCenter (Cross Axis)", "numChildren": 100, - "durationMs": 0.04631933333333412, + "durationMs": 0.09061466666670033, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-3", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 3, - "durationMs": 0.011778000000011465, + "durationMs": 0.01054133333335964, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-5", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 5, - "durationMs": 0.005472333333329971, + "durationMs": 0.012765666666647727, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-10", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 10, - "durationMs": 0.017694333333319417, + "durationMs": 0.024440000000026885, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-15", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 15, - "durationMs": 0.009888666666673393, + "durationMs": 0.04534233333333759, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-20", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 20, - "durationMs": 0.009680666666668003, + "durationMs": 0.04019666666666429, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-50", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 50, - "durationMs": 0.02286099999999654, + "durationMs": 0.09621533333332384, "containerUpdated": false }, { "id": "Row, FlexStart, AlignSelf (Cross Axis)-100", "scenarioName": "Row, FlexStart, AlignSelf (Cross Axis)", "numChildren": 100, - "durationMs": 0.05048633333332949, + "durationMs": 0.4757013333333286, "containerUpdated": false } ] diff --git a/src/tests/flex.performance.spec.ts b/tests/flex.performance.spec.ts similarity index 98% rename from src/tests/flex.performance.spec.ts rename to tests/flex.performance.spec.ts index 5702d72..771c7b3 100644 --- a/src/tests/flex.performance.spec.ts +++ b/tests/flex.performance.spec.ts @@ -1,5 +1,5 @@ -import { ElementNode } from '../elementNode'; -import calculateFlex from '../flex'; +import { ElementNode } from '../src/elementNode.ts'; +import calculateFlex from '../src/flex.ts'; import { describe, it, @@ -11,7 +11,7 @@ import { } from 'vitest'; import fs from 'fs'; import path from 'path'; -import { isElementNode } from '../utils'; // Assuming isElementText and isTextNode are not directly needed for createTestElement logic here +import { isElementNode } from '../src/utils.ts'; // Assuming isElementText and isTextNode are not directly needed for createTestElement logic here // Helper to create a basic ElementNode for flex testing function createTestElement( diff --git a/src/tests/flex.spec.ts b/tests/flex.spec.ts similarity index 99% rename from src/tests/flex.spec.ts rename to tests/flex.spec.ts index a0f4367..88b15a2 100644 --- a/src/tests/flex.spec.ts +++ b/tests/flex.spec.ts @@ -1,8 +1,9 @@ -import { ElementNode } from '../elementNode'; -import calculateFlex from '../flex'; +import { ElementNode } from '../src/elementNode.ts'; +import calculateFlex from '../src/flex.ts'; import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { isElementNode } from '../utils'; -import { NodeType } from '../nodeTypes'; +import { isElementNode } from '../src/utils.ts'; +import { NodeType } from '../src/nodeTypes.ts'; +import type { ElementText, TextNode } from '../src/index.ts'; // Helper to create a basic ElementNode for flex testing // (Adapted from flex.performance.spec.ts) diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 0000000..5885771 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "types": ["node"], + "noEmit": true + }, + "include": ["**/*", "../src/**/*"] +} diff --git a/tsconfig.json b/tsconfig.json index 6ea8fbb..1c59082 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "rootDir": ".", "lib": ["ESNext", "DOM", "ES5"], "outDir": "dist", + "tsBuildInfoFile": "./tsconfig.tsbuildinfo", "allowJs": true, "target": "ESNext", "module": "NodeNext", @@ -19,8 +20,8 @@ "noFallthroughCasesInSwitch": true, "resolveJsonModule": true, "composite": true, - "types": ["vite/client", "vitest/globals"] + "types": [] }, - "include": ["src/*"], - "exclude": ["node_modules", "dist"] + "include": ["src"], + "exclude": ["node_modules", "dist", "tests"] } From aee7289c7ad57859d54fb0f5caef2165d9a6d9a9 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 9 Jul 2025 23:10:26 +0200 Subject: [PATCH 53/78] Remove `undefined` from `AnimationSettings`. (#60) It's already used everywhere as an object. I haven't seen any place that would really use the `| undefined` disjunction. Only places that added it. --- src/elementNode.ts | 7 +++++-- src/intrinsicTypes.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 28e1f10..2fff763 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -243,7 +243,10 @@ export interface ElementNode extends RendererNode { width: number; height: number; zIndex?: number; - transition?: Record | true | false; + transition?: + | Record + | true + | false; /** * Optional handlers for animation events. * @@ -679,7 +682,7 @@ export class ElementNode extends Object { return this._animationSettings || Config.animationSettings; } - set animationSettings(animationSettings: AnimationSettings) { + set animationSettings(animationSettings: AnimationSettings | undefined) { this._animationSettings = animationSettings; } diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index 4c98849..572f8d7 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -12,7 +12,7 @@ import { import { ElementNode, type RendererNode } from './elementNode.js'; import { NodeStates } from './states.js'; -export type AnimationSettings = Partial | undefined; +export type AnimationSettings = Partial; export type AddColorString = { [K in keyof T]: K extends `color${string}` ? string | number : T[K]; From d9a696f8621a23b2d6eaabeb4c77d60da186696d Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 10 Jul 2025 13:38:45 +0200 Subject: [PATCH 54/78] Add `getElementScreenRect` utility (#63) --- src/utils.ts | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/utils.ts b/src/utils.ts index c441ea9..fba1272 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import { INode } from '@lightningjs/renderer'; +import { type INode, type Point } from '@lightningjs/renderer'; import { Config, isDev } from './config.js'; import type { Styles, ElementText, TextNode } from './intrinsicTypes.js'; import { ElementNode } from './elementNode.js'; @@ -156,3 +156,48 @@ const node${i} = renderer.createNode(props${i}); return output; } + +export interface Rect extends Point { + width: number; + height: number; +} + +/** + * Calculates the rectangle of an element on the screen, + * taking into account its position, size, and scaling. + */ +export function getElementScreenRect(el: ElementNode): Rect { + let { width, height } = el; + let x = 0, + y = 0; + + if (el.scaleX != null) width *= el.scaleX; + if (el.scaleY != null) height *= el.scaleY; + + let curr = el as ElementNode | undefined | null; + while (curr != null) { + x += curr.x; + y += curr.y; + + if (curr.scaleX != null) { + x += (curr.width / 2) * (1 - curr.scaleX); + } + if (curr.scaleY != null) { + y += (curr.height / 2) * (1 - curr.scaleY); + } + + curr = curr.parent; + } + + if (Config.rendererOptions != null) { + let dpr = Config.rendererOptions.deviceLogicalPixelRatio; + if (dpr != null) { + x *= dpr; + y *= dpr; + width *= dpr; + height *= dpr; + } + } + + return { x, y, width, height }; +} From ec91ccb4ee519693b0671b773f17f24da4a34867 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Tue, 15 Jul 2025 15:34:21 +0200 Subject: [PATCH 55/78] Add `hasFocus` function to check element focus state (#64) * Add `hasFocus` function to check if an element has focus * Rename `hasFocus` function to `isFocused` * Update `isFocused` function to accept `ElementText` in addition to `ElementNode` * Export `hasFocus` as an alias for `isFocused` function --- src/utils.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/utils.ts b/src/utils.ts index fba1272..405f55a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -201,3 +201,12 @@ export function getElementScreenRect(el: ElementNode): Rect { return { x, y, width, height }; } + +/** + * Checks if the element has focus.\ + * ({@link ElementNode.states} contains the {@link Config.focusStateKey} focus state) + */ +export function isFocused(el: ElementNode | ElementText): boolean { + return el.states.has(Config.focusStateKey); +} +export const hasFocus = isFocused; From e8060f1fffdb1ebb5f7b35619a607ee9d6be105d Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Tue, 15 Jul 2025 15:34:50 +0200 Subject: [PATCH 56/78] Remove selectedNode getter (#65) Removes `selectedNode` getter from `ElementNode`. After https://github.com/lightning-tv/solid/pull/61 is merged, nothing is using this getter anymore. Also this getter is weird in general, so it would be good to remove it. - it doesn't just return the current selected child, but searches for fist available when nothing is selected, even though it's not obvious from the name. - it mutates the element (`this.selected = i`) - it handles selection, which is a system introduced by lightning solid/primitives, not core. --- src/elementNode.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 2fff763..35bfb23 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -397,20 +397,6 @@ export class ElementNode extends Object { } } - get selectedNode(): ElementNode | undefined { - const selectedIndex = this.selected || 0; - - for (let i = selectedIndex; i < this.children.length; i++) { - const element = this.children[i]; - if (isElementNode(element)) { - this.selected = i; - return element; - } - } - - return undefined; - } - set shader( shaderProps: IRendererShader | [kind: string, props: IRendererShaderProps], ) { From edb012d3e49da3a0b4de3964f3180179b7d535d2 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Tue, 15 Jul 2025 16:43:00 +0200 Subject: [PATCH 57/78] Add `from` and `out` params to `getElementScreenRect` (#66) * Add `from` and `out` params to `getElementScreenRect` * Reset output rectangle coordinates in `getElementScreenRect` function --- src/utils.ts | 44 +++++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 405f55a..c79aa4b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -165,25 +165,35 @@ export interface Rect extends Point { /** * Calculates the rectangle of an element on the screen, * taking into account its position, size, and scaling. + * + * @param el - The element to calculate the rectangle for. + * @param from - Optional ancestor element to calculate the rectangle relative to. + * @param out - Optional output rectangle to fill with the result. + * @returns The rectangle of the element on the screen. */ -export function getElementScreenRect(el: ElementNode): Rect { - let { width, height } = el; - let x = 0, - y = 0; - - if (el.scaleX != null) width *= el.scaleX; - if (el.scaleY != null) height *= el.scaleY; +export function getElementScreenRect( + el: ElementNode | ElementText, + from?: ElementNode, + out: Rect = { x: 0, y: 0, width: 0, height: 0 }, +): Rect { + out.x = 0; + out.y = 0; + out.width = el.width; + out.height = el.height; + + if (el.scaleX != null) out.width *= el.scaleX; + if (el.scaleY != null) out.height *= el.scaleY; let curr = el as ElementNode | undefined | null; - while (curr != null) { - x += curr.x; - y += curr.y; + while (curr != null && curr !== from) { + out.x += curr.x; + out.y += curr.y; if (curr.scaleX != null) { - x += (curr.width / 2) * (1 - curr.scaleX); + out.x += (curr.width / 2) * (1 - curr.scaleX); } if (curr.scaleY != null) { - y += (curr.height / 2) * (1 - curr.scaleY); + out.y += (curr.height / 2) * (1 - curr.scaleY); } curr = curr.parent; @@ -192,14 +202,14 @@ export function getElementScreenRect(el: ElementNode): Rect { if (Config.rendererOptions != null) { let dpr = Config.rendererOptions.deviceLogicalPixelRatio; if (dpr != null) { - x *= dpr; - y *= dpr; - width *= dpr; - height *= dpr; + out.x *= dpr; + out.y *= dpr; + out.width *= dpr; + out.height *= dpr; } } - return { x, y, width, height }; + return out; } /** From 2f7cd6c0b22d7ca1240cd840ffb5cce493898ddb Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Sat, 5 Jul 2025 18:06:28 -0400 Subject: [PATCH 58/78] expose destroyed property from renderer --- src/elementNode.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/elementNode.ts b/src/elementNode.ts index 35bfb23..8db885a 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -128,6 +128,7 @@ const LightningRendererNonAnimatingProps = [ 'clipping', 'contain', 'data', + 'destroyed', 'fontFamily', 'fontStretch', 'fontStyle', From 185f133465fa07b87c4ffcc47e4b300fe873c3ac Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Mon, 14 Jul 2025 14:05:26 -0400 Subject: [PATCH 59/78] flex spaceAround support, flexGrow updates to re-process --- src/elementNode.ts | 33 ++++++++++++++++++++++++++++----- src/flex.ts | 33 +++++++++++++++++++++++++++------ 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index 8db885a..6961191 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -56,6 +56,14 @@ import simpleAnimation, { SimpleAnimationSettings } from './animation.js'; let layoutRunQueued = false; const layoutQueue = new Set(); +function addToLayoutQueue(node: ElementNode) { + layoutQueue.add(node); + if (!layoutRunQueued) { + layoutRunQueued = true; + queueMicrotask(runLayout); + } +} + function runLayout() { layoutRunQueued = false; const queue = [...layoutQueue]; @@ -180,6 +188,8 @@ export interface ElementNode extends RendererNode { _animationQueueSettings?: AnimationSettings; _animationRunning?: boolean; _animationSettings?: AnimationSettings; + _autofocus?: boolean; + _containsFlexGrow?: boolean | null; _hasRenderedChildren?: boolean; _effects?: StyleEffects; _id: string | undefined; @@ -231,6 +241,7 @@ export interface ElementNode extends RendererNode { | 'flexEnd' | 'center' | 'spaceBetween' + | 'spaceAround' | 'spaceEvenly'; linearGradient?: LinearGradientProps; radialGradient?: RadialGradientProps; @@ -716,17 +727,29 @@ export class ElementNode extends Object { if (this.hasChildren) { isDev && log('Layout: ', this); + if (this.display === 'flex' && this.flexGrow && this.width === 0) { + return; + } + const flexChanged = this.display === 'flex' && calculateFlex(this); layoutQueue.delete(this); const onLayoutChanged = isFunc(this.onLayout) && this.onLayout.call(this, this); if ((flexChanged || onLayoutChanged) && this.parent) { - layoutQueue.add(this.parent); - if (!layoutRunQueued) { - layoutRunQueued = true; - queueMicrotask(runLayout); - } + addToLayoutQueue(this.parent); + } + + if (this._containsFlexGrow === true) { + // Need to reprocess children + this.children.forEach((c) => { + if (c.display === 'flex' && isElementNode(c)) { + // calculating directly to prevent infinite loops recalculating parents + calculateFlex(c); + isFunc(c.onLayout) && c.onLayout.call(c, c); + addToLayoutQueue(this); + } + }); } } } diff --git a/src/flex.ts b/src/flex.ts index c3cdf73..e0039c4 100644 --- a/src/flex.ts +++ b/src/flex.ts @@ -121,6 +121,11 @@ export default function (node: ElementNode): boolean { pc.totalMainSizeOnAxis = newMainSize + pc.marginStart + pc.marginEnd; } } + // prevent infinite loops by only doing this once + node._containsFlexGrow = node._containsFlexGrow ? null : true; + console.log(node._containsFlexGrow); + } else if (node._containsFlexGrow) { + node._containsFlexGrow = null; } else { // No positive space available for items to grow, or items overflow. // flex-grow has no effect in this case. @@ -134,7 +139,8 @@ export default function (node: ElementNode): boolean { if ( justify === 'center' || justify === 'spaceBetween' || - justify === 'spaceEvenly' + justify === 'spaceEvenly' || + justify === 'spaceAround' ) { for (const pc of processedChildren) { totalItemSize += pc.totalMainSizeOnAxis; @@ -168,8 +174,11 @@ export default function (node: ElementNode): boolean { }; if (isRow && node._calcHeight && !node.flexCrossBoundary) { - const firstChildNode = processedChildren[0]?.node; - const newHeight = firstChildNode?.height || node.height; + const maxHeight = processedChildren.reduce( + (max, pc) => Math.max(max, pc.crossSize), + 0, + ); + const newHeight = maxHeight || node.height; if (newHeight !== node.height) { containerUpdated = true; node.height = containerCrossSize = newHeight; @@ -245,14 +254,26 @@ export default function (node: ElementNode): boolean { currentPos += pc.totalMainSizeOnAxis + spaceBetween; doCrossAlign(pc); } - } else if (justify === 'spaceEvenly') { + } else if (justify === 'spaceAround') { const spaceAround = + numProcessedChildren > 0 + ? (containerSize - totalItemSize - (node.padding || 0) * 2) / + numProcessedChildren + : 0; + currentPos = (node.padding || 0) + spaceAround / 2; + for (const pc of processedChildren) { + pc.node[prop] = currentPos + pc.marginStart; + currentPos += pc.totalMainSizeOnAxis + spaceAround; + doCrossAlign(pc); + } + } else if (justify === 'spaceEvenly') { + const spaceEvenly = (containerSize - totalItemSize - (node.padding || 0) * 2) / (numProcessedChildren + 1); - currentPos = spaceAround + (node.padding || 0); + currentPos = spaceEvenly + (node.padding || 0); for (const pc of processedChildren) { pc.node[prop] = currentPos + pc.marginStart; - currentPos += pc.totalMainSizeOnAxis + spaceAround; + currentPos += pc.totalMainSizeOnAxis + spaceEvenly; doCrossAlign(pc); } } From 46c43fd9e0e803e3b0a750f2bd86a1ec9f10fc84 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Tue, 15 Jul 2025 13:20:09 -0400 Subject: [PATCH 60/78] Revert "Remove selectedNode getter (#65)" This reverts commit e8060f1fffdb1ebb5f7b35619a607ee9d6be105d. --- src/elementNode.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/elementNode.ts b/src/elementNode.ts index 6961191..bebcf1a 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -409,6 +409,20 @@ export class ElementNode extends Object { } } + get selectedNode(): ElementNode | undefined { + const selectedIndex = this.selected || 0; + + for (let i = selectedIndex; i < this.children.length; i++) { + const element = this.children[i]; + if (isElementNode(element)) { + this.selected = i; + return element; + } + } + + return undefined; + } + set shader( shaderProps: IRendererShader | [kind: string, props: IRendererShaderProps], ) { From 2b42263220d92cc454d4dd582b766fc1c8633138 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Tue, 15 Jul 2025 13:43:06 -0400 Subject: [PATCH 61/78] :rocket: bump version v3.0.0-14 --- CHANGELOG.md | 33 ++++++++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2de19a3..0182acf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,25 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-14](https://github.com/lightning-tv/core/compare/v3.0.0-13...v3.0.0-14) + +- Add `from` and `out` params to `getElementScreenRect` [`#66`](https://github.com/lightning-tv/core/pull/66) +- Remove selectedNode getter [`#65`](https://github.com/lightning-tv/core/pull/65) +- Add `hasFocus` function to check element focus state [`#64`](https://github.com/lightning-tv/core/pull/64) +- Add `getElementScreenRect` utility [`#63`](https://github.com/lightning-tv/core/pull/63) +- Remove `undefined` from `AnimationSettings`. [`#60`](https://github.com/lightning-tv/core/pull/60) +- Improve tsc and build setup [`#59`](https://github.com/lightning-tv/core/pull/59) +- Add ForwardFocusHandler type and export focus types from main module [`#58`](https://github.com/lightning-tv/core/pull/58) +- flex spaceAround support, flexGrow updates to re-process [`185f133`](https://github.com/lightning-tv/core/commit/185f133465fa07b87c4ffcc47e4b300fe873c3ac) +- Revert "Remove selectedNode getter (#65)" [`46c43fd`](https://github.com/lightning-tv/core/commit/46c43fd9e0e803e3b0a750f2bd86a1ec9f10fc84) +- expose destroyed property from renderer [`2f7cd6c`](https://github.com/lightning-tv/core/commit/2f7cd6c0b22d7ca1240cd840ffb5cce493898ddb) + #### [v3.0.0-13](https://github.com/lightning-tv/core/compare/v3.0.0-12...v3.0.0-13) +> 4 July 2025 + - fix rendered children being added to unrendered parent [`b9b2285`](https://github.com/lightning-tv/core/commit/b9b2285395b77ad6926291935a44fa31bfabd25e) +- :rocket: bump version v3.0.0-13 [`072805b`](https://github.com/lightning-tv/core/commit/072805bebda76dc1ec734d5d58d25f67ed2d4152) #### [v3.0.0-12](https://github.com/lightning-tv/core/compare/v3.0.0-11...v3.0.0-12) @@ -116,13 +132,28 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) -#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.9.3...v3.0.0-0) +#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.10.0...v3.0.0-0) > 19 March 2025 - get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) - :rocket: bump version v3.0.0-0 [`96d54bf`](https://github.com/lightning-tv/core/commit/96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f) +#### [v2.10.0](https://github.com/lightning-tv/core/compare/v2.9.4...v2.10.0) + +> 14 July 2025 + +- flex spaceAround support, flexGrow updates to re-process [`79587c9`](https://github.com/lightning-tv/core/commit/79587c999ee2abb0e9d1784694029afc0eb1a955) +- :rocket: bump version v2.10.0 [`751fa52`](https://github.com/lightning-tv/core/commit/751fa52155b187ded9d00d696ac13c8d12a0a009) +- remove console.log [`5aa1ca4`](https://github.com/lightning-tv/core/commit/5aa1ca45b9702fe3b7452a33c64a6cd2775cd6ef) + +#### [v2.9.4](https://github.com/lightning-tv/core/compare/v2.9.3...v2.9.4) + +> 4 July 2025 + +- :rocket: bump version v2.9.4 [`a419db3`](https://github.com/lightning-tv/core/commit/a419db37d7191f5a579cde3e830400413fe957f3) +- fix rendered children being added to unrendered parent [`07be7f5`](https://github.com/lightning-tv/core/commit/07be7f526b9965f6e55787e31e8254b24c7379a0) + #### [v2.9.3](https://github.com/lightning-tv/core/compare/v2.9.2...v2.9.3) > 1 July 2025 diff --git a/package.json b/package.json index e088dd8..309f56f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-13", + "version": "3.0.0-14", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 9df049254ad1a1feac6482296907043a0c86ccf1 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Wed, 30 Jul 2025 14:19:48 +0200 Subject: [PATCH 62/78] Update layout on removeChild (#67) * Update layout on removeChild (fixes #64) * Use spliceItem in removeChild --- src/elementNode.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index bebcf1a..c100d26 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -402,10 +402,11 @@ export class ElementNode extends Object { } removeChild(node: ElementNode | ElementText | TextNode) { - const nodeIndexToRemove = this.children.indexOf(node as ElementNode); - if (nodeIndexToRemove >= 0) { - this.children.splice(nodeIndexToRemove, 1); + if (spliceItem(this.children, node, 1) > -1) { node.onRemove?.call(node, node); + if (this.requiresLayout()) { + addToLayoutQueue(this); + } } } From 270b202158703549775ee5af50dc13b0686ad8fe Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 30 Jul 2025 09:43:59 -0400 Subject: [PATCH 63/78] remove console.log --- src/flex.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/flex.ts b/src/flex.ts index e0039c4..e2e780d 100644 --- a/src/flex.ts +++ b/src/flex.ts @@ -123,7 +123,6 @@ export default function (node: ElementNode): boolean { } // prevent infinite loops by only doing this once node._containsFlexGrow = node._containsFlexGrow ? null : true; - console.log(node._containsFlexGrow); } else if (node._containsFlexGrow) { node._containsFlexGrow = null; } else { From e219921af9b3e84ace69aa6cd57def9782009131 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 30 Jul 2025 09:51:44 -0400 Subject: [PATCH 64/78] :rocket: bump version v3.0.0-15 --- CHANGELOG.md | 10 +++++++++- package.json | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0182acf..aa1d7d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,16 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-15](https://github.com/lightning-tv/core/compare/v3.0.0-14...v3.0.0-15) + +- Update layout on removeChild [`#67`](https://github.com/lightning-tv/core/pull/67) +- Update layout on removeChild (#67) [`#64`](https://github.com/lightning-tv/core/issues/64) +- remove console.log [`270b202`](https://github.com/lightning-tv/core/commit/270b202158703549775ee5af50dc13b0686ad8fe) + #### [v3.0.0-14](https://github.com/lightning-tv/core/compare/v3.0.0-13...v3.0.0-14) +> 15 July 2025 + - Add `from` and `out` params to `getElementScreenRect` [`#66`](https://github.com/lightning-tv/core/pull/66) - Remove selectedNode getter [`#65`](https://github.com/lightning-tv/core/pull/65) - Add `hasFocus` function to check element focus state [`#64`](https://github.com/lightning-tv/core/pull/64) @@ -14,8 +22,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Improve tsc and build setup [`#59`](https://github.com/lightning-tv/core/pull/59) - Add ForwardFocusHandler type and export focus types from main module [`#58`](https://github.com/lightning-tv/core/pull/58) - flex spaceAround support, flexGrow updates to re-process [`185f133`](https://github.com/lightning-tv/core/commit/185f133465fa07b87c4ffcc47e4b300fe873c3ac) +- :rocket: bump version v3.0.0-14 [`2b42263`](https://github.com/lightning-tv/core/commit/2b42263220d92cc454d4dd582b766fc1c8633138) - Revert "Remove selectedNode getter (#65)" [`46c43fd`](https://github.com/lightning-tv/core/commit/46c43fd9e0e803e3b0a750f2bd86a1ec9f10fc84) -- expose destroyed property from renderer [`2f7cd6c`](https://github.com/lightning-tv/core/commit/2f7cd6c0b22d7ca1240cd840ffb5cce493898ddb) #### [v3.0.0-13](https://github.com/lightning-tv/core/compare/v3.0.0-12...v3.0.0-13) diff --git a/package.json b/package.json index 309f56f..31de9c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-14", + "version": "3.0.0-15", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 427ab4089f2fc7942b6509d328439b7497b2dcd4 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Thu, 7 Aug 2025 02:13:34 +0200 Subject: [PATCH 65/78] Add default shaders and outside border. (#68) * wip: add default shaders and outside border shader * Implement border gap in domrenderer * Fix ts errors * Border inset prop * Correct shader for inset * Handle non-uniform border width with inset border * Force update when re-setting effects * Handle inset borders in dom renderer * Cleanup and add comments * Update todo comment * Replace float u_inset with bool * Replace borderZero varying with uniform * Cleanup shader --- src/domRenderer.ts | 53 ++-- src/elementNode.ts | 17 +- src/index.ts | 3 +- src/intrinsicTypes.ts | 51 ++-- src/lightningInit.ts | 15 +- src/shaders.ts | 563 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 634 insertions(+), 68 deletions(-) create mode 100644 src/shaders.ts diff --git a/src/domRenderer.ts b/src/domRenderer.ts index e6ee5df..c3e8eb5 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -5,6 +5,7 @@ Experimental DOM renderer */ import * as lng from '@lightningjs/renderer'; +import { EventEmitter } from '@lightningjs/renderer/utils'; import { Config } from './config.js'; import { @@ -19,7 +20,6 @@ import { IRendererTextNode, IRendererTextNodeProps, } from './lightningInit.js'; -import { EventEmitter } from '@lightningjs/renderer/utils'; const colorToRgba = (c: number) => `rgba(${(c >> 24) & 0xff},${(c >> 16) & 0xff},${(c >> 8) & 0xff},${(c & 0xff) / 255})`; @@ -445,32 +445,33 @@ function updateNodeStyles(node: DOMNode | DOMText) { bgStyle += `background-color: ${colorToRgba(props.color)};`; } - if (props.shader != null) { + if (props.shader?.props != null) { let shader = props.shader.props; - if (shader != null) { - const borderWidth = shader['border-width'] as number | undefined; - const borderColor = shader['border-color'] as number | undefined; - const radius = shader['radius'] as - | number - | [number, number, number, number] - | undefined; - - // Border - if ( - typeof borderWidth === 'number' && - borderWidth !== 0 && - typeof borderColor === 'number' && - borderColor !== 0 - ) { - // css border impacts the element's box size when box-shadow doesn't - borderStyle += `box-shadow: inset 0px 0px 0px ${borderWidth}px ${colorToRgba(borderColor)};`; - } - // Rounded - if (typeof radius === 'number' && radius > 0) { - radiusStyle += `border-radius: ${radius}px;`; - } else if (Array.isArray(radius) && radius.length === 4) { - radiusStyle += `border-radius: ${radius[0]}px ${radius[1]}px ${radius[2]}px ${radius[3]}px;`; - } + + let borderWidth = shader['border-width']; + let borderColor = shader['border-color']; + let borderGap = shader['border-gap'] ?? 0; + let borderInset = shader['border-inset'] ?? true; + let radius = shader['radius']; + + // Border + if ( + typeof borderWidth === 'number' && + borderWidth !== 0 && + typeof borderColor === 'number' && + borderColor !== 0 + ) { + // Handle inset borders by making gap negative + let gap = borderInset ? -(borderWidth + borderGap) : borderGap; + + borderStyle += `outline: ${borderWidth}px solid ${colorToRgba(borderColor)};`; + borderStyle += `outline-offset: ${gap}px;`; + } + // Rounded + if (typeof radius === 'number' && radius > 0) { + radiusStyle += `border-radius: ${radius}px;`; + } else if (Array.isArray(radius) && radius.length === 4) { + radiusStyle += `border-radius: ${radius[0]}px ${radius[1]}px ${radius[2]}px ${radius[3]}px;`; } } diff --git a/src/elementNode.ts b/src/elementNode.ts index c100d26..bd144d1 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -37,7 +37,7 @@ import { isFunction, spliceItem, } from './utils.js'; -import { Config, isDev, SHADERS_ENABLED } from './config.js'; +import { Config, DOM_RENDERING, isDev, SHADERS_ENABLED } from './config.js'; import type { RendererMain, INode, @@ -86,14 +86,11 @@ const parseAndAssignShaderProps = ( }); }; -function convertToShader(_node: ElementNode, v: StyleEffects): any { - const { border, shadow } = v; - - const typeParts = ['rounded']; - if (border) typeParts.push('WithBorder'); - if (shadow) typeParts.push('WithShadow'); - - return renderer.createShader(typeParts.join(''), v as IRendererShaderProps); +function convertToShader(_node: ElementNode, v: StyleEffects): IRendererShader { + let type = 'rounded'; + if (v.border) type += 'WithBorder'; + if (v.shadow) type += 'WithShadow'; + return renderer.createShader(type, v as IRendererShaderProps); } export const LightningRendererNumberProps = [ @@ -346,6 +343,8 @@ export class ElementNode extends Object { if (this.rendered) { if (!this.lng.shader) { this.lng.shader = convertToShader(this, target); + } else if (DOM_RENDERING) { + this.lng.shader = this.lng.shader; // lng.shader is a setter, force style update } } else { this.lng.shader = target; diff --git a/src/index.ts b/src/index.ts index 289b8de..74f41e0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,10 +5,9 @@ export * from './utils.js'; export * from './intrinsicTypes.js'; export * from './focusKeyTypes.js'; export * from './config.js'; +export * from './shaders.js'; export type * from '@lightningjs/renderer'; export { type AnimationSettings } from './intrinsicTypes.js'; // hopefully fix up webpack error import { assertTruthy, deg2Rad } from '@lightningjs/renderer/utils'; export { assertTruthy, deg2Rad }; -// export type * from '@lightningjs/renderer/utils'; -export * from './domRenderer.js'; diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index 572f8d7..fd1febe 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -1,18 +1,16 @@ -import { - type AnimationSettings as RendererAnimationSettings, - type LinearGradientProps, - type RadialGradientProps, - type ITextNodeProps, - type HolePunchProps, - type IAnimationController, - type ShadowProps, - NodeLoadedPayload, - NodeFailedPayload, -} from '@lightningjs/renderer'; +import * as lngr from '@lightningjs/renderer'; import { ElementNode, type RendererNode } from './elementNode.js'; import { NodeStates } from './states.js'; +import { + ShaderBorderProps, + ShaderHolePunchProps, + ShaderLinearGradientProps, + ShaderRadialGradientProps, + ShaderRoundedProps, + ShaderShadowProps, +} from './shaders.js'; -export type AnimationSettings = Partial; +export type AnimationSettings = Partial; export type AddColorString = { [K in keyof T]: K extends `color${string}` ? string | number : T[K]; @@ -28,24 +26,17 @@ export type BorderStyle = BorderStyleObject; export type BorderRadius = number | number[]; export interface Effects { - linearGradient?: LinearGradientProps; - radialGradient?: RadialGradientProps; - holePunch?: HolePunchProps; - shadow?: ShadowProps; - rounded?: { radius: BorderRadius }; - borderRadius?: { radius: BorderRadius }; - border?: BorderStyleObject; + linearGradient?: Partial; + radialGradient?: Partial; + holePunch?: Partial; + shadow?: Partial; + rounded?: Partial; + borderRadius?: Partial; + border?: Partial; } export type StyleEffects = Effects; -// Renderer should export EffectDesc -export type ShaderEffectDesc = { - name?: string; - type: keyof StyleEffects; - props: StyleEffects[keyof StyleEffects]; -}; - export type NewOmit = { [P in keyof T as Exclude]: T[P]; }; @@ -55,7 +46,7 @@ export type RemoveUnderscoreProps = { }; type RendererText = AddColorString< - Partial> + Partial> >; type CleanElementNode = NewOmit< @@ -167,15 +158,15 @@ export interface IntrinsicTextNodeStyleProps extends TextStyles {} export type AnimationEvents = 'animating' | 'tick' | 'stopped'; export type AnimationEventHandler = ( - controller: IAnimationController, + controller: lngr.IAnimationController, name: string, endValue: number, props?: any, ) => void; type EventPayloadMap = { - loaded: NodeLoadedPayload; - failed: NodeFailedPayload; + loaded: lngr.NodeLoadedPayload; + failed: lngr.NodeFailedPayload; freed: Event; inBounds: Event; outOfBounds: Event; diff --git a/src/lightningInit.ts b/src/lightningInit.ts index 7d65f59..874582d 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -1,6 +1,14 @@ import * as lng from '@lightningjs/renderer'; import { DOMRendererMain } from './domRenderer.js'; import { DOM_RENDERING } from './config.js'; +import { + ShaderBorderPrefixedProps, + ShaderHolePunchProps, + ShaderLinearGradientProps, + ShaderRadialGradientProps, + ShaderRoundedProps, + ShaderShadowPrefixedProps, +} from './shaders.js'; export type SdfFontType = 'ssdf' | 'msdf'; @@ -37,7 +45,12 @@ export interface IRendererShader { } /** Based on {@link lng.CoreShaderType} */ export interface IRendererShaderType {} -export type IRendererShaderProps = Record; +export type IRendererShaderProps = Partial & + Partial & + Partial & + Partial & + Partial & + Partial; /** Based on {@link lng.Texture} */ export interface IRendererTexture { diff --git a/src/shaders.ts b/src/shaders.ts new file mode 100644 index 0000000..fd50d44 --- /dev/null +++ b/src/shaders.ts @@ -0,0 +1,563 @@ +import * as lngr from '@lightningjs/renderer'; +import * as lngr_shaders from '@lightningjs/renderer/webgl/shaders'; + +import type { + RoundedProps as ShaderRoundedProps, + ShadowProps as ShaderShadowProps, + HolePunchProps as ShaderHolePunchProps, + RadialGradientProps as ShaderRadialGradientProps, + LinearGradientProps as ShaderLinearGradientProps, +} from '@lightningjs/renderer'; +export { + ShaderRoundedProps, + ShaderShadowProps, + ShaderHolePunchProps, + ShaderRadialGradientProps, + ShaderLinearGradientProps, +}; + +import { type WebGlShaderType as WebGlShader } from '@lightningjs/renderer/webgl'; +export { WebGlShader }; + +import { type IRendererShaderManager } from './lightningInit.js'; +import { DOM_RENDERING, SHADERS_ENABLED } from './config.js'; + +export type Vec4 = [x: number, y: number, z: number, w: number]; + +export interface ShaderBorderProps extends lngr.BorderProps { + /** Distance between the border and element edges. */ + gap: number; + /** + * If `false`, the border is drawn outside the element. \ + * If `true`, the border is drawn inside the element. + * @default true + */ + inset: boolean; +} + +export type ShaderBorderPrefixedProps = { + [P in keyof ShaderBorderProps as `border-${P}`]: ShaderBorderProps[P]; +}; +export type ShaderShadowPrefixedProps = { + [P in keyof ShaderShadowProps as `shadow-${P}`]: ShaderShadowProps[P]; +}; + +export type ShaderRoundedWithShadowProps = ShaderRoundedProps & + ShaderShadowPrefixedProps; +export type ShaderRoundedWithBorderProps = ShaderRoundedProps & + ShaderBorderPrefixedProps; +export type ShaderRoundedWithBorderAndShadowProps = ShaderRoundedProps & + ShaderShadowPrefixedProps & + ShaderBorderPrefixedProps; + +export type ShaderRounded = WebGlShader; +export type ShaderShadow = WebGlShader; +export type ShaderRoundedWithBorder = WebGlShader; +export type ShaderRoundedWithShadow = WebGlShader; +export type ShaderRoundedWithBorderAndShadow = + WebGlShader; +export type ShaderHolePunch = WebGlShader; +export type ShaderRadialGradient = WebGlShader; +export type ShaderLinearGradient = WebGlShader; + +export const defaultShaderRounded: ShaderRounded = lngr_shaders.Rounded; +export const defaultShaderShadow: ShaderShadow = lngr_shaders.Shadow; +export const defaultShaderRoundedWithShadow: ShaderRoundedWithShadow = + lngr_shaders.RoundedWithShadow; +// TODO: lngr_shaders.RoundedWithBorderAndShadow doesn't support border-gap +export const defaultShaderRoundedWithBorderAndShadow = + lngr_shaders.RoundedWithBorderAndShadow as ShaderRoundedWithBorderAndShadow; +export const defaultShaderHolePunch: ShaderHolePunch = lngr_shaders.HolePunch; +export const defaultShaderRadialGradient: ShaderRadialGradient = + lngr_shaders.RadialGradient; +export const defaultShaderLinearGradient: ShaderLinearGradient = + lngr_shaders.LinearGradient; + +function calcFactoredRadiusArray( + radius: Vec4, + width: number, + height: number, + out: Vec4 = [0, 0, 0, 0], +): Vec4 { + [out[0], out[1], out[2], out[3]] = radius; + let factor = Math.min( + width / Math.max(width, radius[0] + radius[1]), + width / Math.max(width, radius[2] + radius[3]), + height / Math.max(height, radius[0] + radius[3]), + height / Math.max(height, radius[1] + radius[2]), + 1, + ); + out[0] *= factor; + out[1] *= factor; + out[2] *= factor; + out[3] *= factor; + return out; +} + +function toValidVec4(value: unknown): Vec4 { + if (typeof value === 'number') { + return [value, value, value, value]; + } + if (Array.isArray(value)) { + switch (value.length) { + default: + case 4: + return value as Vec4; + case 3: + return [value[0], value[1], value[2], value[0]]; + case 2: + return [value[0], value[1], value[0], value[1]]; + case 1: + return [value[0], value[0], value[0], value[0]]; + case 0: + break; + } + } + return [0, 0, 0, 0]; +} + +const roundedWithBorderProps: lngr.ShaderProps = { + radius: { + default: [0, 0, 0, 0], + resolve(value) { + return toValidVec4(value); + }, + }, + 'top-left': { + default: 0, + set(value, props) { + (props.radius as Vec4)[0] = value; + }, + get(props) { + return (props.radius as Vec4)[0]; + }, + }, + 'top-right': { + default: 0, + set(value, props) { + (props.radius as Vec4)[1] = value; + }, + get(props) { + return (props.radius as Vec4)[1]; + }, + }, + 'bottom-right': { + default: 0, + set(value, props) { + (props.radius as Vec4)[2] = value; + }, + get(props) { + return (props.radius as Vec4)[2]; + }, + }, + 'bottom-left': { + default: 0, + set(value, props) { + (props.radius as Vec4)[3] = value; + }, + get(props) { + return (props.radius as Vec4)[3]; + }, + }, + 'border-width': { + default: [0, 0, 0, 0], + resolve(value) { + return toValidVec4(value); + }, + }, + 'border-color': 0xffffffff, + 'border-gap': 0, + 'border-top': { + default: 0, + set(value, props) { + (props['border-width'] as Vec4)[0] = value; + }, + get(props) { + return (props['border-width'] as Vec4)[0]; + }, + }, + 'border-right': { + default: 0, + set(value, props) { + (props['border-width'] as Vec4)[1] = value; + }, + get(props) { + return (props['border-width'] as Vec4)[1]; + }, + }, + 'border-bottom': { + default: 0, + set(value, props) { + (props['border-width'] as Vec4)[2] = value; + }, + get(props) { + return (props['border-width'] as Vec4)[2]; + }, + }, + 'border-left': { + default: 0, + set(value, props) { + (props['border-width'] as Vec4)[3] = value; + }, + get(props) { + return (props['border-width'] as Vec4)[3]; + }, + }, + 'border-inset': true, +}; + +export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { + props: roundedWithBorderProps, + canBatch: () => false, + update(node) { + let props = this.props!; + let borderWidth = props['border-width'] as Vec4; + let borderGap = props['border-gap']; + let inset = props['border-inset']; + + let [b_t, b_r, b_b, b_l] = borderWidth; + + this.uniformRGBA('u_borderColor', props['border-color']); + this.uniform4fa('u_border', borderWidth); + this.uniform1f('u_gap', borderGap); + this.uniform1i('u_inset', inset ? 1 : 0); + + // Check if border is zero (no border widths) + let borderZero = b_t === 0 && b_r === 0 && b_b === 0 && b_l === 0; + this.uniform1i('u_borderZero', borderZero ? 1 : 0); + + let origWidth = node.width; + let origHeight = node.height; + this.uniform2f('u_dimensions_orig', origWidth, origHeight); + + let finalWidth = origWidth; + let finalHeight = origHeight; + if (!inset) { + // For outside borders, expand dimensions + finalWidth = origWidth + b_l + b_r + borderGap * 2; + finalHeight = origHeight + b_t + b_b + borderGap * 2; + } + + // u_dimensions for the shader's SDF functions + this.uniform2f('u_dimensions', finalWidth, finalHeight); + + // The `radius` property is for the content rectangle. + // Factor it against the appropriate dimensions to prevent self-intersection. + let contentRadius = calcFactoredRadiusArray( + props.radius as Vec4, + origWidth, + origHeight, + ); + + // Calculate the appropriate radius for the shader based on inset mode + let finalRadius = contentRadius; + if (!inset) { + // For each corner, the total radius is content radius + gap + border thickness. + // Border thickness at a corner is approximated as the max of the two adjacent border sides. + let outerRadius: Vec4 = [ + contentRadius[0] + borderGap + Math.max(b_t, b_l), // top-left + contentRadius[1] + borderGap + Math.max(b_t, b_r), // top-right + contentRadius[2] + borderGap + Math.max(b_b, b_r), // bottom-right + contentRadius[3] + borderGap + Math.max(b_b, b_l), // bottom-left + ]; + calcFactoredRadiusArray( + outerRadius, + finalWidth, + finalHeight, + finalRadius, + ); + } + + this.uniform4fa('u_radius', finalRadius); + }, + vertex: /*glsl*/ ` + # ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp float; + # else + precision mediump float; + # endif + + attribute vec2 a_position; + attribute vec2 a_texcoords; + attribute vec4 a_color; + attribute vec2 a_nodeCoords; + + uniform vec2 u_res; + uniform float u_pr; + uniform vec2 u_dimensions; + uniform vec2 u_dimensions_orig; + + uniform vec4 u_radius; + uniform vec4 u_border; + uniform float u_gap; + uniform bool u_inset; + uniform bool u_borderZero; + + varying vec4 v_color; + varying vec2 v_texcoords; + varying vec2 v_nodeCoords; + varying vec4 v_borderEndRadius; + varying vec2 v_borderEndSize; + + varying vec4 v_innerRadius; + varying vec2 v_innerSize; + varying vec2 v_halfDimensions; + + void main() { + vec2 screen_space = vec2(2.0 / u_res.x, -2.0 / u_res.y); + + v_color = a_color; + v_nodeCoords = a_nodeCoords; + + float b_t = u_border.x; + float b_r = u_border.y; + float b_b = u_border.z; + float b_l = u_border.w; + + // Calculate the offset to expand/contract the quad for border and gap + vec2 expansion_offset = vec2(0.0); + if (!u_inset) { + // Outside border: expand the quad + if (a_nodeCoords.x == 0.0) { // Left edge vertex + expansion_offset.x = -(b_l + u_gap); + } else { // Right edge vertex (a_nodeCoords.x == 1.0) + expansion_offset.x = (b_r + u_gap); + } + if (a_nodeCoords.y == 0.0) { // Top edge vertex + expansion_offset.y = -(b_t + u_gap); + } else { // Bottom edge vertex (a_nodeCoords.y == 1.0) + expansion_offset.y = (b_b + u_gap); + } + } + // For inset borders, no expansion needed - use original position + + // Texture coordinate calculation + v_texcoords = a_texcoords; + if (!u_inset) { // For outside borders, adjust texture coordinates for expansion + v_texcoords *= u_dimensions; + v_texcoords.x -= b_l + u_gap; + v_texcoords.y -= b_t + u_gap; + v_texcoords /= u_dimensions_orig; + } + + v_halfDimensions = u_dimensions * 0.5; + if (!u_borderZero) { + + float gap_x2 = u_gap * 2.0; + + if (u_inset) { + // For inset borders, flip the meaning: + // v_borderEndRadius/Size represents the gap area + // v_innerRadius/Size represents the border area + + // Gap area (v_borderEnd represents gap boundary) - uniform gap + v_borderEndRadius = u_radius - u_gap - 0.5; + v_borderEndSize = (u_dimensions - gap_x2 - 1.0) * 0.5; + + // Border area (v_inner represents border boundary) - individual border widths + v_innerRadius.x = u_radius.x - u_gap - max(b_t, b_l) - 0.5; + v_innerRadius.y = u_radius.y - u_gap - max(b_t, b_r) - 0.5; + v_innerRadius.z = u_radius.z - u_gap - max(b_b, b_r) - 0.5; + v_innerRadius.w = u_radius.w - u_gap - max(b_b, b_l) - 0.5; + + v_innerSize = (u_dimensions - gap_x2 - vec2(b_l + b_r, b_t + b_b) - 1.0) * 0.5; + } else { + // For outside borders, calculate from expanded dimensions inward + v_borderEndRadius.x = u_radius.x - max(b_t, b_l) - 0.5; + v_borderEndRadius.y = u_radius.y - max(b_t, b_r) - 0.5; + v_borderEndRadius.z = u_radius.z - max(b_b, b_r) - 0.5; + v_borderEndRadius.w = u_radius.w - max(b_b, b_l) - 0.5; + + v_borderEndSize = (u_dimensions - vec2(b_l + b_r, b_t + b_b) - 1.0) * 0.5; + + v_innerRadius.x = u_radius.x - max(b_t, b_l) - u_gap - 0.5; + v_innerRadius.y = u_radius.y - max(b_t, b_r) - u_gap - 0.5; + v_innerRadius.z = u_radius.z - max(b_b, b_r) - u_gap - 0.5; + v_innerRadius.w = u_radius.w - max(b_b, b_l) - u_gap - 0.5; + + v_innerSize.x = u_dimensions.x - (b_l + b_r) - gap_x2 - 1.0; + v_innerSize.y = u_dimensions.y - (b_t + b_b) - gap_x2 - 1.0; + v_innerSize *= 0.5; + } + + v_borderEndRadius = max(v_borderEndRadius, vec4(0.0)); + v_innerRadius = max(v_innerRadius, vec4(0.0)); + } + + vec2 normalized = (a_position + expansion_offset) * u_pr; + + gl_Position = vec4(normalized.x * screen_space.x - 1.0, normalized.y * -abs(screen_space.y) + 1.0, 0.0, 1.0); + gl_Position.y = -sign(screen_space.y) * gl_Position.y; + } + `, + fragment: /*glsl*/ ` + # ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp float; + # else + precision mediump float; + # endif + + uniform vec2 u_res; + uniform float u_pr; + uniform float u_alpha; + uniform vec2 u_dimensions; + uniform sampler2D u_texture; + + uniform vec4 u_radius; + + uniform vec4 u_border; + uniform vec4 u_borderColor; + uniform bool u_inset; + uniform bool u_borderZero; + + varying vec4 v_borderEndRadius; + varying vec2 v_borderEndSize; + + varying vec4 v_color; + varying vec2 v_texcoords; + varying vec2 v_nodeCoords; + + varying vec2 v_halfDimensions; + varying vec4 v_innerRadius; + varying vec2 v_innerSize; + + float roundedBox(vec2 p, vec2 s, vec4 r) { + r.xy = (p.x > 0.0) ? r.yz : r.xw; + r.x = (p.y > 0.0) ? r.y : r.x; + vec2 q = abs(p) - s + r.x; + return (min(max(q.x, q.y), 0.0) + length(max(q, 0.0))) - r.x; + } + + void main() { + vec4 contentTexColor = texture2D(u_texture, v_texcoords) * v_color; + + vec2 boxUv = v_nodeCoords.xy * u_dimensions - v_halfDimensions; + float outerShapeDist = roundedBox(boxUv, v_halfDimensions, u_radius); + float outerShapeAlpha = 1.0 - smoothstep(0.0, 1.0, outerShapeDist); // 1 inside, 0 outside + + if (u_borderZero) { // No border, effectively no gap from border logic + gl_FragColor = mix(vec4(0.0), contentTexColor, outerShapeAlpha) * u_alpha; + return; + } + + // Adjust boxUv for non-uniform borders + // This adjusted UV is used for calculating distances to border-end and content shapes + vec2 adjustedBoxUv = boxUv; + vec2 borderAdjustedBoxUv = boxUv; + + if (!u_inset) { + // For outside borders, use same adjustment for both calculations + adjustedBoxUv.x += (u_border.y - u_border.w) * 0.5; + adjustedBoxUv.y += (u_border.z - u_border.x) * 0.5; + borderAdjustedBoxUv = adjustedBoxUv; + } else { + // For inset borders, gap calculation uses no adjustment (uniform gap) + // Border calculation uses adjustment (non-uniform border) + borderAdjustedBoxUv.x += (u_border.y - u_border.w) * 0.5; + borderAdjustedBoxUv.y += (u_border.z - u_border.x) * 0.5; + } + + // Distance to the inner edge of the border (where the gap begins) + float borderEndDist = roundedBox(adjustedBoxUv, v_borderEndSize, v_borderEndRadius); + float borderEndAlpha = 1.0 - smoothstep(0.0, 1.0, borderEndDist); // 1 if inside gap or content, 0 if in border or outside + + // Distance to the content area (after the gap) + float contentDist = roundedBox(borderAdjustedBoxUv, v_innerSize, v_innerRadius); + float contentAlpha = 1.0 - smoothstep(0.0, 1.0, contentDist); // 1 if inside content, 0 if in gap, border or outside + + vec4 finalColor; + if (u_inset) { // For inset borders: border <- gap <- element + // flip the logic: borderEndAlpha becomes gap, contentAlpha becomes border+content + if (contentAlpha > 0.0) { // Pixel is inside the content area (innermost) + finalColor = contentTexColor; + } else if (borderEndAlpha > 0.0) { // Pixel is inside the border area (middle) + vec4 borderColor = u_borderColor; + finalColor = mix(contentTexColor, vec4(borderColor.rgb, 1.0), borderColor.a); + } else { // Pixel is in the gap area (outermost) - show content through gap + finalColor = contentTexColor; + } + } else { // For outside borders: element -> gap -> border + if (contentAlpha > 0.0) { // Pixel is inside the content area + finalColor = contentTexColor; + } else if (borderEndAlpha > 0.0) { // Pixel is inside the gap area + finalColor = vec4(0.0); // Transparent gap + } else { // Pixel is inside the border area + vec4 borderColor = u_borderColor; + finalColor = borderColor; + finalColor.rgb *= finalColor.a; + } + } + + gl_FragColor = mix(vec4(0.0), finalColor, outerShapeAlpha) * u_alpha; + } + `, +}; + +export function registerDefaultShaderRounded( + shManager: IRendererShaderManager, +) { + if (SHADERS_ENABLED && !DOM_RENDERING) + shManager.registerShaderType('rounded', defaultShaderRounded); +} +export function registerDefaultShaderShadow(shManager: IRendererShaderManager) { + if (SHADERS_ENABLED && !DOM_RENDERING) + shManager.registerShaderType('shadow', defaultShaderShadow); +} +export function registerDefaultShaderRoundedWithBorder( + shManager: IRendererShaderManager, +) { + if (SHADERS_ENABLED && !DOM_RENDERING) + shManager.registerShaderType( + 'roundedWithBorder', + defaultShaderRoundedWithBorder, + ); +} +export function registerDefaultShaderRoundedWithShadow( + shManager: IRendererShaderManager, +) { + if (SHADERS_ENABLED && !DOM_RENDERING) + shManager.registerShaderType( + 'roundedWithShadow', + defaultShaderRoundedWithShadow, + ); +} +export function registerDefaultShaderRoundedWithBorderAndShadow( + shManager: IRendererShaderManager, +) { + if (SHADERS_ENABLED && !DOM_RENDERING) + shManager.registerShaderType( + 'roundedWithBorderWithShadow', + defaultShaderRoundedWithBorderAndShadow, + ); +} +export function registerDefaultShaderHolePunch( + shManager: IRendererShaderManager, +) { + if (SHADERS_ENABLED && !DOM_RENDERING) + shManager.registerShaderType('holePunch', defaultShaderHolePunch); +} +export function registerDefaultShaderRadialGradient( + shManager: IRendererShaderManager, +) { + if (SHADERS_ENABLED && !DOM_RENDERING) + shManager.registerShaderType('radialGradient', defaultShaderRadialGradient); +} +export function registerDefaultShaderLinearGradient( + shManager: IRendererShaderManager, +) { + if (SHADERS_ENABLED && !DOM_RENDERING) + shManager.registerShaderType('linearGradient', defaultShaderLinearGradient); +} + +export function registerDefaultShaders(shManager: IRendererShaderManager) { + if (SHADERS_ENABLED && !DOM_RENDERING) { + registerDefaultShaderRounded(shManager); + registerDefaultShaderShadow(shManager); + registerDefaultShaderRoundedWithBorder(shManager); + registerDefaultShaderRoundedWithShadow(shManager); + registerDefaultShaderRoundedWithBorderAndShadow(shManager); + registerDefaultShaderHolePunch(shManager); + registerDefaultShaderRadialGradient(shManager); + registerDefaultShaderLinearGradient(shManager); + } +} From 8005345fa9e310fd3ba9e9efdeda41dbe147f3b0 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Fri, 8 Aug 2025 14:06:01 +0200 Subject: [PATCH 66/78] Fix border shader (#70) --- src/shaders.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/shaders.ts b/src/shaders.ts index fd50d44..6e6f4b3 100644 --- a/src/shaders.ts +++ b/src/shaders.ts @@ -277,16 +277,18 @@ export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { precision mediump float; # endif + /* Passed by lightning renderer */ attribute vec2 a_position; - attribute vec2 a_texcoords; + attribute vec2 a_textureCoords; attribute vec4 a_color; attribute vec2 a_nodeCoords; - uniform vec2 u_res; - uniform float u_pr; + uniform vec2 u_resolution; + uniform float u_pixelRatio; + + /* Passed by shader setup */ uniform vec2 u_dimensions; uniform vec2 u_dimensions_orig; - uniform vec4 u_radius; uniform vec4 u_border; uniform float u_gap; @@ -304,7 +306,7 @@ export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { varying vec2 v_halfDimensions; void main() { - vec2 screen_space = vec2(2.0 / u_res.x, -2.0 / u_res.y); + vec2 screen_space = vec2(2.0 / u_resolution.x, -2.0 / u_resolution.y); v_color = a_color; v_nodeCoords = a_nodeCoords; @@ -332,7 +334,7 @@ export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { // For inset borders, no expansion needed - use original position // Texture coordinate calculation - v_texcoords = a_texcoords; + v_texcoords = a_textureCoords; if (!u_inset) { // For outside borders, adjust texture coordinates for expansion v_texcoords *= u_dimensions; v_texcoords.x -= b_l + u_gap; @@ -384,7 +386,7 @@ export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { v_innerRadius = max(v_innerRadius, vec4(0.0)); } - vec2 normalized = (a_position + expansion_offset) * u_pr; + vec2 normalized = (a_position + expansion_offset) * u_pixelRatio; gl_Position = vec4(normalized.x * screen_space.x - 1.0, normalized.y * -abs(screen_space.y) + 1.0, 0.0, 1.0); gl_Position.y = -sign(screen_space.y) * gl_Position.y; @@ -397,12 +399,14 @@ export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { precision mediump float; # endif - uniform vec2 u_res; - uniform float u_pr; + /* Passed by lightning renderer */ + uniform vec2 u_resolution; + uniform float u_pixelRatio; uniform float u_alpha; uniform vec2 u_dimensions; uniform sampler2D u_texture; + /* Passed by shader setup */ uniform vec4 u_radius; uniform vec4 u_border; From 4c07701e9a3455f46c31cc85029b67fec4547d1b Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 26 Sep 2025 14:22:38 -0400 Subject: [PATCH 67/78] update core to work with latest renderer beta --- package.json | 2 +- pnpm-lock.yaml | 1948 +++++++++++++++++++---------------------- src/domRenderer.ts | 128 ++- src/elementNode.ts | 57 +- src/intrinsicTypes.ts | 3 +- src/lightningInit.ts | 23 +- src/shaders.ts | 26 +- 7 files changed, 1036 insertions(+), 1151 deletions(-) diff --git a/package.json b/package.json index 31de9c2..252f231 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "author": "Chris Lorenzo", "license": "Apache-2.0", "peerDependencies": { - "@lightningjs/renderer": "^3.0.0-beta6" + "@lightningjs/renderer": "^3.0.0-beta14" }, "devDependencies": { "@eslint/js": "^9.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46257a7..70e98b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,69 +9,69 @@ importers: .: dependencies: '@lightningjs/renderer': - specifier: ^3.0.0-beta6 - version: 3.0.0-beta7 + specifier: ^3.0.0-beta14 + version: 3.0.0-beta14 devDependencies: '@eslint/js': specifier: ^9.15.0 - version: 9.15.0 + version: 9.36.0 '@types/eslint__js': specifier: ^8.42.3 version: 8.42.3 '@typescript-eslint/eslint-plugin': specifier: ^8.14.0 - version: 8.14.0(@typescript-eslint/parser@8.14.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3) + version: 8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0)(typescript@5.9.2))(eslint@9.36.0)(typescript@5.9.2) '@typescript-eslint/parser': specifier: ^8.14.0 - version: 8.14.0(eslint@9.15.0)(typescript@5.6.3) + version: 8.44.1(eslint@9.36.0)(typescript@5.9.2) '@vitest/ui': specifier: ^3.1.3 - version: 3.1.3(vitest@3.1.3) + version: 3.2.4(vitest@3.2.4) eslint: specifier: ^9.15.0 - version: 9.15.0 + version: 9.36.0 eslint-config-prettier: specifier: ^9.1.0 - version: 9.1.0(eslint@9.15.0) + version: 9.1.2(eslint@9.36.0) eslint-plugin-prettier: specifier: ^5.2.1 - version: 5.2.1(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.15.0))(eslint@9.15.0)(prettier@3.3.3) + version: 5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.36.0))(eslint@9.36.0)(prettier@3.6.2) globals: specifier: ^15.12.0 - version: 15.12.0 + version: 15.15.0 husky: specifier: ^9.1.6 - version: 9.1.6 + version: 9.1.7 lint-staged: specifier: ^15.2.10 - version: 15.2.10 + version: 15.5.2 prettier: specifier: ^3.3.3 - version: 3.3.3 + version: 3.6.2 release-it: specifier: ^18.1.2 - version: 18.1.2(@types/node@24.0.1)(typescript@5.6.3) + version: 18.1.2(@types/node@24.5.2)(typescript@5.9.2) typescript: specifier: ^5.6.3 - version: 5.6.3 + version: 5.9.2 typescript-eslint: specifier: ^8.14.0 - version: 8.16.0(eslint@9.15.0)(typescript@5.6.3) + version: 8.44.1(eslint@9.36.0)(typescript@5.9.2) vite: specifier: ^5.4.11 - version: 5.4.11(@types/node@24.0.1) + version: 5.4.20(@types/node@24.5.2) vitest: specifier: ^3.1.3 - version: 3.1.3(@types/node@24.0.1)(@vitest/ui@3.1.3) + version: 3.2.4(@types/node@24.5.2)(@vitest/ui@3.2.4) packages: - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} '@esbuild/aix-ppc64@0.21.5': @@ -212,8 +212,8 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.1': - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -222,55 +222,68 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.19.0': - resolution: {integrity: sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==} + '@eslint/config-array@0.21.0': + resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.9.0': - resolution: {integrity: sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==} + '@eslint/config-helpers@0.3.1': + resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.2.0': - resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} + '@eslint/core@0.15.2': + resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.15.0': - resolution: {integrity: sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==} + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.4': - resolution: {integrity: sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==} + '@eslint/js@9.36.0': + resolution: {integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.2.3': - resolution: {integrity: sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==} + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.5': + resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - - '@humanwhocodes/retry@0.4.1': - resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} '@iarna/toml@2.2.5': resolution: {integrity: sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==} - '@inquirer/checkbox@4.1.8': - resolution: {integrity: sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==} + '@inquirer/ansi@1.0.0': + resolution: {integrity: sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==} + engines: {node: '>=18'} + + '@inquirer/checkbox@4.2.4': + resolution: {integrity: sha512-2n9Vgf4HSciFq8ttKXk+qy+GsyTXPV1An6QAwe/8bkbbqvG4VW1I/ZY1pNu2rf+h9bdzMLPbRSfcNxkHBy/Ydw==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/confirm@5.1.18': + resolution: {integrity: sha512-MilmWOzHa3Ks11tzvuAmFoAd/wRuaP3SwlT1IZhyMke31FKLxPiuDWcGXhU+PKveNOpAc4axzAgrgxuIJJRmLw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -278,8 +291,8 @@ packages: '@types/node': optional: true - '@inquirer/confirm@5.1.12': - resolution: {integrity: sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg==} + '@inquirer/core@10.2.2': + resolution: {integrity: sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -287,8 +300,8 @@ packages: '@types/node': optional: true - '@inquirer/core@10.1.13': - resolution: {integrity: sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==} + '@inquirer/editor@4.2.20': + resolution: {integrity: sha512-7omh5y5bK672Q+Brk4HBbnHNowOZwrb/78IFXdrEB9PfdxL3GudQyDk8O9vQ188wj3xrEebS2M9n18BjJoI83g==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -296,8 +309,8 @@ packages: '@types/node': optional: true - '@inquirer/editor@4.2.13': - resolution: {integrity: sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==} + '@inquirer/expand@4.0.20': + resolution: {integrity: sha512-Dt9S+6qUg94fEvgn54F2Syf0Z3U8xmnBI9ATq2f5h9xt09fs2IJXSCIXyyVHwvggKWFXEY/7jATRo2K6Dkn6Ow==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -305,8 +318,8 @@ packages: '@types/node': optional: true - '@inquirer/expand@4.0.15': - resolution: {integrity: sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==} + '@inquirer/external-editor@1.0.2': + resolution: {integrity: sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -314,12 +327,12 @@ packages: '@types/node': optional: true - '@inquirer/figures@1.0.12': - resolution: {integrity: sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==} + '@inquirer/figures@1.0.13': + resolution: {integrity: sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==} engines: {node: '>=18'} - '@inquirer/input@4.1.12': - resolution: {integrity: sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==} + '@inquirer/input@4.2.4': + resolution: {integrity: sha512-cwSGpLBMwpwcZZsc6s1gThm0J+it/KIJ+1qFL2euLmSKUMGumJ5TcbMgxEjMjNHRGadouIYbiIgruKoDZk7klw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -327,8 +340,8 @@ packages: '@types/node': optional: true - '@inquirer/number@3.0.15': - resolution: {integrity: sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==} + '@inquirer/number@3.0.20': + resolution: {integrity: sha512-bbooay64VD1Z6uMfNehED2A2YOPHSJnQLs9/4WNiV/EK+vXczf/R988itL2XLDGTgmhMF2KkiWZo+iEZmc4jqg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -336,8 +349,8 @@ packages: '@types/node': optional: true - '@inquirer/password@4.0.15': - resolution: {integrity: sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==} + '@inquirer/password@4.0.20': + resolution: {integrity: sha512-nxSaPV2cPvvoOmRygQR+h0B+Av73B01cqYLcr7NXcGXhbmsYfUb8fDdw2Us1bI2YsX+VvY7I7upgFYsyf8+Nug==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -345,8 +358,8 @@ packages: '@types/node': optional: true - '@inquirer/prompts@7.5.3': - resolution: {integrity: sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg==} + '@inquirer/prompts@7.8.6': + resolution: {integrity: sha512-68JhkiojicX9SBUD8FE/pSKbOKtwoyaVj1kwqLfvjlVXZvOy3iaSWX4dCLsZyYx/5Ur07Fq+yuDNOen+5ce6ig==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -354,8 +367,8 @@ packages: '@types/node': optional: true - '@inquirer/rawlist@4.1.3': - resolution: {integrity: sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==} + '@inquirer/rawlist@4.1.8': + resolution: {integrity: sha512-CQ2VkIASbgI2PxdzlkeeieLRmniaUU1Aoi5ggEdm6BIyqopE9GuDXdDOj9XiwOqK5qm72oI2i6J+Gnjaa26ejg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -363,8 +376,8 @@ packages: '@types/node': optional: true - '@inquirer/search@3.0.15': - resolution: {integrity: sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==} + '@inquirer/search@3.1.3': + resolution: {integrity: sha512-D5T6ioybJJH0IiSUK/JXcoRrrm8sXwzrVMjibuPs+AgxmogKslaafy1oxFiorNI4s3ElSkeQZbhYQgLqiL8h6Q==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -372,8 +385,8 @@ packages: '@types/node': optional: true - '@inquirer/select@4.2.3': - resolution: {integrity: sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==} + '@inquirer/select@4.3.4': + resolution: {integrity: sha512-Qp20nySRmfbuJBBsgPU7E/cL62Hf250vMZRzYDcBHty2zdD1kKCnoDFWRr0WO2ZzaXp3R7a4esaVGJUx0E6zvA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -381,8 +394,8 @@ packages: '@types/node': optional: true - '@inquirer/type@3.0.7': - resolution: {integrity: sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==} + '@inquirer/type@3.0.8': + resolution: {integrity: sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -390,11 +403,11 @@ packages: '@types/node': optional: true - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@lightningjs/renderer@3.0.0-beta7': - resolution: {integrity: sha512-yY5sbDeK+hEPdMohKdAC+TyEEkCaFhNm5oYKRIbA/tAGl0qqhq7qOguUkbLQEfYy7BtUmDLyjBt6sI73a+oypQ==} + '@lightningjs/renderer@3.0.0-beta14': + resolution: {integrity: sha512-o73TY4IEebAOqywv8U4TJC5GuEpZoyFPNAMMZ5eCbZSf1/SBl18KT1ORgNXkWkSx5chKMoQ7BJtSz0ptnEnqxw==} engines: {node: '>= 18.0.0', npm: '>= 10.0.0', pnpm: '>= 8.9.2'} '@nodelib/fs.scandir@2.1.5': @@ -413,8 +426,8 @@ packages: resolution: {integrity: sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==} engines: {node: '>= 18'} - '@octokit/core@6.1.5': - resolution: {integrity: sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg==} + '@octokit/core@6.1.6': + resolution: {integrity: sha512-kIU8SLQkYWGp3pVKiYzA5OSaNF5EE03P/R8zEmmrG6XwOg5oBjXyQVVIauQ0dgau4zYhpZEhJrvIYt6oM+zZZA==} engines: {node: '>= 18'} '@octokit/endpoint@10.1.4': @@ -425,17 +438,17 @@ packages: resolution: {integrity: sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA==} engines: {node: '>= 18'} - '@octokit/openapi-types@22.2.0': - resolution: {integrity: sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==} + '@octokit/openapi-types@24.2.0': + resolution: {integrity: sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==} '@octokit/openapi-types@25.1.0': resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==} - '@octokit/plugin-paginate-rest@11.3.1': - resolution: {integrity: sha512-ryqobs26cLtM1kQxqeZui4v8FeznirUsksiA+RYemMPJ7Micju0WSkv50dBksTuZks9O5cg4wp+t8fZ/cLY56g==} + '@octokit/plugin-paginate-rest@11.6.0': + resolution: {integrity: sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw==} engines: {node: '>= 18'} peerDependencies: - '@octokit/core': '5' + '@octokit/core': '>=6' '@octokit/plugin-request-log@5.3.1': resolution: {integrity: sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==} @@ -443,32 +456,32 @@ packages: peerDependencies: '@octokit/core': '>=6' - '@octokit/plugin-rest-endpoint-methods@13.2.2': - resolution: {integrity: sha512-EI7kXWidkt3Xlok5uN43suK99VWqc8OaIMktY9d9+RNKl69juoTyxmLoWPIZgJYzi41qj/9zU7G/ljnNOJ5AFA==} + '@octokit/plugin-rest-endpoint-methods@13.5.0': + resolution: {integrity: sha512-9Pas60Iv9ejO3WlAX3maE1+38c5nqbJXV5GrncEfkndIpZrJ/WPMRd2xYDcPPEt5yzpxcjw9fWNoPhsSGzqKqw==} engines: {node: '>= 18'} peerDependencies: - '@octokit/core': ^5 + '@octokit/core': '>=6' '@octokit/request-error@6.1.8': resolution: {integrity: sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ==} engines: {node: '>= 18'} - '@octokit/request@9.2.3': - resolution: {integrity: sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w==} + '@octokit/request@9.2.4': + resolution: {integrity: sha512-q8ybdytBmxa6KogWlNa818r0k1wlqzNC+yNkcQDECHvQo8Vmstrg18JwqJHdJdUiHD2sjlwBgSm9kHkOKe2iyA==} engines: {node: '>= 18'} '@octokit/rest@21.0.2': resolution: {integrity: sha512-+CiLisCoyWmYicH25y1cDfCrv41kRSvTq6pPWtRroRJzhsCZWZyCqGyI8foJT5LmScADSwRAnr/xo+eewL04wQ==} engines: {node: '>= 18'} - '@octokit/types@13.6.1': - resolution: {integrity: sha512-PHZE9Z+kWXb23Ndik8MKPirBPziOc0D2/3KH1P+6jK5nGWe96kadZuE4jev2/Jq7FvIfTlT2Ltg8Fv2x1v0a5g==} + '@octokit/types@13.10.0': + resolution: {integrity: sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==} '@octokit/types@14.1.0': resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==} - '@pkgr/core@0.1.1': - resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} '@pnpm/config.env-replace@1.1.0': @@ -486,93 +499,113 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@rollup/rollup-android-arm-eabi@4.27.2': - resolution: {integrity: sha512-Tj+j7Pyzd15wAdSJswvs5CJzJNV+qqSUcr/aCD+jpQSBtXvGnV0pnrjoc8zFTe9fcKCatkpFpOO7yAzpO998HA==} + '@rollup/rollup-android-arm-eabi@4.52.2': + resolution: {integrity: sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.27.2': - resolution: {integrity: sha512-xsPeJgh2ThBpUqlLgRfiVYBEf/P1nWlWvReG+aBWfNv3XEBpa6ZCmxSVnxJgLgkNz4IbxpLy64h2gCmAAQLneQ==} + '@rollup/rollup-android-arm64@4.52.2': + resolution: {integrity: sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.27.2': - resolution: {integrity: sha512-KnXU4m9MywuZFedL35Z3PuwiTSn/yqRIhrEA9j+7OSkji39NzVkgxuxTYg5F8ryGysq4iFADaU5osSizMXhU2A==} + '@rollup/rollup-darwin-arm64@4.52.2': + resolution: {integrity: sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.27.2': - resolution: {integrity: sha512-Hj77A3yTvUeCIx/Vi+4d4IbYhyTwtHj07lVzUgpUq9YpJSEiGJj4vXMKwzJ3w5zp5v3PFvpJNgc/J31smZey6g==} + '@rollup/rollup-darwin-x64@4.52.2': + resolution: {integrity: sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.27.2': - resolution: {integrity: sha512-RjgKf5C3xbn8gxvCm5VgKZ4nn0pRAIe90J0/fdHUsgztd3+Zesb2lm2+r6uX4prV2eUByuxJNdt647/1KPRq5g==} + '@rollup/rollup-freebsd-arm64@4.52.2': + resolution: {integrity: sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.27.2': - resolution: {integrity: sha512-duq21FoXwQtuws+V9H6UZ+eCBc7fxSpMK1GQINKn3fAyd9DFYKPJNcUhdIKOrMFjLEJgQskoMoiuizMt+dl20g==} + '@rollup/rollup-freebsd-x64@4.52.2': + resolution: {integrity: sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.27.2': - resolution: {integrity: sha512-6npqOKEPRZkLrMcvyC/32OzJ2srdPzCylJjiTJT2c0bwwSGm7nz2F9mNQ1WrAqCBZROcQn91Fno+khFhVijmFA==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.2': + resolution: {integrity: sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.27.2': - resolution: {integrity: sha512-V9Xg6eXtgBtHq2jnuQwM/jr2mwe2EycnopO8cbOvpzFuySCGtKlPCI3Hj9xup/pJK5Q0388qfZZy2DqV2J8ftw==} + '@rollup/rollup-linux-arm-musleabihf@4.52.2': + resolution: {integrity: sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.27.2': - resolution: {integrity: sha512-uCFX9gtZJoQl2xDTpRdseYuNqyKkuMDtH6zSrBTA28yTfKyjN9hQ2B04N5ynR8ILCoSDOrG/Eg+J2TtJ1e/CSA==} + '@rollup/rollup-linux-arm64-gnu@4.52.2': + resolution: {integrity: sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.27.2': - resolution: {integrity: sha512-/PU9P+7Rkz8JFYDHIi+xzHabOu9qEWR07L5nWLIUsvserrxegZExKCi2jhMZRd0ATdboKylu/K5yAXbp7fYFvA==} + '@rollup/rollup-linux-arm64-musl@4.52.2': + resolution: {integrity: sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.27.2': - resolution: {integrity: sha512-eCHmol/dT5odMYi/N0R0HC8V8QE40rEpkyje/ZAXJYNNoSfrObOvG/Mn+s1F/FJyB7co7UQZZf6FuWnN6a7f4g==} + '@rollup/rollup-linux-loong64-gnu@4.52.2': + resolution: {integrity: sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.52.2': + resolution: {integrity: sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.27.2': - resolution: {integrity: sha512-DEP3Njr9/ADDln3kNi76PXonLMSSMiCir0VHXxmGSHxCxDfQ70oWjHcJGfiBugzaqmYdTC7Y+8Int6qbnxPBIQ==} + '@rollup/rollup-linux-riscv64-gnu@4.52.2': + resolution: {integrity: sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.52.2': + resolution: {integrity: sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.27.2': - resolution: {integrity: sha512-NHGo5i6IE/PtEPh5m0yw5OmPMpesFnzMIS/lzvN5vknnC1sXM5Z/id5VgcNPgpD+wHmIcuYYgW+Q53v+9s96lQ==} + '@rollup/rollup-linux-s390x-gnu@4.52.2': + resolution: {integrity: sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.27.2': - resolution: {integrity: sha512-PaW2DY5Tan+IFvNJGHDmUrORadbe/Ceh8tQxi8cmdQVCCYsLoQo2cuaSj+AU+YRX8M4ivS2vJ9UGaxfuNN7gmg==} + '@rollup/rollup-linux-x64-gnu@4.52.2': + resolution: {integrity: sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.27.2': - resolution: {integrity: sha512-dOlWEMg2gI91Qx5I/HYqOD6iqlJspxLcS4Zlg3vjk1srE67z5T2Uz91yg/qA8sY0XcwQrFzWWiZhMNERylLrpQ==} + '@rollup/rollup-linux-x64-musl@4.52.2': + resolution: {integrity: sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.27.2': - resolution: {integrity: sha512-euMIv/4x5Y2/ImlbGl88mwKNXDsvzbWUlT7DFky76z2keajCtcbAsN9LUdmk31hAoVmJJYSThgdA0EsPeTr1+w==} + '@rollup/rollup-openharmony-arm64@4.52.2': + resolution: {integrity: sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.2': + resolution: {integrity: sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.27.2': - resolution: {integrity: sha512-RsnE6LQkUHlkC10RKngtHNLxb7scFykEbEwOFDjr3CeCMG+Rr+cKqlkKc2/wJ1u4u990urRHCbjz31x84PBrSQ==} + '@rollup/rollup-win32-ia32-msvc@4.52.2': + resolution: {integrity: sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.27.2': - resolution: {integrity: sha512-foJM5vv+z2KQmn7emYdDLyTbkoO5bkHZE1oth2tWbQNGW7mX32d46Hz6T0MqXdWS2vBZhaEtHqdy9WYwGfiliA==} + '@rollup/rollup-win32-x64-gnu@4.52.2': + resolution: {integrity: sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.52.2': + resolution: {integrity: sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw==} cpu: [x64] os: [win32] @@ -590,190 +623,136 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/eslint@9.6.1': resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} '@types/eslint__js@8.42.3': resolution: {integrity: sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==} - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@24.0.1': - resolution: {integrity: sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==} + '@types/node@24.5.2': + resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==} '@types/parse-path@7.1.0': resolution: {integrity: sha512-EULJ8LApcVEPbrfND0cRQqutIOdiIgJ1Mgrhpy755r14xMohPTEpkV/k28SJvuOs9bHRFW8x+KeDAEPiGQPB9Q==} deprecated: This is a stub types definition. parse-path provides its own type definitions, so you do not need this installed. - '@typescript-eslint/eslint-plugin@8.14.0': - resolution: {integrity: sha512-tqp8H7UWFaZj0yNO6bycd5YjMwxa6wIHOLZvWPkidwbgLCsBMetQoGj7DPuAlWa2yGO3H48xmPwjhsSPPCGU5w==} + '@typescript-eslint/eslint-plugin@8.44.1': + resolution: {integrity: sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + '@typescript-eslint/parser': ^8.44.1 eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/eslint-plugin@8.16.0': - resolution: {integrity: sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==} + '@typescript-eslint/parser@8.44.1': + resolution: {integrity: sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@8.14.0': - resolution: {integrity: sha512-2p82Yn9juUJq0XynBXtFCyrBDb6/dJombnz6vbo6mgQEtWHfvHbQuEa9kAOVIt1c9YFwi7H6WxtPj1kg+80+RA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.16.0': - resolution: {integrity: sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==} + '@typescript-eslint/project-service@8.44.1': + resolution: {integrity: sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/scope-manager@8.14.0': - resolution: {integrity: sha512-aBbBrnW9ARIDn92Zbo7rguLnqQ/pOrUguVpbUwzOhkFg2npFDwTgPGqFqE0H5feXcOoJOfX3SxlJaKEVtq54dw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.16.0': - resolution: {integrity: sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==} + '@typescript-eslint/scope-manager@8.44.1': + resolution: {integrity: sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.14.0': - resolution: {integrity: sha512-Xcz9qOtZuGusVOH5Uk07NGs39wrKkf3AxlkK79RBK6aJC1l03CobXjJbwBPSidetAOV+5rEVuiT1VSBUOAsanQ==} + '@typescript-eslint/tsconfig-utils@8.44.1': + resolution: {integrity: sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.16.0': - resolution: {integrity: sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==} + '@typescript-eslint/type-utils@8.44.1': + resolution: {integrity: sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.14.0': - resolution: {integrity: sha512-yjeB9fnO/opvLJFAsPNYlKPnEM8+z4og09Pk504dkqonT02AyL5Z9SSqlE0XqezS93v6CXn49VHvB2G7XSsl0g==} + '@typescript-eslint/types@8.44.1': + resolution: {integrity: sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/types@8.16.0': - resolution: {integrity: sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.14.0': - resolution: {integrity: sha512-OPXPLYKGZi9XS/49rdaCbR5j/S14HazviBlUQFvSKz3npr3NikF+mrgK7CFVur6XEt95DZp/cmke9d5i3vtVnQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/typescript-estree@8.16.0': - resolution: {integrity: sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==} + '@typescript-eslint/typescript-estree@8.44.1': + resolution: {integrity: sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.14.0': - resolution: {integrity: sha512-OGqj6uB8THhrHj0Fk27DcHPojW7zKwKkPmHXHvQ58pLYp4hy8CSUdTKykKeh+5vFqTTVmjz0zCOOPKRovdsgHA==} + '@typescript-eslint/utils@8.44.1': + resolution: {integrity: sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.16.0': - resolution: {integrity: sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==} + '@typescript-eslint/visitor-keys@8.44.1': + resolution: {integrity: sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - '@typescript-eslint/visitor-keys@8.14.0': - resolution: {integrity: sha512-vG0XZo8AdTH9OE6VFRwAZldNc7qtJ/6NLGWak+BtENuEUXGZgFpihILPiBvKXvJ2nFu27XNGC6rKiwuaoMbYzQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@typescript-eslint/visitor-keys@8.16.0': - resolution: {integrity: sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@vitest/expect@3.1.3': - resolution: {integrity: sha512-7FTQQuuLKmN1Ig/h+h/GO+44Q1IlglPlR2es4ab7Yvfx+Uk5xsv+Ykk+MEt/M2Yn/xGmzaLKxGw2lgy2bwuYqg==} - - '@vitest/mocker@3.1.3': - resolution: {integrity: sha512-PJbLjonJK82uCWHjzgBJZuR7zmAOrSvKk1QBxrennDIgtH4uK0TB1PvYmc0XBCigxxtiAVPfWtAdy4lpz8SQGQ==} + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.1.3': - resolution: {integrity: sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==} + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/runner@3.1.3': - resolution: {integrity: sha512-Tae+ogtlNfFei5DggOsSUvkIaSuVywujMj6HzR97AHK6XK8i3BuVyIifWAm/sE3a15lF5RH9yQIrbXYuo0IFyA==} + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} - '@vitest/snapshot@3.1.3': - resolution: {integrity: sha512-XVa5OPNTYUsyqG9skuUkFzAeFnEzDp8hQu7kZ0N25B1+6KjGm4hWLtURyBbsIAOekfWQ7Wuz/N/XXzgYO3deWQ==} + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - '@vitest/spy@3.1.3': - resolution: {integrity: sha512-x6w+ctOEmEXdWaa6TO4ilb7l9DxPR5bwEb6hILKuxfU1NqWT2mpJD9NJN7t3OTfxmVlOMrvtoFJGdgyzZ605lQ==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/ui@3.1.3': - resolution: {integrity: sha512-IipSzX+8DptUdXN/GWq3hq5z18MwnpphYdOMm0WndkRGYELzfq7NDP8dMpZT7JGW1uXFrIGxOW2D0Xi++ulByg==} + '@vitest/ui@3.2.4': + resolution: {integrity: sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==} peerDependencies: - vitest: 3.1.3 + vitest: 3.2.4 - '@vitest/utils@3.1.3': - resolution: {integrity: sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==} + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true - agent-base@7.1.3: - resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} ajv@6.12.6: @@ -786,24 +765,24 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - ansi-escapes@7.0.0: - resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + ansi-escapes@7.1.1: + resolution: {integrity: sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q==} engines: {node: '>=18'} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} argparse@2.0.1: @@ -837,11 +816,11 @@ packages: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -863,31 +842,31 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - chai@5.2.0: - resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} - engines: {node: '>=12'} + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chalk@5.4.1: resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@2.1.0: + resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} check-error@2.1.1: resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} engines: {node: '>= 16'} - ci-info@4.2.0: - resolution: {integrity: sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==} + ci-info@4.3.0: + resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} engines: {node: '>=8'} cli-boxes@3.0.0: @@ -920,8 +899,8 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} engines: {node: '>=18'} concat-map@0.0.1: @@ -930,8 +909,8 @@ packages: config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - configstore@7.0.0: - resolution: {integrity: sha512-yk7/5PN5im4qwz0WFZW3PXnzHgPu9mX29Y8uZ3aefe2lBPC1FYttWZRcaW9fKkT0pBCJyuQ2HfbmPVaODi9jcQ==} + configstore@7.1.0: + resolution: {integrity: sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==} engines: {node: '>=18'} cosmiconfig@9.0.0: @@ -943,10 +922,6 @@ packages: typescript: optional: true - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -955,17 +930,8 @@ packages: resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} engines: {node: '>= 14'} - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1004,8 +970,8 @@ packages: resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} engines: {node: '>=18'} - emoji-regex@10.4.0: - resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + emoji-regex@10.5.0: + resolution: {integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1018,8 +984,8 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -1042,19 +1008,19 @@ packages: engines: {node: '>=6.0'} hasBin: true - eslint-config-prettier@9.1.0: - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + eslint-config-prettier@9.1.2: + resolution: {integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-prettier@5.2.1: - resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' eslint: '>=8.0.0' - eslint-config-prettier: '*' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' prettier: '>=3.0.0' peerDependenciesMeta: '@types/eslint': @@ -1062,20 +1028,20 @@ packages: eslint-config-prettier: optional: true - eslint-scope@8.2.0: - resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.15.0: - resolution: {integrity: sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==} + eslint@9.36.0: + resolution: {integrity: sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1084,8 +1050,8 @@ packages: jiti: optional: true - espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} esprima@4.0.1: @@ -1123,14 +1089,10 @@ packages: resolution: {integrity: sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==} engines: {node: ^18.19.0 || >=20.5.0} - expect-type@1.2.1: - resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - fast-content-type-parse@2.0.1: resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} @@ -1140,8 +1102,8 @@ packages: fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: @@ -1150,11 +1112,12 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fdir@6.4.4: - resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -1184,16 +1147,9 @@ packages: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} - flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} - fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -1205,8 +1161,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - get-east-asian-width@1.2.0: - resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} engines: {node: '>=18'} get-stream@8.0.1: @@ -1217,8 +1173,8 @@ packages: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} - get-uri@6.0.3: - resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} + get-uri@6.0.5: + resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} engines: {node: '>= 14'} git-up@8.1.1: @@ -1247,8 +1203,8 @@ packages: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.12.0: - resolution: {integrity: sha512-1+gLErljJFhbOVyaetcwJiJ4+eLe45S2E7P5UiZ9xGfeq3ATQf5DOv9G7MH3gGbKQLkzmNh2DxfZwLdw+j6oTQ==} + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} globby@14.0.2: @@ -1288,21 +1244,25 @@ packages: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} - husky@9.1.6: - resolution: {integrity: sha512-sqbjZKK7kf44hfdE94EoX8MZNk0n7HeW37O4YrVGCF4wzgQjp+akPAkfUK5LZ6KuR/6sqeAVuXHji+RzQgOn5A==} + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} hasBin: true - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} engines: {node: '>=0.10.0'} ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} imurmurhash@0.1.4: @@ -1333,15 +1293,15 @@ packages: resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} engines: {node: '>= 0.10'} - ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} engines: {node: '>= 12'} is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} is-docker@3.0.0: @@ -1361,8 +1321,8 @@ packages: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} engines: {node: '>=12'} - is-fullwidth-code-point@5.0.0: - resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} engines: {node: '>=18'} is-glob@4.0.3: @@ -1387,8 +1347,8 @@ packages: resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} engines: {node: '>=12'} - is-npm@6.0.0: - resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} + is-npm@6.1.0: + resolution: {integrity: sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} is-number@7.0.0: @@ -1403,8 +1363,8 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-ssh@1.4.0: - resolution: {integrity: sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==} + is-ssh@1.4.1: + resolution: {integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==} is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} @@ -1436,13 +1396,13 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true - jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -1455,14 +1415,11 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - ky@1.7.2: - resolution: {integrity: sha512-OzIvbHKKDpi60TnF9t7UUVAF1B4mcqc02z5PIvrm08Wyb+yOcz63GRvEuVxNT18a9E1SrNouhB4W2NNLeD7Ykg==} + ky@1.10.0: + resolution: {integrity: sha512-YRPCzHEWZffbfvmRrfwa+5nwBHwZuYiTrfDX0wuhGBPV0pA/zCqcOq93MDssON/baIkpYbvehIX5aLpMxrRhaA==} engines: {node: '>=18'} latest-version@9.0.0: @@ -1473,20 +1430,20 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - lilconfig@3.1.2: - resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lint-staged@15.2.10: - resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} + lint-staged@15.5.2: + resolution: {integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==} engines: {node: '>=18.12.0'} hasBin: true - listr2@8.2.5: - resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} + listr2@8.3.3: + resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} engines: {node: '>=18.0.0'} locate-path@6.0.0: @@ -1522,19 +1479,19 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} - loupe@3.1.3: - resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} lru-cache@7.18.3: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} - macos-release@3.3.0: - resolution: {integrity: sha512-tPJQ1HeyiU2vRruNGhZ+VleWuMQRro8iFtJxYgnS4NQe+EukKF6aGiIT+7flZhISAt2iaXBCfFGvAyif7/f8nQ==} + macos-release@3.4.0: + resolution: {integrity: sha512-wpGPwyg/xrSp4H4Db4xYSeAr6+cFQGHfspHzDUdYxswDnUW0L5Ov63UuJiSr8NMSpyaChO4u1n0MXUvVPtrN6A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -1584,8 +1541,8 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -1635,10 +1592,6 @@ packages: resolution: {integrity: sha512-bv608E0UX86atYi2GMGjDe0vF/X1TJjemNS8oEW6z22YW1Rc3QykSYoGfkQbX0zZX9H0ZB6CQP/3GTf1I5hURg==} engines: {node: '>=18'} - os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1671,8 +1624,8 @@ packages: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} - parse-path@7.0.0: - resolution: {integrity: sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==} + parse-path@7.1.0: + resolution: {integrity: sha512-EuCycjZtfPcjWk7KTksnJ5xPMvWGA/6i4zrLYhRG0hGvC3GPU/jGUj3Cy+ZR0v30duV3e23R95T1lE2+lsndSw==} parse-url@9.2.0: resolution: {integrity: sha512-bCgsFI+GeGWPAvAiUv63ZorMeif3/U0zaXABGJbOWt5OH2KCaPHF6S+0ok4aqM9RuIPGyZdx9tR9l13PsW4AYQ==} @@ -1704,8 +1657,8 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} picocolors@1.1.1: @@ -1715,8 +1668,8 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} pidtree@0.6.0: @@ -1724,8 +1677,8 @@ packages: engines: {node: '>=0.10'} hasBin: true - postcss@8.4.49: - resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: @@ -1736,20 +1689,20 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true - pretty-ms@9.2.0: - resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - protocols@2.0.1: - resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==} + protocols@2.0.2: + resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} proxy-agent@6.5.0: resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} @@ -1762,8 +1715,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pupa@3.1.0: - resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} + pupa@3.3.0: + resolution: {integrity: sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==} engines: {node: '>=12.20'} queue-microtask@1.2.3: @@ -1777,8 +1730,8 @@ packages: resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} engines: {node: '>= 0.10'} - registry-auth-token@5.0.2: - resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + registry-auth-token@5.1.0: + resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==} engines: {node: '>=14'} registry-url@6.0.1: @@ -1794,8 +1747,9 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} hasBin: true restore-cursor@5.1.0: @@ -1806,20 +1760,20 @@ packages: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.27.2: - resolution: {integrity: sha512-KreA+PzWmk2yaFmZVwe6GB2uBD86nXl86OsDkt1bJS9p3vqWuEQ6HnJJ+j/mZi/q0920P99/MVRlB4L3crpF5w==} + rollup@4.52.2: + resolution: {integrity: sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - run-applescript@7.0.0: - resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} run-async@3.0.0: @@ -1829,8 +1783,8 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} @@ -1840,6 +1794,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1860,8 +1819,8 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} slash@5.1.0: @@ -1872,8 +1831,8 @@ packages: resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} engines: {node: '>=12'} - slice-ansi@7.1.0: - resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} smart-buffer@4.2.0: @@ -1884,8 +1843,8 @@ packages: resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} engines: {node: '>= 14'} - socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} + socks@2.8.7: + resolution: {integrity: sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==} engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} source-map-js@1.2.1: @@ -1896,9 +1855,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -1925,8 +1881,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-final-newline@3.0.0: @@ -1945,6 +1901,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strip-literal@3.0.0: + resolution: {integrity: sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==} + stubborn-fs@1.2.5: resolution: {integrity: sha512-H2N9c26eXjzL/S/K+i/RHHcFanE74dptvvjM8iwzwbVcWY/zjBbgRqF3K0DY4+OD+uTTASTBvDoxPDaPN02D7g==} @@ -1956,8 +1915,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - synckit@0.9.1: - resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} engines: {node: ^14.18.0 || >=16.0.0} tinybench@2.9.0: @@ -1966,26 +1925,22 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} - tinypool@1.0.2: - resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} engines: {node: '>=14.0.0'} - tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1994,11 +1949,11 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - ts-api-utils@1.4.0: - resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==} - engines: {node: '>=16'} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>=4.2.0' + typescript: '>=4.8.4' tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -2015,27 +1970,24 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} - type-fest@4.26.1: - resolution: {integrity: sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} - typescript-eslint@8.16.0: - resolution: {integrity: sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==} + typescript-eslint@8.44.1: + resolution: {integrity: sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <6.0.0' - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + typescript@5.9.2: + resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} engines: {node: '>=14.17'} hasBin: true - undici-types@7.8.0: - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + undici-types@7.12.0: + resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==} undici@6.21.1: resolution: {integrity: sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==} @@ -2052,10 +2004,6 @@ packages: universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - update-notifier@7.3.1: resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==} engines: {node: '>=18'} @@ -2067,13 +2015,13 @@ packages: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - vite-node@3.1.3: - resolution: {integrity: sha512-uHV4plJ2IxCl4u1up1FQRrqclylKAogbtBfOTwcuJ28xFi+89PZ57BRh+naIRvH70HPwxy5QHYzg1OrEaC7AbA==} + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@5.4.11: - resolution: {integrity: sha512-c7jFQRklXua0mTzneGW9QVyxFjUgwcihC4bXEtujIo2ouWCe1Ajt/amn2PCxYnhYfd5k09JX3SB7OYWFKYqj8Q==} + vite@5.4.20: + resolution: {integrity: sha512-j3lYzGC3P+B5Yfy/pfKNgVEg4+UtcIJcVRt2cDjIOmhLourAqPqf8P7acgxeiSgUB7E3p2P8/3gNIgDLpwzs4g==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2103,16 +2051,16 @@ packages: terser: optional: true - vitest@3.1.3: - resolution: {integrity: sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw==} + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.1.3 - '@vitest/ui': 3.1.3 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -2131,8 +2079,8 @@ packages: jsdom: optional: true - when-exit@2.1.3: - resolution: {integrity: sha512-uVieSTccFIr/SFQdFWN/fFaQYmV37OKtuaGphMAzi4DmmUlrvRBJW5WSLkHyjNQY/ePJMz3LoiX9R3yy1Su6Hw==} + when-exit@2.1.4: + resolution: {integrity: sha512-4rnvd3A1t16PWzrBUcSDZqcAmsUIy4minDXT/CZ8F2mVDgd65i4Aalimgz1aQkRGU0iH5eT5+6Rx2TK8o443Pg==} which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} @@ -2163,8 +2111,8 @@ packages: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} - wrap-ansi@9.0.0: - resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} wrappy@1.0.2: @@ -2174,9 +2122,9 @@ packages: resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} engines: {node: '>=12'} - yaml@2.5.1: - resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} - engines: {node: '>= 14'} + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} hasBin: true yargs-parser@21.1.1: @@ -2187,23 +2135,23 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} + yoctocolors-cjs@2.1.3: + resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} - yoctocolors@2.1.1: - resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} snapshots: - '@babel/code-frame@7.26.2': + '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.27.1 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.27.1': {} '@esbuild/aix-ppc64@0.21.5': optional: true @@ -2274,179 +2222,191 @@ snapshots: '@esbuild/win32-x64@0.21.5': optional: true - '@eslint-community/eslint-utils@4.4.1(eslint@9.15.0)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.36.0)': dependencies: - eslint: 9.15.0 + eslint: 9.36.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.19.0': + '@eslint/config-array@0.21.0': dependencies: - '@eslint/object-schema': 2.1.4 - debug: 4.3.7 + '@eslint/object-schema': 2.1.6 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/core@0.9.0': {} + '@eslint/config-helpers@0.3.1': {} - '@eslint/eslintrc@3.2.0': + '@eslint/core@0.15.2': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.3.7 - espree: 10.3.0 + debug: 4.4.3 + espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 - import-fresh: 3.3.0 + import-fresh: 3.3.1 js-yaml: 4.1.0 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.15.0': {} + '@eslint/js@9.36.0': {} - '@eslint/object-schema@2.1.4': {} + '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.2.3': + '@eslint/plugin-kit@0.3.5': dependencies: + '@eslint/core': 0.15.2 levn: 0.4.1 '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.6': + '@humanfs/node@0.16.7': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.1': {} - - '@humanwhocodes/retry@0.4.1': {} + '@humanwhocodes/retry@0.4.3': {} '@iarna/toml@2.2.5': {} - '@inquirer/checkbox@4.1.8(@types/node@24.0.1)': + '@inquirer/ansi@1.0.0': {} + + '@inquirer/checkbox@4.2.4(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@24.0.1) - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.2 + '@inquirer/ansi': 1.0.0 + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.5.2) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/confirm@5.1.12(@types/node@24.0.1)': + '@inquirer/confirm@5.1.18(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/type': 3.0.7(@types/node@24.0.1) + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/type': 3.0.8(@types/node@24.5.2) optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/core@10.1.13(@types/node@24.0.1)': + '@inquirer/core@10.2.2(@types/node@24.5.2)': dependencies: - '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@24.0.1) - ansi-escapes: 4.3.2 + '@inquirer/ansi': 1.0.0 + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.5.2) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.2 + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/editor@4.2.13(@types/node@24.0.1)': + '@inquirer/editor@4.2.20(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/type': 3.0.7(@types/node@24.0.1) - external-editor: 3.1.0 + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/external-editor': 1.0.2(@types/node@24.5.2) + '@inquirer/type': 3.0.8(@types/node@24.5.2) optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/expand@4.0.15(@types/node@24.0.1)': + '@inquirer/expand@4.0.20(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/type': 3.0.7(@types/node@24.0.1) - yoctocolors-cjs: 2.1.2 + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/type': 3.0.8(@types/node@24.5.2) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/figures@1.0.12': {} + '@inquirer/external-editor@1.0.2(@types/node@24.5.2)': + dependencies: + chardet: 2.1.0 + iconv-lite: 0.7.0 + optionalDependencies: + '@types/node': 24.5.2 - '@inquirer/input@4.1.12(@types/node@24.0.1)': + '@inquirer/figures@1.0.13': {} + + '@inquirer/input@4.2.4(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/type': 3.0.7(@types/node@24.0.1) + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/type': 3.0.8(@types/node@24.5.2) optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/number@3.0.15(@types/node@24.0.1)': + '@inquirer/number@3.0.20(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/type': 3.0.7(@types/node@24.0.1) + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/type': 3.0.8(@types/node@24.5.2) optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/password@4.0.15(@types/node@24.0.1)': + '@inquirer/password@4.0.20(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/type': 3.0.7(@types/node@24.0.1) - ansi-escapes: 4.3.2 + '@inquirer/ansi': 1.0.0 + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/type': 3.0.8(@types/node@24.5.2) optionalDependencies: - '@types/node': 24.0.1 - - '@inquirer/prompts@7.5.3(@types/node@24.0.1)': - dependencies: - '@inquirer/checkbox': 4.1.8(@types/node@24.0.1) - '@inquirer/confirm': 5.1.12(@types/node@24.0.1) - '@inquirer/editor': 4.2.13(@types/node@24.0.1) - '@inquirer/expand': 4.0.15(@types/node@24.0.1) - '@inquirer/input': 4.1.12(@types/node@24.0.1) - '@inquirer/number': 3.0.15(@types/node@24.0.1) - '@inquirer/password': 4.0.15(@types/node@24.0.1) - '@inquirer/rawlist': 4.1.3(@types/node@24.0.1) - '@inquirer/search': 3.0.15(@types/node@24.0.1) - '@inquirer/select': 4.2.3(@types/node@24.0.1) + '@types/node': 24.5.2 + + '@inquirer/prompts@7.8.6(@types/node@24.5.2)': + dependencies: + '@inquirer/checkbox': 4.2.4(@types/node@24.5.2) + '@inquirer/confirm': 5.1.18(@types/node@24.5.2) + '@inquirer/editor': 4.2.20(@types/node@24.5.2) + '@inquirer/expand': 4.0.20(@types/node@24.5.2) + '@inquirer/input': 4.2.4(@types/node@24.5.2) + '@inquirer/number': 3.0.20(@types/node@24.5.2) + '@inquirer/password': 4.0.20(@types/node@24.5.2) + '@inquirer/rawlist': 4.1.8(@types/node@24.5.2) + '@inquirer/search': 3.1.3(@types/node@24.5.2) + '@inquirer/select': 4.3.4(@types/node@24.5.2) optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/rawlist@4.1.3(@types/node@24.0.1)': + '@inquirer/rawlist@4.1.8(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/type': 3.0.7(@types/node@24.0.1) - yoctocolors-cjs: 2.1.2 + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/type': 3.0.8(@types/node@24.5.2) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/search@3.0.15(@types/node@24.0.1)': + '@inquirer/search@3.1.3(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@24.0.1) - yoctocolors-cjs: 2.1.2 + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.5.2) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/select@4.2.3(@types/node@24.0.1)': + '@inquirer/select@4.3.4(@types/node@24.5.2)': dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/figures': 1.0.12 - '@inquirer/type': 3.0.7(@types/node@24.0.1) - ansi-escapes: 4.3.2 - yoctocolors-cjs: 2.1.2 + '@inquirer/ansi': 1.0.0 + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/figures': 1.0.13 + '@inquirer/type': 3.0.8(@types/node@24.5.2) + yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@inquirer/type@3.0.7(@types/node@24.0.1)': + '@inquirer/type@3.0.8(@types/node@24.5.2)': optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 - '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@lightningjs/renderer@3.0.0-beta7': {} + '@lightningjs/renderer@3.0.0-beta14': {} '@nodelib/fs.scandir@2.1.5': dependencies: @@ -2458,15 +2418,15 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 + fastq: 1.19.1 '@octokit/auth-token@5.1.2': {} - '@octokit/core@6.1.5': + '@octokit/core@6.1.6': dependencies: '@octokit/auth-token': 5.1.2 '@octokit/graphql': 8.2.2 - '@octokit/request': 9.2.3 + '@octokit/request': 9.2.4 '@octokit/request-error': 6.1.8 '@octokit/types': 14.1.0 before-after-hook: 3.0.2 @@ -2479,33 +2439,33 @@ snapshots: '@octokit/graphql@8.2.2': dependencies: - '@octokit/request': 9.2.3 + '@octokit/request': 9.2.4 '@octokit/types': 14.1.0 universal-user-agent: 7.0.3 - '@octokit/openapi-types@22.2.0': {} + '@octokit/openapi-types@24.2.0': {} '@octokit/openapi-types@25.1.0': {} - '@octokit/plugin-paginate-rest@11.3.1(@octokit/core@6.1.5)': + '@octokit/plugin-paginate-rest@11.6.0(@octokit/core@6.1.6)': dependencies: - '@octokit/core': 6.1.5 - '@octokit/types': 13.6.1 + '@octokit/core': 6.1.6 + '@octokit/types': 13.10.0 - '@octokit/plugin-request-log@5.3.1(@octokit/core@6.1.5)': + '@octokit/plugin-request-log@5.3.1(@octokit/core@6.1.6)': dependencies: - '@octokit/core': 6.1.5 + '@octokit/core': 6.1.6 - '@octokit/plugin-rest-endpoint-methods@13.2.2(@octokit/core@6.1.5)': + '@octokit/plugin-rest-endpoint-methods@13.5.0(@octokit/core@6.1.6)': dependencies: - '@octokit/core': 6.1.5 - '@octokit/types': 13.6.1 + '@octokit/core': 6.1.6 + '@octokit/types': 13.10.0 '@octokit/request-error@6.1.8': dependencies: '@octokit/types': 14.1.0 - '@octokit/request@9.2.3': + '@octokit/request@9.2.4': dependencies: '@octokit/endpoint': 10.1.4 '@octokit/request-error': 6.1.8 @@ -2515,20 +2475,20 @@ snapshots: '@octokit/rest@21.0.2': dependencies: - '@octokit/core': 6.1.5 - '@octokit/plugin-paginate-rest': 11.3.1(@octokit/core@6.1.5) - '@octokit/plugin-request-log': 5.3.1(@octokit/core@6.1.5) - '@octokit/plugin-rest-endpoint-methods': 13.2.2(@octokit/core@6.1.5) + '@octokit/core': 6.1.6 + '@octokit/plugin-paginate-rest': 11.6.0(@octokit/core@6.1.6) + '@octokit/plugin-request-log': 5.3.1(@octokit/core@6.1.6) + '@octokit/plugin-rest-endpoint-methods': 13.5.0(@octokit/core@6.1.6) - '@octokit/types@13.6.1': + '@octokit/types@13.10.0': dependencies: - '@octokit/openapi-types': 22.2.0 + '@octokit/openapi-types': 24.2.0 '@octokit/types@14.1.0': dependencies: '@octokit/openapi-types': 25.1.0 - '@pkgr/core@0.1.1': {} + '@pkgr/core@0.2.9': {} '@pnpm/config.env-replace@1.1.0': {} @@ -2544,58 +2504,70 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@rollup/rollup-android-arm-eabi@4.27.2': + '@rollup/rollup-android-arm-eabi@4.52.2': + optional: true + + '@rollup/rollup-android-arm64@4.52.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.52.2': + optional: true + + '@rollup/rollup-darwin-x64@4.52.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.52.2': optional: true - '@rollup/rollup-android-arm64@4.27.2': + '@rollup/rollup-freebsd-x64@4.52.2': optional: true - '@rollup/rollup-darwin-arm64@4.27.2': + '@rollup/rollup-linux-arm-gnueabihf@4.52.2': optional: true - '@rollup/rollup-darwin-x64@4.27.2': + '@rollup/rollup-linux-arm-musleabihf@4.52.2': optional: true - '@rollup/rollup-freebsd-arm64@4.27.2': + '@rollup/rollup-linux-arm64-gnu@4.52.2': optional: true - '@rollup/rollup-freebsd-x64@4.27.2': + '@rollup/rollup-linux-arm64-musl@4.52.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.27.2': + '@rollup/rollup-linux-loong64-gnu@4.52.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.27.2': + '@rollup/rollup-linux-ppc64-gnu@4.52.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.27.2': + '@rollup/rollup-linux-riscv64-gnu@4.52.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.27.2': + '@rollup/rollup-linux-riscv64-musl@4.52.2': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.27.2': + '@rollup/rollup-linux-s390x-gnu@4.52.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.27.2': + '@rollup/rollup-linux-x64-gnu@4.52.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.27.2': + '@rollup/rollup-linux-x64-musl@4.52.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.27.2': + '@rollup/rollup-openharmony-arm64@4.52.2': optional: true - '@rollup/rollup-linux-x64-musl@4.27.2': + '@rollup/rollup-win32-arm64-msvc@4.52.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.27.2': + '@rollup/rollup-win32-ia32-msvc@4.52.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.27.2': + '@rollup/rollup-win32-x64-gnu@4.52.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.27.2': + '@rollup/rollup-win32-x64-msvc@4.52.2': optional: true '@sec-ant/readable-stream@0.4.1': {} @@ -2606,248 +2578,186 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + + '@types/deep-eql@4.0.2': {} + '@types/eslint@9.6.1': dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 '@types/eslint__js@8.42.3': dependencies: '@types/eslint': 9.6.1 - '@types/estree@1.0.6': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} - '@types/node@24.0.1': + '@types/node@24.5.2': dependencies: - undici-types: 7.8.0 + undici-types: 7.12.0 '@types/parse-path@7.1.0': dependencies: - parse-path: 7.0.0 + parse-path: 7.1.0 - '@typescript-eslint/eslint-plugin@8.14.0(@typescript-eslint/parser@8.14.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0)(typescript@5.9.2))(eslint@9.36.0)(typescript@5.9.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.14.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.14.0 - '@typescript-eslint/type-utils': 8.14.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.14.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.14.0 - eslint: 9.15.0 + '@typescript-eslint/parser': 8.44.1(eslint@9.36.0)(typescript@5.9.2) + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/type-utils': 8.44.1(eslint@9.36.0)(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0)(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.44.1 + eslint: 9.36.0 graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/eslint-plugin@8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.16.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.16.0 - '@typescript-eslint/type-utils': 8.16.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.16.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.16.0 - eslint: 9.15.0 - graphemer: 1.4.0 - ignore: 5.3.2 + ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.14.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/parser@8.44.1(eslint@9.36.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 8.14.0 - '@typescript-eslint/types': 8.14.0 - '@typescript-eslint/typescript-estree': 8.14.0(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.14.0 - debug: 4.3.7 - eslint: 9.15.0 - optionalDependencies: - typescript: 5.6.3 + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/visitor-keys': 8.44.1 + debug: 4.4.3 + eslint: 9.36.0 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.16.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/project-service@8.44.1(typescript@5.9.2)': dependencies: - '@typescript-eslint/scope-manager': 8.16.0 - '@typescript-eslint/types': 8.16.0 - '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.16.0 - debug: 4.3.7 - eslint: 9.15.0 - optionalDependencies: - typescript: 5.6.3 + '@typescript-eslint/tsconfig-utils': 8.44.1(typescript@5.9.2) + '@typescript-eslint/types': 8.44.1 + debug: 4.4.3 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.14.0': + '@typescript-eslint/scope-manager@8.44.1': dependencies: - '@typescript-eslint/types': 8.14.0 - '@typescript-eslint/visitor-keys': 8.14.0 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/visitor-keys': 8.44.1 - '@typescript-eslint/scope-manager@8.16.0': + '@typescript-eslint/tsconfig-utils@8.44.1(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 8.16.0 - '@typescript-eslint/visitor-keys': 8.16.0 + typescript: 5.9.2 - '@typescript-eslint/type-utils@8.14.0(eslint@9.15.0)(typescript@5.6.3)': + '@typescript-eslint/type-utils@8.44.1(eslint@9.36.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.14.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.14.0(eslint@9.15.0)(typescript@5.6.3) - debug: 4.3.7 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0)(typescript@5.9.2) + debug: 4.4.3 + eslint: 9.36.0 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 transitivePeerDependencies: - - eslint - supports-color - '@typescript-eslint/type-utils@8.16.0(eslint@9.15.0)(typescript@5.6.3)': - dependencies: - '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.16.0(eslint@9.15.0)(typescript@5.6.3) - debug: 4.3.7 - eslint: 9.15.0 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.14.0': {} - - '@typescript-eslint/types@8.16.0': {} + '@typescript-eslint/types@8.44.1': {} - '@typescript-eslint/typescript-estree@8.14.0(typescript@5.6.3)': + '@typescript-eslint/typescript-estree@8.44.1(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 8.14.0 - '@typescript-eslint/visitor-keys': 8.14.0 - debug: 4.3.7 - fast-glob: 3.3.2 + '@typescript-eslint/project-service': 8.44.1(typescript@5.9.2) + '@typescript-eslint/tsconfig-utils': 8.44.1(typescript@5.9.2) + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/visitor-keys': 8.44.1 + debug: 4.4.3 + fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.9.2) + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/typescript-estree@8.16.0(typescript@5.6.3)': + '@typescript-eslint/utils@8.44.1(eslint@9.36.0)(typescript@5.9.2)': dependencies: - '@typescript-eslint/types': 8.16.0 - '@typescript-eslint/visitor-keys': 8.16.0 - debug: 4.3.7 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.4.0(typescript@5.6.3) - optionalDependencies: - typescript: 5.6.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.14.0(eslint@9.15.0)(typescript@5.6.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) - '@typescript-eslint/scope-manager': 8.14.0 - '@typescript-eslint/types': 8.14.0 - '@typescript-eslint/typescript-estree': 8.14.0(typescript@5.6.3) - eslint: 9.15.0 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/utils@8.16.0(eslint@9.15.0)(typescript@5.6.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) - '@typescript-eslint/scope-manager': 8.16.0 - '@typescript-eslint/types': 8.16.0 - '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.6.3) - eslint: 9.15.0 - optionalDependencies: - typescript: 5.6.3 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0) + '@typescript-eslint/scope-manager': 8.44.1 + '@typescript-eslint/types': 8.44.1 + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + eslint: 9.36.0 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.14.0': + '@typescript-eslint/visitor-keys@8.44.1': dependencies: - '@typescript-eslint/types': 8.14.0 - eslint-visitor-keys: 3.4.3 - - '@typescript-eslint/visitor-keys@8.16.0': - dependencies: - '@typescript-eslint/types': 8.16.0 - eslint-visitor-keys: 4.2.0 + '@typescript-eslint/types': 8.44.1 + eslint-visitor-keys: 4.2.1 - '@vitest/expect@3.1.3': + '@vitest/expect@3.2.4': dependencies: - '@vitest/spy': 3.1.3 - '@vitest/utils': 3.1.3 - chai: 5.2.0 + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.3(vite@5.4.11(@types/node@24.0.1))': + '@vitest/mocker@3.2.4(vite@5.4.20(@types/node@24.5.2))': dependencies: - '@vitest/spy': 3.1.3 + '@vitest/spy': 3.2.4 estree-walker: 3.0.3 - magic-string: 0.30.17 + magic-string: 0.30.19 optionalDependencies: - vite: 5.4.11(@types/node@24.0.1) + vite: 5.4.20(@types/node@24.5.2) - '@vitest/pretty-format@3.1.3': + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.1.3': + '@vitest/runner@3.2.4': dependencies: - '@vitest/utils': 3.1.3 + '@vitest/utils': 3.2.4 pathe: 2.0.3 + strip-literal: 3.0.0 - '@vitest/snapshot@3.1.3': + '@vitest/snapshot@3.2.4': dependencies: - '@vitest/pretty-format': 3.1.3 - magic-string: 0.30.17 + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.19 pathe: 2.0.3 - '@vitest/spy@3.1.3': + '@vitest/spy@3.2.4': dependencies: - tinyspy: 3.0.2 + tinyspy: 4.0.4 - '@vitest/ui@3.1.3(vitest@3.1.3)': + '@vitest/ui@3.2.4(vitest@3.2.4)': dependencies: - '@vitest/utils': 3.1.3 + '@vitest/utils': 3.2.4 fflate: 0.8.2 flatted: 3.3.3 pathe: 2.0.3 - sirv: 3.0.1 - tinyglobby: 0.2.13 + sirv: 3.0.2 + tinyglobby: 0.2.15 tinyrainbow: 2.0.0 - vitest: 3.1.3(@types/node@24.0.1)(@vitest/ui@3.1.3) + vitest: 3.2.4(@types/node@24.5.2)(@vitest/ui@3.2.4) - '@vitest/utils@3.1.3': + '@vitest/utils@3.2.4': dependencies: - '@vitest/pretty-format': 3.1.3 - loupe: 3.1.3 + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 tinyrainbow: 2.0.0 - acorn-jsx@5.3.2(acorn@8.14.0): + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - acorn: 8.14.0 + acorn: 8.15.0 - acorn@8.14.0: {} + acorn@8.15.0: {} - agent-base@7.1.3: {} + agent-base@7.1.4: {} ajv@6.12.6: dependencies: @@ -2864,19 +2774,19 @@ snapshots: dependencies: type-fest: 0.21.3 - ansi-escapes@7.0.0: + ansi-escapes@7.1.1: dependencies: environment: 1.1.0 ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} argparse@2.0.1: {} @@ -2893,7 +2803,7 @@ snapshots: atomically@2.0.3: dependencies: stubborn-fs: 1.2.5 - when-exit: 2.1.3 + when-exit: 2.1.4 balanced-match@1.0.2: {} @@ -2908,16 +2818,16 @@ snapshots: chalk: 5.4.1 cli-boxes: 3.0.0 string-width: 7.2.0 - type-fest: 4.26.1 + type-fest: 4.41.0 widest-line: 5.0.0 - wrap-ansi: 9.0.0 + wrap-ansi: 9.0.2 - brace-expansion@1.1.11: + brace-expansion@1.1.12: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -2927,7 +2837,7 @@ snapshots: bundle-name@4.1.0: dependencies: - run-applescript: 7.0.0 + run-applescript: 7.1.0 cac@6.7.14: {} @@ -2935,28 +2845,28 @@ snapshots: camelcase@8.0.0: {} - chai@5.2.0: + chai@5.3.3: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 deep-eql: 5.0.2 - loupe: 3.1.3 - pathval: 2.0.0 + loupe: 3.2.1 + pathval: 2.0.1 chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.3.0: {} - chalk@5.4.1: {} - chardet@0.7.0: {} + chalk@5.6.2: {} + + chardet@2.1.0: {} check-error@2.1.1: {} - ci-info@4.2.0: {} + ci-info@4.3.0: {} cli-boxes@3.0.0: {} @@ -2981,7 +2891,7 @@ snapshots: colorette@2.0.20: {} - commander@12.1.0: {} + commander@13.1.0: {} concat-map@0.0.1: {} @@ -2990,27 +2900,21 @@ snapshots: ini: 1.3.8 proto-list: 1.2.4 - configstore@7.0.0: + configstore@7.1.0: dependencies: atomically: 2.0.3 dot-prop: 9.0.0 graceful-fs: 4.2.11 xdg-basedir: 5.1.0 - cosmiconfig@9.0.0(typescript@5.6.3): + cosmiconfig@9.0.0(typescript@5.9.2): dependencies: env-paths: 2.2.1 - import-fresh: 3.3.0 + import-fresh: 3.3.1 js-yaml: 4.1.0 parse-json: 5.2.0 optionalDependencies: - typescript: 5.6.3 - - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 + typescript: 5.9.2 cross-spawn@7.0.6: dependencies: @@ -3020,11 +2924,7 @@ snapshots: data-uri-to-buffer@6.0.2: {} - debug@4.3.7: - dependencies: - ms: 2.1.3 - - debug@4.4.0: + debug@4.4.3: dependencies: ms: 2.1.3 @@ -3051,9 +2951,9 @@ snapshots: dot-prop@9.0.0: dependencies: - type-fest: 4.26.1 + type-fest: 4.41.0 - emoji-regex@10.4.0: {} + emoji-regex@10.5.0: {} emoji-regex@8.0.0: {} @@ -3061,7 +2961,7 @@ snapshots: environment@1.1.0: {} - error-ex@1.3.2: + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -3105,51 +3005,52 @@ snapshots: optionalDependencies: source-map: 0.6.1 - eslint-config-prettier@9.1.0(eslint@9.15.0): + eslint-config-prettier@9.1.2(eslint@9.36.0): dependencies: - eslint: 9.15.0 + eslint: 9.36.0 - eslint-plugin-prettier@5.2.1(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.15.0))(eslint@9.15.0)(prettier@3.3.3): + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint-config-prettier@9.1.2(eslint@9.36.0))(eslint@9.36.0)(prettier@3.6.2): dependencies: - eslint: 9.15.0 - prettier: 3.3.3 + eslint: 9.36.0 + prettier: 3.6.2 prettier-linter-helpers: 1.0.0 - synckit: 0.9.1 + synckit: 0.11.11 optionalDependencies: '@types/eslint': 9.6.1 - eslint-config-prettier: 9.1.0(eslint@9.15.0) + eslint-config-prettier: 9.1.2(eslint@9.36.0) - eslint-scope@8.2.0: + eslint-scope@8.4.0: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} - eslint-visitor-keys@4.2.0: {} + eslint-visitor-keys@4.2.1: {} - eslint@9.15.0: + eslint@9.36.0: dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.19.0 - '@eslint/core': 0.9.0 - '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.15.0 - '@eslint/plugin-kit': 0.2.3 - '@humanfs/node': 0.16.6 + '@eslint/config-array': 0.21.0 + '@eslint/config-helpers': 0.3.1 + '@eslint/core': 0.15.2 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.36.0 + '@eslint/plugin-kit': 0.3.5 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 - '@types/estree': 1.0.6 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.3.7 + debug: 4.4.3 escape-string-regexp: 4.0.0 - eslint-scope: 8.2.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 @@ -3167,11 +3068,11 @@ snapshots: transitivePeerDependencies: - supports-color - espree@10.3.0: + espree@10.4.0: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.2.0 + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 esprima@4.0.1: {} @@ -3187,7 +3088,7 @@ snapshots: estree-walker@3.0.3: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 esutils@2.0.3: {} @@ -3195,7 +3096,7 @@ snapshots: execa@8.0.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 8.0.1 human-signals: 5.0.0 is-stream: 3.0.0 @@ -3215,18 +3116,12 @@ snapshots: is-plain-obj: 4.1.0 is-stream: 4.0.1 npm-run-path: 6.0.0 - pretty-ms: 9.2.0 + pretty-ms: 9.3.0 signal-exit: 4.1.0 strip-final-newline: 4.0.0 - yoctocolors: 2.1.1 - - expect-type@1.2.1: {} + yoctocolors: 2.1.2 - external-editor@3.1.0: - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 + expect-type@1.2.2: {} fast-content-type-parse@2.0.1: {} @@ -3234,7 +3129,7 @@ snapshots: fast-diff@1.3.0: {} - fast-glob@3.3.2: + fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -3246,13 +3141,13 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.17.1: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 - fdir@6.4.4(picomatch@4.0.2): + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: - picomatch: 4.0.2 + picomatch: 4.0.3 fflate@0.8.2: {} @@ -3275,19 +3170,11 @@ snapshots: flat-cache@4.0.1: dependencies: - flatted: 3.3.1 + flatted: 3.3.3 keyv: 4.5.4 - flatted@3.3.1: {} - flatted@3.3.3: {} - fs-extra@11.2.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -3295,7 +3182,7 @@ snapshots: function-bind@1.1.2: {} - get-east-asian-width@1.2.0: {} + get-east-asian-width@1.4.0: {} get-stream@8.0.1: {} @@ -3304,18 +3191,17 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 - get-uri@6.0.3: + get-uri@6.0.5: dependencies: basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 - debug: 4.4.0 - fs-extra: 11.2.0 + debug: 4.4.3 transitivePeerDependencies: - supports-color git-up@8.1.1: dependencies: - is-ssh: 1.4.0 + is-ssh: 1.4.1 parse-url: 9.2.0 git-url-parse@16.0.0: @@ -3345,12 +3231,12 @@ snapshots: globals@14.0.0: {} - globals@15.12.0: {} + globals@15.15.0: {} globby@14.0.2: dependencies: '@sindresorhus/merge-streams': 2.3.0 - fast-glob: 3.3.2 + fast-glob: 3.3.3 ignore: 5.3.2 path-type: 5.0.0 slash: 5.1.0 @@ -3370,15 +3256,15 @@ snapshots: http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 + agent-base: 7.1.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 + agent-base: 7.1.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color @@ -3386,15 +3272,17 @@ snapshots: human-signals@8.0.1: {} - husky@9.1.6: {} + husky@9.1.7: {} - iconv-lite@0.4.24: + iconv-lite@0.7.0: dependencies: safer-buffer: 2.1.2 ignore@5.3.2: {} - import-fresh@3.3.0: + ignore@7.0.5: {} + + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 @@ -3412,27 +3300,24 @@ snapshots: ini@4.1.1: {} - inquirer@12.3.0(@types/node@24.0.1): + inquirer@12.3.0(@types/node@24.5.2): dependencies: - '@inquirer/core': 10.1.13(@types/node@24.0.1) - '@inquirer/prompts': 7.5.3(@types/node@24.0.1) - '@inquirer/type': 3.0.7(@types/node@24.0.1) - '@types/node': 24.0.1 + '@inquirer/core': 10.2.2(@types/node@24.5.2) + '@inquirer/prompts': 7.8.6(@types/node@24.5.2) + '@inquirer/type': 3.0.8(@types/node@24.5.2) + '@types/node': 24.5.2 ansi-escapes: 4.3.2 mute-stream: 2.0.0 run-async: 3.0.0 - rxjs: 7.8.1 + rxjs: 7.8.2 interpret@1.4.0: {} - ip-address@9.0.5: - dependencies: - jsbn: 1.1.0 - sprintf-js: 1.1.3 + ip-address@10.0.1: {} is-arrayish@0.2.1: {} - is-core-module@2.15.1: + is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -3444,9 +3329,9 @@ snapshots: is-fullwidth-code-point@4.0.0: {} - is-fullwidth-code-point@5.0.0: + is-fullwidth-code-point@5.1.0: dependencies: - get-east-asian-width: 1.2.0 + get-east-asian-width: 1.4.0 is-glob@4.0.3: dependencies: @@ -3465,7 +3350,7 @@ snapshots: is-interactive@2.0.0: {} - is-npm@6.0.0: {} + is-npm@6.1.0: {} is-number@7.0.0: {} @@ -3473,9 +3358,9 @@ snapshots: is-plain-obj@4.1.0: {} - is-ssh@1.4.0: + is-ssh@1.4.1: dependencies: - protocols: 2.0.1 + protocols: 2.0.2 is-stream@3.0.0: {} @@ -3501,12 +3386,12 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - jsbn@1.1.0: {} - json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} @@ -3515,17 +3400,11 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} - jsonfile@6.1.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - keyv@4.5.4: dependencies: json-buffer: 3.0.1 - ky@1.7.2: {} + ky@1.10.0: {} latest-version@9.0.0: dependencies: @@ -3536,33 +3415,33 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - lilconfig@3.1.2: {} + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} - lint-staged@15.2.10: + lint-staged@15.5.2: dependencies: - chalk: 5.3.0 - commander: 12.1.0 - debug: 4.3.7 + chalk: 5.6.2 + commander: 13.1.0 + debug: 4.4.3 execa: 8.0.1 - lilconfig: 3.1.2 - listr2: 8.2.5 + lilconfig: 3.1.3 + listr2: 8.3.3 micromatch: 4.0.8 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.5.1 + yaml: 2.8.1 transitivePeerDependencies: - supports-color - listr2@8.2.5: + listr2@8.3.3: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 eventemitter3: 5.0.1 log-update: 6.1.0 rfdc: 1.4.1 - wrap-ansi: 9.0.0 + wrap-ansi: 9.0.2 locate-path@6.0.0: dependencies: @@ -3589,21 +3468,21 @@ snapshots: log-update@6.1.0: dependencies: - ansi-escapes: 7.0.0 + ansi-escapes: 7.1.1 cli-cursor: 5.0.0 - slice-ansi: 7.1.0 - strip-ansi: 7.1.0 - wrap-ansi: 9.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 - loupe@3.1.3: {} + loupe@3.2.1: {} lru-cache@7.18.3: {} - macos-release@3.3.0: {} + macos-release@3.4.0: {} - magic-string@0.30.17: + magic-string@0.30.19: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 merge-stream@2.0.0: {} @@ -3626,11 +3505,11 @@ snapshots: minimatch@3.1.2: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.12 minimatch@9.0.5: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.0.2 minimist@1.2.8: {} @@ -3640,7 +3519,7 @@ snapshots: mute-stream@2.0.0: {} - nanoid@3.3.7: {} + nanoid@3.3.11: {} natural-compare@1.4.0: {} @@ -3697,15 +3576,13 @@ snapshots: log-symbols: 6.0.0 stdin-discarder: 0.2.2 string-width: 7.2.0 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 os-name@6.0.0: dependencies: - macos-release: 3.3.0 + macos-release: 3.4.0 windows-release: 6.1.0 - os-tmpdir@1.0.2: {} - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -3717,9 +3594,9 @@ snapshots: pac-proxy-agent@7.2.0: dependencies: '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.3 - debug: 4.4.0 - get-uri: 6.0.3 + agent-base: 7.1.4 + debug: 4.4.3 + get-uri: 6.0.5 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 pac-resolver: 7.0.1 @@ -3734,8 +3611,8 @@ snapshots: package-json@10.0.1: dependencies: - ky: 1.7.2 - registry-auth-token: 5.0.2 + ky: 1.10.0 + registry-auth-token: 5.1.0 registry-url: 6.0.1 semver: 7.6.3 @@ -3745,21 +3622,21 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.26.2 - error-ex: 1.3.2 + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 parse-ms@4.0.0: {} - parse-path@7.0.0: + parse-path@7.1.0: dependencies: - protocols: 2.0.1 + protocols: 2.0.2 parse-url@9.2.0: dependencies: '@types/parse-path': 7.1.0 - parse-path: 7.0.0 + parse-path: 7.1.0 path-exists@4.0.0: {} @@ -3775,19 +3652,19 @@ snapshots: pathe@2.0.3: {} - pathval@2.0.0: {} + pathval@2.0.1: {} picocolors@1.1.1: {} picomatch@2.3.1: {} - picomatch@4.0.2: {} + picomatch@4.0.3: {} pidtree@0.6.0: {} - postcss@8.4.49: + postcss@8.5.6: dependencies: - nanoid: 3.3.7 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -3797,20 +3674,20 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier@3.3.3: {} + prettier@3.6.2: {} - pretty-ms@9.2.0: + pretty-ms@9.3.0: dependencies: parse-ms: 4.0.0 proto-list@1.2.4: {} - protocols@2.0.1: {} + protocols@2.0.2: {} proxy-agent@6.5.0: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 + agent-base: 7.1.4 + debug: 4.4.3 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 lru-cache: 7.18.3 @@ -3824,7 +3701,7 @@ snapshots: punycode@2.3.1: {} - pupa@3.1.0: + pupa@3.3.0: dependencies: escape-goat: 4.0.0 @@ -3839,9 +3716,9 @@ snapshots: rechoir@0.6.2: dependencies: - resolve: 1.22.8 + resolve: 1.22.10 - registry-auth-token@5.0.2: + registry-auth-token@5.1.0: dependencies: '@pnpm/npm-conf': 2.3.1 @@ -3849,18 +3726,18 @@ snapshots: dependencies: rc: 1.2.8 - release-it@18.1.2(@types/node@24.0.1)(typescript@5.6.3): + release-it@18.1.2(@types/node@24.5.2)(typescript@5.9.2): dependencies: '@iarna/toml': 2.2.5 '@octokit/rest': 21.0.2 async-retry: 1.3.3 chalk: 5.4.1 - ci-info: 4.2.0 - cosmiconfig: 9.0.0(typescript@5.6.3) + ci-info: 4.3.0 + cosmiconfig: 9.0.0(typescript@5.9.2) execa: 9.5.2 git-url-parse: 16.0.0 globby: 14.0.2 - inquirer: 12.3.0(@types/node@24.0.1) + inquirer: 12.3.0(@types/node@24.5.2) issue-parser: 7.0.1 lodash: 4.17.21 mime-types: 2.1.35 @@ -3883,9 +3760,9 @@ snapshots: resolve-from@4.0.0: {} - resolve@1.22.8: + resolve@1.22.10: dependencies: - is-core-module: 2.15.1 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -3896,35 +3773,39 @@ snapshots: retry@0.13.1: {} - reusify@1.0.4: {} + reusify@1.1.0: {} rfdc@1.4.1: {} - rollup@4.27.2: + rollup@4.52.2: dependencies: - '@types/estree': 1.0.6 + '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.27.2 - '@rollup/rollup-android-arm64': 4.27.2 - '@rollup/rollup-darwin-arm64': 4.27.2 - '@rollup/rollup-darwin-x64': 4.27.2 - '@rollup/rollup-freebsd-arm64': 4.27.2 - '@rollup/rollup-freebsd-x64': 4.27.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.27.2 - '@rollup/rollup-linux-arm-musleabihf': 4.27.2 - '@rollup/rollup-linux-arm64-gnu': 4.27.2 - '@rollup/rollup-linux-arm64-musl': 4.27.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.27.2 - '@rollup/rollup-linux-riscv64-gnu': 4.27.2 - '@rollup/rollup-linux-s390x-gnu': 4.27.2 - '@rollup/rollup-linux-x64-gnu': 4.27.2 - '@rollup/rollup-linux-x64-musl': 4.27.2 - '@rollup/rollup-win32-arm64-msvc': 4.27.2 - '@rollup/rollup-win32-ia32-msvc': 4.27.2 - '@rollup/rollup-win32-x64-msvc': 4.27.2 + '@rollup/rollup-android-arm-eabi': 4.52.2 + '@rollup/rollup-android-arm64': 4.52.2 + '@rollup/rollup-darwin-arm64': 4.52.2 + '@rollup/rollup-darwin-x64': 4.52.2 + '@rollup/rollup-freebsd-arm64': 4.52.2 + '@rollup/rollup-freebsd-x64': 4.52.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.2 + '@rollup/rollup-linux-arm-musleabihf': 4.52.2 + '@rollup/rollup-linux-arm64-gnu': 4.52.2 + '@rollup/rollup-linux-arm64-musl': 4.52.2 + '@rollup/rollup-linux-loong64-gnu': 4.52.2 + '@rollup/rollup-linux-ppc64-gnu': 4.52.2 + '@rollup/rollup-linux-riscv64-gnu': 4.52.2 + '@rollup/rollup-linux-riscv64-musl': 4.52.2 + '@rollup/rollup-linux-s390x-gnu': 4.52.2 + '@rollup/rollup-linux-x64-gnu': 4.52.2 + '@rollup/rollup-linux-x64-musl': 4.52.2 + '@rollup/rollup-openharmony-arm64': 4.52.2 + '@rollup/rollup-win32-arm64-msvc': 4.52.2 + '@rollup/rollup-win32-ia32-msvc': 4.52.2 + '@rollup/rollup-win32-x64-gnu': 4.52.2 + '@rollup/rollup-win32-x64-msvc': 4.52.2 fsevents: 2.3.3 - run-applescript@7.0.0: {} + run-applescript@7.1.0: {} run-async@3.0.0: {} @@ -3932,7 +3813,7 @@ snapshots: dependencies: queue-microtask: 1.2.3 - rxjs@7.8.1: + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -3940,6 +3821,8 @@ snapshots: semver@7.6.3: {} + semver@7.7.2: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3956,7 +3839,7 @@ snapshots: signal-exit@4.1.0: {} - sirv@3.0.1: + sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 @@ -3966,27 +3849,27 @@ snapshots: slice-ansi@5.0.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 is-fullwidth-code-point: 4.0.0 - slice-ansi@7.1.0: + slice-ansi@7.1.2: dependencies: - ansi-styles: 6.2.1 - is-fullwidth-code-point: 5.0.0 + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 smart-buffer@4.2.0: {} socks-proxy-agent@8.0.5: dependencies: - agent-base: 7.1.3 - debug: 4.4.0 - socks: 2.8.3 + agent-base: 7.1.4 + debug: 4.4.3 + socks: 2.8.7 transitivePeerDependencies: - supports-color - socks@2.8.3: + socks@2.8.7: dependencies: - ip-address: 9.0.5 + ip-address: 10.0.1 smart-buffer: 4.2.0 source-map-js@1.2.1: {} @@ -3994,8 +3877,6 @@ snapshots: source-map@0.6.1: optional: true - sprintf-js@1.1.3: {} - stackback@0.0.2: {} std-env@3.9.0: {} @@ -4012,17 +3893,17 @@ snapshots: string-width@7.2.0: dependencies: - emoji-regex: 10.4.0 - get-east-asian-width: 1.2.0 - strip-ansi: 7.1.0 + emoji-regex: 10.5.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 strip-final-newline@3.0.0: {} @@ -4032,6 +3913,10 @@ snapshots: strip-json-comments@3.1.1: {} + strip-literal@3.0.0: + dependencies: + js-tokens: 9.0.1 + stubborn-fs@1.2.5: {} supports-color@7.2.0: @@ -4040,29 +3925,24 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - synckit@0.9.1: + synckit@0.11.11: dependencies: - '@pkgr/core': 0.1.1 - tslib: 2.8.1 + '@pkgr/core': 0.2.9 tinybench@2.9.0: {} tinyexec@0.3.2: {} - tinyglobby@0.2.13: + tinyglobby@0.2.15: dependencies: - fdir: 6.4.4(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 - tinypool@1.0.2: {} + tinypool@1.1.1: {} tinyrainbow@2.0.0: {} - tinyspy@3.0.2: {} - - tmp@0.0.33: - dependencies: - os-tmpdir: 1.0.2 + tinyspy@4.0.4: {} to-regex-range@5.0.1: dependencies: @@ -4070,9 +3950,9 @@ snapshots: totalist@3.0.1: {} - ts-api-utils@1.4.0(typescript@5.6.3): + ts-api-utils@2.1.0(typescript@5.9.2): dependencies: - typescript: 5.6.3 + typescript: 5.9.2 tslib@2.8.1: {} @@ -4084,22 +3964,22 @@ snapshots: type-fest@2.19.0: {} - type-fest@4.26.1: {} + type-fest@4.41.0: {} - typescript-eslint@8.16.0(eslint@9.15.0)(typescript@5.6.3): + typescript-eslint@8.44.1(eslint@9.36.0)(typescript@5.9.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0)(typescript@5.6.3))(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/parser': 8.16.0(eslint@9.15.0)(typescript@5.6.3) - '@typescript-eslint/utils': 8.16.0(eslint@9.15.0)(typescript@5.6.3) - eslint: 9.15.0 - optionalDependencies: - typescript: 5.6.3 + '@typescript-eslint/eslint-plugin': 8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0)(typescript@5.9.2))(eslint@9.36.0)(typescript@5.9.2) + '@typescript-eslint/parser': 8.44.1(eslint@9.36.0)(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.9.2) + '@typescript-eslint/utils': 8.44.1(eslint@9.36.0)(typescript@5.9.2) + eslint: 9.36.0 + typescript: 5.9.2 transitivePeerDependencies: - supports-color - typescript@5.6.3: {} + typescript@5.9.2: {} - undici-types@7.8.0: {} + undici-types@7.12.0: {} undici@6.21.1: {} @@ -4109,18 +3989,16 @@ snapshots: universal-user-agent@7.0.3: {} - universalify@2.0.1: {} - update-notifier@7.3.1: dependencies: boxen: 8.0.1 chalk: 5.4.1 - configstore: 7.0.0 + configstore: 7.1.0 is-in-ci: 1.0.0 is-installed-globally: 1.0.0 - is-npm: 6.0.0 + is-npm: 6.1.0 latest-version: 9.0.0 - pupa: 3.1.0 + pupa: 3.3.0 semver: 7.6.3 xdg-basedir: 5.1.0 @@ -4130,13 +4008,13 @@ snapshots: url-join@5.0.0: {} - vite-node@3.1.3(@types/node@24.0.1): + vite-node@3.2.4(@types/node@24.5.2): dependencies: cac: 6.7.14 - debug: 4.4.0 + debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 5.4.11(@types/node@24.0.1) + vite: 5.4.20(@types/node@24.5.2) transitivePeerDependencies: - '@types/node' - less @@ -4148,41 +4026,43 @@ snapshots: - supports-color - terser - vite@5.4.11(@types/node@24.0.1): + vite@5.4.20(@types/node@24.5.2): dependencies: esbuild: 0.21.5 - postcss: 8.4.49 - rollup: 4.27.2 + postcss: 8.5.6 + rollup: 4.52.2 optionalDependencies: - '@types/node': 24.0.1 + '@types/node': 24.5.2 fsevents: 2.3.3 - vitest@3.1.3(@types/node@24.0.1)(@vitest/ui@3.1.3): - dependencies: - '@vitest/expect': 3.1.3 - '@vitest/mocker': 3.1.3(vite@5.4.11(@types/node@24.0.1)) - '@vitest/pretty-format': 3.1.3 - '@vitest/runner': 3.1.3 - '@vitest/snapshot': 3.1.3 - '@vitest/spy': 3.1.3 - '@vitest/utils': 3.1.3 - chai: 5.2.0 - debug: 4.4.0 - expect-type: 1.2.1 - magic-string: 0.30.17 + vitest@3.2.4(@types/node@24.5.2)(@vitest/ui@3.2.4): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@5.4.20(@types/node@24.5.2)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.2.2 + magic-string: 0.30.19 pathe: 2.0.3 + picomatch: 4.0.3 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.13 - tinypool: 1.0.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 5.4.11(@types/node@24.0.1) - vite-node: 3.1.3(@types/node@24.0.1) + vite: 5.4.20(@types/node@24.5.2) + vite-node: 3.2.4(@types/node@24.5.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.0.1 - '@vitest/ui': 3.1.3(vitest@3.1.3) + '@types/node': 24.5.2 + '@vitest/ui': 3.2.4(vitest@3.2.4) transitivePeerDependencies: - less - lightningcss @@ -4194,7 +4074,7 @@ snapshots: - supports-color - terser - when-exit@2.1.3: {} + when-exit@2.1.4: {} which@2.0.2: dependencies: @@ -4223,22 +4103,22 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - wrap-ansi@9.0.0: + wrap-ansi@9.0.2: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 7.2.0 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 wrappy@1.0.2: {} xdg-basedir@5.1.0: {} - yaml@2.5.1: {} + yaml@2.8.1: {} yargs-parser@21.1.1: {} yocto-queue@0.1.0: {} - yoctocolors-cjs@2.1.2: {} + yoctocolors-cjs@2.1.3: {} - yoctocolors@2.1.1: {} + yoctocolors@2.1.2: {} diff --git a/src/domRenderer.ts b/src/domRenderer.ts index c3e8eb5..79c41e9 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -276,11 +276,11 @@ function updateNodeStyles(node: DOMNode | DOMText) { let { x, y } = props; if (props.mountX != null) { - x -= (props.width ?? 0) * props.mountX; + x -= (props.w ?? 0) * props.mountX; } if (props.mountY != null) { - y -= (props.height ?? 0) * props.mountY; + y -= (props.h ?? 0) * props.mountY; } if (x !== 0) transform += `translateX(${x}px)`; @@ -320,9 +320,6 @@ function updateNodeStyles(node: DOMNode | DOMText) { if (textProps.fontWeight !== 'normal') { style += `font-weight: ${textProps.fontWeight};`; } - if (textProps.fontStretch !== 'normal') { - style += `font-stretch: ${textProps.fontStretch};`; - } if (textProps.lineHeight != null) { style += `line-height: ${textProps.lineHeight}px;`; } @@ -336,14 +333,14 @@ function updateNodeStyles(node: DOMNode | DOMText) { let maxLines = textProps.maxLines || Infinity; switch (textProps.contain) { case 'width': - style += `width: ${props.width}px; overflow: hidden;`; + style += `width: ${props.w}px; overflow: hidden;`; break; case 'both': { let lineHeight = getNodeLineHeight(textProps); - maxLines = Math.min(maxLines, Math.floor(props.height / lineHeight)); + maxLines = Math.min(maxLines, Math.floor(props.h / lineHeight)); maxLines = Math.max(1, maxLines); let height = maxLines * lineHeight; - style += `width: ${props.width}px; height: ${height}px; overflow: hidden;`; + style += `width: ${props.w}px; height: ${height}px; overflow: hidden;`; break; } case 'none': @@ -367,8 +364,8 @@ function updateNodeStyles(node: DOMNode | DOMText) { } // else { - if (props.width !== 0) style += `width: ${props.width}px;`; - if (props.height !== 0) style += `height: ${props.height}px;`; + if (props.w !== 0) style += `width: ${props.w}px;`; + if (props.h !== 0) style += `height: ${props.h}px;`; let vGradient = props.colorBottom !== props.colorTop @@ -448,7 +445,7 @@ function updateNodeStyles(node: DOMNode | DOMText) { if (props.shader?.props != null) { let shader = props.shader.props; - let borderWidth = shader['border-width']; + let borderWidth = shader['border-w']; let borderColor = shader['border-color']; let borderGap = shader['border-gap'] ?? 0; let borderInset = shader['border-inset'] ?? true; @@ -538,20 +535,17 @@ function updateDOMTextSize(node: DOMText): void { switch (node.contain) { case 'width': size = getElSize(node); - if (node.props.height !== size.height) { - node.props.height = size.height; + if (node.props.h !== size.height) { + node.props.h = size.height; updateNodeStyles(node); node.emit('loaded'); } break; case 'none': size = getElSize(node); - if ( - node.props.height !== size.height || - node.props.width !== size.width - ) { - node.props.width = size.width; - node.props.height = size.height; + if (node.props.h !== size.height || node.props.w !== size.width) { + node.props.w = size.width; + node.props.h = size.height; updateNodeStyles(node); node.emit('loaded'); } @@ -603,8 +597,8 @@ function resolveNodeDefaults( return { x: props.x ?? 0, y: props.y ?? 0, - width: props.width ?? 0, - height: props.height ?? 0, + w: props.w ?? 0, + h: props.h ?? 0, alpha: props.alpha ?? 1, autosize: props.autosize ?? false, boundsMargin: props.boundsMargin ?? null, @@ -644,7 +638,6 @@ function resolveNodeDefaults( rtt: props.rtt ?? false, data: {}, imageType: props.imageType, - strictBounds: props.strictBounds ?? false, }; } @@ -659,20 +652,19 @@ function resolveTextNodeDefaults( fontFamily: props.fontFamily ?? 'sans-serif', fontStyle: props.fontStyle ?? 'normal', fontWeight: props.fontWeight ?? 'normal', - fontStretch: props.fontStretch ?? 'normal', + forceLoad: props.forceLoad ?? false, textAlign: props.textAlign ?? 'left', contain: props.contain ?? 'none', - scrollable: props.scrollable ?? false, - scrollY: props.scrollY ?? 0, offsetY: props.offsetY ?? 0, letterSpacing: props.letterSpacing ?? 0, - lineHeight: props.lineHeight, // `undefined` is a valid value + lineHeight: props.lineHeight ?? 0, maxLines: props.maxLines ?? 0, + maxWidth: props.maxWidth ?? 0, + maxHeight: props.maxHeight ?? 0, textBaseline: props.textBaseline ?? 'alphabetic', verticalAlign: props.verticalAlign ?? 'middle', overflowSuffix: props.overflowSuffix ?? '...', wordBreak: props.wordBreak ?? 'normal', - debug: props.debug ?? {}, }; } @@ -737,18 +729,32 @@ class DOMNode extends EventEmitter implements IRendererNode { this.props.y = v; updateNodeStyles(this); } + get w() { + return this.props.w; + } + set w(v) { + this.props.w = v; + updateNodeStyles(this); + } + get h() { + return this.props.h; + } + set h(v) { + this.props.h = v; + updateNodeStyles(this); + } get width() { - return this.props.width; + return this.props.w; } set width(v) { - this.props.width = v; + this.props.w = v; updateNodeStyles(this); } get height() { - return this.props.height; + return this.props.h; } set height(v) { - this.props.height = v; + this.props.h = v; updateNodeStyles(this); } get alpha() { @@ -954,14 +960,6 @@ class DOMNode extends EventEmitter implements IRendererNode { this.props.shader = v; updateNodeStyles(this); } - get strictBounds() { - return this.props.strictBounds; - } - set strictBounds(v) { - this.props.strictBounds = v; - updateNodeStyles(this); - } - get data(): IRendererNode['data'] { return this.props.data; } @@ -1061,12 +1059,11 @@ class DOMText extends DOMNode { this.props.fontWeight = v; updateNodeStyles(this); } - get fontStretch() { - return this.props.fontStretch; + get forceLoad() { + return this.props.forceLoad; } - set fontStretch(v) { - this.props.fontStretch = v; - updateNodeStyles(this); + set forceLoad(v) { + this.props.forceLoad = v; } get lineHeight() { return this.props.lineHeight; @@ -1075,6 +1072,20 @@ class DOMText extends DOMNode { this.props.lineHeight = v; updateNodeStyles(this); } + get maxWidth() { + return this.props.maxWidth; + } + set maxWidth(v) { + this.props.maxWidth = v; + updateNodeStyles(this); + } + get maxHeight() { + return this.props.maxHeight; + } + set maxHeight(v) { + this.props.maxHeight = v; + updateNodeStyles(this); + } get letterSpacing() { return this.props.letterSpacing; } @@ -1131,20 +1142,6 @@ class DOMText extends DOMNode { this.props.textRendererOverride = v; updateNodeStyles(this); } - get scrollable() { - return this.props.scrollable; - } - set scrollable(v) { - this.props.scrollable = v; - updateNodeStyles(this); - } - get scrollY() { - return this.props.scrollY; - } - set scrollY(v) { - this.props.scrollY = v; - updateNodeStyles(this); - } get offsetY() { return this.props.offsetY; } @@ -1159,13 +1156,6 @@ class DOMText extends DOMNode { this.props.wordBreak = v; updateNodeStyles(this); } - get debug() { - return this.props.debug; - } - set debug(v) { - this.props.debug = v; - updateNodeStyles(this); - } } function updateRootPosition(this: DOMRendererMain) { @@ -1226,9 +1216,7 @@ export class DOMRendererMain implements IRendererMain { renderer: { mode: 'canvas', }, - fontManager: { - addFontFace: () => {}, - }, + loadFont: async () => {}, shManager: { registerShaderType() {}, }, @@ -1241,8 +1229,8 @@ export class DOMRendererMain implements IRendererMain { this.root = new DOMNode( this.stage, resolveNodeDefaults({ - width: settings.appWidth ?? 1920, - height: settings.appHeight ?? 1080, + w: settings.appWidth ?? 1920, + h: settings.appHeight ?? 1080, shader: defaultShader, zIndex: 65534, }), diff --git a/src/elementNode.ts b/src/elementNode.ts index f514eb6..d8f9fd9 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -104,7 +104,7 @@ export const LightningRendererNumberProps = [ 'colorTr', 'colorBl', 'colorBr', - 'height', + 'h', 'fontSize', 'lineHeight', 'mount', @@ -117,7 +117,7 @@ export const LightningRendererNumberProps = [ 'scale', 'scaleX', 'scaleY', - 'width', + 'w', 'worldX', 'worldY', 'x', @@ -140,7 +140,9 @@ const LightningRendererNonAnimatingProps = [ 'fontWeight', 'imageType', 'letterSpacing', + 'maxHeight', 'maxLines', + 'maxWidth', 'offsetY', 'overflowSuffix', 'preventCleanup', @@ -251,8 +253,8 @@ export interface ElementNode extends RendererNode { x: number; y: number; throttleInput?: number; - width: number; - height: number; + w: number; + h: number; zIndex?: number; transition?: | Record @@ -375,6 +377,22 @@ export class ElementNode extends Object { } } + get height() { + return this.h; + } + + set height(h) { + this.h = h; + } + + get width() { + return this.w; + } + + set width(w) { + this.w = w; + } + insertChild( node: ElementNode | ElementText | TextNode, beforeNode?: ElementNode | ElementText | TextNode | null, @@ -866,8 +884,8 @@ export class ElementNode extends Object { } const props = node.lng; - const parentWidth = parent.width || 0; - const parentHeight = parent.height || 0; + const parentWidth = parent.w || 0; + const parentHeight = parent.h || 0; props.x = props.x || 0; props.y = props.y || 0; @@ -914,23 +932,30 @@ export class ElementNode extends Object { // contain is either width or both if (textProps.contain) { - if (!textProps.width) { - textProps.width = + if (!textProps.w) { + textProps.w = parentWidth - textProps.x! - (textProps.marginRight || 0); } if ( textProps.contain === 'both' && - !textProps.height && + !textProps.h && !textProps.maxLines ) { - textProps.height = + textProps.h = parentHeight - textProps.y! - (textProps.marginBottom || 0); } else if (textProps.maxLines === 1) { - textProps.height = (textProps.height || + textProps.h = (textProps.h || textProps.lineHeight || textProps.fontSize) as number; } + + if (textProps.contain === 'both') { + textProps.maxWidth = textProps.w; + textProps.maxHeight = textProps.h; + } else if (textProps.contain === 'width') { + textProps.maxWidth = textProps.w; + } } // Can you put effects on Text nodes? Need to confirm... @@ -943,7 +968,7 @@ export class ElementNode extends Object { props as unknown as IRendererTextNodeProps, ); if (parent.requiresLayout()) { - if (!props.width || !props.height) { + if (!props.w || !props.h) { node._layoutOnLoad(); } } @@ -951,13 +976,13 @@ export class ElementNode extends Object { // If its not an image or texture apply some defaults if (!props.texture) { // Set width and height to parent less offset - if (isNaN(props.width as number)) { - props.width = node.flexGrow ? 0 : parentWidth - props.x; + if (isNaN(props.w as number)) { + props.w = node.flexGrow ? 0 : parentWidth - props.x; node._calcWidth = true; } - if (isNaN(props.height as number)) { - props.height = parentHeight - props.y; + if (isNaN(props.h as number)) { + props.h = parentHeight - props.y; node._calcHeight = true; } diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index fd1febe..7c8218a 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -77,7 +77,7 @@ export interface ElementText ElementNode, '_type' | 'parent' | 'children' | 'src' | 'scale' >, - NewOmit { + NewOmit { _type: 'textNode'; parent?: ElementNode; children: TextNode[]; @@ -142,6 +142,7 @@ export interface TextProps > > { states?: NodeStates; + fontWeight: number | string; style?: TextStyles; } diff --git a/src/lightningInit.ts b/src/lightningInit.ts index 874582d..5a95385 100644 --- a/src/lightningInit.ts +++ b/src/lightningInit.ts @@ -24,12 +24,12 @@ export interface IRendererFontManager { export interface IRendererStage { root: IRendererNode; renderer: IRendererCoreRenderer; - fontManager: IRendererFontManager; shManager: IRendererShaderManager; animationManager: { registerAnimation: (anim: any) => void; unregisterAnimation: (anim: any) => void; }; + loadFont(kind: string, props: any): Promise; } /** Based on {@link lng.CoreShaderManager} */ @@ -92,6 +92,8 @@ export interface IRendererTextNodeProps extends Omit { shader: IRendererShader | null; parent: IRendererNode | null; + fontWeight?: string; + contain?: string; } /** Based on {@link lng.ITextNode} */ export interface IRendererTextNode @@ -128,13 +130,7 @@ export function startLightningRenderer( : (new lng.RendererMain(options, rootId) as any as IRendererMain); return renderer; } - -export function loadFonts( - fonts: ( - | lng.WebTrFontFaceOptions - | (Partial & { type: SdfFontType }) - )[], -) { +export function loadFonts(fonts: any[]) { for (const font of fonts) { // WebGL — SDF if ( @@ -142,16 +138,11 @@ export function loadFonts( 'type' in font && (font.type === 'msdf' || font.type === 'ssdf') ) { - renderer.stage.fontManager.addFontFace( - new lng.SdfTrFontFace(font.type, { - ...font, - stage: renderer.stage as any, - } as lng.SdfTrFontFaceOptions), - ); + renderer.stage.loadFont('sdf', font); } // Canvas — Web - else if ('fontUrl' in font) { - renderer.stage.fontManager.addFontFace(new lng.WebTrFontFace(font)); + else if ('fontUrl' in font && renderer.stage.renderer.mode !== 'webgl') { + renderer.stage.loadFont('canvas', font); } } } diff --git a/src/shaders.ts b/src/shaders.ts index 6e6f4b3..6f939bf 100644 --- a/src/shaders.ts +++ b/src/shaders.ts @@ -159,7 +159,7 @@ const roundedWithBorderProps: lngr.ShaderProps = { return (props.radius as Vec4)[3]; }, }, - 'border-width': { + 'border-w': { default: [0, 0, 0, 0], resolve(value) { return toValidVec4(value); @@ -170,37 +170,37 @@ const roundedWithBorderProps: lngr.ShaderProps = { 'border-top': { default: 0, set(value, props) { - (props['border-width'] as Vec4)[0] = value; + (props['border-w'] as Vec4)[0] = value; }, get(props) { - return (props['border-width'] as Vec4)[0]; + return (props['border-w'] as Vec4)[0]; }, }, 'border-right': { default: 0, set(value, props) { - (props['border-width'] as Vec4)[1] = value; + (props['border-w'] as Vec4)[1] = value; }, get(props) { - return (props['border-width'] as Vec4)[1]; + return (props['border-w'] as Vec4)[1]; }, }, 'border-bottom': { default: 0, set(value, props) { - (props['border-width'] as Vec4)[2] = value; + (props['border-w'] as Vec4)[2] = value; }, get(props) { - return (props['border-width'] as Vec4)[2]; + return (props['border-w'] as Vec4)[2]; }, }, 'border-left': { default: 0, set(value, props) { - (props['border-width'] as Vec4)[3] = value; + (props['border-w'] as Vec4)[3] = value; }, get(props) { - return (props['border-width'] as Vec4)[3]; + return (props['border-w'] as Vec4)[3]; }, }, 'border-inset': true, @@ -211,7 +211,7 @@ export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { canBatch: () => false, update(node) { let props = this.props!; - let borderWidth = props['border-width'] as Vec4; + let borderWidth = props['border-w'] as Vec4; let borderGap = props['border-gap']; let inset = props['border-inset']; @@ -226,8 +226,8 @@ export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { let borderZero = b_t === 0 && b_r === 0 && b_b === 0 && b_l === 0; this.uniform1i('u_borderZero', borderZero ? 1 : 0); - let origWidth = node.width; - let origHeight = node.height; + let origWidth = node.w; + let origHeight = node.h; this.uniform2f('u_dimensions_orig', origWidth, origHeight); let finalWidth = origWidth; @@ -285,7 +285,7 @@ export const defaultShaderRoundedWithBorder: ShaderRoundedWithBorder = { uniform vec2 u_resolution; uniform float u_pixelRatio; - + /* Passed by shader setup */ uniform vec2 u_dimensions; uniform vec2 u_dimensions_orig; From b0ecc99a09ff3bda59c2dd8a9bd47cbece591590 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 26 Sep 2025 14:24:42 -0400 Subject: [PATCH 68/78] :rocket: bump version v3.0.0-16 --- CHANGELOG.md | 15 ++++++++++++--- package.json | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19c3379..3aec97f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,16 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). -#### [v2.12.2](https://github.com/lightning-tv/core/compare/v3.0.0-15...v2.12.2) +#### [v3.0.0-16](https://github.com/lightning-tv/core/compare/v3.0.0-15...v3.0.0-16) +- Fix border shader [`#70`](https://github.com/lightning-tv/core/pull/70) +- Add default shaders and outside border. [`#68`](https://github.com/lightning-tv/core/pull/68) - Backport changes from rc3 [`#69`](https://github.com/lightning-tv/core/pull/69) - Add onRender and onRemove handlers [`#56`](https://github.com/lightning-tv/core/pull/56) - Port dom renderer from v3 to v2 [`#55`](https://github.com/lightning-tv/core/pull/55) +- update core to work with latest renderer beta [`4c07701`](https://github.com/lightning-tv/core/commit/4c07701e9a3455f46c31cc85029b67fec4547d1b) - add simple animation manager for transitions [`5980ff9`](https://github.com/lightning-tv/core/commit/5980ff9008b881c6452db5bcd1d50630656e868e) - :rocket: bump version v2.8.0-0 [`25de938`](https://github.com/lightning-tv/core/commit/25de938190227e4e996700582634450b9b614a70) -- add global and node throttleInput support [`f1309b9`](https://github.com/lightning-tv/core/commit/f1309b9b770e750ec6bccee1d3c33e34f0d05a62) #### [v3.0.0-15](https://github.com/lightning-tv/core/compare/v3.0.0-14...v3.0.0-15) @@ -152,13 +154,20 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) -#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.12.1...v3.0.0-0) +#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.12.2...v3.0.0-0) > 19 March 2025 - get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) - :rocket: bump version v3.0.0-0 [`96d54bf`](https://github.com/lightning-tv/core/commit/96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f) +#### [v2.12.2](https://github.com/lightning-tv/core/compare/v2.12.1...v2.12.2) + +> 16 September 2025 + +- :rocket: bump version v2.12.2 [`6d1662d`](https://github.com/lightning-tv/core/commit/6d1662de14b2d1799678e921572c7117cf77f82e) +- fix swap back of nodes, always remove nodes [`b567a5f`](https://github.com/lightning-tv/core/commit/b567a5fb0278efbea85b5108fddb8213511de2e7) + #### [v2.12.1](https://github.com/lightning-tv/core/compare/v2.12.0...v2.12.1) > 10 September 2025 diff --git a/package.json b/package.json index 252f231..c2715ee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-15", + "version": "3.0.0-16", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 6929613d4d57ee9b52c38ee3a63a9c8fc12e84fd Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 26 Sep 2025 15:55:44 -0400 Subject: [PATCH 69/78] fontWeight should be optional --- src/intrinsicTypes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/intrinsicTypes.ts b/src/intrinsicTypes.ts index 7c8218a..6e220de 100644 --- a/src/intrinsicTypes.ts +++ b/src/intrinsicTypes.ts @@ -142,7 +142,7 @@ export interface TextProps > > { states?: NodeStates; - fontWeight: number | string; + fontWeight?: number | string; style?: TextStyles; } From f7b9b451059b04907e2fb2e88419a9c0e44322b5 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 26 Sep 2025 16:11:22 -0400 Subject: [PATCH 70/78] add fontWeight to change fontFamily as shortcut --- src/elementNode.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/elementNode.ts b/src/elementNode.ts index d8f9fd9..e160757 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -393,6 +393,15 @@ export class ElementNode extends Object { this.w = w; } + set fontWeight(v) { + this._fontWeight = v; + this.fontFamily = `{${this.fontFamily}${v}`; + } + + get fontWeight() { + return this._fontWeight; + } + insertChild( node: ElementNode | ElementText | TextNode, beforeNode?: ElementNode | ElementText | TextNode | null, From 1d40158489d4d32c05b3945f2aa0636661ebc06f Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Fri, 26 Sep 2025 17:21:06 -0400 Subject: [PATCH 71/78] :rocket: bump version v3.0.0-17 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aec97f..00e83a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,15 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-17](https://github.com/lightning-tv/core/compare/v3.0.0-16...v3.0.0-17) + +- add fontWeight to change fontFamily as shortcut [`f7b9b45`](https://github.com/lightning-tv/core/commit/f7b9b451059b04907e2fb2e88419a9c0e44322b5) +- fontWeight should be optional [`6929613`](https://github.com/lightning-tv/core/commit/6929613d4d57ee9b52c38ee3a63a9c8fc12e84fd) + #### [v3.0.0-16](https://github.com/lightning-tv/core/compare/v3.0.0-15...v3.0.0-16) +> 26 September 2025 + - Fix border shader [`#70`](https://github.com/lightning-tv/core/pull/70) - Add default shaders and outside border. [`#68`](https://github.com/lightning-tv/core/pull/68) - Backport changes from rc3 [`#69`](https://github.com/lightning-tv/core/pull/69) diff --git a/package.json b/package.json index c2715ee..32e4240 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-16", + "version": "3.0.0-17", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From 811c0b93137a11e5c7f81e75a190f66fe3d0b4b2 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Tue, 30 Sep 2025 10:10:01 -0400 Subject: [PATCH 72/78] fix border-w as new name from width --- src/elementNode.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/elementNode.ts b/src/elementNode.ts index e160757..2f93af3 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -82,7 +82,8 @@ const parseAndAssignShaderProps = ( if (!obj) return; props[prefix] = obj; Object.entries(obj).forEach(([key, value]) => { - props[`${prefix}-${key}`] = value; + let transformedKey = key === 'width' ? 'w' : key; + props[`${prefix}-${transformedKey}`] = value; }); }; From 1829494db7ee84e3c2c64061b371ac0a023f6e64 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Wed, 1 Oct 2025 19:19:14 -0400 Subject: [PATCH 73/78] :rocket: bump version v3.0.0-18 --- CHANGELOG.md | 17 ++++++++++++++++- package.json | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00e83a0..f9fd54b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,15 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-18](https://github.com/lightning-tv/core/compare/v3.0.0-17...v3.0.0-18) + +- fix border-w as new name from width [`811c0b9`](https://github.com/lightning-tv/core/commit/811c0b93137a11e5c7f81e75a190f66fe3d0b4b2) + #### [v3.0.0-17](https://github.com/lightning-tv/core/compare/v3.0.0-16...v3.0.0-17) +> 26 September 2025 + +- :rocket: bump version v3.0.0-17 [`1d40158`](https://github.com/lightning-tv/core/commit/1d40158489d4d32c05b3945f2aa0636661ebc06f) - add fontWeight to change fontFamily as shortcut [`f7b9b45`](https://github.com/lightning-tv/core/commit/f7b9b451059b04907e2fb2e88419a9c0e44322b5) - fontWeight should be optional [`6929613`](https://github.com/lightning-tv/core/commit/6929613d4d57ee9b52c38ee3a63a9c8fc12e84fd) @@ -161,13 +168,21 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) -#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.12.2...v3.0.0-0) +#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.13.0...v3.0.0-0) > 19 March 2025 - get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) - :rocket: bump version v3.0.0-0 [`96d54bf`](https://github.com/lightning-tv/core/commit/96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f) +#### [v2.13.0](https://github.com/lightning-tv/core/compare/v2.12.2...v2.13.0) + +> 30 September 2025 + +- :rocket: bump version v2.13.0 [`398f35d`](https://github.com/lightning-tv/core/commit/398f35d474cb5eb4bcd40d35c6ad5998c389a336) +- update dom rendering to use vite so it tree shakes properly [`e4b3e37`](https://github.com/lightning-tv/core/commit/e4b3e37446501f5d8478a0605c1d1b818ff38691) +- only throttle the same input keys [`361162b`](https://github.com/lightning-tv/core/commit/361162bf5f38668f1f4aba543541711f0b4cae48) + #### [v2.12.2](https://github.com/lightning-tv/core/compare/v2.12.1...v2.12.2) > 16 September 2025 diff --git a/package.json b/package.json index 32e4240..7378b9b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-17", + "version": "3.0.0-18", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From b774ba83323f333958f00e86a651a38aecf2ea14 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Thu, 6 Nov 2025 11:29:00 -0500 Subject: [PATCH 74/78] update to latest renderer --- package.json | 2 +- pnpm-lock.yaml | 12 ++++++------ src/animation.ts | 9 +++++---- src/domRenderer.ts | 18 +++++++++--------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 7378b9b..330f884 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "author": "Chris Lorenzo", "license": "Apache-2.0", "peerDependencies": { - "@lightningjs/renderer": "^3.0.0-beta14" + "@lightningjs/renderer": "^3.0.0-beta15" }, "devDependencies": { "@eslint/js": "^9.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70e98b3..f6ae379 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@lightningjs/renderer': - specifier: ^3.0.0-beta14 - version: 3.0.0-beta14 + specifier: ^3.0.0-beta15 + version: 3.0.0-beta15 devDependencies: '@eslint/js': specifier: ^9.15.0 @@ -406,9 +406,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@lightningjs/renderer@3.0.0-beta14': - resolution: {integrity: sha512-o73TY4IEebAOqywv8U4TJC5GuEpZoyFPNAMMZ5eCbZSf1/SBl18KT1ORgNXkWkSx5chKMoQ7BJtSz0ptnEnqxw==} - engines: {node: '>= 18.0.0', npm: '>= 10.0.0', pnpm: '>= 8.9.2'} + '@lightningjs/renderer@3.0.0-beta15': + resolution: {integrity: sha512-vG0ZZc7u9mXWJaSmMfJo1DgVTq14z8slQ3ZAugsHjUmKpmd5MUwxzCmK70jd29kuP07PwPcOwBQZIMd2DBa5+w==} + engines: {node: '>= 18.0.0', npm: '>= 10.0.0', pnpm: '>= 10.17.0'} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -2406,7 +2406,7 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} - '@lightningjs/renderer@3.0.0-beta14': {} + '@lightningjs/renderer@3.0.0-beta15': {} '@nodelib/fs.scandir@2.1.5': dependencies: diff --git a/src/animation.ts b/src/animation.ts index 564fbf7..b4e65da 100644 --- a/src/animation.ts +++ b/src/animation.ts @@ -5,6 +5,8 @@ import { LightningRendererNumberProps, } from './elementNode.js'; import { type IRendererStage } from './lightningInit.js'; +import { TimingFunction } from '@lightningjs/renderer'; +import { isFunc } from './utils.js'; /** * Simplified Animation Settings @@ -12,7 +14,7 @@ import { type IRendererStage } from './lightningInit.js'; export interface SimpleAnimationSettings { duration?: number; delay?: number; - easing?: string; + easing?: string | TimingFunction; } /** @@ -29,7 +31,7 @@ interface SimpleAnimationNodeConfig { node: ElementNode; duration: number; delay: number; - easing: string; + easing: string | TimingFunction; progress: number; delayFor: number; timingFunction: (t: number) => number | undefined; @@ -74,8 +76,7 @@ export class SimpleAnimation { const duration = settings.duration ?? 0; const delay = settings.delay ?? 0; const easing = settings.easing || 'linear'; - const timingFunction = getTimingFunction(easing); - + const timingFunction = isFunc(easing) ? easing : getTimingFunction(easing); const targetValue = value; const startValue = node[key] as number; diff --git a/src/domRenderer.ts b/src/domRenderer.ts index 79c41e9..60e0d7e 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -20,11 +20,19 @@ import { IRendererTextNode, IRendererTextNodeProps, } from './lightningInit.js'; +import { isFunc } from './utils.js'; const colorToRgba = (c: number) => `rgba(${(c >> 24) & 0xff},${(c >> 16) & 0xff},${(c >> 8) & 0xff},${(c & 0xff) / 255})`; -function applyEasing(easing: string, progress: number): number { +function applyEasing( + easing: string | lng.TimingFunction, + progress: number, +): number { + if (isFunc(easing)) { + return easing(progress); + } + switch (easing) { case 'linear': default: @@ -661,7 +669,6 @@ function resolveTextNodeDefaults( maxLines: props.maxLines ?? 0, maxWidth: props.maxWidth ?? 0, maxHeight: props.maxHeight ?? 0, - textBaseline: props.textBaseline ?? 'alphabetic', verticalAlign: props.verticalAlign ?? 'middle', overflowSuffix: props.overflowSuffix ?? '...', wordBreak: props.wordBreak ?? 'normal', @@ -1128,13 +1135,6 @@ class DOMText extends DOMNode { this.props.verticalAlign = v; updateNodeStyles(this); } - get textBaseline() { - return this.props.textBaseline; - } - set textBaseline(v) { - this.props.textBaseline = v; - updateNodeStyles(this); - } get textRendererOverride() { return this.props.textRendererOverride; } From 9369e2a3d2dab3bf1efe70c65b7b7b1aef0818de Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Thu, 6 Nov 2025 11:32:59 -0500 Subject: [PATCH 75/78] :rocket: bump version v3.0.0-19 --- CHANGELOG.md | 30 +++++++++++++++++++++++++++++- package.json | 2 +- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9fd54b..e716880 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,15 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-19](https://github.com/lightning-tv/core/compare/v3.0.0-18...v3.0.0-19) + +- update to latest renderer [`b774ba8`](https://github.com/lightning-tv/core/commit/b774ba83323f333958f00e86a651a38aecf2ea14) + #### [v3.0.0-18](https://github.com/lightning-tv/core/compare/v3.0.0-17...v3.0.0-18) +> 1 October 2025 + +- :rocket: bump version v3.0.0-18 [`1829494`](https://github.com/lightning-tv/core/commit/1829494db7ee84e3c2c64061b371ac0a023f6e64) - fix border-w as new name from width [`811c0b9`](https://github.com/lightning-tv/core/commit/811c0b93137a11e5c7f81e75a190f66fe3d0b4b2) #### [v3.0.0-17](https://github.com/lightning-tv/core/compare/v3.0.0-16...v3.0.0-17) @@ -168,13 +175,34 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - :rocket: bump version v2.7.7 [`e6d0193`](https://github.com/lightning-tv/core/commit/e6d0193dab280f1621ca9cb0eb4a52bdc456798d) - add isDev checks to remove log and assertTruthy from builds [`fd05557`](https://github.com/lightning-tv/core/commit/fd055579034d0a60f300da4fb54d74d377311b31) -#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.13.0...v3.0.0-0) +#### [v3.0.0-0](https://github.com/lightning-tv/core/compare/v2.14.0...v3.0.0-0) > 19 March 2025 - get core working with Renderer 3 [`d21db88`](https://github.com/lightning-tv/core/commit/d21db88693c897ce0fc82f5b670c1001b86cd9b2) - :rocket: bump version v3.0.0-0 [`96d54bf`](https://github.com/lightning-tv/core/commit/96d54bf7b3553f0ea3d81d2d1bda7b112bf0325f) +#### [v2.14.0](https://github.com/lightning-tv/core/compare/v2.13.2...v2.14.0) + +> 29 October 2025 + +- update to latest renderer, use renderer timings [`474a0b6`](https://github.com/lightning-tv/core/commit/474a0b65e9dc05de81913233e9b71e87f333c926) +- :rocket: bump version v2.14.0 [`2525c76`](https://github.com/lightning-tv/core/commit/2525c76e2114a3ccfb812ca1712f27d6ca7b1782) + +#### [v2.13.2](https://github.com/lightning-tv/core/compare/v2.13.1...v2.13.2) + +> 27 October 2025 + +- add ts docs for element properties [`4d88314`](https://github.com/lightning-tv/core/commit/4d88314a5c1be958b9d7bd45c272df3ebd8147ba) +- :rocket: bump version v2.13.2 [`305c60e`](https://github.com/lightning-tv/core/commit/305c60ec67cd12afa9b69220f2051fb456bfedae) + +#### [v2.13.1](https://github.com/lightning-tv/core/compare/v2.13.0...v2.13.1) + +> 22 October 2025 + +- :rocket: bump version v2.13.1 [`bb7e2bf`](https://github.com/lightning-tv/core/commit/bb7e2bf88afa80be8e0eb1396eff3add56eb2ba3) +- add shortcuts for w (width) and h (height) for upcoming renderer changes [`8f31fe0`](https://github.com/lightning-tv/core/commit/8f31fe00bf4b9e5aac8b852d9411be2effd11e0e) + #### [v2.13.0](https://github.com/lightning-tv/core/compare/v2.12.2...v2.13.0) > 30 September 2025 diff --git a/package.json b/package.json index 330f884..5b688c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-18", + "version": "3.0.0-19", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": { From b0358d298bd1601756e3666811b71fcf2d4b73b1 Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Sat, 15 Nov 2025 18:48:15 -0500 Subject: [PATCH 76/78] set max lines when width is set --- src/elementNode.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/elementNode.ts b/src/elementNode.ts index 2f93af3..f203949 100644 --- a/src/elementNode.ts +++ b/src/elementNode.ts @@ -965,6 +965,7 @@ export class ElementNode extends Object { textProps.maxHeight = textProps.h; } else if (textProps.contain === 'width') { textProps.maxWidth = textProps.w; + textProps.maxLines = textProps.maxLines ?? 1; } } From 08448f9cb15138f8bc2a24a37ad79bf196a02ecb Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Mon, 24 Nov 2025 12:06:38 -0500 Subject: [PATCH 77/78] update renderer.16 --- package.json | 2 +- pnpm-lock.yaml | 10 +++++----- src/domRenderer.ts | 4 ---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 5b688c0..6298aa6 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "author": "Chris Lorenzo", "license": "Apache-2.0", "peerDependencies": { - "@lightningjs/renderer": "^3.0.0-beta15" + "@lightningjs/renderer": "^3.0.0-beta16" }, "devDependencies": { "@eslint/js": "^9.15.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6ae379..5d62101 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@lightningjs/renderer': - specifier: ^3.0.0-beta15 - version: 3.0.0-beta15 + specifier: ^3.0.0-beta16 + version: 3.0.0-beta16 devDependencies: '@eslint/js': specifier: ^9.15.0 @@ -406,8 +406,8 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@lightningjs/renderer@3.0.0-beta15': - resolution: {integrity: sha512-vG0ZZc7u9mXWJaSmMfJo1DgVTq14z8slQ3ZAugsHjUmKpmd5MUwxzCmK70jd29kuP07PwPcOwBQZIMd2DBa5+w==} + '@lightningjs/renderer@3.0.0-beta16': + resolution: {integrity: sha512-5hhDPpHKIAAsgEe1TEBqf+FV71F3xz5/jpYr2hoNB/pawHp4gnvCiZ3gqQWHZ2We2uOA61Nq6QPC3HpR2XwBfQ==} engines: {node: '>= 18.0.0', npm: '>= 10.0.0', pnpm: '>= 10.17.0'} '@nodelib/fs.scandir@2.1.5': @@ -2406,7 +2406,7 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} - '@lightningjs/renderer@3.0.0-beta15': {} + '@lightningjs/renderer@3.0.0-beta16': {} '@nodelib/fs.scandir@2.1.5': dependencies: diff --git a/src/domRenderer.ts b/src/domRenderer.ts index 60e0d7e..dabb6cd 100644 --- a/src/domRenderer.ts +++ b/src/domRenderer.ts @@ -112,9 +112,6 @@ function updateAnimations(time: number) { if (task.settings.loop || task.iteration < task.settings.repeat - 1) { task.iteration++; task.timeStart = time - task.settings.delay; - if (task.settings.repeatDelay > 0) { - task.timeStart += task.settings.repeatDelay; - } requestAnimationUpdate(); } // Animation complete @@ -171,7 +168,6 @@ class AnimationController implements lng.IAnimationController { easing: rawSettings.easing ?? 'linear', loop: rawSettings.loop ?? false, repeat: rawSettings.repeat ?? 1, - repeatDelay: rawSettings.repeatDelay ?? 0, stopMethod: false, }; From 64c3a8662c7def9f612963b7065b9bf8656e609f Mon Sep 17 00:00:00 2001 From: Chris Lorenzo Date: Mon, 24 Nov 2025 12:10:28 -0500 Subject: [PATCH 78/78] :rocket: bump version v3.0.0-20 --- CHANGELOG.md | 8 ++++++++ package.json | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e716880..1d2f30e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,17 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [v3.0.0-20](https://github.com/lightning-tv/core/compare/v3.0.0-19...v3.0.0-20) + +- update renderer.16 [`08448f9`](https://github.com/lightning-tv/core/commit/08448f9cb15138f8bc2a24a37ad79bf196a02ecb) +- set max lines when width is set [`b0358d2`](https://github.com/lightning-tv/core/commit/b0358d298bd1601756e3666811b71fcf2d4b73b1) + #### [v3.0.0-19](https://github.com/lightning-tv/core/compare/v3.0.0-18...v3.0.0-19) +> 6 November 2025 + - update to latest renderer [`b774ba8`](https://github.com/lightning-tv/core/commit/b774ba83323f333958f00e86a651a38aecf2ea14) +- :rocket: bump version v3.0.0-19 [`9369e2a`](https://github.com/lightning-tv/core/commit/9369e2a3d2dab3bf1efe70c65b7b7b1aef0818de) #### [v3.0.0-18](https://github.com/lightning-tv/core/compare/v3.0.0-17...v3.0.0-18) diff --git a/package.json b/package.json index 6298aa6..da17326 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lightningtv/core", - "version": "3.0.0-19", + "version": "3.0.0-20", "description": "Lightning TV Core for Universal Renderers", "type": "module", "exports": {