feat(stage-tamagotchi-godot): G1.3 default visual baseline (#1904)
Some checks failed
CI / Check Provenance (push) Has been cancelled
Update Nix pnpmDeps Hash / update (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Build Test (stage-tamagotchi) (push) Has been cancelled
CI / Build Test (stage-tamagotchi-godot) (push) Has been cancelled
CI / Build Test (stage-web) (push) Has been cancelled
CI / Build Test (ui-loading-screens) (push) Has been cancelled
CI / Build Test (ui-transitions) (push) Has been cancelled
CI / Unit Test (push) Has been cancelled
CI / Type Check (push) Has been cancelled
Cloudflare Workers / Deploy - stage-web (push) Has been cancelled

## Summary

Add the G1.3 default visual baseline for `stage-tamagotchi-godot`.

This moves the Godot stage from a model-loading/runtime skeleton toward
a default presentation stage: fixed sky environment, visual grid ground,
centre marker, fixed lighting rig, camera ground constraint, and focused
material/rendering verification.

## Changes

- Add a fixed runtime visual preset for the Godot stage
  - WorldEnvironment with the existing stage-ui-three HDRI skybox
  - visual-only grid ground at world `Y=0`
  - centre `T` marker at the world origin
  - fixed directional light rig replacing the old single `OmniLight3D`

- Reuse the existing three-stage HDRI asset
- resolve
`packages/stage-ui-three/src/components/Environment/assets/sky_linekotsi_23_HDRI.hdr`
from a workspace checkout
  - do not copy or move the asset into Godot `assets/`
  - keep release packaging for the shared HDRI as a follow-up

- Add camera ground constraint in view-state rules
  - clamp camera position `Y` during view-state normalisation / commit
- applies uniformly to local input, settings-driven patches, and future
remote/agent patches

- Patch vendored Godot MToon shader behaviour
  - disable implicit WorldEnvironment ambient light for MToon materials
- use Godot alpha scissor / alpha-to-coverage path for cutout MToon
variants
- document the local vendor patch and removal conditions in
`docs/vendor-patches.md`

- Improve Godot rendering defaults
  - enable 3D MSAA
  - render 3D at 2x scale to avoid low-DPI/viewport scaling aliasing

- Add verification harnesses
- C# engine-local checks for camera ground constraint and HDRI asset
resolution
- Godot material rendering check scene for AvatarSample A/B material
import coverage

- Clean stage root presentation
  - remove the old on-screen runtime status label
  - keep Electron bridge and scene/view runtime wiring intact

## Notes

The HDRI path resolution is intentionally workspace-only in this PR.
Release packaging for sharing the three-stage HDRI with the exported
Godot sidecar remains a follow-up.

The MToon shader changes are deliberate vendor patches, recorded in
`docs/vendor-patches.md`, because `ambient_light_disabled`,
`alpha_to_coverage`, and alpha scissor behaviour are shader-level
changes rather than runtime material parameters.

The material rendering check validates imported material structure, not
visual golden output.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Lilia_Chen 2026-05-30 15:42:26 +01:00 committed by GitHub
parent f2897f7663
commit cb5c0783e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1114 additions and 83 deletions

View file

@ -6,6 +6,9 @@
bin/
obj/
# Godot can scan verification test sources during project imports; these are not stage resources.
tests/**/*.uid
# Developer-provided local VRM fixtures for editor preview scenes.
assets/fixtures/vrm/*
!assets/fixtures/vrm/.gitkeep

View file

@ -139,6 +139,40 @@ Runtime import details live in [`docs/vrm-runtime-import.md`](docs/vrm-runtime-i
Vendored add-on local patches and generated metadata differences are tracked in
[`docs/vendor-patches.md`](docs/vendor-patches.md).
## Default Stage Visuals
G1.3 installs a fixed default presentation preset at runtime. It includes a sky
environment, a large world-anchored grid ground at `Y=0`, a center `T` marker at
the world origin, and a fixed directional light rig.
The sky environment reuses the existing stage-ui-three HDRI at
`packages/stage-ui-three/src/components/Environment/assets/sky_linekotsi_23_HDRI.hdr`
when the Godot stage runs from a workspace checkout. The HDRI is not copied into
Godot `assets/`, and the release packaging path for this shared asset remains a
separate follow-up.
The grid ground is visual-only. It does not move the avatar root away from
`(0, 0, 0)`, and its shader fades distant grid lines into the horizon color
without enabling volumetric fog.
## Material Rendering Check
G1.3 includes a focused runtime material verification scene for the committed
AvatarSample A/B fixtures:
```powershell
& $env:GODOT4 --headless --path . `
--quit-after 5 `
--log-file material-check.log `
tests/material-rendering-check/materialRenderingCheck.tscn
```
This check imports both samples through `VrmRuntimeImporter.gd` and verifies the
runtime material surface covers MToon, alpha/cutout, transparent materials,
outline passes, and mesh shadow casters. The current A/B fixtures do not contain
unlit materials, so this check reports `unlit = 0` and does not treat that as a
failure.
## Live Debugging From The Godot Editor
Use this path when Electron is the real host and the model is selected from the

View file

@ -1,5 +1,7 @@
render_mode skip_vertex_transform;
//render_mode specular_disabled,ambient_light_disabled;
render_mode ambient_light_disabled;
// Keep specular active while validating whether environment ambient/radiance is the washout source.
// render_mode specular_disabled;
// VARIANTS:
// DEFAULT_MODE:
@ -326,7 +328,15 @@ void fragment() {
#if defined(ALPHA_BLEND)
ALPHA = alpha;
#elif defined(ALPHA_CUTOUT)
if (_AlphaCutoutEnable > 0.5 && alpha < _Cutoff) { discard; }
if (_AlphaCutoutEnable > 0.5) {
// NOTICE: Godot's alpha scissor path reads the sampled alpha from ALPHA.
// This can route cutout shaders through the transparent pipeline; AIRI
// accepts that tradeoff for alpha-to-coverage AA. See docs/vendor-patches.md.
ALPHA = alpha;
ALPHA_SCISSOR_THRESHOLD = _Cutoff;
ALPHA_ANTIALIASING_EDGE = min(_Cutoff * 0.6, 0.3);
ALPHA_TEXTURE_COORDINATE = mainUv * vec2(textureSize(_MainTex, 0));
}
#endif
//METALLIC = metallic;

View file

@ -1,5 +1,6 @@
shader_type spatial;
render_mode alpha_to_coverage;
#define ALPHA_CUTOUT
#include "./mtoon_common.gdshaderinc"
#include "./mtoon_common.gdshaderinc"

View file

@ -1,6 +1,7 @@
shader_type spatial;
render_mode cull_disabled;
render_mode alpha_to_coverage;
#define ALPHA_CUTOUT
#include "./mtoon_common.gdshaderinc"
#include "./mtoon_common.gdshaderinc"

View file

@ -1,7 +1,8 @@
shader_type spatial;
render_mode cull_front;
render_mode alpha_to_coverage;
#define IS_OUTLINE
#define ALPHA_CUTOUT
#include "./mtoon_common.gdshaderinc"
#include "./mtoon_common.gdshaderinc"

View file

@ -32,6 +32,42 @@ whenever vendored add-on files differ from their upstream source.
lookup fix or otherwise handles missing `secondary` nodes before parsing
spring bones.
### MToon ambient and cutout shader patch
Files:
- `addons/Godot-MToon-Shader/mtoon_common.gdshaderinc`
- `addons/Godot-MToon-Shader/mtoon_cutout.gdshader`
- `addons/Godot-MToon-Shader/mtoon_cutout_cull_off.gdshader`
- `addons/Godot-MToon-Shader/mtoon_outline_cutout.gdshader`
- Local change: enable `render_mode ambient_light_disabled` for the shared MToon
shader include.
- Local change: route MToon cutout materials through Godot's alpha scissor
antialiasing path by writing `ALPHA`, setting `ALPHA_SCISSOR_THRESHOLD`,
`ALPHA_ANTIALIASING_EDGE`, and `ALPHA_TEXTURE_COORDINATE`, and enabling
`render_mode alpha_to_coverage`.
- Reason: AIRI's stage preset owns sky, ground, and environment presentation, but
avatar toon materials need stable character color. Godot's default ambient
light and radiance contribution can wash out MToon avatars as the stage
environment changes, so AIRI isolates avatar MToon materials from implicit
WorldEnvironment ambient while keeping direct light handling in the shader.
- Reason: VRM hair, lashes, accessories, and outline cutout passes use hard alpha
edges. At distance, those edges collapse into visible stair-step or dashed
pixels. Godot's alpha-to-coverage path works with 3D MSAA, and Godot's shader
alpha scissor path requires `ALPHA` to receive the sampled texture alpha.
Writing `ALPHA` may route these shaders through Godot's transparent pipeline,
which can introduce sorting or shadow-casting regressions. AIRI accepts that
tradeoff for the current visual baseline and should revisit it if those
regressions appear or upstream exposes a cutout antialiasing path that
preserves opaque shadow semantics.
- Validation: `tests/material-rendering-check/materialRenderingCheck.tscn`
imports AvatarSample A/B and still detects MToon, cutout, transparent,
outline, and shadow-caster materials after the patch.
- Removal condition: remove this patch if AIRI moves to an owned MToon shader
variant, or if upstream exposes a supported way to opt MToon materials out of
Godot environment ambient/radiance while preserving VRM import compatibility.
## Generated Metadata Differences
These files differ from the upstream commit after opening/importing the add-on

View file

@ -29,5 +29,7 @@ enabled=PackedStringArray("res://addons/Godot-MToon-Shader/plugin.cfg", "res://a
[rendering]
anti_aliasing/quality/msaa_3d=2
scaling_3d/scale=2.0
rendering_device/driver.windows="d3d12"
textures/vram_compression/import_etc2_astc=true

View file

@ -12,8 +12,3 @@ script = ExtResource("1_k8kx6")
[node name="Camera3D" type="Camera3D" parent="." unique_id=1342789791]
transform = Transform3D(1, 0, 0, 0, 0.93969274, 0.34201992, 0, -0.34201992, 0.93969274, 0, 1.2, 3.5)
current = true
[node name="OmniLight3D" type="OmniLight3D" parent="." unique_id=1692990068]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2, 2, 2)
light_color = Color(1, 0.956863, 0.909804, 1)
omni_range = 12.0

View file

@ -5,17 +5,7 @@ using Godot;
/// <summary>
/// Root node for the Godot sidecar stage runtime.
///
/// Use when:
/// - Running the desktop Godot stage through Electron main.
/// - Receiving scene input from the current stage settings model selection.
///
/// Expects:
/// - Electron launches Godot with <c>--airi-ws-url=&lt;runtime-url&gt;</c>.
/// - The scene contains or can create an avatar root node.
///
/// Returns:
/// - A running stage process that reports ready/applied/error envelopes to Electron main.
/// </summary>
///
/// Call stack:
///
@ -25,7 +15,6 @@ using Godot;
/// -> <see cref="_Process"/>
/// -> <see cref="StageBridge.Poll"/>
/// -> <see cref="HandleMessage"/>
/// </summary>
public partial class StageRoot : Node3D
{
private const string AvatarRootNodeName = "AvatarRoot";
@ -42,7 +31,6 @@ public partial class StageRoot : Node3D
private StageBridge _bridge = null!;
private StageSceneController _sceneController = null!;
private Label3D _statusLabel = null!;
private StageViewController _viewController = null!;
private StageCameraInputController _viewInputController = null!;
private StageViewRuntime _viewRuntime = null!;
@ -53,8 +41,7 @@ public partial class StageRoot : Node3D
public override void _Ready()
{
HideEditorPreviewRoot();
_statusLabel = CreateStatusLabel();
AddChild(_statusLabel);
StageVisualPreset.Apply(this);
var avatarRoot = ResolveAvatarRoot();
_sceneController = new StageSceneController(avatarRoot, new VrmAvatarLoader());
@ -63,7 +50,6 @@ public partial class StageRoot : Node3D
var webSocketUrl = ResolveWebSocketUrl();
if (string.IsNullOrWhiteSpace(webSocketUrl))
{
UpdateStatus("Missing Electron bridge URL.");
GD.PushWarning("Godot stage missing --airi-ws-url argument.");
return;
}
@ -76,13 +62,10 @@ public partial class StageRoot : Node3D
var connectError = _bridge.Connect(webSocketUrl);
if (connectError != Error.Ok)
{
UpdateStatus("Failed to connect to Electron main.");
GD.PushError($"Godot stage failed to connect to Electron main: {connectError}.");
GetTree().Quit();
return;
}
UpdateStatus("Connecting to Electron main...");
}
/// <inheritdoc/>
@ -107,7 +90,6 @@ public partial class StageRoot : Node3D
private void HandleBridgeOpened()
{
_bridge.SendEnvelope("stage.ready");
UpdateStatus("Connected to Electron main.");
}
private void HandleBridgeClosed(string message)
@ -118,7 +100,6 @@ public partial class StageRoot : Node3D
return;
}
UpdateStatus(message);
GD.PushWarning(message);
GetTree().Quit();
}
@ -168,19 +149,6 @@ public partial class StageRoot : Node3D
editorPreviewRoot.ProcessMode = ProcessModeEnum.Disabled;
}
private static Label3D CreateStatusLabel()
{
return new Label3D
{
Billboard = BaseMaterial3D.BillboardModeEnum.Enabled,
FontSize = 34,
Modulate = new Color(0.95f, 0.98f, 1.0f),
PixelSize = 0.0035f,
Position = new Vector3(-1.45f, 1.75f, 0.0f),
Text = "Godot Stage (experimental)",
};
}
private void HandleMessage(string rawMessage)
{
try
@ -204,7 +172,6 @@ public partial class StageRoot : Node3D
break;
case "host.shutdown":
_shutdownRequested = true;
UpdateStatus("Shutdown requested by Electron main.");
GetTree().Quit();
break;
}
@ -212,7 +179,6 @@ public partial class StageRoot : Node3D
catch (Exception error)
{
var message = $"Failed to parse Electron message: {error.Message}";
UpdateStatus(message);
SendSceneError(message);
}
}
@ -242,16 +208,14 @@ public partial class StageRoot : Node3D
{
// TODO:
// Make avatar apply and view bootstrap one transaction. Today avatar apply commits
// before bootstrap, so a bootstrap failure reports scene.error with the new avatar loaded.
// before bootstrap. If bootstrap fails, scene.error is reported with the new
// avatar already loaded.
var avatar = _sceneController.Apply(payload);
_viewController?.UseAvatar(avatar);
_viewRuntime?.BootstrapForAvatar();
_activeSceneModelId = payload.ModelId;
}
var fileName = System.IO.Path.GetFileName(payload.Path);
UpdateStatus($"Connected to Electron main.\nModel: {payload.Name}\nAsset: {fileName}");
_bridge.SendEnvelope("scene.applied", new
{
modelId = payload.ModelId,
@ -260,7 +224,6 @@ public partial class StageRoot : Node3D
catch (Exception error)
{
var message = $"Failed to apply scene input: {error.Message}";
UpdateStatus(message);
SendSceneError(message);
}
}
@ -334,7 +297,8 @@ public partial class StageRoot : Node3D
var cameraController = new StageCameraPoseController(ResolveCamera());
_viewController = new StageViewController(avatarRoot, cameraController);
_viewRuntime = new StageViewRuntime(_viewController);
_viewRuntime.SnapshotReady += payload => _bridge.SendEnvelope("stage.view.snapshot", payload);
_viewRuntime.SnapshotReady += payload =>
_bridge.SendEnvelope("stage.view.snapshot", payload);
_viewRuntime.ErrorReady += payload => _bridge.SendEnvelope("stage.view.error", payload);
_viewInputController = new StageCameraInputController(_viewRuntime, cameraController);
}
@ -346,7 +310,4 @@ public partial class StageRoot : Node3D
message,
});
}
private void UpdateStatus(string message) =>
_statusLabel.Text = $"Godot Stage (experimental)\n{message}";
}

View file

@ -9,6 +9,7 @@ public static class StageViewStateRules
public const double CameraMaxPitchDeg = 80;
public const double CameraMinFovDeg = 10;
public const double CameraMaxFovDeg = 120;
public const double CameraMinPositionY = 0.05;
public static StageViewState CreateDefault() => new(
1,
@ -35,7 +36,11 @@ public static class StageViewStateRules
|| patch.Camera?.FovDeg != null;
}
public static StageViewState ApplyPatch(StageViewState current, StageViewPatch patch, long updatedAt)
public static StageViewState ApplyPatch(
StageViewState current,
StageViewPatch patch,
long updatedAt
)
{
if (!HasMutation(patch))
{
@ -72,6 +77,7 @@ public static class StageViewStateRules
SchemaVersion = 1,
Camera = state.Camera with
{
Position = ClampCameraPosition(state.Camera.Position),
YawDeg = NormalizeDegrees(state.Camera.YawDeg),
PitchDeg = Clamp(state.Camera.PitchDeg, CameraMinPitchDeg, CameraMaxPitchDeg),
FovDeg = Clamp(state.Camera.FovDeg, CameraMinFovDeg, CameraMaxFovDeg),
@ -115,6 +121,15 @@ public static class StageViewStateRules
return patch?.X != null || patch?.Y != null || patch?.Z != null;
}
private static StageViewVec3 ClampCameraPosition(StageViewVec3 position)
{
return new StageViewVec3(
position.X,
Clamp(position.Y, CameraMinPositionY, double.PositiveInfinity),
position.Z
);
}
private static double Clamp(double value, double min, double max)
{
return Math.Min(max, Math.Max(min, value));

View file

@ -0,0 +1,67 @@
using System.IO;
/// <summary>
/// Resolves presentation-stage environment assets that are owned outside the Godot project.
///
/// Use when:
/// - The Godot stage is running from a workspace checkout and should reuse existing stage assets.
/// - A fixed visual preset needs a local filesystem path instead of a Godot imported resource.
///
/// Expects:
/// - <c>startDirectory</c> is inside or below the AIRI repository when workspace assets exist.
///
/// Returns:
/// - Absolute filesystem paths for found assets, or an empty string when unavailable.
/// </summary>
public static class StageEnvironmentAssetResolver
{
private static readonly string[] ThreeStageSkyHdriPathSegments = new[]
{
"packages",
"stage-ui-three",
"src",
"components",
"Environment",
"assets",
"sky_linekotsi_23_HDRI.hdr",
};
/// <summary>
/// Resolves the existing three-stage HDRI sky texture from a workspace directory.
///
/// Use when:
/// - The Godot stage should reuse the stage-ui-three default skybox without copying it.
///
/// Expects:
/// - <paramref name="startDirectory"/> is a filesystem directory path.
///
/// Returns:
/// - Absolute path to the HDRI file, or <see cref="string.Empty"/> when it is unavailable.
/// </summary>
public static string ResolveThreeStageSkyHdriPath(string startDirectory)
{
if (string.IsNullOrWhiteSpace(startDirectory))
{
return string.Empty;
}
var currentDirectory = Path.GetFullPath(startDirectory);
while (!string.IsNullOrWhiteSpace(currentDirectory))
{
var candidatePath = Path.GetFullPath(Path.Combine(
currentDirectory,
Path.Combine(ThreeStageSkyHdriPathSegments)
));
if (File.Exists(candidatePath))
{
return candidatePath;
}
// Walk upward from the Godot project directory until a repository root candidate matches.
currentDirectory = Directory.GetParent(currentDirectory)?.FullName;
}
return string.Empty;
}
}

View file

@ -0,0 +1,343 @@
using System;
using Godot;
using GodotEnvironment = Godot.Environment;
/// <summary>
/// Installs the fixed G1.3 default stage visual baseline.
///
/// Use when:
/// - The sidecar starts the default AIRI presentation stage.
/// - Camera, lighting, sky, and ground references should be available without host settings.
///
/// Expects:
/// - The stage root is a live <see cref="Node3D"/>.
/// - Workspace runs can reach the existing stage-ui-three HDRI from the Godot project path.
///
/// Returns:
/// - A single runtime-owned visual preset subtree with sky, ground references, and light rig.
/// </summary>
public static class StageVisualPreset
{
private const string VisualPresetRootNodeName = "DefaultStageVisualPreset";
private const string WorldEnvironmentNodeName = "StageWorldEnvironment";
private const string LightingRigNodeName = "StageLightingRig";
private const string GroundPlaneNodeName = "StageGroundPlane";
private const string CenterMarkerNodeName = "StageCenterT";
private const float GroundExtent = 180.0f;
private const float LightShadowMaxDistance = 24.0f;
private const float CenterMarkerElevation = 0.008f;
private const string FadingGridShaderCode = """
shader_type spatial;
render_mode unshaded, cull_disabled, blend_mix;
uniform vec4 ground_color : source_color;
uniform vec4 horizon_mist_color : source_color;
uniform vec4 minor_grid_color : source_color;
uniform vec4 major_grid_color : source_color;
uniform vec4 center_grid_color : source_color;
uniform float minor_spacing = 0.25;
uniform float major_spacing = 1.0;
uniform float fade_start = 8.0;
uniform float fade_end = 22.0;
uniform float minor_line_width = 0.34;
uniform float major_line_width = 0.58;
uniform float axis_width_world = 0.018;
varying vec3 world_position;
void vertex() {
world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
}
float grid_line(vec2 position, float spacing, float width) {
vec2 coordinate = position / spacing;
vec2 derivative = max(fwidth(coordinate), vec2(0.0001));
vec2 grid = abs(fract(coordinate - 0.5) - 0.5) / derivative;
return 1.0 - smoothstep(width, width + 1.0, min(grid.x, grid.y));
}
float axis_line(float coordinate) {
float derivative = max(fwidth(coordinate), 0.0001);
return 1.0 - smoothstep(axis_width_world, axis_width_world + derivative, abs(coordinate));
}
void fragment() {
float distance_to_camera = distance(CAMERA_POSITION_WORLD.xz, world_position.xz);
float horizon_fade = smoothstep(fade_start, fade_end, distance_to_camera);
float grid_fade = pow(1.0 - horizon_fade, 3.2);
float minor_line = grid_line(world_position.xz, minor_spacing, minor_line_width);
float major_line = grid_line(world_position.xz, major_spacing, major_line_width);
float axis_line_alpha = max(axis_line(world_position.x), axis_line(world_position.z));
vec3 base_color = mix(
ground_color.rgb,
horizon_mist_color.rgb,
horizon_fade * horizon_mist_color.a
);
vec3 grid_color = mix(minor_grid_color.rgb, major_grid_color.rgb, major_line);
grid_color = mix(grid_color, center_grid_color.rgb, axis_line_alpha);
float line_alpha = max(
minor_line * minor_grid_color.a,
major_line * major_grid_color.a
);
line_alpha = max(line_alpha, axis_line_alpha * center_grid_color.a) * grid_fade;
ALBEDO = mix(base_color, grid_color, line_alpha);
ALPHA = 1.0 - horizon_fade;
ROUGHNESS = 0.88;
}
""";
private static readonly Color GroundColor = new(0.27f, 0.34f, 0.37f, 1.0f);
private static readonly Color HorizonMistColor = new(0.62f, 0.73f, 0.76f, 0.82f);
private static readonly Color MinorGridColor = new(1.0f, 1.0f, 1.0f, 0.20f);
private static readonly Color MajorGridColor = new(1.0f, 1.0f, 1.0f, 0.38f);
private static readonly Color CenterGridColor = new(1.0f, 1.0f, 1.0f, 0.72f);
private static readonly Color CenterMarkerColor = new(0.72f, 0.94f, 1.0f, 0.92f);
/// <summary>
/// Applies the stage visual preset under the provided root node.
///
/// Use when:
/// - A stage scene should install its default visual baseline during startup.
///
/// Expects:
/// - <paramref name="stageRoot"/> is not null.
///
/// Returns:
/// - Existing preset subtree is replaced with a fresh fixed visual preset.
/// </summary>
public static void Apply(Node3D stageRoot)
{
if (stageRoot == null)
{
throw new ArgumentNullException(nameof(stageRoot));
}
RemoveExistingPreset(stageRoot);
var visualRoot = new Node3D
{
Name = VisualPresetRootNodeName,
};
stageRoot.AddChild(visualRoot);
visualRoot.AddChild(CreateWorldEnvironment());
visualRoot.AddChild(CreateGroundPlane());
visualRoot.AddChild(CreateCenterMarker());
visualRoot.AddChild(CreateLightingRig());
}
private static void RemoveExistingPreset(Node stageRoot)
{
var existingPreset = stageRoot.GetNodeOrNull<Node>(VisualPresetRootNodeName);
if (existingPreset == null)
{
return;
}
stageRoot.RemoveChild(existingPreset);
existingPreset.QueueFree();
}
private static WorldEnvironment CreateWorldEnvironment()
{
var skyMaterial = new PanoramaSkyMaterial
{
EnergyMultiplier = 1.0f,
Panorama = LoadSkyTexture(),
};
var sky = new Sky
{
ProcessMode = Sky.ProcessModeEnum.Quality,
SkyMaterial = skyMaterial,
};
var environment = new GodotEnvironment
{
AmbientLightEnergy = 0.26f,
AmbientLightSkyContribution = 0.48f,
AmbientLightSource = GodotEnvironment.AmbientSource.Sky,
BackgroundEnergyMultiplier = 1.06f,
BackgroundMode = GodotEnvironment.BGMode.Sky,
ReflectedLightSource = GodotEnvironment.ReflectionSource.Sky,
Sky = sky,
// Keep MToon/NPR avatars out of filmic tone mapping, then apply a small
// stylized display grade. The VRM materials already use source_color
// texture inputs; the remaining gap to three-stage is presentation color,
// not importer-side texture conversion.
// Three-stage disables tone mapping per MToon material; Godot applies
// environment tone mapping globally, so filmic curves wash avatar colors out.
AdjustmentContrast = 1.03f,
AdjustmentEnabled = true,
AdjustmentSaturation = 1.24f,
TonemapExposure = 1.0f,
TonemapMode = GodotEnvironment.ToneMapper.Linear,
};
return new WorldEnvironment
{
Name = WorldEnvironmentNodeName,
Environment = environment,
};
}
private static Texture2D LoadSkyTexture()
{
var skyHdriPath = StageEnvironmentAssetResolver.ResolveThreeStageSkyHdriPath(
ProjectSettings.GlobalizePath("res://")
);
if (string.IsNullOrWhiteSpace(skyHdriPath))
{
GD.PushWarning("Default stage HDR skybox could not be found in stage-ui-three assets.");
return null;
}
var image = Image.LoadFromFile(skyHdriPath);
if (image == null || image.GetWidth() <= 0 || image.GetHeight() <= 0)
{
GD.PushWarning($"Default stage HDR skybox could not be loaded: {skyHdriPath}.");
return null;
}
return ImageTexture.CreateFromImage(image);
}
private static MeshInstance3D CreateGroundPlane()
{
var shader = new Shader
{
Code = FadingGridShaderCode,
};
var material = new ShaderMaterial
{
Shader = shader,
};
material.SetShaderParameter("ground_color", GroundColor);
material.SetShaderParameter("horizon_mist_color", HorizonMistColor);
material.SetShaderParameter("minor_grid_color", MinorGridColor);
material.SetShaderParameter("major_grid_color", MajorGridColor);
material.SetShaderParameter("center_grid_color", CenterGridColor);
return new MeshInstance3D
{
CastShadow = GeometryInstance3D.ShadowCastingSetting.Off,
MaterialOverride = material,
Mesh = new PlaneMesh
{
Orientation = PlaneMesh.OrientationEnum.Y,
Size = new Vector2(GroundExtent * 2.0f, GroundExtent * 2.0f),
SubdivideDepth = 24,
SubdivideWidth = 24,
},
Name = GroundPlaneNodeName,
};
}
private static Node3D CreateCenterMarker()
{
var root = new Node3D
{
Name = CenterMarkerNodeName,
};
var material = new StandardMaterial3D
{
AlbedoColor = CenterMarkerColor,
ShadingMode = BaseMaterial3D.ShadingModeEnum.Unshaded,
};
root.AddChild(CreateCenterMarkerBar(
"Top",
new Vector3(0.0f, CenterMarkerElevation, 0.15f),
new Vector3(0.36f, 0.012f, 0.0275f),
material
));
root.AddChild(CreateCenterMarkerBar(
"Stem",
new Vector3(0.0f, CenterMarkerElevation, -0.02f),
new Vector3(0.0275f, 0.012f, 0.34f),
material
));
return root;
}
private static MeshInstance3D CreateCenterMarkerBar(
string name,
Vector3 position,
Vector3 size,
Material material
)
{
return new MeshInstance3D
{
CastShadow = GeometryInstance3D.ShadowCastingSetting.Off,
MaterialOverride = material,
Mesh = new BoxMesh
{
Size = size,
},
Name = name,
Position = position,
};
}
private static Node3D CreateLightingRig()
{
var rig = new Node3D
{
Name = LightingRigNodeName,
};
rig.AddChild(CreateDirectionalLight(
"KeyLight",
new Vector3(-48.0f, -34.0f, 0.0f),
new Color(1.0f, 1.0f, 1.0f),
0.76f,
1.1f,
true
));
rig.AddChild(CreateDirectionalLight(
"FillLight",
new Vector3(-20.0f, 122.0f, 0.0f),
new Color(1.0f, 1.0f, 1.0f),
0.06f,
0.0f,
false
));
rig.AddChild(CreateDirectionalLight(
"RimLight",
new Vector3(-18.0f, 205.0f, 0.0f),
new Color(1.0f, 1.0f, 1.0f),
0.20f,
0.0f,
false
));
return rig;
}
private static DirectionalLight3D CreateDirectionalLight(
string name,
Vector3 rotationDegrees,
Color color,
float energy,
float angularDistance,
bool shadowEnabled
)
{
return new DirectionalLight3D
{
DirectionalShadowMaxDistance = LightShadowMaxDistance,
LightAngularDistance = angularDistance,
LightColor = color,
LightEnergy = energy,
Name = name,
RotationDegrees = rotationDegrees,
ShadowEnabled = shadowEnabled,
SkyMode = DirectionalLight3D.SkyModeEnum.LightOnly,
};
}
}

View file

@ -5,4 +5,9 @@
<EnableDynamicLoading>true</EnableDynamicLoading>
<RootNamespace>StageTamagotchiGodot</RootNamespace>
</PropertyGroup>
</Project>
<ItemGroup>
<!-- Keep generated sources and verification harnesses out of the game assembly. -->
<Compile Remove=".godot\**\*.cs" />
<Compile Remove="tests\**\*.cs" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<ArtifactsRoot>$(MSBuildThisFileDirectory)..\..\.godot\mono\temp\tests\</ArtifactsRoot>
<ArtifactsRoot>$([System.IO.Path]::GetFullPath('$(ArtifactsRoot)'))</ArtifactsRoot>
<BaseOutputPath>$(ArtifactsRoot)bin\</BaseOutputPath>
<BaseIntermediateOutputPath>$(ArtifactsRoot)obj\</BaseIntermediateOutputPath>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,191 @@
using System;
using System.IO;
/// <summary>
/// Runs focused engine-local regression checks that do not require the Godot editor.
///
/// Call stack:
///
/// dotnet run --project tests/StageTamagotchiGodot.Tests
/// -> <see cref="Main"/>
/// -> <see cref="CameraGroundConstraint"/>
/// -> <see cref="StageViewStateRules"/>
/// -> <see cref="StageEnvironmentAssets"/>
/// -> <see cref="StageEnvironmentAssetResolver"/>
/// </summary>
internal static class Program
{
private const double ExpectedCameraMinY = 0.05;
private const string ThreeStageSkyHdriRelativePath =
"packages/stage-ui-three/src/components/Environment/assets/sky_linekotsi_23_HDRI.hdr";
private static int Main()
{
try
{
CameraGroundConstraint.ApplyPatchClampsCameraYToStageGround();
CameraGroundConstraint.NormalizeClampsCameraYToStageGround();
CameraGroundConstraint.NormalizePreservesCameraYAboveStageGround();
StageEnvironmentAssets.ResolvesThreeStageSkyHdriFromGodotProjectDirectory();
StageEnvironmentAssets.ReturnsEmptyPathWhenThreeStageSkyHdriIsUnavailable();
Console.WriteLine("StageTamagotchiGodot.Tests passed.");
return 0;
}
catch (Exception error)
{
Console.Error.WriteLine(error.Message);
return 1;
}
}
private static class StageEnvironmentAssets
{
public static void ResolvesThreeStageSkyHdriFromGodotProjectDirectory()
{
var projectDirectory = FindGodotProjectDirectory();
var skyHdriPath = StageEnvironmentAssetResolver.ResolveThreeStageSkyHdriPath(
projectDirectory
);
AssertEqual(
NormalizePath(FindRepositoryRootFrom(projectDirectory), ThreeStageSkyHdriRelativePath),
skyHdriPath,
"three-stage sky HDRI path"
);
}
public static void ReturnsEmptyPathWhenThreeStageSkyHdriIsUnavailable()
{
var missingRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
var skyHdriPath = StageEnvironmentAssetResolver.ResolveThreeStageSkyHdriPath(missingRoot);
AssertEqual(string.Empty, skyHdriPath, "missing three-stage sky HDRI path");
}
}
private static class CameraGroundConstraint
{
public static void ApplyPatchClampsCameraYToStageGround()
{
var current = StageViewStateRules.CreateDefault();
var patch = new StageViewPatch(
Camera: new StageCameraPosePatch(
Position: new StageViewVec3Patch(Y: -2)
)
);
var next = StageViewStateRules.ApplyPatch(current, patch, 123);
AssertEqual(ExpectedCameraMinY, next.Camera.Position.Y, "remote patch camera Y");
AssertEqual(1, next.Revision, "revision after camera ground clamp");
AssertEqual(123, next.UpdatedAt, "updatedAt after camera ground clamp");
}
public static void NormalizeClampsCameraYToStageGround()
{
var state = StageViewStateRules.CreateDefault() with
{
Camera = StageViewStateRules.CreateDefault().Camera with
{
Position = new StageViewVec3(0, -100, 3.5),
},
};
var normalized = StageViewStateRules.Normalize(state);
AssertEqual(ExpectedCameraMinY, normalized.Camera.Position.Y, "normalized camera Y");
}
public static void NormalizePreservesCameraYAboveStageGround()
{
var state = StageViewStateRules.CreateDefault() with
{
Camera = StageViewStateRules.CreateDefault().Camera with
{
Position = new StageViewVec3(0, 2.5, 3.5),
},
};
var normalized = StageViewStateRules.Normalize(state);
AssertEqual(2.5, normalized.Camera.Position.Y, "above-ground camera Y");
}
}
private static void AssertEqual(double expected, double actual, string label)
{
if (Math.Abs(expected - actual) <= 0.000001)
{
return;
}
throw new InvalidOperationException(
$"Expected {label} to be {expected}, got {actual}."
);
}
private static void AssertEqual(long expected, long actual, string label)
{
if (expected == actual)
{
return;
}
throw new InvalidOperationException(
$"Expected {label} to be {expected}, got {actual}."
);
}
private static void AssertEqual(string expected, string actual, string label)
{
if (string.Equals(expected, actual, StringComparison.Ordinal))
{
return;
}
throw new InvalidOperationException(
$"Expected {label} to be '{expected}', got '{actual}'."
);
}
private static string FindGodotProjectDirectory()
{
var currentDirectory = AppContext.BaseDirectory;
while (!string.IsNullOrWhiteSpace(currentDirectory))
{
if (File.Exists(Path.Combine(currentDirectory, "project.godot")))
{
return currentDirectory;
}
currentDirectory = Directory.GetParent(currentDirectory)?.FullName;
}
throw new InvalidOperationException("Could not find Godot project directory.");
}
private static string FindRepositoryRootFrom(string startDirectory)
{
var currentDirectory = startDirectory;
while (!string.IsNullOrWhiteSpace(currentDirectory))
{
var gitMarkerPath = Path.Combine(currentDirectory, ".git");
// Git worktrees store .git as a file that points to the real common directory.
if (Directory.Exists(gitMarkerPath) || File.Exists(gitMarkerPath))
{
return currentDirectory;
}
currentDirectory = Directory.GetParent(currentDirectory)?.FullName;
}
throw new InvalidOperationException("Could not find repository root.");
}
private static string NormalizePath(string root, string relativePath) =>
Path.GetFullPath(
Path.Combine(root, relativePath.Replace('/', Path.DirectorySeparatorChar))
);
}

View file

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ProjectDirInput>$(MSBuildThisFileDirectory)..\..</ProjectDirInput>
<GodotProjectDir>$([System.IO.Path]::GetFullPath('$(ProjectDirInput)'))</GodotProjectDir>
<GodotDir>$([MSBuild]::EnsureTrailingSlash('$(GodotProjectDir)'))</GodotDir>
<GodotProjectDir>$(GodotDir)</GodotProjectDir>
<GodotProjectDirBase64>$([MSBuild]::ConvertToBase64('$(GodotDir)'))</GodotProjectDirBase64>
<ImplicitUsings>false</ImplicitUsings>
<Nullable>disable</Nullable>
</PropertyGroup>
<ItemGroup>
<CompilerVisibleProperty Include="GodotProjectDir" />
<CompilerVisibleProperty Include="GodotProjectDirBase64" />
<CompilerVisibleProperty Include="GodotSourceGenerators" />
<CompilerVisibleProperty Include="IsGodotToolsProject" />
<ProjectReference Include="..\..\stage-tamagotchi-godot.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,119 @@
extends Node
const vrm_runtime_importer = preload("res://scripts/vrm/VrmRuntimeImporter.gd")
const SAMPLE_PATHS := [
"res://../../packages/stage-ui/src/assets/vrm/models/AvatarSample-A/AvatarSample_A.vrm",
"res://../../packages/stage-ui/src/assets/vrm/models/AvatarSample-B/AvatarSample_B.vrm",
]
func _ready() -> void:
var failures: Array[String] = []
for sample_path in SAMPLE_PATHS:
var summary := _inspect_sample(sample_path)
print("Material check: %s" % summary)
_collect_sample_failures(summary, failures)
for failure in failures:
push_error(failure)
get_tree().quit(1 if failures.size() > 0 else 0)
func _inspect_sample(sample_path: String) -> Dictionary:
var importer: RefCounted = vrm_runtime_importer.new()
var native_path := ProjectSettings.globalize_path(sample_path)
var avatar: Node = importer.import_vrm(native_path)
var summary := {
"sample": sample_path.get_file(),
"mtoon": 0,
"cutout": 0,
"transparent": 0,
"outline": 0,
"shadowCasters": 0,
"unlit": 0,
"error": "",
}
if avatar == null:
summary["error"] = importer.get_last_error()
return summary
_scan_node(avatar, summary)
avatar.free()
return summary
func _scan_node(node: Node, summary: Dictionary) -> void:
if node is MeshInstance3D:
_scan_mesh_instance(node, summary)
for child in node.get_children():
_scan_node(child, summary)
func _scan_mesh_instance(mesh_instance: MeshInstance3D, summary: Dictionary) -> void:
if mesh_instance.cast_shadow != GeometryInstance3D.SHADOW_CASTING_SETTING_OFF:
summary["shadowCasters"] += 1
if mesh_instance.mesh == null:
return
for surface_index in range(mesh_instance.mesh.get_surface_count()):
var material := mesh_instance.get_surface_override_material(surface_index)
if material == null:
material = mesh_instance.mesh.surface_get_material(surface_index)
_scan_material(material, summary)
func _scan_material(material: Material, summary: Dictionary) -> void:
if material == null:
return
if material is StandardMaterial3D:
if material.shading_mode == BaseMaterial3D.SHADING_MODE_UNSHADED:
summary["unlit"] += 1
return
if material is not ShaderMaterial or material.shader == null:
return
var shader_path: String = material.shader.resource_path
if shader_path.find("mtoon") == -1:
return
summary["mtoon"] += 1
if shader_path.find("_cutout") != -1:
summary["cutout"] += 1
if shader_path.find("_trans") != -1:
summary["transparent"] += 1
if material.next_pass is ShaderMaterial and material.next_pass.shader != null:
var outline_path: String = material.next_pass.shader.resource_path
if outline_path.find("mtoon_outline") != -1:
summary["outline"] += 1
func _collect_sample_failures(summary: Dictionary, failures: Array[String]) -> void:
if not summary["error"].is_empty():
failures.push_back("%s import failed: %s" % [summary["sample"], summary["error"]])
return
if summary["mtoon"] == 0:
failures.push_back("%s did not import MToon materials." % summary["sample"])
if summary["cutout"] == 0:
failures.push_back("%s did not import alpha/cutout MToon materials." % summary["sample"])
if summary["transparent"] == 0:
failures.push_back("%s did not import transparent MToon materials." % summary["sample"])
if summary["outline"] == 0:
failures.push_back("%s did not import MToon outline passes." % summary["sample"])
if summary["shadowCasters"] == 0:
failures.push_back("%s did not expose any mesh shadow casters." % summary["sample"])

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://tests/material-rendering-check/materialRenderingCheck.gd" id="1_n1afi"]
[node name="MaterialRenderingCheck" type="Node"]
script = ExtResource("1_n1afi")

264
pnpm-lock.yaml generated
View file

@ -1495,7 +1495,7 @@ importers:
version: 3.0.2(electron@41.2.1)
'@electron-toolkit/tsconfig':
specifier: ^2.0.0
version: 2.0.0(@types/node@25.6.0)
version: 2.0.0(@types/node@24.12.2)
'@electron-toolkit/utils':
specifier: ^4.0.0
version: 4.0.0(electron@41.2.1)
@ -1534,7 +1534,7 @@ importers:
version: 3.1.0
'@intlify/unplugin-vue-i18n':
specifier: ^11.0.7
version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@modelcontextprotocol/sdk':
specifier: 'catalog:'
version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6)
@ -1570,10 +1570,10 @@ importers:
version: link:../../packages/ui-transitions
'@proj-airi/unplugin-fetch':
specifier: 'catalog:'
version: 0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@proj-airi/unplugin-live2d-sdk':
specifier: ^0.1.7
version: 0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
version: 0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@types/audioworklet':
specifier: 'catalog:'
version: 0.0.97
@ -1600,7 +1600,7 @@ importers:
version: 2.10.3
'@vitejs/plugin-vue':
specifier: ^6.0.6
version: 6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
version: 6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/volar':
specifier: ^3.1.2
version: 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
@ -1633,7 +1633,7 @@ importers:
version: 6.8.3
electron-vite:
specifier: ^5.0.0
version: 5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
get-port-please:
specifier: 'catalog:'
version: 3.2.0
@ -1654,31 +1654,31 @@ importers:
version: 2.2.6
unocss-preset-scrollbar:
specifier: ^4.0.0
version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
version: 4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
unplugin-info:
specifier: ^1.3.2
version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unplugin-yaml:
specifier: ^4.1.0
version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite:
specifier: 'catalog:'
version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
version: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-bundle-visualizer:
specifier: ^1.2.1
version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1)
vite-plugin-mkcert:
specifier: 'catalog:'
version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-plugin-vue-devtools:
specifier: ^8.1.1
version: 8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
version: 8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
vite-plugin-vue-layouts:
specifier: ^0.11.0
version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
version: 0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
vue-macros:
specifier: ^3.1.2
version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
version: 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
vue-tsc:
specifier: ^3.2.6
version: 3.2.6(typescript@5.9.3)
@ -19998,9 +19998,9 @@ snapshots:
dependencies:
electron: 41.2.1
'@electron-toolkit/tsconfig@2.0.0(@types/node@25.6.0)':
'@electron-toolkit/tsconfig@2.0.0(@types/node@24.12.2)':
dependencies:
'@types/node': 25.6.0
'@types/node': 24.12.2
'@electron-toolkit/utils@4.0.0(electron@41.2.1)':
dependencies:
@ -20912,6 +20912,31 @@ snapshots:
- supports-color
- typescript
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1))
'@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))
'@intlify/shared': 11.3.2
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.32)(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
'@typescript-eslint/scope-manager': 8.58.1
'@typescript-eslint/typescript-estree': 8.58.1(typescript@5.9.3)
debug: 4.4.3(supports-color@10.2.2)
fast-glob: 3.3.3
pathe: 2.0.3
picocolors: 1.1.1
unplugin: 2.3.11
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
optionalDependencies:
vue-i18n: 11.3.2(vue@3.5.32(typescript@5.9.3))
transitivePeerDependencies:
- '@vue/compiler-dom'
- eslint
- rollup
- supports-color
- typescript
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1))
@ -22910,11 +22935,35 @@ snapshots:
transitivePeerDependencies:
- magicast
'@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
yauzl: 3.3.0
transitivePeerDependencies:
- '@types/node'
- '@vitejs/devtools'
- esbuild
- jiti
- less
- sass
- sass-embedded
- stylus
- sugarss
- terser
- tsx
- yaml
'@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)':
dependencies:
ofetch: 1.5.1
@ -23815,6 +23864,7 @@ snapshots:
'@types/node@25.6.0':
dependencies:
undici-types: 7.19.2
optional: true
'@types/nprogress@0.2.3': {}
@ -24480,6 +24530,12 @@ snapshots:
vite: 6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
'@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.13
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
'@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.13
@ -24560,9 +24616,9 @@ snapshots:
obug: 2.1.1
std-env: 4.1.0
tinyrainbow: 3.1.0
vitest: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@25.6.0)(@vitest/browser-playwright@4.1.4)(@vitest/coverage-v8@4.1.4)(jsdom@29.1.1(@noble/hashes@2.0.1)(canvas@3.2.3))(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vitest: 4.1.4(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/browser-playwright@4.1.4)(@vitest/coverage-v8@4.1.4)(jsdom@29.1.1(@noble/hashes@2.0.1)(canvas@3.2.3))(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
optionalDependencies:
'@vitest/browser': 4.1.4(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.4)
'@vitest/browser': 4.1.4(bufferutil@4.1.0)(utf-8-validate@5.0.10)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vitest@4.1.4)
'@vitest/eslint-plugin@1.6.15(@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@10.2.1(jiti@2.6.1))(typescript@5.9.3))(eslint@10.2.1(jiti@2.6.1))(typescript@5.9.3))(eslint@10.2.1(jiti@2.6.1))(typescript@5.9.3)(vitest@4.1.4)':
dependencies:
@ -24782,6 +24838,15 @@ snapshots:
transitivePeerDependencies:
- vue
'@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
sirv: 3.0.2
vue: 3.5.32(typescript@5.9.3)
optionalDependencies:
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- typescript
'@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
sirv: 3.0.2
@ -26923,7 +26988,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron-vite@5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
electron-vite@5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0)
@ -26931,7 +26996,7 @@ snapshots:
esbuild: 0.25.12
magic-string: 0.30.21
picocolors: 1.1.1
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
@ -32847,7 +32912,8 @@ snapshots:
undici-types@7.16.0: {}
undici-types@7.19.2: {}
undici-types@7.19.2:
optional: true
undici@6.24.1: {}
@ -32959,11 +33025,6 @@ snapshots:
'@unocss/preset-mini': 66.6.8
unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unocss-preset-scrollbar@4.0.0(unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))):
dependencies:
'@unocss/preset-mini': 66.6.8
unocss: 66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unocss@66.6.8(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vite@6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@unocss/cli': 66.6.8
@ -33060,6 +33121,14 @@ snapshots:
unplugin: 2.3.11
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
optionalDependencies:
esbuild: 0.27.2
rolldown: 1.0.0-rc.16
rollup: 4.60.1
unplugin: 2.3.11
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
optionalDependencies:
esbuild: 0.27.2
@ -33094,6 +33163,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ci-info: 4.4.0
git-url-parse: 16.1.0
simple-git: 3.36.0
unplugin: 2.3.11
optionalDependencies:
esbuild: 0.27.2
rollup: 4.60.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ci-info: 4.4.0
@ -33203,6 +33285,17 @@ snapshots:
rollup: 4.60.1
vite: 6.4.2(@types/node@25.6.0)(jiti@2.6.1)(less@4.6.4)(lightningcss@1.32.0)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-yaml@4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
unplugin: 3.0.0
yaml: 2.8.3
optionalDependencies:
esbuild: 0.27.2
rolldown: 1.0.0-rc.16
rollup: 4.60.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin@2.3.11:
dependencies:
'@jridgewell/remapping': 2.3.5
@ -33434,12 +33527,22 @@ snapshots:
- rollup
- supports-color
vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
birpc: 2.9.0
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client: 2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
birpc: 2.9.0
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client: 2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-hot-client@2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client@2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
@ -33485,6 +33588,21 @@ snapshots:
- tsx
- yaml
vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ansis: 4.2.0
debug: 4.4.3(supports-color@10.2.2)
error-stack-parser-es: 1.0.5
ohash: 2.0.11
open: 10.2.0
perfect-debounce: 2.1.0
sirv: 3.0.2
unplugin-utils: 0.3.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-dev-rpc: 1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
transitivePeerDependencies:
- supports-color
vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ansis: 4.2.0
@ -33516,6 +33634,13 @@ snapshots:
- typescript
- ws
vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
supports-color: 10.2.2
undici: 8.1.0
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
@ -33534,6 +33659,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3))
'@vue/devtools-kit': 8.1.1
'@vue/devtools-shared': 8.1.1
sirv: 3.0.2
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-plugin-inspect: 11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-plugin-vue-inspector: 5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
transitivePeerDependencies:
- '@nuxt/kit'
- supports-color
- vue
vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3))
@ -33548,6 +33687,21 @@ snapshots:
- supports-color
- vue
vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.29.0)
'@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0)
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0)
'@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0)
'@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.0)
'@vue/compiler-dom': 3.5.32
kolorist: 1.8.0
magic-string: 0.30.21
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
@ -33563,6 +33717,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
fast-glob: 3.3.3
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
vue-router: 5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
transitivePeerDependencies:
- supports-color
vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
@ -33829,6 +33993,54 @@ snapshots:
- vue-tsc
- webpack
vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))
'@vue-macros/boolean-prop': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/chain-call': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/common': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/config': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-emit': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-models': 3.1.2(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-prop': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-props': 3.1.2(@vue-macros/reactivity-transform@3.1.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-props-refs': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-render': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-slots': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-stylex': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/devtools': 3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@vue-macros/export-expose': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/export-props': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/export-render': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/hoist-static': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/jsx-directive': 3.1.2(typescript@5.9.3)
'@vue-macros/named-template': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/reactivity-transform': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/script-lang': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-block': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-component': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-sfc': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-bind': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-emits': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-vmodel': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/volar': 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
unplugin: 2.3.11
unplugin-combine: 2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unplugin-vue-define-options: 3.1.2(vue@3.5.32(typescript@5.9.3))
vue: 3.5.32(typescript@5.9.3)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
- '@rspack/core'
- '@vueuse/core'
- esbuild
- rolldown
- rollup
- typescript
- vite
- vue-tsc
- webpack
vue-macros@3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue-macros/better-define': 3.1.2(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(vue@3.5.32(typescript@5.9.3))