@pixi-ui-editor/runtime-pixi 0.18.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"ButtonNodeView.js","sourceRoot":"","sources":["../../src/views/ButtonNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { BUTTON_STATE_KEYS, type ButtonStateKey, type LayoutPadding, type UINode } from \"@pixi-ui-editor/schema\";\r\nimport { FancyButton } from \"@pixi/ui\";\r\nimport { Container, Graphics, NineSliceSprite, Sprite, Texture } from \"pixi.js\";\r\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\r\nimport type { SceneAudio } from \"../audio/bus.js\";\r\n\r\nexport type ButtonNode = Extract<UINode, { type: \"button\" }>;\r\n\r\nconst FANCY_STATE_BY_KEY = { normal: \"default\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\r\nconst KEY_BY_FANCY_STATE = { default: \"normal\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\r\n\r\n/**\r\n * Adapts a button node to `@pixi/ui`'s `FancyButton`, which owns the pointer state machine —\r\n * this class never reimplements it. Optional states fall back to the normal image, and runtime\r\n * changes to `enabled` or the current state stay in the view: they never touch the document.\r\n *\r\n * `nineSlice`, when set, applies the same borders to all four state views (`NineSliceSprite`\r\n * instead of `Sprite`) — the button's frame shape is normally the same regardless of which state\r\n * image is currently shown.\r\n */\r\nexport class ButtonNodeView extends NodeView {\r\n private button!: FancyButton;\r\n private stateViews!: Record<ButtonStateKey, Container>;\r\n private readonly interaction: SceneInteractionMode;\r\n private readonly audio: SceneAudio | undefined;\r\n private readonly nodeId: string;\r\n private soundAssetIds: ButtonNode[\"sounds\"];\r\n private enabledState: boolean;\r\n\r\n constructor(node: ButtonNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, audio?: SceneAudio) {\r\n super(textures);\r\n this.interaction = interaction;\r\n this.audio = audio;\r\n this.nodeId = node.id;\r\n this.soundAssetIds = node.sounds;\r\n this.enabledState = node.enabled;\r\n this.buildButton(node);\r\n this.applyEnabled(node.enabled);\r\n }\r\n\r\n /**\r\n * Invisible (alpha 0) but still hit-testable: Pixi's `containsPoint` hit-tests the fill's geometry\r\n * regardless of its alpha. Painted with a real size *before* the view ever reaches `FancyButton`:\r\n * `FancyButton` reads `defaultView`'s bounds exactly once, synchronously during construction, to\r\n * freeze its own `hitArea` — it never re-reads them afterwards. A `Graphics` still at its default\r\n * (0, 0) size at that moment would freeze a zero-size, permanently unclickable hit area.\r\n */\r\n private static paintPlaceholder(graphics: Graphics, width: number, height: number): void {\r\n graphics.clear().rect(0, 0, width, height).fill({ color: 0x000000, alpha: 0 });\r\n }\r\n\r\n private static createStateView(texture: Texture | undefined, nineSlice: LayoutPadding | undefined, size: { width: number; height: number }): Container {\r\n if (texture === undefined) {\r\n const graphics = new Graphics();\r\n ButtonNodeView.paintPlaceholder(graphics, size.width, size.height);\r\n return graphics;\r\n }\r\n return nineSlice === undefined\r\n ? new Sprite(texture)\r\n : new NineSliceSprite({ texture, leftWidth: nineSlice.left, topHeight: nineSlice.top, rightWidth: nineSlice.right, bottomHeight: nineSlice.bottom });\r\n }\r\n\r\n private static applyStateAppearance(view: Container, node: ButtonNode, state: ButtonStateKey): void {\r\n view.tint = node.states[`${state}Tint`] ?? \"#ffffff\";\r\n view.alpha = node.states[`${state}Opacity`] ?? 1;\r\n }\r\n\r\n /**\r\n * (Re)builds `FancyButton` and its four state views. `FancyButton` has no API to replace a view\r\n * after construction, so any change to a view's *type* — nine-slice toggled, or an asset\r\n * finishing an async load — goes through here instead of `syncContent`'s cheap in-place path.\r\n *\r\n * `size` is the best size known *right now*: the resolved layout rectangle when called from\r\n * `syncContent`, or the authored transform when called from the constructor (before any layout\r\n * pass has run). Only feeds the initial placeholder paint above — see its doc comment for why.\r\n */\r\n private buildButton(node: ButtonNode, size?: { width: number; height: number }): void {\r\n const nineSlice = node.nineSlice;\r\n const resolvedSize = size ?? { width: node.transform.width, height: node.transform.height };\r\n const normalTexture = this.textureFor(node.states.normalAssetId);\r\n const stateViews = {} as Record<ButtonStateKey, Container>;\r\n const viewFor = (state: ButtonStateKey, assetId: string | undefined): Container => {\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n const view = ButtonNodeView.createStateView(texture, nineSlice, resolvedSize);\r\n ButtonNodeView.applyStateAppearance(view, node, state);\r\n stateViews[state] = view;\r\n return view;\r\n };\r\n\r\n const button = new FancyButton({\r\n defaultView: viewFor(\"normal\", node.states.normalAssetId),\r\n hoverView: viewFor(\"hover\", node.states.hoverAssetId),\r\n pressedView: viewFor(\"pressed\", node.states.pressedAssetId),\r\n disabledView: viewFor(\"disabled\", node.states.disabledAssetId),\r\n });\r\n // Authoring-сцена инертна: FancyButton уже включил себе eventMode \"static\", снимаем его до applyEnabled.\r\n // Выделение и drag при этом не страдают: попадание ловит hitArea базового NodeView, а не это поддерево.\r\n if (this.interaction === \"authoring\") button.eventMode = \"none\";\r\n else if (this.audio !== undefined) {\r\n // Вьюха только объявляет, что произошло: играть или нет — решает подписчик шины (см. audio/bus.ts).\r\n button.onPress.connect(() => this.audio?.cue(\"button.press\", this.nodeId, this.soundAssetIds?.pressAssetId));\r\n button.onHover.connect(() => this.audio?.cue(\"button.hover\", this.nodeId, this.soundAssetIds?.hoverAssetId));\r\n }\r\n this.button = button;\r\n this.stateViews = stateViews;\r\n this.setContent(button);\r\n }\r\n\r\n /** In authoring mode `enabled` may only drive the visuals: `FancyButton` couples it to `eventMode`. */\r\n private applyEnabled(enabled: boolean): void {\r\n this.enabledState = enabled;\r\n if (this.interaction === \"runtime\") this.button.enabled = enabled;\r\n else this.button.setState(enabled ? \"default\" : \"disabled\");\r\n }\r\n\r\n protected syncContent(node: UINode, transform: UINode[\"transform\"]): void {\r\n if (node.type !== \"button\") return;\r\n this.soundAssetIds = node.sounds;\r\n const nineSlice = node.nineSlice;\r\n const normalTexture = this.textureFor(node.states.normalAssetId);\r\n\r\n // A state view's required *type* (Graphics/Sprite/NineSliceSprite) may no longer match what's\r\n // needed — nine-slice toggled, or a texture finished loading after this view had to fall back\r\n // to Graphics. FancyButton can't swap a view after construction, so that goes through a full\r\n // rebuild; everything else (texture identity, border values, size) updates the live views in\r\n // place, which is the path a nine-slice gizmo drag runs every frame.\r\n let needsRebuild = false;\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const assetId = node.states[`${state}AssetId`];\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n const view = this.stateViews[state];\r\n const wantsNineSlice = texture !== undefined && nineSlice !== undefined;\r\n const matches = texture === undefined ? view instanceof Graphics : wantsNineSlice ? view instanceof NineSliceSprite : view instanceof Sprite && !(view instanceof NineSliceSprite);\r\n if (!matches) { needsRebuild = true; break; }\r\n }\r\n\r\n if (needsRebuild) {\r\n this.buildButton(node, transform);\r\n } else {\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const view = this.stateViews[state];\r\n const assetId = node.states[`${state}AssetId`];\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n // Set the texture before the layout size: Sprite/NineSliceSprite preserve scale when their\r\n // texture changes, so sizing first would shift visual bounds for a differently-sized source.\r\n if (view instanceof NineSliceSprite) {\r\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\r\n if (nineSlice !== undefined) {\r\n view.leftWidth = nineSlice.left;\r\n view.topHeight = nineSlice.top;\r\n view.rightWidth = nineSlice.right;\r\n view.bottomHeight = nineSlice.bottom;\r\n }\r\n view.setSize(transform.width, transform.height);\r\n } else if (view instanceof Sprite) {\r\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\r\n view.setSize(transform.width, transform.height);\r\n } else if (view instanceof Graphics) {\r\n ButtonNodeView.paintPlaceholder(view, transform.width, transform.height);\r\n }\r\n ButtonNodeView.applyStateAppearance(view, node, state);\r\n }\r\n }\r\n\r\n // Только на изменение документа: иначе любой пересчёт layout сбрасывал бы transient preview state.\r\n if (node.enabled !== this.enabledState) this.applyEnabled(node.enabled);\r\n }\r\n\r\n /** Editor-only live preview of nine-slice border edits while dragging the canvas gizmo. */\r\n previewNineSlice(nineSlice: LayoutPadding): void {\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const view = this.stateViews[state];\r\n if (!(view instanceof NineSliceSprite)) continue;\r\n view.leftWidth = nineSlice.left;\r\n view.topHeight = nineSlice.top;\r\n view.rightWidth = nineSlice.right;\r\n view.bottomHeight = nineSlice.bottom;\r\n }\r\n }\r\n\r\n get enabled(): boolean {\r\n return this.enabledState;\r\n }\r\n\r\n set enabled(value: boolean) {\r\n this.applyEnabled(value);\r\n }\r\n\r\n get state(): ButtonStateKey {\r\n return KEY_BY_FANCY_STATE[this.button.state];\r\n }\r\n\r\n /** Forces a visual state without changing `enabled`, so a disabled button still emits no press. */\r\n setState(state: ButtonStateKey): void {\r\n this.button.setState(FANCY_STATE_BY_KEY[state]);\r\n }\r\n\r\n get onPress(): FancyButton[\"onPress\"] { return this.button.onPress; }\r\n get onDown(): FancyButton[\"onDown\"] { return this.button.onDown; }\r\n get onUp(): FancyButton[\"onUp\"] { return this.button.onUp; }\r\n get onHover(): FancyButton[\"onHover\"] { return this.button.onHover; }\r\n get onOut(): FancyButton[\"onOut\"] { return this.button.onOut; }\r\n}\r\n"]}
1
+ {"version":3,"file":"ButtonNodeView.js","sourceRoot":"","sources":["../../src/views/ButtonNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { BUTTON_STATE_KEYS, type ButtonStateKey, type LayoutPadding, type UINode } from \"@pixi-ui-editor/schema\";\r\nimport { FancyButton } from \"@pixi/ui\";\r\nimport { Container, Graphics, NineSliceSprite, Sprite, Texture } from \"pixi.js\";\r\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\r\nimport type { SceneAudio } from \"../audio/bus.js\";\r\n\r\nexport type ButtonNode = Extract<UINode, { type: \"button\" }>;\r\n\r\nconst FANCY_STATE_BY_KEY = { normal: \"default\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\r\nconst KEY_BY_FANCY_STATE = { default: \"normal\", hover: \"hover\", pressed: \"pressed\", disabled: \"disabled\" } as const;\r\n\r\n/**\r\n * Adapts a button node to `@pixi/ui`'s `FancyButton`, which owns the pointer state machine —\r\n * this class never reimplements it. Optional states fall back to the normal image, and runtime\r\n * changes to `enabled` or the current state stay in the view: they never touch the document.\r\n *\r\n * `nineSlice`, when set, applies the same borders to all four state views (`NineSliceSprite`\r\n * instead of `Sprite`) — the button's frame shape is normally the same regardless of which state\r\n * image is currently shown.\r\n */\r\nexport class ButtonNodeView extends NodeView {\r\n private button!: FancyButton;\r\n private stateViews!: Record<ButtonStateKey, Container>;\r\n private readonly interaction: SceneInteractionMode;\r\n private readonly audio: SceneAudio | undefined;\r\n private readonly nodeId: string;\r\n private soundAssetIds: ButtonNode[\"sounds\"];\r\n private enabledState: boolean;\r\n\r\n constructor(node: ButtonNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, audio?: SceneAudio) {\r\n super(textures);\r\n this.interaction = interaction;\r\n this.audio = audio;\r\n this.nodeId = node.id;\r\n this.soundAssetIds = node.sounds;\r\n this.enabledState = node.enabled;\r\n this.buildButton(node);\r\n this.applyEnabled(node.enabled);\r\n }\r\n\r\n /**\r\n * Invisible (alpha 0) but still hit-testable: Pixi's `containsPoint` hit-tests the fill's geometry\r\n * regardless of its alpha. Painted with a real size *before* the view ever reaches `FancyButton`:\r\n * `FancyButton` reads `defaultView`'s bounds exactly once, synchronously during construction, to\r\n * freeze its own `hitArea` — it never re-reads them afterwards. A `Graphics` still at its default\r\n * (0, 0) size at that moment would freeze a zero-size, permanently unclickable hit area.\r\n */\r\n private static paintPlaceholder(graphics: Graphics, width: number, height: number): void {\r\n graphics.clear().rect(0, 0, width, height).fill({ color: 0x000000, alpha: 0 });\r\n }\r\n\r\n private static createStateView(texture: Texture | undefined, nineSlice: LayoutPadding | undefined, size: { width: number; height: number }): Container {\r\n if (texture === undefined) {\r\n const graphics = new Graphics();\r\n ButtonNodeView.paintPlaceholder(graphics, size.width, size.height);\r\n return graphics;\r\n }\r\n return nineSlice === undefined\r\n ? new Sprite(texture)\r\n : new NineSliceSprite({ texture, leftWidth: nineSlice.left, topHeight: nineSlice.top, rightWidth: nineSlice.right, bottomHeight: nineSlice.bottom });\r\n }\r\n\r\n private static applyStateAppearance(view: Container, node: ButtonNode, state: ButtonStateKey): void {\r\n view.tint = node.states[`${state}Tint`] ?? \"#ffffff\";\r\n view.alpha = node.states[`${state}Opacity`] ?? 1;\r\n }\r\n\r\n /**\r\n * (Re)builds `FancyButton` and its four state views. `FancyButton` has no API to replace a view\r\n * after construction, so any change to a view's *type* — nine-slice toggled, or an asset\r\n * finishing an async load — goes through here instead of `syncContent`'s cheap in-place path.\r\n *\r\n * `size` is the best size known *right now*: the resolved layout rectangle when called from\r\n * `syncContent`, or the authored transform when called from the constructor (before any layout\r\n * pass has run). Only feeds the initial placeholder paint above — see its doc comment for why.\r\n */\r\n private buildButton(node: ButtonNode, size?: { width: number; height: number }): void {\r\n // `FancyButton` always starts in `default`. An internal rebuild must stay invisible to hosts\r\n // and to the Inspector's transient state preview: otherwise rebuilding after a nine-slice or\r\n // asset-type change leaves the Inspector on (say) Hover while the canvas silently shows Normal.\r\n const previousState = this.button === undefined ? undefined : this.state;\r\n const nineSlice = node.nineSlice;\r\n const resolvedSize = size ?? { width: node.transform.width, height: node.transform.height };\r\n const normalTexture = this.textureFor(node.states.normalAssetId);\r\n const stateViews = {} as Record<ButtonStateKey, Container>;\r\n const viewFor = (state: ButtonStateKey, assetId: string | undefined): Container => {\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n const view = ButtonNodeView.createStateView(texture, nineSlice, resolvedSize);\r\n ButtonNodeView.applyStateAppearance(view, node, state);\r\n stateViews[state] = view;\r\n return view;\r\n };\r\n\r\n const button = new FancyButton({\r\n defaultView: viewFor(\"normal\", node.states.normalAssetId),\r\n hoverView: viewFor(\"hover\", node.states.hoverAssetId),\r\n pressedView: viewFor(\"pressed\", node.states.pressedAssetId),\r\n disabledView: viewFor(\"disabled\", node.states.disabledAssetId),\r\n });\r\n // Authoring-сцена инертна: FancyButton уже включил себе eventMode \"static\", снимаем его до applyEnabled.\r\n // Выделение и drag при этом не страдают: попадание ловит hitArea базового NodeView, а не это поддерево.\r\n if (this.interaction === \"authoring\") button.eventMode = \"none\";\r\n else if (this.audio !== undefined) {\r\n // Вьюха только объявляет, что произошло: играть или нет — решает подписчик шины (см. audio/bus.ts).\r\n button.onPress.connect(() => this.audio?.cue(\"button.press\", this.nodeId, this.soundAssetIds?.pressAssetId));\r\n button.onHover.connect(() => this.audio?.cue(\"button.hover\", this.nodeId, this.soundAssetIds?.hoverAssetId));\r\n }\r\n this.button = button;\r\n this.stateViews = stateViews;\r\n this.setContent(button);\r\n if (previousState !== undefined) {\r\n this.applyEnabled(this.enabledState);\r\n this.button.setState(FANCY_STATE_BY_KEY[previousState]);\r\n }\r\n }\r\n\r\n /** In authoring mode `enabled` may only drive the visuals: `FancyButton` couples it to `eventMode`. */\r\n private applyEnabled(enabled: boolean): void {\r\n this.enabledState = enabled;\r\n if (this.interaction === \"runtime\") this.button.enabled = enabled;\r\n else this.button.setState(enabled ? \"default\" : \"disabled\");\r\n }\r\n\r\n protected syncContent(node: UINode, transform: UINode[\"transform\"]): void {\r\n if (node.type !== \"button\") return;\r\n this.soundAssetIds = node.sounds;\r\n const nineSlice = node.nineSlice;\r\n const normalTexture = this.textureFor(node.states.normalAssetId);\r\n\r\n // A state view's required *type* (Graphics/Sprite/NineSliceSprite) may no longer match what's\r\n // needed — nine-slice toggled, or a texture finished loading after this view had to fall back\r\n // to Graphics. FancyButton can't swap a view after construction, so that goes through a full\r\n // rebuild; everything else (texture identity, border values, size) updates the live views in\r\n // place, which is the path a nine-slice gizmo drag runs every frame.\r\n let needsRebuild = false;\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const assetId = node.states[`${state}AssetId`];\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n const view = this.stateViews[state];\r\n const wantsNineSlice = texture !== undefined && nineSlice !== undefined;\r\n const matches = texture === undefined ? view instanceof Graphics : wantsNineSlice ? view instanceof NineSliceSprite : view instanceof Sprite && !(view instanceof NineSliceSprite);\r\n if (!matches) { needsRebuild = true; break; }\r\n }\r\n\r\n if (needsRebuild) {\r\n this.buildButton(node, transform);\r\n } else {\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const view = this.stateViews[state];\r\n const assetId = node.states[`${state}AssetId`];\r\n const texture = (assetId === undefined ? undefined : this.textureFor(assetId)) ?? normalTexture;\r\n // Set the texture before the layout size: Sprite/NineSliceSprite preserve scale when their\r\n // texture changes, so sizing first would shift visual bounds for a differently-sized source.\r\n if (view instanceof NineSliceSprite) {\r\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\r\n if (nineSlice !== undefined) {\r\n view.leftWidth = nineSlice.left;\r\n view.topHeight = nineSlice.top;\r\n view.rightWidth = nineSlice.right;\r\n view.bottomHeight = nineSlice.bottom;\r\n }\r\n view.setSize(transform.width, transform.height);\r\n } else if (view instanceof Sprite) {\r\n if (texture !== undefined && view.texture !== texture) view.texture = texture;\r\n view.setSize(transform.width, transform.height);\r\n } else if (view instanceof Graphics) {\r\n ButtonNodeView.paintPlaceholder(view, transform.width, transform.height);\r\n }\r\n ButtonNodeView.applyStateAppearance(view, node, state);\r\n }\r\n // Re-activate the current view after changing its appearance. Besides keeping FancyButton's\r\n // visibility bookkeeping authoritative, this invalidates the shown branch immediately when\r\n // Inspector color/opacity previews update an already-built button.\r\n this.button.setState(this.button.state, true);\r\n }\r\n\r\n // Только на изменение документа: иначе любой пересчёт layout сбрасывал бы transient preview state.\r\n if (node.enabled !== this.enabledState) this.applyEnabled(node.enabled);\r\n }\r\n\r\n /** Editor-only live preview of nine-slice border edits while dragging the canvas gizmo. */\r\n previewNineSlice(nineSlice: LayoutPadding): void {\r\n for (const state of BUTTON_STATE_KEYS) {\r\n const view = this.stateViews[state];\r\n if (!(view instanceof NineSliceSprite)) continue;\r\n view.leftWidth = nineSlice.left;\r\n view.topHeight = nineSlice.top;\r\n view.rightWidth = nineSlice.right;\r\n view.bottomHeight = nineSlice.bottom;\r\n }\r\n }\r\n\r\n get enabled(): boolean {\r\n return this.enabledState;\r\n }\r\n\r\n set enabled(value: boolean) {\r\n this.applyEnabled(value);\r\n }\r\n\r\n get state(): ButtonStateKey {\r\n return KEY_BY_FANCY_STATE[this.button.state];\r\n }\r\n\r\n /** Forces a visual state without changing `enabled`, so a disabled button still emits no press. */\r\n setState(state: ButtonStateKey): void {\r\n this.button.setState(FANCY_STATE_BY_KEY[state]);\r\n }\r\n\r\n get onPress(): FancyButton[\"onPress\"] { return this.button.onPress; }\r\n get onDown(): FancyButton[\"onDown\"] { return this.button.onDown; }\r\n get onUp(): FancyButton[\"onUp\"] { return this.button.onUp; }\r\n get onHover(): FancyButton[\"onHover\"] { return this.button.onHover; }\r\n get onOut(): FancyButton[\"onOut\"] { return this.button.onOut; }\r\n}\r\n"]}
@@ -1,5 +1,5 @@
1
1
  import { type PaginationNode, type UINode } from "@pixi-ui-editor/schema";
2
- import { Container, NineSliceSprite, Sprite, Text, Texture } from "pixi.js";
2
+ import { Container, Text, Texture } from "pixi.js";
3
3
  import { Signal } from "typed-signals";
4
4
  import { NodeView, type SceneInteractionMode } from "./NodeView.js";
5
5
  export type { PaginationNode };
@@ -15,28 +15,22 @@ export type PaginationArrow = {
15
15
  enabled: boolean;
16
16
  };
17
17
  /**
18
- * One `numbers`-style item: a centered page-number label, plus an optional "pill" background shown
19
- * only while it is the current page. `width`/`height` are overridden to the authored `dotSize` — the
20
- * same fixed-footprint trick `ScrollItemContainer` uses so the `List` spaces every item evenly
21
- * whether or not its (rarely-present) background happens to be attached right now.
18
+ * One `numbers`-style item: a centered page-number label. `width`/`height` are overridden to the
19
+ * authored `dotSize` the same fixed-footprint trick `ScrollItemContainer` uses so the `List`
20
+ * spaces every item evenly. The active-page highlight is a separate authored node the widget only
21
+ * toggles `visible` on (see `PaginationNodeView.setActiveBackground`), never a child of this item.
22
22
  */
23
- /** @internal Exported only so DOM-free render-contract tests can exercise the background in isolation. */
23
+ /** @internal Exported only so DOM-free render-contract tests can exercise the footprint in isolation. */
24
24
  export declare class NumberItem extends Container {
25
25
  readonly numberText: Text;
26
26
  private itemWidth;
27
27
  private itemHeight;
28
- private background?;
29
- private backgroundMask?;
30
- private backgroundCornerRadius?;
31
28
  constructor(numberText: Text);
32
29
  get width(): number;
33
30
  set width(value: number);
34
31
  get height(): number;
35
32
  set height(value: number);
36
33
  resize(width: number, height: number): void;
37
- /** Replaces the pill behind the label (destroying the previous one); `undefined` removes it. */
38
- setBackground(view: Sprite | NineSliceSprite | undefined, cornerRadius: number | undefined): void;
39
- private syncBackgroundMask;
40
34
  }
41
35
  /**
42
36
  * Standalone page indicator: N procedurally generated items arranged by `@pixi/ui`'s `List` (the same
@@ -47,12 +41,11 @@ export declare class NumberItem extends Container {
47
41
  *
48
42
  * `style` picks between two mutually exclusive, differently-rendered item kinds — image dots
49
43
  * (`Sprite`, two states) or rendered page-number text (`NumberItem`: a label, base style + an optional
50
- * highlight color, plus an optional pill background behind only the current page) — never both.
51
- * Switching `style` rebuilds every item, the same "view's required *type* changed" rule
52
- * `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize` changes do too, since the
53
- * item count/footprint is a runtime fact about this control, not scene topology it only rebuilds this
54
- * view's own item children, never the owning `NodeView` or the wider scene tree. The active pill itself
55
- * is not structural: switching pages only recreates the (one) background view that needs to move.
44
+ * highlight color) never both. Switching `style` rebuilds every item, the same "view's required
45
+ * *type* changed" rule `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize`
46
+ * changes do too, since the item count/footprint is a runtime fact about this control, not scene
47
+ * topology it only rebuilds this view's own item children, never the owning `NodeView` or the wider
48
+ * scene tree.
56
49
  *
57
50
  * With `visiblePageCount` set, the indicator becomes a fixed strip of that many slots with the active
58
51
  * page always in the middle one (4 pages, page 3 active → 2-3-4). A slot pointing outside the real
@@ -60,7 +53,10 @@ export declare class NumberItem extends Container {
60
53
  *
61
54
  * The prev/next arrows are ordinary authored button nodes placed wherever the layout needs them; the
62
55
  * widget only takes their views (`scene.ts`'s `wireNodeReferences`, the same address → view resolution
63
- * Spine connector targets use) and steps the page on press. It never draws an arrow itself.
56
+ * Spine connector targets use) and steps the page on press. It never draws an arrow itself. The
57
+ * `numbers`-style active-page highlight follows the same reference-not-owned contract: it is an
58
+ * authored node the widget only toggles `visible` on while `style: "numbers"` — the node carries its
59
+ * own image / nine-slice / rounded corners / size / position, none of it configured here.
64
60
  */
65
61
  export declare class PaginationNodeView extends NodeView {
66
62
  private readonly list;
@@ -74,9 +70,8 @@ export declare class PaginationNodeView extends NodeView {
74
70
  private inactiveTexture;
75
71
  private numberStyle;
76
72
  private numberActiveColor;
77
- private numberActiveBackgroundTexture;
78
- private numberActiveNineSlice;
79
- private numberActiveCornerRadius;
73
+ /** The authored highlight node; the widget owns only its `visible` flag while `style: "numbers"`. */
74
+ private activeBackground;
80
75
  private pageCountState;
81
76
  private currentPage;
82
77
  private dotWidth;
@@ -85,7 +80,6 @@ export declare class PaginationNodeView extends NodeView {
85
80
  /** Fires only from a click on an item (when `clickable`); setting `page` programmatically never emits. */
86
81
  readonly onPageChange: Signal<(page: number) => void>;
87
82
  constructor(node: PaginationNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, fonts: ReadonlyMap<string, string> | undefined);
88
- private createBackgroundView;
89
83
  /** How many indicator slots are drawn: the fixed authored window, or one slot per real page. */
90
84
  private get slotCount();
91
85
  /** The page a slot points at. Windowed slots slide with the current page, which keeps its middle seat. */
@@ -107,6 +101,13 @@ export declare class PaginationNodeView extends NodeView {
107
101
  }): void;
108
102
  /** На краях диапазона соответствующая стрелка недоступна — она никуда не ведёт. */
109
103
  private applyArrowState;
104
+ /**
105
+ * Связывает виджет с авторской нодой-подсветкой активной страницы. Как и стрелки, нода остаётся
106
+ * обычным узлом сцены со своим визуалом — виджет лишь владеет её флагом `visible`: показывает при
107
+ * `style: "numbers"` (активная страница есть всегда) и прячет в стиле `dots`.
108
+ */
109
+ setActiveBackground(view: Container | undefined): void;
110
+ private applyActiveBackgroundVisibility;
110
111
  protected syncContent(node: UINode): void;
111
112
  get pageCount(): number;
112
113
  /** Used by `setPaginationViewPageCount` once game code knows the real content length. */
@@ -1 +1 @@
1
- {"version":3,"file":"PaginationNodeView.d.ts","sourceRoot":"","sources":["../../src/views/PaginationNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,cAAc,EAA4B,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAExH,OAAO,EAAE,SAAS,EAAY,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAyB,MAAM,SAAS,CAAC;AAC7G,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,QAAQ,EAAE,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEpE,YAAY,EAAE,cAAc,EAAE,CAAC;AAuB/B;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG;IAAE,OAAO,EAAE;QAAE,OAAO,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,OAAO,CAAA;KAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAOvG;;;;;GAKG;AACH,0GAA0G;AAC1G,qBAAa,UAAW,SAAQ,SAAS;IAQ3B,QAAQ,CAAC,UAAU,EAAE,IAAI;IAPrC,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,UAAU,CAAC,CAA2B;IAC9C,OAAO,CAAC,cAAc,CAAC,CAAW;IAClC,OAAO,CAAC,sBAAsB,CAAC,CAAS;IAGxC,YAAqB,UAAU,EAAE,IAAI,EAIpC;IAED,IAAa,KAAK,IAAI,MAAM,CAA2B;IACvD,IAAa,KAAK,CAAC,KAAK,EAAE,MAAM,EAA6B;IAC7D,IAAa,MAAM,IAAI,MAAM,CAA4B;IACzD,IAAa,MAAM,CAAC,KAAK,EAAE,MAAM,EAA8B;IAE/D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAM1C;IAED,gGAAgG;IAChG,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAehG;IAED,OAAO,CAAC,kBAAkB;CAK3B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAuB;IACnD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA0C;IAChE,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,MAAM,CAAC,CAAyD;IACxE,OAAO,CAAC,SAAS,CAA0B;IAC3C,OAAO,CAAC,aAAa,CAAU;IAC/B,OAAO,CAAC,eAAe,CAAU;IACjC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,6BAA6B,CAAsB;IAC3D,OAAO,CAAC,qBAAqB,CAA4B;IACzD,OAAO,CAAC,wBAAwB,CAAqB;IACrD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAU;IAEhC,0GAA0G;IAC1G,QAAQ,CAAC,YAAY,gBAAqB,MAAM,KAAK,IAAI,EAAI;IAE7D,YAAY,IAAI,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,WAAW,EAAE,oBAAoB,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,EAqBtK;IAED,OAAO,CAAC,oBAAoB;IAM5B,gGAAgG;IAChG,OAAO,KAAK,SAAS,GAEpB;IAED,0GAA0G;IAC1G,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,UAAU;IAmBlB,2FAA2F;IAC3F,OAAO,CAAC,UAAU;IAYlB,uGAAuG;IACvG,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,gBAAgB;IAmBxB;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,EAAE,eAAe,CAAC;QAAC,IAAI,CAAC,EAAE,eAAe,CAAA;KAAE,GAAG,IAAI,CAK9E;IAED,mFAAmF;IACnF,OAAO,CAAC,eAAe;IAMvB,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA4BxC;IAED,IAAI,SAAS,IAAI,MAAM,CAAgC;IAEvD,yFAAyF;IACzF,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAQ1B;IAED,IAAI,IAAI,IAAI,MAAM,CAA6B;IAE/C,4GAA4G;IAC5G,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAKrB;IAED,iHAAiH;IACjH,OAAO,CAAC,OAAO;CAKhB"}
1
+ {"version":3,"file":"PaginationNodeView.d.ts","sourceRoot":"","sources":["../../src/views/PaginationNodeView.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { List } from "@pixi/ui";
2
- import { Container, Graphics, NineSliceSprite, Sprite, Text, Texture } from "pixi.js";
2
+ import { Container, Sprite, Text, Texture } from "pixi.js";
3
3
  import { Signal } from "typed-signals";
4
4
  import { NodeView } from "./NodeView.js";
5
5
  const DEFAULT_NUMBER_STYLE = { fontFamily: "Arial", fontSize: 18, fontWeight: "normal", fontStyle: "normal", fill: "#FFFFFF", align: "center", verticalAlign: "middle", wordWrap: false, breakWords: false, letterSpacing: 0 };
@@ -26,19 +26,16 @@ function clampPage(page, pageCount) {
26
26
  return Math.min(Math.max(0, Math.round(page)), Math.max(0, pageCount - 1));
27
27
  }
28
28
  /**
29
- * One `numbers`-style item: a centered page-number label, plus an optional "pill" background shown
30
- * only while it is the current page. `width`/`height` are overridden to the authored `dotSize` — the
31
- * same fixed-footprint trick `ScrollItemContainer` uses so the `List` spaces every item evenly
32
- * whether or not its (rarely-present) background happens to be attached right now.
29
+ * One `numbers`-style item: a centered page-number label. `width`/`height` are overridden to the
30
+ * authored `dotSize` the same fixed-footprint trick `ScrollItemContainer` uses so the `List`
31
+ * spaces every item evenly. The active-page highlight is a separate authored node the widget only
32
+ * toggles `visible` on (see `PaginationNodeView.setActiveBackground`), never a child of this item.
33
33
  */
34
- /** @internal Exported only so DOM-free render-contract tests can exercise the background in isolation. */
34
+ /** @internal Exported only so DOM-free render-contract tests can exercise the footprint in isolation. */
35
35
  export class NumberItem extends Container {
36
36
  numberText;
37
37
  itemWidth = 0;
38
38
  itemHeight = 0;
39
- background;
40
- backgroundMask;
41
- backgroundCornerRadius;
42
39
  // Named `numberText`, not `label`: `Container.label` is pixi.js's own display-object name string.
43
40
  constructor(numberText) {
44
41
  super();
@@ -54,39 +51,6 @@ export class NumberItem extends Container {
54
51
  this.itemWidth = width;
55
52
  this.itemHeight = height;
56
53
  this.numberText.position.set(width / 2, height / 2);
57
- this.background?.setSize(width, height);
58
- this.syncBackgroundMask();
59
- }
60
- /** Replaces the pill behind the label (destroying the previous one); `undefined` removes it. */
61
- setBackground(view, cornerRadius) {
62
- if (this.background !== undefined) {
63
- super.removeChild(this.background);
64
- this.background.destroy();
65
- }
66
- if (this.backgroundMask !== undefined) {
67
- super.removeChild(this.backgroundMask);
68
- this.backgroundMask.destroy();
69
- this.backgroundMask = undefined;
70
- }
71
- this.background = view;
72
- this.backgroundCornerRadius = cornerRadius;
73
- if (view === undefined)
74
- return;
75
- view.setSize(this.itemWidth, this.itemHeight);
76
- super.addChildAt(view, 0);
77
- if (cornerRadius !== undefined) {
78
- this.backgroundMask = new Graphics();
79
- this.syncBackgroundMask();
80
- // Keep the mask before the background in this item's render tree, as SceneViewport does.
81
- super.addChildAt(this.backgroundMask, 0);
82
- view.mask = this.backgroundMask;
83
- }
84
- }
85
- syncBackgroundMask() {
86
- if (this.backgroundMask === undefined)
87
- return;
88
- const radius = Math.min(this.backgroundCornerRadius ?? 0, this.itemWidth / 2, this.itemHeight / 2);
89
- this.backgroundMask.clear().roundRect(0, 0, this.itemWidth, this.itemHeight, radius).fill(0xffffff);
90
54
  }
91
55
  }
92
56
  /**
@@ -98,12 +62,11 @@ export class NumberItem extends Container {
98
62
  *
99
63
  * `style` picks between two mutually exclusive, differently-rendered item kinds — image dots
100
64
  * (`Sprite`, two states) or rendered page-number text (`NumberItem`: a label, base style + an optional
101
- * highlight color, plus an optional pill background behind only the current page) — never both.
102
- * Switching `style` rebuilds every item, the same "view's required *type* changed" rule
103
- * `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize` changes do too, since the
104
- * item count/footprint is a runtime fact about this control, not scene topology it only rebuilds this
105
- * view's own item children, never the owning `NodeView` or the wider scene tree. The active pill itself
106
- * is not structural: switching pages only recreates the (one) background view that needs to move.
65
+ * highlight color) never both. Switching `style` rebuilds every item, the same "view's required
66
+ * *type* changed" rule `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize`
67
+ * changes do too, since the item count/footprint is a runtime fact about this control, not scene
68
+ * topology it only rebuilds this view's own item children, never the owning `NodeView` or the wider
69
+ * scene tree.
107
70
  *
108
71
  * With `visiblePageCount` set, the indicator becomes a fixed strip of that many slots with the active
109
72
  * page always in the middle one (4 pages, page 3 active → 2-3-4). A slot pointing outside the real
@@ -111,7 +74,10 @@ export class NumberItem extends Container {
111
74
  *
112
75
  * The prev/next arrows are ordinary authored button nodes placed wherever the layout needs them; the
113
76
  * widget only takes their views (`scene.ts`'s `wireNodeReferences`, the same address → view resolution
114
- * Spine connector targets use) and steps the page on press. It never draws an arrow itself.
77
+ * Spine connector targets use) and steps the page on press. It never draws an arrow itself. The
78
+ * `numbers`-style active-page highlight follows the same reference-not-owned contract: it is an
79
+ * authored node the widget only toggles `visible` on while `style: "numbers"` — the node carries its
80
+ * own image / nine-slice / rounded corners / size / position, none of it configured here.
115
81
  */
116
82
  export class PaginationNodeView extends NodeView {
117
83
  list;
@@ -125,9 +91,8 @@ export class PaginationNodeView extends NodeView {
125
91
  inactiveTexture;
126
92
  numberStyle;
127
93
  numberActiveColor;
128
- numberActiveBackgroundTexture;
129
- numberActiveNineSlice;
130
- numberActiveCornerRadius;
94
+ /** The authored highlight node; the widget owns only its `visible` flag while `style: "numbers"`. */
95
+ activeBackground;
131
96
  pageCountState;
132
97
  currentPage;
133
98
  dotWidth;
@@ -144,9 +109,6 @@ export class PaginationNodeView extends NodeView {
144
109
  this.inactiveTexture = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;
145
110
  this.numberStyle = node.numberStyle;
146
111
  this.numberActiveColor = node.numberActiveColor;
147
- this.numberActiveBackgroundTexture = node.numberActiveBackgroundAssetId === undefined ? undefined : this.textureFor(node.numberActiveBackgroundAssetId);
148
- this.numberActiveNineSlice = node.numberActiveNineSlice;
149
- this.numberActiveCornerRadius = node.numberActiveNineSlice === undefined ? node.numberActiveCornerRadius : undefined;
150
112
  this.pageCountState = Math.max(1, Math.round(node.pageCount));
151
113
  this.visibleSlots = node.visiblePageCount;
152
114
  this.currentPage = clampPage(node.defaultPage, this.pageCount);
@@ -157,11 +119,6 @@ export class PaginationNodeView extends NodeView {
157
119
  this.buildItems();
158
120
  this.setContent(this.list);
159
121
  }
160
- createBackgroundView(texture) {
161
- return this.numberActiveNineSlice === undefined
162
- ? new Sprite(texture)
163
- : new NineSliceSprite({ texture, leftWidth: this.numberActiveNineSlice.left, topHeight: this.numberActiveNineSlice.top, rightWidth: this.numberActiveNineSlice.right, bottomHeight: this.numberActiveNineSlice.bottom });
164
- }
165
122
  /** How many indicator slots are drawn: the fixed authored window, or one slot per real page. */
166
123
  get slotCount() {
167
124
  return this.visibleSlots === undefined ? this.pageCount : Math.max(1, Math.round(this.visibleSlots));
@@ -185,8 +142,6 @@ export class PaginationNodeView extends NodeView {
185
142
  const fill = isActive ? this.numberActiveColor ?? baseFill : baseFill;
186
143
  const item = new NumberItem(new Text({ text: exists ? String(page + 1) : "", style: numberTextStyle(this.numberStyle, fill, this.fonts) }));
187
144
  item.resize(this.dotWidth, this.dotHeight);
188
- if (isActive && this.numberActiveBackgroundTexture !== undefined)
189
- item.setBackground(this.createBackgroundView(this.numberActiveBackgroundTexture), this.numberActiveCornerRadius);
190
145
  return item;
191
146
  }
192
147
  /** Recreates every item for the current `styleMode`/`slotCount`/`dotWidth`/`dotHeight`. */
@@ -225,7 +180,6 @@ export class PaginationNodeView extends NodeView {
225
180
  if (item instanceof NumberItem) {
226
181
  item.numberText.text = exists ? String(page + 1) : "";
227
182
  item.numberText.style = numberTextStyle(this.numberStyle, isActive ? this.numberActiveColor ?? baseFill : baseFill, this.fonts);
228
- item.setBackground(isActive && this.numberActiveBackgroundTexture !== undefined ? this.createBackgroundView(this.numberActiveBackgroundTexture) : undefined, this.numberActiveCornerRadius);
229
183
  return;
230
184
  }
231
185
  item.texture = isActive ? this.activeTexture : this.inactiveTexture;
@@ -254,6 +208,20 @@ export class PaginationNodeView extends NodeView {
254
208
  if (this.arrows.next !== undefined)
255
209
  this.arrows.next.enabled = this.currentPage < this.pageCount - 1;
256
210
  }
211
+ /**
212
+ * Связывает виджет с авторской нодой-подсветкой активной страницы. Как и стрелки, нода остаётся
213
+ * обычным узлом сцены со своим визуалом — виджет лишь владеет её флагом `visible`: показывает при
214
+ * `style: "numbers"` (активная страница есть всегда) и прячет в стиле `dots`.
215
+ */
216
+ setActiveBackground(view) {
217
+ this.activeBackground = view;
218
+ this.applyActiveBackgroundVisibility();
219
+ }
220
+ applyActiveBackgroundVisibility() {
221
+ if (this.activeBackground === undefined)
222
+ return;
223
+ this.activeBackground.visible = this.styleMode === "numbers";
224
+ }
257
225
  syncContent(node) {
258
226
  if (node.type !== "pagination")
259
227
  return;
@@ -270,9 +238,7 @@ export class PaginationNodeView extends NodeView {
270
238
  this.inactiveTexture = inactive;
271
239
  this.numberStyle = node.numberStyle;
272
240
  this.numberActiveColor = node.numberActiveColor;
273
- this.numberActiveBackgroundTexture = node.numberActiveBackgroundAssetId === undefined ? undefined : this.textureFor(node.numberActiveBackgroundAssetId);
274
- this.numberActiveNineSlice = node.numberActiveNineSlice;
275
- this.numberActiveCornerRadius = node.numberActiveNineSlice === undefined ? node.numberActiveCornerRadius : undefined;
241
+ this.applyActiveBackgroundVisibility();
276
242
  this.dotWidth = node.dotSize.width;
277
243
  this.dotHeight = node.dotSize.height;
278
244
  this.currentPage = clampPage(this.currentPage, this.pageCount);
@@ -1 +1 @@
1
- {"version":3,"file":"PaginationNodeView.js","sourceRoot":"","sources":["../../src/views/PaginationNodeView.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAyB,MAAM,SAAS,CAAC;AAC7G,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,QAAQ,EAA6B,MAAM,eAAe,CAAC;AAIpE,MAAM,oBAAoB,GAAwB,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;AAEpP;wGACwG;AACxG,SAAS,eAAe,CAAC,KAAsC,EAAE,IAAY,EAAE,KAA8C;IAC3H,MAAM,QAAQ,GAAG,KAAK,IAAI,oBAAoB,CAAC;IAC/C,OAAO;QACL,UAAU,EAAE,QAAQ,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,QAAQ,CAAC,UAAU;QAC9H,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,SAAS,EAAE,QAAQ,CAAC,SAAS;QAC7B,IAAI;QACJ,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,UAAU,EAAE,QAAQ,CAAC,UAAU;QAC/B,aAAa,EAAE,QAAQ,CAAC,aAAa;QACrC,MAAM,EAAE,QAAQ,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE;KACnH,CAAC;AACJ,CAAC;AASD,oGAAoG;AACpG,SAAS,SAAS,CAAC,IAAY,EAAE,SAAiB;IAChD,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,0GAA0G;AAC1G,MAAM,OAAO,UAAW,SAAQ,SAAS;IAQlB,UAAU;IAPvB,SAAS,GAAG,CAAC,CAAC;IACd,UAAU,GAAG,CAAC,CAAC;IACf,UAAU,CAA4B;IACtC,cAAc,CAAY;IAC1B,sBAAsB,CAAU;IAExC,kGAAkG;IAClG,YAAqB,UAAgB;QACnC,KAAK,EAAE,CAAC;0BADW,UAAU;QAE7B,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3B,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC7B,CAAC;IAED,IAAa,KAAK,KAAa,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACvD,IAAa,KAAK,CAAC,KAAa,IAAI,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC;IAC7D,IAAa,MAAM,KAAa,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACzD,IAAa,MAAM,CAAC,KAAa,IAAI,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC;IAE/D,MAAM,CAAC,KAAa,EAAE,MAAc;QAClC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;QACzB,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACxC,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC5B,CAAC;IAED,gGAAgG;IAChG,aAAa,CAAC,IAA0C,EAAE,YAAgC;QACxF,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QAAC,CAAC;QACrG,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;YAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAAC,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC;YAAC,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAAC,CAAC;QAClJ,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,sBAAsB,GAAG,YAAY,CAAC;QAC3C,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO;QAC/B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC9C,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAC1B,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,cAAc,GAAG,IAAI,QAAQ,EAAE,CAAC;YACrC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC1B,yFAAyF;YACzF,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC;QAClC,CAAC;IACH,CAAC;IAEO,kBAAkB;QACxB,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS;YAAE,OAAO;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sBAAsB,IAAI,CAAC,EAAE,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;QACnG,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACtG,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,kBAAmB,SAAQ,QAAQ;IAC7B,IAAI,CAAO;IACX,WAAW,CAAuB;IAClC,KAAK,CAA0C;IACxD,KAAK,GAA4B,EAAE,CAAC;IACpC,YAAY,CAAqB;IACjC,MAAM,CAA0D;IAChE,SAAS,CAA0B;IACnC,aAAa,CAAU;IACvB,eAAe,CAAU;IACzB,WAAW,CAAkC;IAC7C,iBAAiB,CAAqB;IACtC,6BAA6B,CAAsB;IACnD,qBAAqB,CAA4B;IACjD,wBAAwB,CAAqB;IAC7C,cAAc,CAAS;IACvB,WAAW,CAAS;IACpB,QAAQ,CAAS;IACjB,SAAS,CAAS;IAClB,cAAc,CAAU;IAEhC,0GAA0G;IACjG,YAAY,GAAG,IAAI,MAAM,EAA0B,CAAC;IAE7D,YAAY,IAAoB,EAAE,QAAkD,EAAE,WAAiC,EAAE,KAA8C;QACrK,KAAK,CAAC,QAAQ,CAAC,CAAC;QAChB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC;QAC7H,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC;QACnI,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC;QAChD,IAAI,CAAC,6BAA6B,GAAG,IAAI,CAAC,6BAA6B,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QACxJ,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAC;QACxD,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC,qBAAqB,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,SAAS,CAAC;QACrH,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAC1C,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACrC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC;QACrC,IAAI,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7E,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAEO,oBAAoB,CAAC,OAAgB;QAC3C,OAAO,IAAI,CAAC,qBAAqB,KAAK,SAAS;YAC7C,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC;YACrB,CAAC,CAAC,IAAI,eAAe,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,qBAAqB,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7N,CAAC;IAED,gGAAgG;IAChG,IAAY,SAAS;QACnB,OAAO,IAAI,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;IACvG,CAAC;IAED,0GAA0G;IAClG,WAAW,CAAC,IAAY;QAC9B,OAAO,IAAI,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC;IAC3G,CAAC;IAEO,UAAU,CAAC,IAAY;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAClD,MAAM,QAAQ,GAAG,MAAM,IAAI,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC;QACrD,IAAI,IAAI,CAAC,SAAS,KAAK,MAAM,EAAE,CAAC;YAC9B,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAC7E,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAC3C,+FAA+F;YAC/F,GAAG,CAAC,OAAO,GAAG,MAAM,CAAC;YACrB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,oBAAoB,CAAC,IAAI,CAAC;QACrE,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtE,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5I,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,QAAQ,IAAI,IAAI,CAAC,6BAA6B,KAAK,SAAS;YAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,6BAA6B,CAAC,EAAE,IAAI,CAAC,wBAAwB,CAAC,CAAC;QACnL,OAAO,IAAI,CAAC;IACd,CAAC;IAED,2FAA2F;IACnF,UAAU;QAChB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;QAChB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC1B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAED,uGAAuG;IAC/F,QAAQ,CAAC,IAAyB,EAAE,IAAY;QACtD,IAAI,IAAI,CAAC,WAAW,KAAK,WAAW,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAAC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC;YAAC,OAAO;QAAC,CAAC;QAClG,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QACxB,gGAAgG;QAChG,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,GAAG,EAAE;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS;gBAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,gBAAgB;QACtB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,IAAI,oBAAoB,CAAC,IAAI,CAAC;QACrE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;YAChC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;YAClD,MAAM,QAAQ,GAAG,MAAM,IAAI,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC;YACrD,IAAI,IAAI,YAAY,UAAU,EAAE,CAAC;gBAC/B,IAAI,CAAC,UAAU,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtD,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,iBAAiB,IAAI,QAAQ,CAAC,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBAChI,IAAI,CAAC,aAAa,CAAC,QAAQ,IAAI,IAAI,CAAC,6BAA6B,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,wBAAwB,CAAC,CAAC;gBAC5L,OAAO;YACT,CAAC;YACD,IAAI,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC;YACpE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAC5C,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,MAA8D;QACtE,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3E,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAED,mFAAmF;IAC3E,eAAe;QACrB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO;QACtC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;QAC5F,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS;YAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;IACvG,CAAC;IAES,WAAW,CAAC,IAAY;QAChC,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC;QACvH,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC;QAC7H,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QAC1D,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC;QAErC,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC;QACzC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAC1C,IAAI,CAAC,cAAc,GAAG,SAAS,CAAC;QAChC,MAAM,gBAAgB,GAAG,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,iBAAiB,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,KAAK,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC;QACjL,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5B,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC;QAC5B,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC;QAChC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;QACpC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC,iBAAiB,CAAC;QAChD,IAAI,CAAC,6BAA6B,GAAG,IAAI,CAAC,6BAA6B,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QACxJ,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,qBAAqB,CAAC;QACxD,IAAI,CAAC,wBAAwB,GAAG,IAAI,CAAC,qBAAqB,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,SAAS,CAAC;QACrH,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC;QACnC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;QACrC,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAE/D,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QACvE,IAAI,IAAI,CAAC,IAAI,CAAC,cAAc,KAAK,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC;QAEvF,IAAI,gBAAgB;YAAE,IAAI,CAAC,UAAU,EAAE,CAAC;;YACnC,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC/B,CAAC;IAED,IAAI,SAAS,KAAa,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;IAEvD,yFAAyF;IACzF,IAAI,SAAS,CAAC,KAAa;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5C,IAAI,IAAI,KAAK,IAAI,CAAC,cAAc;YAAE,OAAO;QACzC,MAAM,iBAAiB,GAAG,IAAI,CAAC,SAAS,CAAC;QACzC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/D,IAAI,IAAI,CAAC,SAAS,KAAK,iBAAiB;YAAE,IAAI,CAAC,gBAAgB,EAAE,CAAC;;YAC7D,IAAI,CAAC,UAAU,EAAE,CAAC;IACzB,CAAC;IAED,IAAI,IAAI,KAAa,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;IAE/C,4GAA4G;IAC5G,IAAI,IAAI,CAAC,KAAa;QACpB,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QACnD,IAAI,IAAI,KAAK,IAAI,CAAC,WAAW;YAAE,OAAO;QACtC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAED,iHAAiH;IACzG,OAAO,CAAC,KAAa;QAC3B,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QACnD,IAAI,IAAI,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;YAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAAC,CAAC;QACpF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;CACF","sourcesContent":["import { type LayoutPadding, type PaginationNode, type TextStyleDefinition, type UINode } from \"@pixi-ui-editor/schema\";\nimport { List } from \"@pixi/ui\";\nimport { Container, Graphics, NineSliceSprite, Sprite, Text, Texture, type TextStyleOptions } from \"pixi.js\";\nimport { Signal } from \"typed-signals\";\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\n\nexport type { PaginationNode };\n\nconst DEFAULT_NUMBER_STYLE: TextStyleDefinition = { fontFamily: \"Arial\", fontSize: 18, fontWeight: \"normal\", fontStyle: \"normal\", fill: \"#FFFFFF\", align: \"center\", verticalAlign: \"middle\", wordWrap: false, breakWords: false, letterSpacing: 0 };\n\n/** Same shape `ValueControlNodeViews.ts`'s own `textStyle` resolves — kept as its own small copy rather\n * than shared, the same call ADR 0029 makes for two similar-but-separately-evolving style resolvers. */\nfunction numberTextStyle(style: TextStyleDefinition | undefined, fill: string, fonts: ReadonlyMap<string, string> | undefined): Partial<TextStyleOptions> {\n const resolved = style ?? DEFAULT_NUMBER_STYLE;\n return {\n fontFamily: resolved.fontAssetId === undefined ? resolved.fontFamily : fonts?.get(resolved.fontAssetId) ?? resolved.fontFamily,\n fontSize: resolved.fontSize,\n fontWeight: resolved.fontWeight,\n fontStyle: resolved.fontStyle,\n fill,\n align: resolved.align,\n wordWrap: resolved.wordWrap,\n breakWords: resolved.breakWords,\n lineHeight: resolved.lineHeight,\n letterSpacing: resolved.letterSpacing,\n stroke: resolved.stroke === undefined ? undefined : { color: resolved.stroke.color, width: resolved.stroke.width },\n };\n}\n\n/**\n * Кнопка-стрелка пагинатора: ровно та часть `ButtonNodeView`/`StagedButtonNodeView`, которая нужна\n * виджету, — нажатие и «доступна ли». Тип объявлен структурно, чтобы этот файл не зависел от обоих\n * классов кнопок и не решал, какой из них «настоящий».\n */\nexport type PaginationArrow = { onPress: { connect(handler: () => void): unknown }; enabled: boolean };\n\n/** Clamps a page index into the valid `[0, pageCount)` range; an empty widget still shows dot 0. */\nfunction clampPage(page: number, pageCount: number): number {\n return Math.min(Math.max(0, Math.round(page)), Math.max(0, pageCount - 1));\n}\n\n/**\n * One `numbers`-style item: a centered page-number label, plus an optional \"pill\" background shown\n * only while it is the current page. `width`/`height` are overridden to the authored `dotSize` — the\n * same fixed-footprint trick `ScrollItemContainer` uses — so the `List` spaces every item evenly\n * whether or not its (rarely-present) background happens to be attached right now.\n */\n/** @internal Exported only so DOM-free render-contract tests can exercise the background in isolation. */\nexport class NumberItem extends Container {\n private itemWidth = 0;\n private itemHeight = 0;\n private background?: Sprite | NineSliceSprite;\n private backgroundMask?: Graphics;\n private backgroundCornerRadius?: number;\n\n // Named `numberText`, not `label`: `Container.label` is pixi.js's own display-object name string.\n constructor(readonly numberText: Text) {\n super();\n numberText.anchor.set(0.5);\n super.addChild(numberText);\n }\n\n override get width(): number { return this.itemWidth; }\n override set width(value: number) { this.itemWidth = value; }\n override get height(): number { return this.itemHeight; }\n override set height(value: number) { this.itemHeight = value; }\n\n resize(width: number, height: number): void {\n this.itemWidth = width;\n this.itemHeight = height;\n this.numberText.position.set(width / 2, height / 2);\n this.background?.setSize(width, height);\n this.syncBackgroundMask();\n }\n\n /** Replaces the pill behind the label (destroying the previous one); `undefined` removes it. */\n setBackground(view: Sprite | NineSliceSprite | undefined, cornerRadius: number | undefined): void {\n if (this.background !== undefined) { super.removeChild(this.background); this.background.destroy(); }\n if (this.backgroundMask !== undefined) { super.removeChild(this.backgroundMask); this.backgroundMask.destroy(); this.backgroundMask = undefined; }\n this.background = view;\n this.backgroundCornerRadius = cornerRadius;\n if (view === undefined) return;\n view.setSize(this.itemWidth, this.itemHeight);\n super.addChildAt(view, 0);\n if (cornerRadius !== undefined) {\n this.backgroundMask = new Graphics();\n this.syncBackgroundMask();\n // Keep the mask before the background in this item's render tree, as SceneViewport does.\n super.addChildAt(this.backgroundMask, 0);\n view.mask = this.backgroundMask;\n }\n }\n\n private syncBackgroundMask(): void {\n if (this.backgroundMask === undefined) return;\n const radius = Math.min(this.backgroundCornerRadius ?? 0, this.itemWidth / 2, this.itemHeight / 2);\n this.backgroundMask.clear().roundRect(0, 0, this.itemWidth, this.itemHeight, radius).fill(0xffffff);\n }\n}\n\n/**\n * Standalone page indicator: N procedurally generated items arranged by `@pixi/ui`'s `List` (the same\n * approach `ScrollViewNodeView`/`scrollView.ts` use for arranging children) — this view never\n * reimplements arrangement. Deliberately not wired to any scroll-view: game code owns the actual page\n * transition and drives the widget one-way through `setPaginationViewPage`/`setPaginationViewPageCount`,\n * the same relationship a slider or progress bar has to whatever value it displays.\n *\n * `style` picks between two mutually exclusive, differently-rendered item kinds — image dots\n * (`Sprite`, two states) or rendered page-number text (`NumberItem`: a label, base style + an optional\n * highlight color, plus an optional pill background behind only the current page) — never both.\n * Switching `style` rebuilds every item, the same \"view's required *type* changed\" rule\n * `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize` changes do too, since the\n * item count/footprint is a runtime fact about this control, not scene topology — it only rebuilds this\n * view's own item children, never the owning `NodeView` or the wider scene tree. The active pill itself\n * is not structural: switching pages only recreates the (one) background view that needs to move.\n *\n * With `visiblePageCount` set, the indicator becomes a fixed strip of that many slots with the active\n * page always in the middle one (4 pages, page 3 active → 2-3-4). A slot pointing outside the real\n * page range keeps its footprint and renders nothing, so the active page never shifts sideways.\n *\n * The prev/next arrows are ordinary authored button nodes placed wherever the layout needs them; the\n * widget only takes their views (`scene.ts`'s `wireNodeReferences`, the same address → view resolution\n * Spine connector targets use) and steps the page on press. It never draws an arrow itself.\n */\nexport class PaginationNodeView extends NodeView {\n private readonly list: List;\n private readonly interaction: SceneInteractionMode;\n private readonly fonts: ReadonlyMap<string, string> | undefined;\n private items: (Sprite | NumberItem)[] = [];\n private visibleSlots: number | undefined;\n private arrows?: { previous?: PaginationArrow; next?: PaginationArrow };\n private styleMode: PaginationNode[\"style\"];\n private activeTexture: Texture;\n private inactiveTexture: Texture;\n private numberStyle: TextStyleDefinition | undefined;\n private numberActiveColor: string | undefined;\n private numberActiveBackgroundTexture: Texture | undefined;\n private numberActiveNineSlice: LayoutPadding | undefined;\n private numberActiveCornerRadius: number | undefined;\n private pageCountState: number;\n private currentPage: number;\n private dotWidth: number;\n private dotHeight: number;\n private clickableState: boolean;\n\n /** Fires only from a click on an item (when `clickable`); setting `page` programmatically never emits. */\n readonly onPageChange = new Signal<(page: number) => void>();\n\n constructor(node: PaginationNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, fonts: ReadonlyMap<string, string> | undefined) {\n super(textures);\n this.interaction = interaction;\n this.fonts = fonts;\n this.styleMode = node.style;\n this.activeTexture = node.activeAssetId === undefined ? Texture.WHITE : this.textureFor(node.activeAssetId) ?? Texture.WHITE;\n this.inactiveTexture = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;\n this.numberStyle = node.numberStyle;\n this.numberActiveColor = node.numberActiveColor;\n this.numberActiveBackgroundTexture = node.numberActiveBackgroundAssetId === undefined ? undefined : this.textureFor(node.numberActiveBackgroundAssetId);\n this.numberActiveNineSlice = node.numberActiveNineSlice;\n this.numberActiveCornerRadius = node.numberActiveNineSlice === undefined ? node.numberActiveCornerRadius : undefined;\n this.pageCountState = Math.max(1, Math.round(node.pageCount));\n this.visibleSlots = node.visiblePageCount;\n this.currentPage = clampPage(node.defaultPage, this.pageCount);\n this.dotWidth = node.dotSize.width;\n this.dotHeight = node.dotSize.height;\n this.clickableState = node.clickable;\n this.list = new List({ type: node.direction, elementsMargin: node.spacing });\n this.buildItems();\n this.setContent(this.list);\n }\n\n private createBackgroundView(texture: Texture): Sprite | NineSliceSprite {\n return this.numberActiveNineSlice === undefined\n ? new Sprite(texture)\n : new NineSliceSprite({ texture, leftWidth: this.numberActiveNineSlice.left, topHeight: this.numberActiveNineSlice.top, rightWidth: this.numberActiveNineSlice.right, bottomHeight: this.numberActiveNineSlice.bottom });\n }\n\n /** How many indicator slots are drawn: the fixed authored window, or one slot per real page. */\n private get slotCount(): number {\n return this.visibleSlots === undefined ? this.pageCount : Math.max(1, Math.round(this.visibleSlots));\n }\n\n /** The page a slot points at. Windowed slots slide with the current page, which keeps its middle seat. */\n private pageForSlot(slot: number): number {\n return this.visibleSlots === undefined ? slot : this.currentPage - Math.floor(this.slotCount / 2) + slot;\n }\n\n private createItem(slot: number): Sprite | NumberItem {\n const page = this.pageForSlot(slot);\n const exists = page >= 0 && page < this.pageCount;\n const isActive = exists && page === this.currentPage;\n if (this.styleMode === \"dots\") {\n const dot = new Sprite(isActive ? this.activeTexture : this.inactiveTexture);\n dot.setSize(this.dotWidth, this.dotHeight);\n // An out-of-range slot renders nothing but keeps its size, so `List` still reserves its place.\n dot.visible = exists;\n return dot;\n }\n const baseFill = this.numberStyle?.fill ?? DEFAULT_NUMBER_STYLE.fill;\n const fill = isActive ? this.numberActiveColor ?? baseFill : baseFill;\n const item = new NumberItem(new Text({ text: exists ? String(page + 1) : \"\", style: numberTextStyle(this.numberStyle, fill, this.fonts) }));\n item.resize(this.dotWidth, this.dotHeight);\n if (isActive && this.numberActiveBackgroundTexture !== undefined) item.setBackground(this.createBackgroundView(this.numberActiveBackgroundTexture), this.numberActiveCornerRadius);\n return item;\n }\n\n /** Recreates every item for the current `styleMode`/`slotCount`/`dotWidth`/`dotHeight`. */\n private buildItems(): void {\n this.list.removeChildren();\n this.items = [];\n for (let slot = 0; slot < this.slotCount; slot++) {\n const item = this.createItem(slot);\n this.wireItem(item, slot);\n this.items.push(item);\n this.list.addChild(item);\n }\n this.applyArrowState();\n }\n\n /** Authoring canvas stays inert like every other control; a clickable item is only live at runtime. */\n private wireItem(item: Sprite | NumberItem, slot: number): void {\n if (this.interaction === \"authoring\" || !this.clickableState) { item.eventMode = \"none\"; return; }\n item.eventMode = \"static\";\n item.cursor = \"pointer\";\n // Resolved on click, not captured: a windowed slot points at a different page after every move.\n item.on(\"pointertap\", () => {\n const page = this.pageForSlot(slot);\n if (page >= 0 && page < this.pageCount) this.setPage(page);\n });\n }\n\n private applyCurrentPage(): void {\n const baseFill = this.numberStyle?.fill ?? DEFAULT_NUMBER_STYLE.fill;\n this.items.forEach((item, slot) => {\n const page = this.pageForSlot(slot);\n const exists = page >= 0 && page < this.pageCount;\n const isActive = exists && page === this.currentPage;\n if (item instanceof NumberItem) {\n item.numberText.text = exists ? String(page + 1) : \"\";\n item.numberText.style = numberTextStyle(this.numberStyle, isActive ? this.numberActiveColor ?? baseFill : baseFill, this.fonts);\n item.setBackground(isActive && this.numberActiveBackgroundTexture !== undefined ? this.createBackgroundView(this.numberActiveBackgroundTexture) : undefined, this.numberActiveCornerRadius);\n return;\n }\n item.texture = isActive ? this.activeTexture : this.inactiveTexture;\n item.setSize(this.dotWidth, this.dotHeight);\n item.visible = exists;\n });\n this.applyArrowState();\n }\n\n /**\n * Подключает авторские кнопки-стрелки: нажатие шагает на страницу назад/вперёд и эмитит\n * `onPageChange`, как клик по самому пункту. Кнопки остаются обычными нодами сцены — виджет только\n * подписывается на них и гасит ту, за которой страниц больше нет.\n */\n setArrows(arrows: { previous?: PaginationArrow; next?: PaginationArrow }): void {\n this.arrows = arrows;\n arrows.previous?.onPress.connect(() => this.setPage(this.currentPage - 1));\n arrows.next?.onPress.connect(() => this.setPage(this.currentPage + 1));\n this.applyArrowState();\n }\n\n /** На краях диапазона соответствующая стрелка недоступна — она никуда не ведёт. */\n private applyArrowState(): void {\n if (this.arrows === undefined) return;\n if (this.arrows.previous !== undefined) this.arrows.previous.enabled = this.currentPage > 0;\n if (this.arrows.next !== undefined) this.arrows.next.enabled = this.currentPage < this.pageCount - 1;\n }\n\n protected syncContent(node: UINode): void {\n if (node.type !== \"pagination\") return;\n const active = node.activeAssetId === undefined ? Texture.WHITE : this.textureFor(node.activeAssetId) ?? Texture.WHITE;\n const inactive = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;\n const pageCount = Math.max(1, Math.round(node.pageCount));\n this.clickableState = node.clickable;\n\n const previousSlotCount = this.slotCount;\n this.visibleSlots = node.visiblePageCount;\n this.pageCountState = pageCount;\n const structureChanged = node.style !== this.styleMode || this.slotCount !== previousSlotCount || node.dotSize.width !== this.dotWidth || node.dotSize.height !== this.dotHeight;\n this.styleMode = node.style;\n this.activeTexture = active;\n this.inactiveTexture = inactive;\n this.numberStyle = node.numberStyle;\n this.numberActiveColor = node.numberActiveColor;\n this.numberActiveBackgroundTexture = node.numberActiveBackgroundAssetId === undefined ? undefined : this.textureFor(node.numberActiveBackgroundAssetId);\n this.numberActiveNineSlice = node.numberActiveNineSlice;\n this.numberActiveCornerRadius = node.numberActiveNineSlice === undefined ? node.numberActiveCornerRadius : undefined;\n this.dotWidth = node.dotSize.width;\n this.dotHeight = node.dotSize.height;\n this.currentPage = clampPage(this.currentPage, this.pageCount);\n\n if (this.list.type !== node.direction) this.list.type = node.direction;\n if (this.list.elementsMargin !== node.spacing) this.list.elementsMargin = node.spacing;\n\n if (structureChanged) this.buildItems();\n else this.applyCurrentPage();\n }\n\n get pageCount(): number { return this.pageCountState; }\n\n /** Used by `setPaginationViewPageCount` once game code knows the real content length. */\n set pageCount(value: number) {\n const next = Math.max(1, Math.round(value));\n if (next === this.pageCountState) return;\n const previousSlotCount = this.slotCount;\n this.pageCountState = next;\n this.currentPage = clampPage(this.currentPage, this.pageCount);\n if (this.slotCount === previousSlotCount) this.applyCurrentPage();\n else this.buildItems();\n }\n\n get page(): number { return this.currentPage; }\n\n /** External sync from game code (or editor preview): highlights an item without emitting `onPageChange`. */\n set page(value: number) {\n const next = clampPage(value, this.pageCountState);\n if (next === this.currentPage) return;\n this.currentPage = next;\n this.applyCurrentPage();\n }\n\n /** An item click: highlights immediately, then notifies game code — the same order a checkbox/slider follows. */\n private setPage(index: number): void {\n const next = clampPage(index, this.pageCountState);\n if (next !== this.currentPage) { this.currentPage = next; this.applyCurrentPage(); }\n this.onPageChange.emit(next);\n }\n}\n"]}
1
+ {"version":3,"file":"PaginationNodeView.js","sourceRoot":"","sources":["../../src/views/PaginationNodeView.ts"],"names":[],"mappings":"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","sourcesContent":["import { type PaginationNode, type TextStyleDefinition, type UINode } from \"@pixi-ui-editor/schema\";\nimport { List } from \"@pixi/ui\";\nimport { Container, Sprite, Text, Texture, type TextStyleOptions } from \"pixi.js\";\nimport { Signal } from \"typed-signals\";\nimport { NodeView, type SceneInteractionMode } from \"./NodeView.js\";\n\nexport type { PaginationNode };\n\nconst DEFAULT_NUMBER_STYLE: TextStyleDefinition = { fontFamily: \"Arial\", fontSize: 18, fontWeight: \"normal\", fontStyle: \"normal\", fill: \"#FFFFFF\", align: \"center\", verticalAlign: \"middle\", wordWrap: false, breakWords: false, letterSpacing: 0 };\n\n/** Same shape `ValueControlNodeViews.ts`'s own `textStyle` resolves — kept as its own small copy rather\n * than shared, the same call ADR 0029 makes for two similar-but-separately-evolving style resolvers. */\nfunction numberTextStyle(style: TextStyleDefinition | undefined, fill: string, fonts: ReadonlyMap<string, string> | undefined): Partial<TextStyleOptions> {\n const resolved = style ?? DEFAULT_NUMBER_STYLE;\n return {\n fontFamily: resolved.fontAssetId === undefined ? resolved.fontFamily : fonts?.get(resolved.fontAssetId) ?? resolved.fontFamily,\n fontSize: resolved.fontSize,\n fontWeight: resolved.fontWeight,\n fontStyle: resolved.fontStyle,\n fill,\n align: resolved.align,\n wordWrap: resolved.wordWrap,\n breakWords: resolved.breakWords,\n lineHeight: resolved.lineHeight,\n letterSpacing: resolved.letterSpacing,\n stroke: resolved.stroke === undefined ? undefined : { color: resolved.stroke.color, width: resolved.stroke.width },\n };\n}\n\n/**\n * Кнопка-стрелка пагинатора: ровно та часть `ButtonNodeView`/`StagedButtonNodeView`, которая нужна\n * виджету, — нажатие и «доступна ли». Тип объявлен структурно, чтобы этот файл не зависел от обоих\n * классов кнопок и не решал, какой из них «настоящий».\n */\nexport type PaginationArrow = { onPress: { connect(handler: () => void): unknown }; enabled: boolean };\n\n/** Clamps a page index into the valid `[0, pageCount)` range; an empty widget still shows dot 0. */\nfunction clampPage(page: number, pageCount: number): number {\n return Math.min(Math.max(0, Math.round(page)), Math.max(0, pageCount - 1));\n}\n\n/**\n * One `numbers`-style item: a centered page-number label. `width`/`height` are overridden to the\n * authored `dotSize` — the same fixed-footprint trick `ScrollItemContainer` uses — so the `List`\n * spaces every item evenly. The active-page highlight is a separate authored node the widget only\n * toggles `visible` on (see `PaginationNodeView.setActiveBackground`), never a child of this item.\n */\n/** @internal Exported only so DOM-free render-contract tests can exercise the footprint in isolation. */\nexport class NumberItem extends Container {\n private itemWidth = 0;\n private itemHeight = 0;\n\n // Named `numberText`, not `label`: `Container.label` is pixi.js's own display-object name string.\n constructor(readonly numberText: Text) {\n super();\n numberText.anchor.set(0.5);\n super.addChild(numberText);\n }\n\n override get width(): number { return this.itemWidth; }\n override set width(value: number) { this.itemWidth = value; }\n override get height(): number { return this.itemHeight; }\n override set height(value: number) { this.itemHeight = value; }\n\n resize(width: number, height: number): void {\n this.itemWidth = width;\n this.itemHeight = height;\n this.numberText.position.set(width / 2, height / 2);\n }\n}\n\n/**\n * Standalone page indicator: N procedurally generated items arranged by `@pixi/ui`'s `List` (the same\n * approach `ScrollViewNodeView`/`scrollView.ts` use for arranging children) — this view never\n * reimplements arrangement. Deliberately not wired to any scroll-view: game code owns the actual page\n * transition and drives the widget one-way through `setPaginationViewPage`/`setPaginationViewPageCount`,\n * the same relationship a slider or progress bar has to whatever value it displays.\n *\n * `style` picks between two mutually exclusive, differently-rendered item kinds — image dots\n * (`Sprite`, two states) or rendered page-number text (`NumberItem`: a label, base style + an optional\n * highlight color) — never both. Switching `style` rebuilds every item, the same \"view's required\n * *type* changed\" rule `StagedButtonNodeView` follows for its state views; `pageCount`/`dotSize`\n * changes do too, since the item count/footprint is a runtime fact about this control, not scene\n * topology — it only rebuilds this view's own item children, never the owning `NodeView` or the wider\n * scene tree.\n *\n * With `visiblePageCount` set, the indicator becomes a fixed strip of that many slots with the active\n * page always in the middle one (4 pages, page 3 active → 2-3-4). A slot pointing outside the real\n * page range keeps its footprint and renders nothing, so the active page never shifts sideways.\n *\n * The prev/next arrows are ordinary authored button nodes placed wherever the layout needs them; the\n * widget only takes their views (`scene.ts`'s `wireNodeReferences`, the same address → view resolution\n * Spine connector targets use) and steps the page on press. It never draws an arrow itself. The\n * `numbers`-style active-page highlight follows the same reference-not-owned contract: it is an\n * authored node the widget only toggles `visible` on while `style: \"numbers\"` — the node carries its\n * own image / nine-slice / rounded corners / size / position, none of it configured here.\n */\nexport class PaginationNodeView extends NodeView {\n private readonly list: List;\n private readonly interaction: SceneInteractionMode;\n private readonly fonts: ReadonlyMap<string, string> | undefined;\n private items: (Sprite | NumberItem)[] = [];\n private visibleSlots: number | undefined;\n private arrows?: { previous?: PaginationArrow; next?: PaginationArrow };\n private styleMode: PaginationNode[\"style\"];\n private activeTexture: Texture;\n private inactiveTexture: Texture;\n private numberStyle: TextStyleDefinition | undefined;\n private numberActiveColor: string | undefined;\n /** The authored highlight node; the widget owns only its `visible` flag while `style: \"numbers\"`. */\n private activeBackground: Container | undefined;\n private pageCountState: number;\n private currentPage: number;\n private dotWidth: number;\n private dotHeight: number;\n private clickableState: boolean;\n\n /** Fires only from a click on an item (when `clickable`); setting `page` programmatically never emits. */\n readonly onPageChange = new Signal<(page: number) => void>();\n\n constructor(node: PaginationNode, textures: ReadonlyMap<string, Texture> | undefined, interaction: SceneInteractionMode, fonts: ReadonlyMap<string, string> | undefined) {\n super(textures);\n this.interaction = interaction;\n this.fonts = fonts;\n this.styleMode = node.style;\n this.activeTexture = node.activeAssetId === undefined ? Texture.WHITE : this.textureFor(node.activeAssetId) ?? Texture.WHITE;\n this.inactiveTexture = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;\n this.numberStyle = node.numberStyle;\n this.numberActiveColor = node.numberActiveColor;\n this.pageCountState = Math.max(1, Math.round(node.pageCount));\n this.visibleSlots = node.visiblePageCount;\n this.currentPage = clampPage(node.defaultPage, this.pageCount);\n this.dotWidth = node.dotSize.width;\n this.dotHeight = node.dotSize.height;\n this.clickableState = node.clickable;\n this.list = new List({ type: node.direction, elementsMargin: node.spacing });\n this.buildItems();\n this.setContent(this.list);\n }\n\n /** How many indicator slots are drawn: the fixed authored window, or one slot per real page. */\n private get slotCount(): number {\n return this.visibleSlots === undefined ? this.pageCount : Math.max(1, Math.round(this.visibleSlots));\n }\n\n /** The page a slot points at. Windowed slots slide with the current page, which keeps its middle seat. */\n private pageForSlot(slot: number): number {\n return this.visibleSlots === undefined ? slot : this.currentPage - Math.floor(this.slotCount / 2) + slot;\n }\n\n private createItem(slot: number): Sprite | NumberItem {\n const page = this.pageForSlot(slot);\n const exists = page >= 0 && page < this.pageCount;\n const isActive = exists && page === this.currentPage;\n if (this.styleMode === \"dots\") {\n const dot = new Sprite(isActive ? this.activeTexture : this.inactiveTexture);\n dot.setSize(this.dotWidth, this.dotHeight);\n // An out-of-range slot renders nothing but keeps its size, so `List` still reserves its place.\n dot.visible = exists;\n return dot;\n }\n const baseFill = this.numberStyle?.fill ?? DEFAULT_NUMBER_STYLE.fill;\n const fill = isActive ? this.numberActiveColor ?? baseFill : baseFill;\n const item = new NumberItem(new Text({ text: exists ? String(page + 1) : \"\", style: numberTextStyle(this.numberStyle, fill, this.fonts) }));\n item.resize(this.dotWidth, this.dotHeight);\n return item;\n }\n\n /** Recreates every item for the current `styleMode`/`slotCount`/`dotWidth`/`dotHeight`. */\n private buildItems(): void {\n this.list.removeChildren();\n this.items = [];\n for (let slot = 0; slot < this.slotCount; slot++) {\n const item = this.createItem(slot);\n this.wireItem(item, slot);\n this.items.push(item);\n this.list.addChild(item);\n }\n this.applyArrowState();\n }\n\n /** Authoring canvas stays inert like every other control; a clickable item is only live at runtime. */\n private wireItem(item: Sprite | NumberItem, slot: number): void {\n if (this.interaction === \"authoring\" || !this.clickableState) { item.eventMode = \"none\"; return; }\n item.eventMode = \"static\";\n item.cursor = \"pointer\";\n // Resolved on click, not captured: a windowed slot points at a different page after every move.\n item.on(\"pointertap\", () => {\n const page = this.pageForSlot(slot);\n if (page >= 0 && page < this.pageCount) this.setPage(page);\n });\n }\n\n private applyCurrentPage(): void {\n const baseFill = this.numberStyle?.fill ?? DEFAULT_NUMBER_STYLE.fill;\n this.items.forEach((item, slot) => {\n const page = this.pageForSlot(slot);\n const exists = page >= 0 && page < this.pageCount;\n const isActive = exists && page === this.currentPage;\n if (item instanceof NumberItem) {\n item.numberText.text = exists ? String(page + 1) : \"\";\n item.numberText.style = numberTextStyle(this.numberStyle, isActive ? this.numberActiveColor ?? baseFill : baseFill, this.fonts);\n return;\n }\n item.texture = isActive ? this.activeTexture : this.inactiveTexture;\n item.setSize(this.dotWidth, this.dotHeight);\n item.visible = exists;\n });\n this.applyArrowState();\n }\n\n /**\n * Подключает авторские кнопки-стрелки: нажатие шагает на страницу назад/вперёд и эмитит\n * `onPageChange`, как клик по самому пункту. Кнопки остаются обычными нодами сцены — виджет только\n * подписывается на них и гасит ту, за которой страниц больше нет.\n */\n setArrows(arrows: { previous?: PaginationArrow; next?: PaginationArrow }): void {\n this.arrows = arrows;\n arrows.previous?.onPress.connect(() => this.setPage(this.currentPage - 1));\n arrows.next?.onPress.connect(() => this.setPage(this.currentPage + 1));\n this.applyArrowState();\n }\n\n /** На краях диапазона соответствующая стрелка недоступна — она никуда не ведёт. */\n private applyArrowState(): void {\n if (this.arrows === undefined) return;\n if (this.arrows.previous !== undefined) this.arrows.previous.enabled = this.currentPage > 0;\n if (this.arrows.next !== undefined) this.arrows.next.enabled = this.currentPage < this.pageCount - 1;\n }\n\n /**\n * Связывает виджет с авторской нодой-подсветкой активной страницы. Как и стрелки, нода остаётся\n * обычным узлом сцены со своим визуалом — виджет лишь владеет её флагом `visible`: показывает при\n * `style: \"numbers\"` (активная страница есть всегда) и прячет в стиле `dots`.\n */\n setActiveBackground(view: Container | undefined): void {\n this.activeBackground = view;\n this.applyActiveBackgroundVisibility();\n }\n\n private applyActiveBackgroundVisibility(): void {\n if (this.activeBackground === undefined) return;\n this.activeBackground.visible = this.styleMode === \"numbers\";\n }\n\n protected syncContent(node: UINode): void {\n if (node.type !== \"pagination\") return;\n const active = node.activeAssetId === undefined ? Texture.WHITE : this.textureFor(node.activeAssetId) ?? Texture.WHITE;\n const inactive = node.inactiveAssetId === undefined ? Texture.WHITE : this.textureFor(node.inactiveAssetId) ?? Texture.WHITE;\n const pageCount = Math.max(1, Math.round(node.pageCount));\n this.clickableState = node.clickable;\n\n const previousSlotCount = this.slotCount;\n this.visibleSlots = node.visiblePageCount;\n this.pageCountState = pageCount;\n const structureChanged = node.style !== this.styleMode || this.slotCount !== previousSlotCount || node.dotSize.width !== this.dotWidth || node.dotSize.height !== this.dotHeight;\n this.styleMode = node.style;\n this.activeTexture = active;\n this.inactiveTexture = inactive;\n this.numberStyle = node.numberStyle;\n this.numberActiveColor = node.numberActiveColor;\n this.applyActiveBackgroundVisibility();\n this.dotWidth = node.dotSize.width;\n this.dotHeight = node.dotSize.height;\n this.currentPage = clampPage(this.currentPage, this.pageCount);\n\n if (this.list.type !== node.direction) this.list.type = node.direction;\n if (this.list.elementsMargin !== node.spacing) this.list.elementsMargin = node.spacing;\n\n if (structureChanged) this.buildItems();\n else this.applyCurrentPage();\n }\n\n get pageCount(): number { return this.pageCountState; }\n\n /** Used by `setPaginationViewPageCount` once game code knows the real content length. */\n set pageCount(value: number) {\n const next = Math.max(1, Math.round(value));\n if (next === this.pageCountState) return;\n const previousSlotCount = this.slotCount;\n this.pageCountState = next;\n this.currentPage = clampPage(this.currentPage, this.pageCount);\n if (this.slotCount === previousSlotCount) this.applyCurrentPage();\n else this.buildItems();\n }\n\n get page(): number { return this.currentPage; }\n\n /** External sync from game code (or editor preview): highlights an item without emitting `onPageChange`. */\n set page(value: number) {\n const next = clampPage(value, this.pageCountState);\n if (next === this.currentPage) return;\n this.currentPage = next;\n this.applyCurrentPage();\n }\n\n /** An item click: highlights immediately, then notifies game code — the same order a checkbox/slider follows. */\n private setPage(index: number): void {\n const next = clampPage(index, this.pageCountState);\n if (next !== this.currentPage) { this.currentPage = next; this.applyCurrentPage(); }\n this.onPageChange.emit(next);\n }\n}\n"]}
@@ -18,7 +18,7 @@ export declare class SpineNodeView extends NodeView {
18
18
  private connectorBonesActive;
19
19
  /** Layout profile the view was last synced with; connector multipliers resolve against it. */
20
20
  private activeProfile;
21
- constructor(skeletonData: SkeletonData | undefined, animation: string | undefined, autoplay: boolean, loop: boolean, animationSpeed: number, initializeForAuthoring: boolean, referenceFrame?: SpineReferenceFrame, ticker?: Ticker);
21
+ constructor(skeletonData: SkeletonData | undefined, animation: string | undefined, autoplay: boolean, loop: boolean, animationSpeed: number, initializeForAuthoring: boolean, referenceFrame?: SpineReferenceFrame, ticker?: Ticker, animationMix?: number);
22
22
  /** Runtime switch used by game code; authored connector definitions are inactive by default. */
23
23
  get activeConnectorBones(): boolean;
24
24
  set activeConnectorBones(active: boolean);
@@ -1 +1 @@
1
- {"version":3,"file":"SpineNodeView.d.ts","sourceRoot":"","sources":["../../src/views/SpineNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACvH,OAAO,EAA2C,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACnK,OAAO,EAAE,SAAS,EAAoB,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,KAAK,gBAAgB,GAAG,cAAc,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAC/D,KAAK,uBAAuB,GAAG,gBAAgB,GAAG,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAqB9I,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAU;IACjD,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAAyB;IACjD,OAAO,CAAC,oBAAoB,CAAS;IACrC,8FAA8F;IAC9F,OAAO,CAAC,aAAa,CAA8B;IAEnD,YAAY,YAAY,EAAE,YAAY,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,sBAAsB,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,MAAM,EA6BlO;IAED,gGAAgG;IAChG,IAAI,oBAAoB,IAAI,OAAO,CAElC;IAED,IAAI,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAEvC;IAED,0FAA0F;IAC1F,iBAAiB,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,GAAG,IAAI,CAKpE;IAED,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwC7C;IAED,8EAA8E;IAC9E,qBAAqB,IAAI,IAAI,CAkD5B;IAED,OAAO,CAAC,uBAAuB;IAiB/B,iBAAiB,CAAC,cAAc,EAAE,mBAAmB,GAAG,SAAS,GAAG,IAAI,CAEvE;IAED;;;;;OAKG;IACH,SAAS,CAAC,wBAAwB,IAAI,IAAI,CAEzC;IAED,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAgClG;CACF"}
1
+ {"version":3,"file":"SpineNodeView.d.ts","sourceRoot":"","sources":["../../src/views/SpineNodeView.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACvH,OAAO,EAA2C,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,mBAAmB,EAAE,KAAK,MAAM,EAAE,MAAM,wBAAwB,CAAC;AACnK,OAAO,EAAE,SAAS,EAAoB,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAEnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,KAAK,gBAAgB,GAAG,cAAc,GAAG;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,CAAC;AAC/D,KAAK,uBAAuB,GAAG,gBAAgB,GAAG,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAqB9I,qBAAa,aAAc,SAAQ,QAAQ;IACzC,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAU;IACjD,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAAyB;IACjD,OAAO,CAAC,oBAAoB,CAAS;IACrC,8FAA8F;IAC9F,OAAO,CAAC,aAAa,CAA8B;IAEnD,YAAY,YAAY,EAAE,YAAY,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,sBAAsB,EAAE,OAAO,EAAE,cAAc,CAAC,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,SAAI,EAkCpP;IAED,gGAAgG;IAChG,IAAI,oBAAoB,IAAI,OAAO,CAElC;IAED,IAAI,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAEvC;IAED,0FAA0F;IAC1F,iBAAiB,CAAC,QAAQ,EAAE,SAAS,uBAAuB,EAAE,GAAG,IAAI,CAKpE;IAED,uBAAuB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAwC7C;IAED,8EAA8E;IAC9E,qBAAqB,IAAI,IAAI,CAkD5B;IAED,OAAO,CAAC,uBAAuB;IAiB/B,iBAAiB,CAAC,cAAc,EAAE,mBAAmB,GAAG,SAAS,GAAG,IAAI,CAEvE;IAED;;;;;OAKG;IACH,SAAS,CAAC,wBAAwB,IAAI,IAAI,CAEzC;IAED,SAAS,CAAC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI,CAuClG;CACF"}
@@ -18,7 +18,7 @@ export class SpineNodeView extends NodeView {
18
18
  connectorBonesActive = false;
19
19
  /** Layout profile the view was last synced with; connector multipliers resolve against it. */
20
20
  activeProfile = "desktop";
21
- constructor(skeletonData, animation, autoplay, loop, animationSpeed, initializeForAuthoring, referenceFrame, ticker) {
21
+ constructor(skeletonData, animation, autoplay, loop, animationSpeed, initializeForAuthoring, referenceFrame, ticker, animationMix = 0) {
22
22
  super();
23
23
  this.referenceFrame = referenceFrame;
24
24
  this.initializeForAuthoring = initializeForAuthoring;
@@ -36,6 +36,11 @@ export class SpineNodeView extends NodeView {
36
36
  // the game touching `autoUpdate` itself.
37
37
  const spine = new Spine({ skeletonData, autoUpdate: initializeForAuthoring ? autoplay : true, ...(ticker === undefined ? {} : { ticker }) });
38
38
  spine.afterUpdateWorldTransforms = () => this.refreshConnectorBones();
39
+ // Кроссфейд между анимациями живёт на `AnimationStateData.defaultMix`, а не на конкретном
40
+ // треке: так плавность получают и переключения из инспектора, и любые `state.setAnimation`
41
+ // из игрового кода — без того, чтобы игра знала про авторскую настройку. 0 сохраняет прежний
42
+ // резкий переход.
43
+ spine.state.data.defaultMix = animationMix;
39
44
  // A configured animation always gets a track, posed to its own first frame — a skeleton left
40
45
  // without one instead renders Spine's setup/bind pose, which is almost never the intended "static"
41
46
  // look and was reported as a visibly broken/twitching pose (still-running physics/IK constraints
@@ -187,6 +192,8 @@ export class SpineNodeView extends NodeView {
187
192
  }
188
193
  syncContent(node, transform, profile) {
189
194
  this.activeProfile = profile;
195
+ if (node.type === "spine" && this.content !== undefined)
196
+ this.content.alpha = node.opacity ?? 1;
190
197
  if (this.content instanceof Spine) {
191
198
  if (node.type === "spine") {
192
199
  const settings = resolveSpineSettings(node, profile);
@@ -198,12 +205,19 @@ export class SpineNodeView extends NodeView {
198
205
  // never Spine's setup/bind pose.
199
206
  const playing = settings.autoplay || this.initializeForAuthoring;
200
207
  const desiredTimeScale = playing ? settings.animationSpeed : 0;
208
+ // Правка ползунка должна влиять на следующий переход, а не только на момент создания view.
209
+ this.content.state.data.defaultMix = settings.animationMix;
201
210
  if (settings.animation === undefined) {
202
211
  if (track !== null && track !== undefined)
203
212
  this.content.state.clearTrack(0);
204
213
  }
205
214
  else if ((animationChanged || loopChanged) && this.content.skeleton.data.findAnimation(settings.animation) !== null) {
206
- this.content.state.setAnimation(0, settings.animation, settings.loop).timeScale = desiredTimeScale;
215
+ const entry = this.content.state.setAnimation(0, settings.animation, settings.loop);
216
+ entry.timeScale = desiredTimeScale;
217
+ // Замороженный на кадре 0 скелет не проигрывает микс (mixTime двигается временем трека),
218
+ // и остался бы навсегда в полусмешанной позе — статичной ноде переход всегда мгновенный.
219
+ if (!playing)
220
+ entry.mixDuration = 0;
207
221
  }
208
222
  else if (track !== null && track !== undefined) {
209
223
  track.timeScale = desiredTimeScale;