osolmaz HF Staff commited on
Commit
5aed101
·
verified ·
1 Parent(s): 95eef6f

feat: replace server app with live TypeScript renderer

Browse files
Files changed (14) hide show
  1. .gitignore +3 -0
  2. README.md +7 -8
  3. app.py +0 -196
  4. index.html +91 -0
  5. package-lock.json +1641 -0
  6. package.json +16 -0
  7. requirements.txt +0 -1
  8. src/layout.test.ts +39 -0
  9. src/layout.ts +122 -0
  10. src/main.ts +202 -0
  11. src/render.ts +80 -0
  12. src/style.css +66 -0
  13. src/vite-env.d.ts +1 -0
  14. tsconfig.json +17 -0
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ node_modules/
2
+ dist/
3
+ *.log
README.md CHANGED
@@ -3,16 +3,15 @@ title: TileKit Wallpaper Studio
3
  emoji: 🤗
4
  colorFrom: yellow
5
  colorTo: blue
6
- sdk: gradio
7
- sdk_version: 6.20.0
8
- app_file: app.py
9
- python_version: "3.12"
10
- short_description: Interactive Hugging Face logo wallpaper generator
11
  ---
12
 
13
  # TileKit Wallpaper Studio
14
 
15
- An interactive Gradio version of [TileKit](https://github.com/osolmaz/tools/tree/main/tilekit).
16
- Adjust the canvas, density, logo size, spacing, rotation, and seed, then download the generated PNG.
17
 
18
- The Space uses TileKit from the exact Git commit that introduced the merged tool.
 
3
  emoji: 🤗
4
  colorFrom: yellow
5
  colorTo: blue
6
+ sdk: static
7
+ app_build_command: npm run build
8
+ app_file: dist/index.html
9
+ short_description: Live Hugging Face wallpaper designer
 
10
  ---
11
 
12
  # TileKit Wallpaper Studio
13
 
14
+ Browser-native TypeScript version of [TileKit](https://github.com/osolmaz/tools/tree/main/tilekit).
15
+ Every control updates the canvas immediately. Full-resolution PNGs are rendered locally in the browser.
16
 
17
+ The layout engine uses deterministic typed arrays and spatial hashing. No images or settings leave the browser.
app.py DELETED
@@ -1,196 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import replace
4
- from pathlib import Path
5
-
6
- import gradio as gr
7
- from tilekit import PRESETS, CanvasSize, load_tile, render_tiles
8
-
9
-
10
- APP_DIR = Path(__file__).parent
11
- LOGO = load_tile(APP_DIR / "assets" / "hugging-face.png")
12
-
13
- CSS = """
14
- #app-shell { max-width: 1320px; margin: 0 auto; }
15
- #wallpaper-output img { object-fit: contain; }
16
- .dark .gradio-container { color: var(--body-text-color); }
17
- """
18
-
19
-
20
- def generate_wallpaper(
21
- preset_name: str = "denser-even",
22
- width: int = 1920,
23
- height: int = 1080,
24
- density: int = 74,
25
- logo_scale: float = 1.0,
26
- spacing_scale: float = 1.0,
27
- rotation: int = 52,
28
- seed: int = 84237,
29
- ) -> tuple[object, str]:
30
- """Create a Hugging Face logo wallpaper with a deterministic TileKit layout.
31
-
32
- Args:
33
- preset_name: Starting layout style.
34
- width: Output width in pixels.
35
- height: Output height in pixels.
36
- density: Number of primary logo tiles to prepare.
37
- logo_scale: Multiplier applied to every logo size band.
38
- spacing_scale: Multiplier applied to collision padding and clearances.
39
- rotation: Maximum logo rotation in degrees.
40
- seed: Deterministic random seed.
41
- """
42
- width = int(width)
43
- height = int(height)
44
- density = int(density)
45
- rotation = int(rotation)
46
- seed = int(seed)
47
-
48
- base = PRESETS[preset_name]
49
- preset = replace(
50
- base,
51
- seed=seed,
52
- count=density,
53
- min_clearance=base.min_clearance * spacing_scale,
54
- soft_clearance=base.soft_clearance * spacing_scale,
55
- radius_padding=base.radius_padding * spacing_scale,
56
- ideal_clearance=(
57
- base.ideal_clearance * spacing_scale
58
- if base.ideal_clearance is not None
59
- else None
60
- ),
61
- width_bands=tuple(
62
- (threshold, low * logo_scale, high * logo_scale)
63
- for threshold, low, high in base.width_bands
64
- ),
65
- angle_limit=float(rotation),
66
- )
67
- result = render_tiles(LOGO, preset, CanvasSize(width=width, height=height))
68
-
69
- status = (
70
- f"**{result.placed_count} of {result.prepared_count} logos placed** · "
71
- f"seed `{seed}` · preset `{preset_name}`"
72
- )
73
- if result.failures:
74
- status += "\n\nLayout notes: " + ", ".join(result.failures) + "."
75
- else:
76
- status += "\n\nAll TileKit spacing and balance checks passed."
77
- return result.image.convert("RGB"), status
78
-
79
-
80
- with gr.Blocks() as demo:
81
- with gr.Column(elem_id="app-shell"):
82
- gr.Markdown(
83
- "# 🤗 TileKit Wallpaper Studio\n"
84
- "Create a balanced Hugging Face wallpaper, then download the full-resolution PNG. "
85
- "Layouts are deterministic: reuse a seed to recreate the same result."
86
- )
87
-
88
- with gr.Row(equal_height=False):
89
- with gr.Column(scale=1, min_width=320):
90
- preset = gr.Dropdown(
91
- choices=list(PRESETS),
92
- value="denser-even",
93
- label="Layout style",
94
- )
95
- width = gr.Slider(
96
- 640,
97
- 3840,
98
- value=1920,
99
- step=160,
100
- label="Width",
101
- info="Pixels",
102
- )
103
- height = gr.Slider(
104
- 360,
105
- 2160,
106
- value=1080,
107
- step=90,
108
- label="Height",
109
- info="Pixels",
110
- )
111
- density = gr.Slider(
112
- 24,
113
- 120,
114
- value=74,
115
- step=1,
116
- label="Logo density",
117
- info="Primary logos before optional void filling",
118
- )
119
- logo_scale = gr.Slider(
120
- 0.55,
121
- 1.6,
122
- value=1.0,
123
- step=0.05,
124
- label="Logo size",
125
- )
126
- spacing_scale = gr.Slider(
127
- 0.5,
128
- 2.0,
129
- value=1.0,
130
- step=0.05,
131
- label="Spacing",
132
- )
133
- rotation = gr.Slider(
134
- 0,
135
- 70,
136
- value=52,
137
- step=1,
138
- label="Maximum rotation",
139
- info="Degrees",
140
- )
141
- seed = gr.Slider(
142
- 0,
143
- 999999,
144
- value=84237,
145
- step=1,
146
- label="Seed",
147
- randomize=True,
148
- )
149
- generate = gr.Button("Generate wallpaper", variant="huggingface")
150
-
151
- with gr.Column(scale=3, min_width=560):
152
- output = gr.Image(
153
- label="Wallpaper",
154
- format="png",
155
- buttons=["download", "fullscreen"],
156
- elem_id="wallpaper-output",
157
- )
158
- status = gr.Markdown()
159
-
160
- gr.Examples(
161
- examples=[
162
- ["denser-even", 1920, 1080],
163
- ["closer", 2560, 1440],
164
- ["size-aware-dense", 3840, 2160],
165
- ],
166
- inputs=[preset, width, height],
167
- outputs=[output, status],
168
- fn=generate_wallpaper,
169
- cache_examples=True,
170
- cache_mode="lazy",
171
- )
172
-
173
- gr.Markdown(
174
- "Built with [TileKit](https://github.com/osolmaz/tools/tree/main/tilekit)."
175
- )
176
-
177
- controls = [
178
- preset,
179
- width,
180
- height,
181
- density,
182
- logo_scale,
183
- spacing_scale,
184
- rotation,
185
- seed,
186
- ]
187
- generate.click(
188
- generate_wallpaper,
189
- inputs=controls,
190
- outputs=[output, status],
191
- api_name="generate_wallpaper",
192
- )
193
-
194
-
195
- if __name__ == "__main__":
196
- demo.launch(mcp_server=True, theme=gr.themes.Citrus(), css=CSS)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
index.html ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="theme-color" content="#0d82df" />
7
+ <meta
8
+ name="description"
9
+ content="Design and export balanced Hugging Face logo wallpapers in real time."
10
+ />
11
+ <title>TileKit Wallpaper Studio</title>
12
+ </head>
13
+ <body>
14
+ <main class="app-shell">
15
+ <header class="hero">
16
+ <div>
17
+ <p class="eyebrow">Browser-native TileKit</p>
18
+ <h1><span aria-hidden="true">🤗</span> Wallpaper Studio</h1>
19
+ <p>Every change renders live. Export a full-resolution PNG when it feels right.</p>
20
+ </div>
21
+ <a href="https://github.com/osolmaz/tools/tree/main/tilekit" target="_blank" rel="noreferrer">
22
+ Source
23
+ </a>
24
+ </header>
25
+
26
+ <section class="workspace">
27
+ <aside class="controls" aria-label="Wallpaper controls">
28
+ <div class="control-section">
29
+ <div class="section-heading">
30
+ <h2>Canvas</h2>
31
+ <span id="pixel-count">2.1 MP</span>
32
+ </div>
33
+ <label>
34
+ Resolution
35
+ <select id="resolution">
36
+ <option value="1920x1080">Full HD · 1920×1080</option>
37
+ <option value="2560x1440">QHD · 2560×1440</option>
38
+ <option value="3840x2160" selected>4K · 3840×2160</option>
39
+ <option value="5120x2880">5K · 5120×2880</option>
40
+ <option value="7680x4320">8K · 7680×4320</option>
41
+ <option value="custom">Custom</option>
42
+ </select>
43
+ </label>
44
+ <div class="split-inputs">
45
+ <label>Width <input id="width" type="number" min="640" max="7680" step="8" value="3840" /></label>
46
+ <label>Height <input id="height" type="number" min="360" max="4320" step="8" value="2160" /></label>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="control-section">
51
+ <div class="section-heading"><h2>Layout</h2><button id="reset" class="text-button">Reset</button></div>
52
+ <label class="range-label">
53
+ <span>Density <output id="density-value">88</output></span>
54
+ <input id="density" type="range" min="24" max="180" step="1" value="88" />
55
+ </label>
56
+ <label class="range-label">
57
+ <span>Logo size <output id="size-value">1.00×</output></span>
58
+ <input id="logo-size" type="range" min="0.5" max="1.6" step="0.01" value="1" />
59
+ </label>
60
+ <label class="range-label">
61
+ <span>Spacing <output id="spacing-value">1.00×</output></span>
62
+ <input id="spacing" type="range" min="0.25" max="2" step="0.01" value="1" />
63
+ </label>
64
+ <label class="range-label">
65
+ <span>Rotation <output id="rotation-value">52°</output></span>
66
+ <input id="rotation" type="range" min="0" max="70" step="1" value="52" />
67
+ </label>
68
+ <div class="seed-row">
69
+ <label>Seed <input id="seed" type="number" min="0" max="4294967295" step="1" value="84237" /></label>
70
+ <button id="randomize" class="icon-button" title="Randomize seed" aria-label="Randomize seed">↻</button>
71
+ </div>
72
+ </div>
73
+
74
+ <button id="export" class="export-button">Render 4K PNG</button>
75
+ <p id="export-status" class="export-status" role="status">Rendered locally in your browser.</p>
76
+ </aside>
77
+
78
+ <section class="preview-panel" aria-label="Live wallpaper preview">
79
+ <div class="preview-toolbar">
80
+ <span><i class="live-dot"></i> Live preview</span>
81
+ <span id="render-stats">Ready</span>
82
+ </div>
83
+ <div id="canvas-wrap" class="canvas-wrap">
84
+ <canvas id="preview" aria-label="Wallpaper preview"></canvas>
85
+ </div>
86
+ </section>
87
+ </section>
88
+ </main>
89
+ <script type="module" src="/src/main.ts"></script>
90
+ </body>
91
+ </html>
package-lock.json ADDED
@@ -0,0 +1,1641 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "tilekit-wallpaper-studio",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "tilekit-wallpaper-studio",
9
+ "version": "1.0.0",
10
+ "devDependencies": {
11
+ "typescript": "7.0.2",
12
+ "vite": "8.1.4",
13
+ "vitest": "4.1.10"
14
+ }
15
+ },
16
+ "node_modules/@emnapi/core": {
17
+ "version": "1.11.1",
18
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz",
19
+ "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==",
20
+ "dev": true,
21
+ "license": "MIT",
22
+ "optional": true,
23
+ "dependencies": {
24
+ "@emnapi/wasi-threads": "1.2.2",
25
+ "tslib": "^2.4.0"
26
+ }
27
+ },
28
+ "node_modules/@emnapi/runtime": {
29
+ "version": "1.11.1",
30
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz",
31
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
32
+ "dev": true,
33
+ "license": "MIT",
34
+ "optional": true,
35
+ "dependencies": {
36
+ "tslib": "^2.4.0"
37
+ }
38
+ },
39
+ "node_modules/@emnapi/wasi-threads": {
40
+ "version": "1.2.2",
41
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
42
+ "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
43
+ "dev": true,
44
+ "license": "MIT",
45
+ "optional": true,
46
+ "dependencies": {
47
+ "tslib": "^2.4.0"
48
+ }
49
+ },
50
+ "node_modules/@jridgewell/sourcemap-codec": {
51
+ "version": "1.5.5",
52
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
53
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
54
+ "dev": true,
55
+ "license": "MIT"
56
+ },
57
+ "node_modules/@napi-rs/wasm-runtime": {
58
+ "version": "1.1.6",
59
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
60
+ "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
61
+ "dev": true,
62
+ "license": "MIT",
63
+ "optional": true,
64
+ "dependencies": {
65
+ "@tybys/wasm-util": "^0.10.3"
66
+ },
67
+ "funding": {
68
+ "type": "github",
69
+ "url": "https://github.com/sponsors/Brooooooklyn"
70
+ },
71
+ "peerDependencies": {
72
+ "@emnapi/core": "^1.7.1",
73
+ "@emnapi/runtime": "^1.7.1"
74
+ }
75
+ },
76
+ "node_modules/@oxc-project/types": {
77
+ "version": "0.139.0",
78
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz",
79
+ "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==",
80
+ "dev": true,
81
+ "license": "MIT",
82
+ "funding": {
83
+ "url": "https://github.com/sponsors/Boshen"
84
+ }
85
+ },
86
+ "node_modules/@rolldown/binding-android-arm64": {
87
+ "version": "1.1.5",
88
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz",
89
+ "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==",
90
+ "cpu": [
91
+ "arm64"
92
+ ],
93
+ "dev": true,
94
+ "license": "MIT",
95
+ "optional": true,
96
+ "os": [
97
+ "android"
98
+ ],
99
+ "engines": {
100
+ "node": "^20.19.0 || >=22.12.0"
101
+ }
102
+ },
103
+ "node_modules/@rolldown/binding-darwin-arm64": {
104
+ "version": "1.1.5",
105
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz",
106
+ "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==",
107
+ "cpu": [
108
+ "arm64"
109
+ ],
110
+ "dev": true,
111
+ "license": "MIT",
112
+ "optional": true,
113
+ "os": [
114
+ "darwin"
115
+ ],
116
+ "engines": {
117
+ "node": "^20.19.0 || >=22.12.0"
118
+ }
119
+ },
120
+ "node_modules/@rolldown/binding-darwin-x64": {
121
+ "version": "1.1.5",
122
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz",
123
+ "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==",
124
+ "cpu": [
125
+ "x64"
126
+ ],
127
+ "dev": true,
128
+ "license": "MIT",
129
+ "optional": true,
130
+ "os": [
131
+ "darwin"
132
+ ],
133
+ "engines": {
134
+ "node": "^20.19.0 || >=22.12.0"
135
+ }
136
+ },
137
+ "node_modules/@rolldown/binding-freebsd-x64": {
138
+ "version": "1.1.5",
139
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz",
140
+ "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==",
141
+ "cpu": [
142
+ "x64"
143
+ ],
144
+ "dev": true,
145
+ "license": "MIT",
146
+ "optional": true,
147
+ "os": [
148
+ "freebsd"
149
+ ],
150
+ "engines": {
151
+ "node": "^20.19.0 || >=22.12.0"
152
+ }
153
+ },
154
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
155
+ "version": "1.1.5",
156
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz",
157
+ "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==",
158
+ "cpu": [
159
+ "arm"
160
+ ],
161
+ "dev": true,
162
+ "license": "MIT",
163
+ "optional": true,
164
+ "os": [
165
+ "linux"
166
+ ],
167
+ "engines": {
168
+ "node": "^20.19.0 || >=22.12.0"
169
+ }
170
+ },
171
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
172
+ "version": "1.1.5",
173
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz",
174
+ "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==",
175
+ "cpu": [
176
+ "arm64"
177
+ ],
178
+ "dev": true,
179
+ "license": "MIT",
180
+ "optional": true,
181
+ "os": [
182
+ "linux"
183
+ ],
184
+ "engines": {
185
+ "node": "^20.19.0 || >=22.12.0"
186
+ }
187
+ },
188
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
189
+ "version": "1.1.5",
190
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz",
191
+ "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==",
192
+ "cpu": [
193
+ "arm64"
194
+ ],
195
+ "dev": true,
196
+ "license": "MIT",
197
+ "optional": true,
198
+ "os": [
199
+ "linux"
200
+ ],
201
+ "engines": {
202
+ "node": "^20.19.0 || >=22.12.0"
203
+ }
204
+ },
205
+ "node_modules/@rolldown/binding-linux-ppc64-gnu": {
206
+ "version": "1.1.5",
207
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz",
208
+ "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==",
209
+ "cpu": [
210
+ "ppc64"
211
+ ],
212
+ "dev": true,
213
+ "license": "MIT",
214
+ "optional": true,
215
+ "os": [
216
+ "linux"
217
+ ],
218
+ "engines": {
219
+ "node": "^20.19.0 || >=22.12.0"
220
+ }
221
+ },
222
+ "node_modules/@rolldown/binding-linux-s390x-gnu": {
223
+ "version": "1.1.5",
224
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz",
225
+ "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==",
226
+ "cpu": [
227
+ "s390x"
228
+ ],
229
+ "dev": true,
230
+ "license": "MIT",
231
+ "optional": true,
232
+ "os": [
233
+ "linux"
234
+ ],
235
+ "engines": {
236
+ "node": "^20.19.0 || >=22.12.0"
237
+ }
238
+ },
239
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
240
+ "version": "1.1.5",
241
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz",
242
+ "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==",
243
+ "cpu": [
244
+ "x64"
245
+ ],
246
+ "dev": true,
247
+ "license": "MIT",
248
+ "optional": true,
249
+ "os": [
250
+ "linux"
251
+ ],
252
+ "engines": {
253
+ "node": "^20.19.0 || >=22.12.0"
254
+ }
255
+ },
256
+ "node_modules/@rolldown/binding-linux-x64-musl": {
257
+ "version": "1.1.5",
258
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz",
259
+ "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==",
260
+ "cpu": [
261
+ "x64"
262
+ ],
263
+ "dev": true,
264
+ "license": "MIT",
265
+ "optional": true,
266
+ "os": [
267
+ "linux"
268
+ ],
269
+ "engines": {
270
+ "node": "^20.19.0 || >=22.12.0"
271
+ }
272
+ },
273
+ "node_modules/@rolldown/binding-openharmony-arm64": {
274
+ "version": "1.1.5",
275
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz",
276
+ "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==",
277
+ "cpu": [
278
+ "arm64"
279
+ ],
280
+ "dev": true,
281
+ "license": "MIT",
282
+ "optional": true,
283
+ "os": [
284
+ "openharmony"
285
+ ],
286
+ "engines": {
287
+ "node": "^20.19.0 || >=22.12.0"
288
+ }
289
+ },
290
+ "node_modules/@rolldown/binding-wasm32-wasi": {
291
+ "version": "1.1.5",
292
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz",
293
+ "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==",
294
+ "cpu": [
295
+ "wasm32"
296
+ ],
297
+ "dev": true,
298
+ "license": "MIT",
299
+ "optional": true,
300
+ "dependencies": {
301
+ "@emnapi/core": "1.11.1",
302
+ "@emnapi/runtime": "1.11.1",
303
+ "@napi-rs/wasm-runtime": "^1.1.6"
304
+ },
305
+ "engines": {
306
+ "node": "^20.19.0 || >=22.12.0"
307
+ }
308
+ },
309
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
310
+ "version": "1.1.5",
311
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz",
312
+ "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==",
313
+ "cpu": [
314
+ "arm64"
315
+ ],
316
+ "dev": true,
317
+ "license": "MIT",
318
+ "optional": true,
319
+ "os": [
320
+ "win32"
321
+ ],
322
+ "engines": {
323
+ "node": "^20.19.0 || >=22.12.0"
324
+ }
325
+ },
326
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
327
+ "version": "1.1.5",
328
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz",
329
+ "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==",
330
+ "cpu": [
331
+ "x64"
332
+ ],
333
+ "dev": true,
334
+ "license": "MIT",
335
+ "optional": true,
336
+ "os": [
337
+ "win32"
338
+ ],
339
+ "engines": {
340
+ "node": "^20.19.0 || >=22.12.0"
341
+ }
342
+ },
343
+ "node_modules/@rolldown/pluginutils": {
344
+ "version": "1.0.1",
345
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
346
+ "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
347
+ "dev": true,
348
+ "license": "MIT"
349
+ },
350
+ "node_modules/@standard-schema/spec": {
351
+ "version": "1.1.0",
352
+ "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
353
+ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
354
+ "dev": true,
355
+ "license": "MIT"
356
+ },
357
+ "node_modules/@tybys/wasm-util": {
358
+ "version": "0.10.3",
359
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
360
+ "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==",
361
+ "dev": true,
362
+ "license": "MIT",
363
+ "optional": true,
364
+ "dependencies": {
365
+ "tslib": "^2.4.0"
366
+ }
367
+ },
368
+ "node_modules/@types/chai": {
369
+ "version": "5.2.3",
370
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
371
+ "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
372
+ "dev": true,
373
+ "license": "MIT",
374
+ "dependencies": {
375
+ "@types/deep-eql": "*",
376
+ "assertion-error": "^2.0.1"
377
+ }
378
+ },
379
+ "node_modules/@types/deep-eql": {
380
+ "version": "4.0.2",
381
+ "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
382
+ "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
383
+ "dev": true,
384
+ "license": "MIT"
385
+ },
386
+ "node_modules/@types/estree": {
387
+ "version": "1.0.9",
388
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
389
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
390
+ "dev": true,
391
+ "license": "MIT"
392
+ },
393
+ "node_modules/@typescript/typescript-aix-ppc64": {
394
+ "version": "7.0.2",
395
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
396
+ "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
397
+ "cpu": [
398
+ "ppc64"
399
+ ],
400
+ "dev": true,
401
+ "license": "Apache-2.0",
402
+ "optional": true,
403
+ "os": [
404
+ "aix"
405
+ ],
406
+ "engines": {
407
+ "node": ">=16.20.0"
408
+ }
409
+ },
410
+ "node_modules/@typescript/typescript-darwin-arm64": {
411
+ "version": "7.0.2",
412
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
413
+ "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
414
+ "cpu": [
415
+ "arm64"
416
+ ],
417
+ "dev": true,
418
+ "license": "Apache-2.0",
419
+ "optional": true,
420
+ "os": [
421
+ "darwin"
422
+ ],
423
+ "engines": {
424
+ "node": ">=16.20.0"
425
+ }
426
+ },
427
+ "node_modules/@typescript/typescript-darwin-x64": {
428
+ "version": "7.0.2",
429
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
430
+ "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
431
+ "cpu": [
432
+ "x64"
433
+ ],
434
+ "dev": true,
435
+ "license": "Apache-2.0",
436
+ "optional": true,
437
+ "os": [
438
+ "darwin"
439
+ ],
440
+ "engines": {
441
+ "node": ">=16.20.0"
442
+ }
443
+ },
444
+ "node_modules/@typescript/typescript-freebsd-arm64": {
445
+ "version": "7.0.2",
446
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
447
+ "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
448
+ "cpu": [
449
+ "arm64"
450
+ ],
451
+ "dev": true,
452
+ "license": "Apache-2.0",
453
+ "optional": true,
454
+ "os": [
455
+ "freebsd"
456
+ ],
457
+ "engines": {
458
+ "node": ">=16.20.0"
459
+ }
460
+ },
461
+ "node_modules/@typescript/typescript-freebsd-x64": {
462
+ "version": "7.0.2",
463
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
464
+ "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
465
+ "cpu": [
466
+ "x64"
467
+ ],
468
+ "dev": true,
469
+ "license": "Apache-2.0",
470
+ "optional": true,
471
+ "os": [
472
+ "freebsd"
473
+ ],
474
+ "engines": {
475
+ "node": ">=16.20.0"
476
+ }
477
+ },
478
+ "node_modules/@typescript/typescript-linux-arm": {
479
+ "version": "7.0.2",
480
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
481
+ "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
482
+ "cpu": [
483
+ "arm"
484
+ ],
485
+ "dev": true,
486
+ "license": "Apache-2.0",
487
+ "optional": true,
488
+ "os": [
489
+ "linux"
490
+ ],
491
+ "engines": {
492
+ "node": ">=16.20.0"
493
+ }
494
+ },
495
+ "node_modules/@typescript/typescript-linux-arm64": {
496
+ "version": "7.0.2",
497
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
498
+ "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
499
+ "cpu": [
500
+ "arm64"
501
+ ],
502
+ "dev": true,
503
+ "license": "Apache-2.0",
504
+ "optional": true,
505
+ "os": [
506
+ "linux"
507
+ ],
508
+ "engines": {
509
+ "node": ">=16.20.0"
510
+ }
511
+ },
512
+ "node_modules/@typescript/typescript-linux-loong64": {
513
+ "version": "7.0.2",
514
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
515
+ "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
516
+ "cpu": [
517
+ "loong64"
518
+ ],
519
+ "dev": true,
520
+ "license": "Apache-2.0",
521
+ "optional": true,
522
+ "os": [
523
+ "linux"
524
+ ],
525
+ "engines": {
526
+ "node": ">=16.20.0"
527
+ }
528
+ },
529
+ "node_modules/@typescript/typescript-linux-mips64el": {
530
+ "version": "7.0.2",
531
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
532
+ "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
533
+ "cpu": [
534
+ "mips64el"
535
+ ],
536
+ "dev": true,
537
+ "license": "Apache-2.0",
538
+ "optional": true,
539
+ "os": [
540
+ "linux"
541
+ ],
542
+ "engines": {
543
+ "node": ">=16.20.0"
544
+ }
545
+ },
546
+ "node_modules/@typescript/typescript-linux-ppc64": {
547
+ "version": "7.0.2",
548
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
549
+ "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
550
+ "cpu": [
551
+ "ppc64"
552
+ ],
553
+ "dev": true,
554
+ "license": "Apache-2.0",
555
+ "optional": true,
556
+ "os": [
557
+ "linux"
558
+ ],
559
+ "engines": {
560
+ "node": ">=16.20.0"
561
+ }
562
+ },
563
+ "node_modules/@typescript/typescript-linux-riscv64": {
564
+ "version": "7.0.2",
565
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
566
+ "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
567
+ "cpu": [
568
+ "riscv64"
569
+ ],
570
+ "dev": true,
571
+ "license": "Apache-2.0",
572
+ "optional": true,
573
+ "os": [
574
+ "linux"
575
+ ],
576
+ "engines": {
577
+ "node": ">=16.20.0"
578
+ }
579
+ },
580
+ "node_modules/@typescript/typescript-linux-s390x": {
581
+ "version": "7.0.2",
582
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
583
+ "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
584
+ "cpu": [
585
+ "s390x"
586
+ ],
587
+ "dev": true,
588
+ "license": "Apache-2.0",
589
+ "optional": true,
590
+ "os": [
591
+ "linux"
592
+ ],
593
+ "engines": {
594
+ "node": ">=16.20.0"
595
+ }
596
+ },
597
+ "node_modules/@typescript/typescript-linux-x64": {
598
+ "version": "7.0.2",
599
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
600
+ "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
601
+ "cpu": [
602
+ "x64"
603
+ ],
604
+ "dev": true,
605
+ "license": "Apache-2.0",
606
+ "optional": true,
607
+ "os": [
608
+ "linux"
609
+ ],
610
+ "engines": {
611
+ "node": ">=16.20.0"
612
+ }
613
+ },
614
+ "node_modules/@typescript/typescript-netbsd-arm64": {
615
+ "version": "7.0.2",
616
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
617
+ "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
618
+ "cpu": [
619
+ "arm64"
620
+ ],
621
+ "dev": true,
622
+ "license": "Apache-2.0",
623
+ "optional": true,
624
+ "os": [
625
+ "netbsd"
626
+ ],
627
+ "engines": {
628
+ "node": ">=16.20.0"
629
+ }
630
+ },
631
+ "node_modules/@typescript/typescript-netbsd-x64": {
632
+ "version": "7.0.2",
633
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
634
+ "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
635
+ "cpu": [
636
+ "x64"
637
+ ],
638
+ "dev": true,
639
+ "license": "Apache-2.0",
640
+ "optional": true,
641
+ "os": [
642
+ "netbsd"
643
+ ],
644
+ "engines": {
645
+ "node": ">=16.20.0"
646
+ }
647
+ },
648
+ "node_modules/@typescript/typescript-openbsd-arm64": {
649
+ "version": "7.0.2",
650
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
651
+ "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
652
+ "cpu": [
653
+ "arm64"
654
+ ],
655
+ "dev": true,
656
+ "license": "Apache-2.0",
657
+ "optional": true,
658
+ "os": [
659
+ "openbsd"
660
+ ],
661
+ "engines": {
662
+ "node": ">=16.20.0"
663
+ }
664
+ },
665
+ "node_modules/@typescript/typescript-openbsd-x64": {
666
+ "version": "7.0.2",
667
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
668
+ "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
669
+ "cpu": [
670
+ "x64"
671
+ ],
672
+ "dev": true,
673
+ "license": "Apache-2.0",
674
+ "optional": true,
675
+ "os": [
676
+ "openbsd"
677
+ ],
678
+ "engines": {
679
+ "node": ">=16.20.0"
680
+ }
681
+ },
682
+ "node_modules/@typescript/typescript-sunos-x64": {
683
+ "version": "7.0.2",
684
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
685
+ "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
686
+ "cpu": [
687
+ "x64"
688
+ ],
689
+ "dev": true,
690
+ "license": "Apache-2.0",
691
+ "optional": true,
692
+ "os": [
693
+ "sunos"
694
+ ],
695
+ "engines": {
696
+ "node": ">=16.20.0"
697
+ }
698
+ },
699
+ "node_modules/@typescript/typescript-win32-arm64": {
700
+ "version": "7.0.2",
701
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
702
+ "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
703
+ "cpu": [
704
+ "arm64"
705
+ ],
706
+ "dev": true,
707
+ "license": "Apache-2.0",
708
+ "optional": true,
709
+ "os": [
710
+ "win32"
711
+ ],
712
+ "engines": {
713
+ "node": ">=16.20.0"
714
+ }
715
+ },
716
+ "node_modules/@typescript/typescript-win32-x64": {
717
+ "version": "7.0.2",
718
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
719
+ "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
720
+ "cpu": [
721
+ "x64"
722
+ ],
723
+ "dev": true,
724
+ "license": "Apache-2.0",
725
+ "optional": true,
726
+ "os": [
727
+ "win32"
728
+ ],
729
+ "engines": {
730
+ "node": ">=16.20.0"
731
+ }
732
+ },
733
+ "node_modules/@vitest/expect": {
734
+ "version": "4.1.10",
735
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz",
736
+ "integrity": "sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==",
737
+ "dev": true,
738
+ "license": "MIT",
739
+ "dependencies": {
740
+ "@standard-schema/spec": "^1.1.0",
741
+ "@types/chai": "^5.2.2",
742
+ "@vitest/spy": "4.1.10",
743
+ "@vitest/utils": "4.1.10",
744
+ "chai": "^6.2.2",
745
+ "tinyrainbow": "^3.1.0"
746
+ },
747
+ "funding": {
748
+ "url": "https://opencollective.com/vitest"
749
+ }
750
+ },
751
+ "node_modules/@vitest/mocker": {
752
+ "version": "4.1.10",
753
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.10.tgz",
754
+ "integrity": "sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==",
755
+ "dev": true,
756
+ "license": "MIT",
757
+ "dependencies": {
758
+ "@vitest/spy": "4.1.10",
759
+ "estree-walker": "^3.0.3",
760
+ "magic-string": "^0.30.21"
761
+ },
762
+ "funding": {
763
+ "url": "https://opencollective.com/vitest"
764
+ },
765
+ "peerDependencies": {
766
+ "msw": "^2.4.9",
767
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
768
+ },
769
+ "peerDependenciesMeta": {
770
+ "msw": {
771
+ "optional": true
772
+ },
773
+ "vite": {
774
+ "optional": true
775
+ }
776
+ }
777
+ },
778
+ "node_modules/@vitest/pretty-format": {
779
+ "version": "4.1.10",
780
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.10.tgz",
781
+ "integrity": "sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==",
782
+ "dev": true,
783
+ "license": "MIT",
784
+ "dependencies": {
785
+ "tinyrainbow": "^3.1.0"
786
+ },
787
+ "funding": {
788
+ "url": "https://opencollective.com/vitest"
789
+ }
790
+ },
791
+ "node_modules/@vitest/runner": {
792
+ "version": "4.1.10",
793
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.10.tgz",
794
+ "integrity": "sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==",
795
+ "dev": true,
796
+ "license": "MIT",
797
+ "dependencies": {
798
+ "@vitest/utils": "4.1.10",
799
+ "pathe": "^2.0.3"
800
+ },
801
+ "funding": {
802
+ "url": "https://opencollective.com/vitest"
803
+ }
804
+ },
805
+ "node_modules/@vitest/snapshot": {
806
+ "version": "4.1.10",
807
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.10.tgz",
808
+ "integrity": "sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==",
809
+ "dev": true,
810
+ "license": "MIT",
811
+ "dependencies": {
812
+ "@vitest/pretty-format": "4.1.10",
813
+ "@vitest/utils": "4.1.10",
814
+ "magic-string": "^0.30.21",
815
+ "pathe": "^2.0.3"
816
+ },
817
+ "funding": {
818
+ "url": "https://opencollective.com/vitest"
819
+ }
820
+ },
821
+ "node_modules/@vitest/spy": {
822
+ "version": "4.1.10",
823
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.10.tgz",
824
+ "integrity": "sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==",
825
+ "dev": true,
826
+ "license": "MIT",
827
+ "funding": {
828
+ "url": "https://opencollective.com/vitest"
829
+ }
830
+ },
831
+ "node_modules/@vitest/utils": {
832
+ "version": "4.1.10",
833
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.10.tgz",
834
+ "integrity": "sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==",
835
+ "dev": true,
836
+ "license": "MIT",
837
+ "dependencies": {
838
+ "@vitest/pretty-format": "4.1.10",
839
+ "convert-source-map": "^2.0.0",
840
+ "tinyrainbow": "^3.1.0"
841
+ },
842
+ "funding": {
843
+ "url": "https://opencollective.com/vitest"
844
+ }
845
+ },
846
+ "node_modules/assertion-error": {
847
+ "version": "2.0.1",
848
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
849
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
850
+ "dev": true,
851
+ "license": "MIT",
852
+ "engines": {
853
+ "node": ">=12"
854
+ }
855
+ },
856
+ "node_modules/chai": {
857
+ "version": "6.2.2",
858
+ "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
859
+ "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
860
+ "dev": true,
861
+ "license": "MIT",
862
+ "engines": {
863
+ "node": ">=18"
864
+ }
865
+ },
866
+ "node_modules/convert-source-map": {
867
+ "version": "2.0.0",
868
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
869
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
870
+ "dev": true,
871
+ "license": "MIT"
872
+ },
873
+ "node_modules/detect-libc": {
874
+ "version": "2.1.2",
875
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
876
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
877
+ "dev": true,
878
+ "license": "Apache-2.0",
879
+ "engines": {
880
+ "node": ">=8"
881
+ }
882
+ },
883
+ "node_modules/es-module-lexer": {
884
+ "version": "2.3.1",
885
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz",
886
+ "integrity": "sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==",
887
+ "dev": true,
888
+ "license": "MIT"
889
+ },
890
+ "node_modules/estree-walker": {
891
+ "version": "3.0.3",
892
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
893
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
894
+ "dev": true,
895
+ "license": "MIT",
896
+ "dependencies": {
897
+ "@types/estree": "^1.0.0"
898
+ }
899
+ },
900
+ "node_modules/expect-type": {
901
+ "version": "1.4.0",
902
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz",
903
+ "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==",
904
+ "dev": true,
905
+ "license": "Apache-2.0",
906
+ "engines": {
907
+ "node": ">=12.0.0"
908
+ }
909
+ },
910
+ "node_modules/fdir": {
911
+ "version": "6.5.0",
912
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
913
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
914
+ "dev": true,
915
+ "license": "MIT",
916
+ "engines": {
917
+ "node": ">=12.0.0"
918
+ },
919
+ "peerDependencies": {
920
+ "picomatch": "^3 || ^4"
921
+ },
922
+ "peerDependenciesMeta": {
923
+ "picomatch": {
924
+ "optional": true
925
+ }
926
+ }
927
+ },
928
+ "node_modules/fsevents": {
929
+ "version": "2.3.3",
930
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
931
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
932
+ "dev": true,
933
+ "hasInstallScript": true,
934
+ "license": "MIT",
935
+ "optional": true,
936
+ "os": [
937
+ "darwin"
938
+ ],
939
+ "engines": {
940
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
941
+ }
942
+ },
943
+ "node_modules/lightningcss": {
944
+ "version": "1.32.0",
945
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz",
946
+ "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==",
947
+ "dev": true,
948
+ "license": "MPL-2.0",
949
+ "dependencies": {
950
+ "detect-libc": "^2.0.3"
951
+ },
952
+ "engines": {
953
+ "node": ">= 12.0.0"
954
+ },
955
+ "funding": {
956
+ "type": "opencollective",
957
+ "url": "https://opencollective.com/parcel"
958
+ },
959
+ "optionalDependencies": {
960
+ "lightningcss-android-arm64": "1.32.0",
961
+ "lightningcss-darwin-arm64": "1.32.0",
962
+ "lightningcss-darwin-x64": "1.32.0",
963
+ "lightningcss-freebsd-x64": "1.32.0",
964
+ "lightningcss-linux-arm-gnueabihf": "1.32.0",
965
+ "lightningcss-linux-arm64-gnu": "1.32.0",
966
+ "lightningcss-linux-arm64-musl": "1.32.0",
967
+ "lightningcss-linux-x64-gnu": "1.32.0",
968
+ "lightningcss-linux-x64-musl": "1.32.0",
969
+ "lightningcss-win32-arm64-msvc": "1.32.0",
970
+ "lightningcss-win32-x64-msvc": "1.32.0"
971
+ }
972
+ },
973
+ "node_modules/lightningcss-android-arm64": {
974
+ "version": "1.32.0",
975
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz",
976
+ "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==",
977
+ "cpu": [
978
+ "arm64"
979
+ ],
980
+ "dev": true,
981
+ "license": "MPL-2.0",
982
+ "optional": true,
983
+ "os": [
984
+ "android"
985
+ ],
986
+ "engines": {
987
+ "node": ">= 12.0.0"
988
+ },
989
+ "funding": {
990
+ "type": "opencollective",
991
+ "url": "https://opencollective.com/parcel"
992
+ }
993
+ },
994
+ "node_modules/lightningcss-darwin-arm64": {
995
+ "version": "1.32.0",
996
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz",
997
+ "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==",
998
+ "cpu": [
999
+ "arm64"
1000
+ ],
1001
+ "dev": true,
1002
+ "license": "MPL-2.0",
1003
+ "optional": true,
1004
+ "os": [
1005
+ "darwin"
1006
+ ],
1007
+ "engines": {
1008
+ "node": ">= 12.0.0"
1009
+ },
1010
+ "funding": {
1011
+ "type": "opencollective",
1012
+ "url": "https://opencollective.com/parcel"
1013
+ }
1014
+ },
1015
+ "node_modules/lightningcss-darwin-x64": {
1016
+ "version": "1.32.0",
1017
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz",
1018
+ "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==",
1019
+ "cpu": [
1020
+ "x64"
1021
+ ],
1022
+ "dev": true,
1023
+ "license": "MPL-2.0",
1024
+ "optional": true,
1025
+ "os": [
1026
+ "darwin"
1027
+ ],
1028
+ "engines": {
1029
+ "node": ">= 12.0.0"
1030
+ },
1031
+ "funding": {
1032
+ "type": "opencollective",
1033
+ "url": "https://opencollective.com/parcel"
1034
+ }
1035
+ },
1036
+ "node_modules/lightningcss-freebsd-x64": {
1037
+ "version": "1.32.0",
1038
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz",
1039
+ "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==",
1040
+ "cpu": [
1041
+ "x64"
1042
+ ],
1043
+ "dev": true,
1044
+ "license": "MPL-2.0",
1045
+ "optional": true,
1046
+ "os": [
1047
+ "freebsd"
1048
+ ],
1049
+ "engines": {
1050
+ "node": ">= 12.0.0"
1051
+ },
1052
+ "funding": {
1053
+ "type": "opencollective",
1054
+ "url": "https://opencollective.com/parcel"
1055
+ }
1056
+ },
1057
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
1058
+ "version": "1.32.0",
1059
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz",
1060
+ "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==",
1061
+ "cpu": [
1062
+ "arm"
1063
+ ],
1064
+ "dev": true,
1065
+ "license": "MPL-2.0",
1066
+ "optional": true,
1067
+ "os": [
1068
+ "linux"
1069
+ ],
1070
+ "engines": {
1071
+ "node": ">= 12.0.0"
1072
+ },
1073
+ "funding": {
1074
+ "type": "opencollective",
1075
+ "url": "https://opencollective.com/parcel"
1076
+ }
1077
+ },
1078
+ "node_modules/lightningcss-linux-arm64-gnu": {
1079
+ "version": "1.32.0",
1080
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz",
1081
+ "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==",
1082
+ "cpu": [
1083
+ "arm64"
1084
+ ],
1085
+ "dev": true,
1086
+ "license": "MPL-2.0",
1087
+ "optional": true,
1088
+ "os": [
1089
+ "linux"
1090
+ ],
1091
+ "engines": {
1092
+ "node": ">= 12.0.0"
1093
+ },
1094
+ "funding": {
1095
+ "type": "opencollective",
1096
+ "url": "https://opencollective.com/parcel"
1097
+ }
1098
+ },
1099
+ "node_modules/lightningcss-linux-arm64-musl": {
1100
+ "version": "1.32.0",
1101
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz",
1102
+ "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==",
1103
+ "cpu": [
1104
+ "arm64"
1105
+ ],
1106
+ "dev": true,
1107
+ "license": "MPL-2.0",
1108
+ "optional": true,
1109
+ "os": [
1110
+ "linux"
1111
+ ],
1112
+ "engines": {
1113
+ "node": ">= 12.0.0"
1114
+ },
1115
+ "funding": {
1116
+ "type": "opencollective",
1117
+ "url": "https://opencollective.com/parcel"
1118
+ }
1119
+ },
1120
+ "node_modules/lightningcss-linux-x64-gnu": {
1121
+ "version": "1.32.0",
1122
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz",
1123
+ "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==",
1124
+ "cpu": [
1125
+ "x64"
1126
+ ],
1127
+ "dev": true,
1128
+ "license": "MPL-2.0",
1129
+ "optional": true,
1130
+ "os": [
1131
+ "linux"
1132
+ ],
1133
+ "engines": {
1134
+ "node": ">= 12.0.0"
1135
+ },
1136
+ "funding": {
1137
+ "type": "opencollective",
1138
+ "url": "https://opencollective.com/parcel"
1139
+ }
1140
+ },
1141
+ "node_modules/lightningcss-linux-x64-musl": {
1142
+ "version": "1.32.0",
1143
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz",
1144
+ "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==",
1145
+ "cpu": [
1146
+ "x64"
1147
+ ],
1148
+ "dev": true,
1149
+ "license": "MPL-2.0",
1150
+ "optional": true,
1151
+ "os": [
1152
+ "linux"
1153
+ ],
1154
+ "engines": {
1155
+ "node": ">= 12.0.0"
1156
+ },
1157
+ "funding": {
1158
+ "type": "opencollective",
1159
+ "url": "https://opencollective.com/parcel"
1160
+ }
1161
+ },
1162
+ "node_modules/lightningcss-win32-arm64-msvc": {
1163
+ "version": "1.32.0",
1164
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz",
1165
+ "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==",
1166
+ "cpu": [
1167
+ "arm64"
1168
+ ],
1169
+ "dev": true,
1170
+ "license": "MPL-2.0",
1171
+ "optional": true,
1172
+ "os": [
1173
+ "win32"
1174
+ ],
1175
+ "engines": {
1176
+ "node": ">= 12.0.0"
1177
+ },
1178
+ "funding": {
1179
+ "type": "opencollective",
1180
+ "url": "https://opencollective.com/parcel"
1181
+ }
1182
+ },
1183
+ "node_modules/lightningcss-win32-x64-msvc": {
1184
+ "version": "1.32.0",
1185
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz",
1186
+ "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==",
1187
+ "cpu": [
1188
+ "x64"
1189
+ ],
1190
+ "dev": true,
1191
+ "license": "MPL-2.0",
1192
+ "optional": true,
1193
+ "os": [
1194
+ "win32"
1195
+ ],
1196
+ "engines": {
1197
+ "node": ">= 12.0.0"
1198
+ },
1199
+ "funding": {
1200
+ "type": "opencollective",
1201
+ "url": "https://opencollective.com/parcel"
1202
+ }
1203
+ },
1204
+ "node_modules/magic-string": {
1205
+ "version": "0.30.21",
1206
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
1207
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
1208
+ "dev": true,
1209
+ "license": "MIT",
1210
+ "dependencies": {
1211
+ "@jridgewell/sourcemap-codec": "^1.5.5"
1212
+ }
1213
+ },
1214
+ "node_modules/nanoid": {
1215
+ "version": "3.3.16",
1216
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz",
1217
+ "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==",
1218
+ "dev": true,
1219
+ "funding": [
1220
+ {
1221
+ "type": "github",
1222
+ "url": "https://github.com/sponsors/ai"
1223
+ }
1224
+ ],
1225
+ "license": "MIT",
1226
+ "bin": {
1227
+ "nanoid": "bin/nanoid.cjs"
1228
+ },
1229
+ "engines": {
1230
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
1231
+ }
1232
+ },
1233
+ "node_modules/obug": {
1234
+ "version": "2.1.3",
1235
+ "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz",
1236
+ "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==",
1237
+ "dev": true,
1238
+ "funding": [
1239
+ "https://github.com/sponsors/sxzz",
1240
+ "https://opencollective.com/debug"
1241
+ ],
1242
+ "license": "MIT",
1243
+ "engines": {
1244
+ "node": ">=12.20.0"
1245
+ }
1246
+ },
1247
+ "node_modules/pathe": {
1248
+ "version": "2.0.3",
1249
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
1250
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
1251
+ "dev": true,
1252
+ "license": "MIT"
1253
+ },
1254
+ "node_modules/picocolors": {
1255
+ "version": "1.1.1",
1256
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
1257
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
1258
+ "dev": true,
1259
+ "license": "ISC"
1260
+ },
1261
+ "node_modules/picomatch": {
1262
+ "version": "4.0.5",
1263
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
1264
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
1265
+ "dev": true,
1266
+ "license": "MIT",
1267
+ "engines": {
1268
+ "node": ">=12"
1269
+ },
1270
+ "funding": {
1271
+ "url": "https://github.com/sponsors/jonschlinkert"
1272
+ }
1273
+ },
1274
+ "node_modules/postcss": {
1275
+ "version": "8.5.19",
1276
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.19.tgz",
1277
+ "integrity": "sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==",
1278
+ "dev": true,
1279
+ "funding": [
1280
+ {
1281
+ "type": "opencollective",
1282
+ "url": "https://opencollective.com/postcss/"
1283
+ },
1284
+ {
1285
+ "type": "tidelift",
1286
+ "url": "https://tidelift.com/funding/github/npm/postcss"
1287
+ },
1288
+ {
1289
+ "type": "github",
1290
+ "url": "https://github.com/sponsors/ai"
1291
+ }
1292
+ ],
1293
+ "license": "MIT",
1294
+ "dependencies": {
1295
+ "nanoid": "^3.3.12",
1296
+ "picocolors": "^1.1.1",
1297
+ "source-map-js": "^1.2.1"
1298
+ },
1299
+ "engines": {
1300
+ "node": "^10 || ^12 || >=14"
1301
+ }
1302
+ },
1303
+ "node_modules/rolldown": {
1304
+ "version": "1.1.5",
1305
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz",
1306
+ "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==",
1307
+ "dev": true,
1308
+ "license": "MIT",
1309
+ "dependencies": {
1310
+ "@oxc-project/types": "=0.139.0",
1311
+ "@rolldown/pluginutils": "^1.0.0"
1312
+ },
1313
+ "bin": {
1314
+ "rolldown": "bin/cli.mjs"
1315
+ },
1316
+ "engines": {
1317
+ "node": "^20.19.0 || >=22.12.0"
1318
+ },
1319
+ "optionalDependencies": {
1320
+ "@rolldown/binding-android-arm64": "1.1.5",
1321
+ "@rolldown/binding-darwin-arm64": "1.1.5",
1322
+ "@rolldown/binding-darwin-x64": "1.1.5",
1323
+ "@rolldown/binding-freebsd-x64": "1.1.5",
1324
+ "@rolldown/binding-linux-arm-gnueabihf": "1.1.5",
1325
+ "@rolldown/binding-linux-arm64-gnu": "1.1.5",
1326
+ "@rolldown/binding-linux-arm64-musl": "1.1.5",
1327
+ "@rolldown/binding-linux-ppc64-gnu": "1.1.5",
1328
+ "@rolldown/binding-linux-s390x-gnu": "1.1.5",
1329
+ "@rolldown/binding-linux-x64-gnu": "1.1.5",
1330
+ "@rolldown/binding-linux-x64-musl": "1.1.5",
1331
+ "@rolldown/binding-openharmony-arm64": "1.1.5",
1332
+ "@rolldown/binding-wasm32-wasi": "1.1.5",
1333
+ "@rolldown/binding-win32-arm64-msvc": "1.1.5",
1334
+ "@rolldown/binding-win32-x64-msvc": "1.1.5"
1335
+ }
1336
+ },
1337
+ "node_modules/siginfo": {
1338
+ "version": "2.0.0",
1339
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
1340
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
1341
+ "dev": true,
1342
+ "license": "ISC"
1343
+ },
1344
+ "node_modules/source-map-js": {
1345
+ "version": "1.2.1",
1346
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
1347
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
1348
+ "dev": true,
1349
+ "license": "BSD-3-Clause",
1350
+ "engines": {
1351
+ "node": ">=0.10.0"
1352
+ }
1353
+ },
1354
+ "node_modules/stackback": {
1355
+ "version": "0.0.2",
1356
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
1357
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
1358
+ "dev": true,
1359
+ "license": "MIT"
1360
+ },
1361
+ "node_modules/std-env": {
1362
+ "version": "4.2.0",
1363
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz",
1364
+ "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==",
1365
+ "dev": true,
1366
+ "license": "MIT"
1367
+ },
1368
+ "node_modules/tinybench": {
1369
+ "version": "2.9.0",
1370
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
1371
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
1372
+ "dev": true,
1373
+ "license": "MIT"
1374
+ },
1375
+ "node_modules/tinyexec": {
1376
+ "version": "1.2.4",
1377
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz",
1378
+ "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==",
1379
+ "dev": true,
1380
+ "license": "MIT",
1381
+ "engines": {
1382
+ "node": ">=18"
1383
+ }
1384
+ },
1385
+ "node_modules/tinyglobby": {
1386
+ "version": "0.2.17",
1387
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
1388
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
1389
+ "dev": true,
1390
+ "license": "MIT",
1391
+ "dependencies": {
1392
+ "fdir": "^6.5.0",
1393
+ "picomatch": "^4.0.4"
1394
+ },
1395
+ "engines": {
1396
+ "node": ">=12.0.0"
1397
+ },
1398
+ "funding": {
1399
+ "url": "https://github.com/sponsors/SuperchupuDev"
1400
+ }
1401
+ },
1402
+ "node_modules/tinyrainbow": {
1403
+ "version": "3.1.0",
1404
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
1405
+ "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
1406
+ "dev": true,
1407
+ "license": "MIT",
1408
+ "engines": {
1409
+ "node": ">=14.0.0"
1410
+ }
1411
+ },
1412
+ "node_modules/tslib": {
1413
+ "version": "2.8.1",
1414
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
1415
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
1416
+ "dev": true,
1417
+ "license": "0BSD",
1418
+ "optional": true
1419
+ },
1420
+ "node_modules/typescript": {
1421
+ "version": "7.0.2",
1422
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
1423
+ "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
1424
+ "dev": true,
1425
+ "license": "Apache-2.0",
1426
+ "bin": {
1427
+ "tsc": "bin/tsc"
1428
+ },
1429
+ "engines": {
1430
+ "node": ">=16.20.0"
1431
+ },
1432
+ "optionalDependencies": {
1433
+ "@typescript/typescript-aix-ppc64": "7.0.2",
1434
+ "@typescript/typescript-darwin-arm64": "7.0.2",
1435
+ "@typescript/typescript-darwin-x64": "7.0.2",
1436
+ "@typescript/typescript-freebsd-arm64": "7.0.2",
1437
+ "@typescript/typescript-freebsd-x64": "7.0.2",
1438
+ "@typescript/typescript-linux-arm": "7.0.2",
1439
+ "@typescript/typescript-linux-arm64": "7.0.2",
1440
+ "@typescript/typescript-linux-loong64": "7.0.2",
1441
+ "@typescript/typescript-linux-mips64el": "7.0.2",
1442
+ "@typescript/typescript-linux-ppc64": "7.0.2",
1443
+ "@typescript/typescript-linux-riscv64": "7.0.2",
1444
+ "@typescript/typescript-linux-s390x": "7.0.2",
1445
+ "@typescript/typescript-linux-x64": "7.0.2",
1446
+ "@typescript/typescript-netbsd-arm64": "7.0.2",
1447
+ "@typescript/typescript-netbsd-x64": "7.0.2",
1448
+ "@typescript/typescript-openbsd-arm64": "7.0.2",
1449
+ "@typescript/typescript-openbsd-x64": "7.0.2",
1450
+ "@typescript/typescript-sunos-x64": "7.0.2",
1451
+ "@typescript/typescript-win32-arm64": "7.0.2",
1452
+ "@typescript/typescript-win32-x64": "7.0.2"
1453
+ }
1454
+ },
1455
+ "node_modules/vite": {
1456
+ "version": "8.1.4",
1457
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.4.tgz",
1458
+ "integrity": "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==",
1459
+ "dev": true,
1460
+ "license": "MIT",
1461
+ "dependencies": {
1462
+ "lightningcss": "^1.32.0",
1463
+ "picomatch": "^4.0.5",
1464
+ "postcss": "^8.5.16",
1465
+ "rolldown": "~1.1.4",
1466
+ "tinyglobby": "^0.2.17"
1467
+ },
1468
+ "bin": {
1469
+ "vite": "bin/vite.js"
1470
+ },
1471
+ "engines": {
1472
+ "node": "^20.19.0 || >=22.12.0"
1473
+ },
1474
+ "funding": {
1475
+ "url": "https://github.com/vitejs/vite?sponsor=1"
1476
+ },
1477
+ "optionalDependencies": {
1478
+ "fsevents": "~2.3.3"
1479
+ },
1480
+ "peerDependencies": {
1481
+ "@types/node": "^20.19.0 || >=22.12.0",
1482
+ "@vitejs/devtools": "^0.3.0",
1483
+ "esbuild": "^0.27.0 || ^0.28.0",
1484
+ "jiti": ">=1.21.0",
1485
+ "less": "^4.0.0",
1486
+ "sass": "^1.70.0",
1487
+ "sass-embedded": "^1.70.0",
1488
+ "stylus": ">=0.54.8",
1489
+ "sugarss": "^5.0.0",
1490
+ "terser": "^5.16.0",
1491
+ "tsx": "^4.8.1",
1492
+ "yaml": "^2.4.2"
1493
+ },
1494
+ "peerDependenciesMeta": {
1495
+ "@types/node": {
1496
+ "optional": true
1497
+ },
1498
+ "@vitejs/devtools": {
1499
+ "optional": true
1500
+ },
1501
+ "esbuild": {
1502
+ "optional": true
1503
+ },
1504
+ "jiti": {
1505
+ "optional": true
1506
+ },
1507
+ "less": {
1508
+ "optional": true
1509
+ },
1510
+ "sass": {
1511
+ "optional": true
1512
+ },
1513
+ "sass-embedded": {
1514
+ "optional": true
1515
+ },
1516
+ "stylus": {
1517
+ "optional": true
1518
+ },
1519
+ "sugarss": {
1520
+ "optional": true
1521
+ },
1522
+ "terser": {
1523
+ "optional": true
1524
+ },
1525
+ "tsx": {
1526
+ "optional": true
1527
+ },
1528
+ "yaml": {
1529
+ "optional": true
1530
+ }
1531
+ }
1532
+ },
1533
+ "node_modules/vitest": {
1534
+ "version": "4.1.10",
1535
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.10.tgz",
1536
+ "integrity": "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==",
1537
+ "dev": true,
1538
+ "license": "MIT",
1539
+ "dependencies": {
1540
+ "@vitest/expect": "4.1.10",
1541
+ "@vitest/mocker": "4.1.10",
1542
+ "@vitest/pretty-format": "4.1.10",
1543
+ "@vitest/runner": "4.1.10",
1544
+ "@vitest/snapshot": "4.1.10",
1545
+ "@vitest/spy": "4.1.10",
1546
+ "@vitest/utils": "4.1.10",
1547
+ "es-module-lexer": "^2.0.0",
1548
+ "expect-type": "^1.3.0",
1549
+ "magic-string": "^0.30.21",
1550
+ "obug": "^2.1.1",
1551
+ "pathe": "^2.0.3",
1552
+ "picomatch": "^4.0.3",
1553
+ "std-env": "^4.0.0-rc.1",
1554
+ "tinybench": "^2.9.0",
1555
+ "tinyexec": "^1.0.2",
1556
+ "tinyglobby": "^0.2.15",
1557
+ "tinyrainbow": "^3.1.0",
1558
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
1559
+ "why-is-node-running": "^2.3.0"
1560
+ },
1561
+ "bin": {
1562
+ "vitest": "vitest.mjs"
1563
+ },
1564
+ "engines": {
1565
+ "node": "^20.0.0 || ^22.0.0 || >=24.0.0"
1566
+ },
1567
+ "funding": {
1568
+ "url": "https://opencollective.com/vitest"
1569
+ },
1570
+ "peerDependencies": {
1571
+ "@edge-runtime/vm": "*",
1572
+ "@opentelemetry/api": "^1.9.0",
1573
+ "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
1574
+ "@vitest/browser-playwright": "4.1.10",
1575
+ "@vitest/browser-preview": "4.1.10",
1576
+ "@vitest/browser-webdriverio": "4.1.10",
1577
+ "@vitest/coverage-istanbul": "4.1.10",
1578
+ "@vitest/coverage-v8": "4.1.10",
1579
+ "@vitest/ui": "4.1.10",
1580
+ "happy-dom": "*",
1581
+ "jsdom": "*",
1582
+ "vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
1583
+ },
1584
+ "peerDependenciesMeta": {
1585
+ "@edge-runtime/vm": {
1586
+ "optional": true
1587
+ },
1588
+ "@opentelemetry/api": {
1589
+ "optional": true
1590
+ },
1591
+ "@types/node": {
1592
+ "optional": true
1593
+ },
1594
+ "@vitest/browser-playwright": {
1595
+ "optional": true
1596
+ },
1597
+ "@vitest/browser-preview": {
1598
+ "optional": true
1599
+ },
1600
+ "@vitest/browser-webdriverio": {
1601
+ "optional": true
1602
+ },
1603
+ "@vitest/coverage-istanbul": {
1604
+ "optional": true
1605
+ },
1606
+ "@vitest/coverage-v8": {
1607
+ "optional": true
1608
+ },
1609
+ "@vitest/ui": {
1610
+ "optional": true
1611
+ },
1612
+ "happy-dom": {
1613
+ "optional": true
1614
+ },
1615
+ "jsdom": {
1616
+ "optional": true
1617
+ },
1618
+ "vite": {
1619
+ "optional": false
1620
+ }
1621
+ }
1622
+ },
1623
+ "node_modules/why-is-node-running": {
1624
+ "version": "2.3.0",
1625
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
1626
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
1627
+ "dev": true,
1628
+ "license": "MIT",
1629
+ "dependencies": {
1630
+ "siginfo": "^2.0.0",
1631
+ "stackback": "0.0.2"
1632
+ },
1633
+ "bin": {
1634
+ "why-is-node-running": "cli.js"
1635
+ },
1636
+ "engines": {
1637
+ "node": ">=8"
1638
+ }
1639
+ }
1640
+ }
1641
+ }
package.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "tilekit-wallpaper-studio",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "tsc --noEmit && vite build",
8
+ "dev": "vite",
9
+ "test": "vitest run"
10
+ },
11
+ "devDependencies": {
12
+ "typescript": "7.0.2",
13
+ "vite": "8.1.4",
14
+ "vitest": "4.1.10"
15
+ }
16
+ }
requirements.txt DELETED
@@ -1 +0,0 @@
1
- tilekit @ git+https://github.com/osolmaz/tools.git@461669a3f35a4ed7c49ac209fdea5e95f82f18da#subdirectory=tilekit
 
 
src/layout.test.ts ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from "vitest";
2
+ import { generateLayout, PLACEMENT_STRIDE } from "./layout";
3
+
4
+ const options = {
5
+ aspect: 16 / 9,
6
+ count: 88,
7
+ logoScale: 1,
8
+ spacing: 1,
9
+ rotation: 52,
10
+ seed: 84237,
11
+ };
12
+
13
+ describe("generateLayout", () => {
14
+ it("is deterministic", () => {
15
+ expect(generateLayout(options)).toEqual(generateLayout(options));
16
+ });
17
+
18
+ it("returns finite packed placements", () => {
19
+ const layout = generateLayout(options);
20
+ expect(layout).toHaveLength(options.count * PLACEMENT_STRIDE);
21
+ expect([...layout].every(Number.isFinite)).toBe(true);
22
+ });
23
+
24
+ it("responds to the seed", () => {
25
+ expect(generateLayout(options)).not.toEqual(generateLayout({ ...options, seed: 84238 }));
26
+ });
27
+
28
+ it("spreads placements across the canvas", () => {
29
+ const layout = generateLayout(options);
30
+ const xs: number[] = [];
31
+ const ys: number[] = [];
32
+ for (let offset = 0; offset < layout.length; offset += PLACEMENT_STRIDE) {
33
+ xs.push(layout[offset] ?? 0);
34
+ ys.push(layout[offset + 1] ?? 0);
35
+ }
36
+ expect(Math.max(...xs) - Math.min(...xs)).toBeGreaterThan(1.5);
37
+ expect(Math.max(...ys) - Math.min(...ys)).toBeGreaterThan(0.85);
38
+ });
39
+ });
src/layout.ts ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export interface LayoutOptions {
2
+ aspect: number;
3
+ count: number;
4
+ logoScale: number;
5
+ spacing: number;
6
+ rotation: number;
7
+ seed: number;
8
+ }
9
+
10
+ export const PLACEMENT_STRIDE = 4;
11
+
12
+ const SIZE_BANDS = [
13
+ [0.12, 0.18, 0.259],
14
+ [0.42, 0.13, 0.19],
15
+ [0.84, 0.084, 0.141],
16
+ [1, 0.054, 0.09],
17
+ ] as const;
18
+
19
+ class Random {
20
+ private state: number;
21
+
22
+ constructor(seed: number) {
23
+ this.state = seed >>> 0 || 0x6d2b79f5;
24
+ }
25
+
26
+ next(): number {
27
+ let value = (this.state += 0x6d2b79f5);
28
+ value = Math.imul(value ^ (value >>> 15), value | 1);
29
+ value ^= value + Math.imul(value ^ (value >>> 7), value | 61);
30
+ return ((value ^ (value >>> 14)) >>> 0) / 4294967296;
31
+ }
32
+ }
33
+
34
+ function pickWidth(random: Random, scale: number): number {
35
+ const roll = random.next();
36
+ for (const [threshold, low, high] of SIZE_BANDS) {
37
+ if (roll <= threshold) return (low + random.next() * (high - low)) * scale;
38
+ }
39
+ return 0.07 * scale;
40
+ }
41
+
42
+ function cellIndex(value: number, cellSize: number, limit: number): number {
43
+ return Math.max(0, Math.min(limit - 1, Math.floor(value / cellSize)));
44
+ }
45
+
46
+ export function generateLayout(options: LayoutOptions): Float32Array {
47
+ const random = new Random(options.seed);
48
+ const count = Math.max(1, Math.floor(options.count));
49
+ const width = Math.max(0.5, options.aspect);
50
+ const height = 1;
51
+ const widths = new Float32Array(count);
52
+ for (let index = 0; index < count; index += 1) widths[index] = pickWidth(random, options.logoScale);
53
+ widths.sort();
54
+ widths.reverse();
55
+
56
+ const maxRadius = (widths[0] ?? 0.1) * 0.49;
57
+ const minimumGap = 0.0037 * options.spacing;
58
+ const cellSize = Math.max(0.025, maxRadius * 1.7 + minimumGap);
59
+ const gridWidth = Math.max(1, Math.ceil(width / cellSize));
60
+ const gridHeight = Math.max(1, Math.ceil(height / cellSize));
61
+ const grid: number[][] = Array.from({ length: gridWidth * gridHeight }, () => []);
62
+ const output = new Float32Array(count * PLACEMENT_STRIDE);
63
+
64
+ const neighborScore = (x: number, y: number, radius: number): number => {
65
+ const reach = Math.ceil((radius + maxRadius + minimumGap) / cellSize);
66
+ const centerX = cellIndex(x, cellSize, gridWidth);
67
+ const centerY = cellIndex(y, cellSize, gridHeight);
68
+ let closest = Number.POSITIVE_INFINITY;
69
+ for (let gridY = Math.max(0, centerY - reach); gridY <= Math.min(gridHeight - 1, centerY + reach); gridY += 1) {
70
+ for (let gridX = Math.max(0, centerX - reach); gridX <= Math.min(gridWidth - 1, centerX + reach); gridX += 1) {
71
+ const bucket = grid[gridY * gridWidth + gridX];
72
+ if (!bucket) continue;
73
+ for (const other of bucket) {
74
+ const offset = other * PLACEMENT_STRIDE;
75
+ const dx = x - (output[offset] ?? 0);
76
+ const dy = y - (output[offset + 1] ?? 0);
77
+ const otherRadius = (output[offset + 2] ?? 0) * 0.49;
78
+ closest = Math.min(closest, Math.hypot(dx, dy) - radius - otherRadius);
79
+ }
80
+ }
81
+ }
82
+ return closest;
83
+ };
84
+
85
+ for (let index = 0; index < count; index += 1) {
86
+ const logoWidth = widths[index] ?? 0.08;
87
+ const radius = logoWidth * 0.49;
88
+ const bleed = radius * 0.34;
89
+ let bestX = width * 0.5;
90
+ let bestY = height * 0.5;
91
+ let bestScore = Number.NEGATIVE_INFINITY;
92
+ const attempts = index < 24 ? 42 : 30;
93
+
94
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
95
+ const x = -bleed + random.next() * (width + bleed * 2);
96
+ const y = -bleed + random.next() * (height + bleed * 2);
97
+ const gap = neighborScore(x, y, radius);
98
+ const edgeDistance = Math.min(x + radius, width - x + radius, y + radius, height - y + radius);
99
+ const score = gap + Math.min(0, edgeDistance) * 0.65;
100
+ if (score > bestScore) {
101
+ bestScore = score;
102
+ bestX = x;
103
+ bestY = y;
104
+ }
105
+ }
106
+
107
+ const triangular = random.next() + random.next() - 1;
108
+ const rawAngle = triangular * options.rotation;
109
+ const angle = rawAngle > 0 ? rawAngle * 1.35 : rawAngle * 0.72;
110
+ const offset = index * PLACEMENT_STRIDE;
111
+ output[offset] = bestX;
112
+ output[offset + 1] = bestY;
113
+ output[offset + 2] = logoWidth;
114
+ output[offset + 3] = angle;
115
+
116
+ const gridX = cellIndex(bestX, cellSize, gridWidth);
117
+ const gridY = cellIndex(bestY, cellSize, gridHeight);
118
+ grid[gridY * gridWidth + gridX]?.push(index);
119
+ }
120
+
121
+ return output;
122
+ }
src/main.ts ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import "./style.css";
2
+ import { generateLayout, type LayoutOptions } from "./layout";
3
+ import { renderWallpaper } from "./render";
4
+
5
+ interface Config extends LayoutOptions {
6
+ width: number;
7
+ height: number;
8
+ }
9
+
10
+ function element<T extends HTMLElement>(id: string): T {
11
+ const value = document.getElementById(id);
12
+ if (!value) throw new Error(`Missing element: ${id}`);
13
+ return value as T;
14
+ }
15
+
16
+ const preview = element<HTMLCanvasElement>("preview");
17
+ const canvasWrap = element<HTMLDivElement>("canvas-wrap");
18
+ const resolution = element<HTMLSelectElement>("resolution");
19
+ const widthInput = element<HTMLInputElement>("width");
20
+ const heightInput = element<HTMLInputElement>("height");
21
+ const densityInput = element<HTMLInputElement>("density");
22
+ const sizeInput = element<HTMLInputElement>("logo-size");
23
+ const spacingInput = element<HTMLInputElement>("spacing");
24
+ const rotationInput = element<HTMLInputElement>("rotation");
25
+ const seedInput = element<HTMLInputElement>("seed");
26
+ const exportButton = element<HTMLButtonElement>("export");
27
+ const exportStatus = element<HTMLParagraphElement>("export-status");
28
+ const renderStats = element<HTMLSpanElement>("render-stats");
29
+
30
+ let logo: CanvasImageSource;
31
+ let currentLayout: Float32Array<ArrayBufferLike> = new Float32Array();
32
+ let currentConfigKey = "";
33
+ let framePending = false;
34
+
35
+ function numberValue(input: HTMLInputElement, fallback: number): number {
36
+ return Number.isFinite(input.valueAsNumber) ? input.valueAsNumber : fallback;
37
+ }
38
+
39
+ function readConfig(): Config {
40
+ const width = Math.round(Math.min(7680, Math.max(640, numberValue(widthInput, 3840))));
41
+ const height = Math.round(Math.min(4320, Math.max(360, numberValue(heightInput, 2160))));
42
+ return {
43
+ width,
44
+ height,
45
+ aspect: width / height,
46
+ count: Math.round(numberValue(densityInput, 88)),
47
+ logoScale: numberValue(sizeInput, 1),
48
+ spacing: numberValue(spacingInput, 1),
49
+ rotation: numberValue(rotationInput, 52),
50
+ seed: Math.round(numberValue(seedInput, 84237)) >>> 0,
51
+ };
52
+ }
53
+
54
+ function configKey(config: Config): string {
55
+ return [config.aspect.toFixed(6), config.count, config.logoScale, config.spacing, config.rotation, config.seed].join(":");
56
+ }
57
+
58
+ function updateLabels(config: Config): void {
59
+ element<HTMLOutputElement>("density-value").value = String(config.count);
60
+ element<HTMLOutputElement>("size-value").value = `${config.logoScale.toFixed(2)}×`;
61
+ element<HTMLOutputElement>("spacing-value").value = `${config.spacing.toFixed(2)}×`;
62
+ element<HTMLOutputElement>("rotation-value").value = `${Math.round(config.rotation)}°`;
63
+ element<HTMLSpanElement>("pixel-count").textContent = `${((config.width * config.height) / 1_000_000).toFixed(1)} MP`;
64
+ const label = config.width === 3840 && config.height === 2160 ? "4K" : `${config.width}×${config.height}`;
65
+ exportButton.textContent = `Render ${label} PNG`;
66
+ }
67
+
68
+ function renderPreview(): void {
69
+ framePending = false;
70
+ if (!logo) return;
71
+ const started = performance.now();
72
+ const config = readConfig();
73
+ const key = configKey(config);
74
+ if (key !== currentConfigKey) {
75
+ currentLayout = generateLayout(config);
76
+ currentConfigKey = key;
77
+ }
78
+ updateLabels(config);
79
+
80
+ const bounds = canvasWrap.getBoundingClientRect();
81
+ const cssWidth = Math.max(320, bounds.width);
82
+ const cssHeight = cssWidth / config.aspect;
83
+ const pixelRatio = Math.min(2, window.devicePixelRatio || 1);
84
+ preview.style.aspectRatio = String(config.aspect);
85
+ preview.width = Math.max(1, Math.round(cssWidth * pixelRatio));
86
+ preview.height = Math.max(1, Math.round(cssHeight * pixelRatio));
87
+ const context = preview.getContext("2d", { alpha: false });
88
+ if (!context) throw new Error("Canvas 2D is not available");
89
+ renderWallpaper(context, preview.width, preview.height, currentLayout, logo);
90
+ renderStats.textContent = `${config.count} logos · ${(performance.now() - started).toFixed(1)} ms`;
91
+ }
92
+
93
+ function schedulePreview(): void {
94
+ if (framePending) return;
95
+ framePending = true;
96
+ requestAnimationFrame(renderPreview);
97
+ }
98
+
99
+ async function canvasToBlob(canvas: HTMLCanvasElement | OffscreenCanvas): Promise<Blob> {
100
+ if (canvas instanceof OffscreenCanvas) return canvas.convertToBlob({ type: "image/png" });
101
+ return new Promise((resolve, reject) => {
102
+ canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new Error("PNG encoding failed"))), "image/png");
103
+ });
104
+ }
105
+
106
+ async function exportPng(): Promise<void> {
107
+ const config = readConfig();
108
+ const pixels = config.width * config.height;
109
+ if (pixels > 33_177_600) {
110
+ exportStatus.textContent = "Maximum export size is 7680×4320 (33.2 MP).";
111
+ return;
112
+ }
113
+
114
+ exportButton.disabled = true;
115
+ exportButton.textContent = `Rendering ${config.width}×${config.height}…`;
116
+ exportStatus.textContent = "Drawing full-resolution canvas…";
117
+ await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
118
+ const started = performance.now();
119
+
120
+ try {
121
+ const key = configKey(config);
122
+ const layout = key === currentConfigKey ? currentLayout : generateLayout(config);
123
+ const surface: HTMLCanvasElement | OffscreenCanvas =
124
+ typeof OffscreenCanvas === "undefined"
125
+ ? Object.assign(document.createElement("canvas"), { width: config.width, height: config.height })
126
+ : new OffscreenCanvas(config.width, config.height);
127
+ const context = surface.getContext("2d", { alpha: false });
128
+ if (!context || !("drawImage" in context)) throw new Error("Canvas 2D is not available");
129
+ renderWallpaper(context, config.width, config.height, layout, logo);
130
+ const blob = await canvasToBlob(surface);
131
+ const url = URL.createObjectURL(blob);
132
+ const link = document.createElement("a");
133
+ link.href = url;
134
+ link.download = `tilekit-${config.width}x${config.height}-seed-${config.seed}.png`;
135
+ link.click();
136
+ window.setTimeout(() => URL.revokeObjectURL(url), 30_000);
137
+ const elapsed = performance.now() - started;
138
+ exportStatus.textContent = `${config.width}×${config.height} PNG · ${(blob.size / 1_000_000).toFixed(1)} MB · ${(elapsed / 1000).toFixed(2)} s`;
139
+ } catch (error) {
140
+ exportStatus.textContent = error instanceof Error ? error.message : "PNG export failed";
141
+ } finally {
142
+ exportButton.disabled = false;
143
+ updateLabels(config);
144
+ }
145
+ }
146
+
147
+ function setDefaults(): void {
148
+ resolution.value = "3840x2160";
149
+ widthInput.value = "3840";
150
+ heightInput.value = "2160";
151
+ densityInput.value = "88";
152
+ sizeInput.value = "1";
153
+ spacingInput.value = "1";
154
+ rotationInput.value = "52";
155
+ seedInput.value = "84237";
156
+ schedulePreview();
157
+ }
158
+
159
+ resolution.addEventListener("change", () => {
160
+ if (resolution.value !== "custom") {
161
+ const [width, height] = resolution.value.split("x");
162
+ if (width && height) {
163
+ widthInput.value = width;
164
+ heightInput.value = height;
165
+ }
166
+ }
167
+ schedulePreview();
168
+ });
169
+
170
+ for (const input of [widthInput, heightInput, densityInput, sizeInput, spacingInput, rotationInput, seedInput]) {
171
+ input.addEventListener("input", () => {
172
+ if (input === widthInput || input === heightInput) resolution.value = "custom";
173
+ schedulePreview();
174
+ });
175
+ }
176
+
177
+ element<HTMLButtonElement>("randomize").addEventListener("click", () => {
178
+ const value = new Uint32Array(1);
179
+ crypto.getRandomValues(value);
180
+ seedInput.value = String(value[0] ?? 84237);
181
+ schedulePreview();
182
+ });
183
+ element<HTMLButtonElement>("reset").addEventListener("click", setDefaults);
184
+ exportButton.addEventListener("click", () => void exportPng());
185
+ new ResizeObserver(schedulePreview).observe(canvasWrap);
186
+
187
+ const logoImage = new Image();
188
+ logoImage.decoding = "async";
189
+ logoImage.addEventListener("load", () => {
190
+ logo = logoImage;
191
+ schedulePreview();
192
+ if (typeof createImageBitmap === "function") {
193
+ void createImageBitmap(logoImage).then((bitmap) => {
194
+ logo = bitmap;
195
+ schedulePreview();
196
+ });
197
+ }
198
+ });
199
+ logoImage.addEventListener("error", () => {
200
+ renderStats.textContent = "Could not load logo";
201
+ });
202
+ logoImage.src = new URL("../assets/hugging-face.png", import.meta.url).href;
src/render.ts ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { PLACEMENT_STRIDE } from "./layout";
2
+
3
+ type RenderContext = CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D;
4
+
5
+ let noiseTile: HTMLCanvasElement | undefined;
6
+
7
+ function getNoiseTile(): HTMLCanvasElement {
8
+ if (noiseTile) return noiseTile;
9
+ noiseTile = document.createElement("canvas");
10
+ noiseTile.width = 96;
11
+ noiseTile.height = 96;
12
+ const context = noiseTile.getContext("2d", { alpha: true });
13
+ if (!context) throw new Error("Could not create noise texture");
14
+ const pixels = context.createImageData(96, 96);
15
+ let state = 31;
16
+ for (let index = 0; index < pixels.data.length; index += 4) {
17
+ state = Math.imul(state ^ (state >>> 15), 1 | state);
18
+ state ^= state + Math.imul(state ^ (state >>> 7), 61 | state);
19
+ const value = (state ^ (state >>> 14)) & 0xff;
20
+ pixels.data[index] = value;
21
+ pixels.data[index + 1] = value;
22
+ pixels.data[index + 2] = value;
23
+ pixels.data[index + 3] = 28;
24
+ }
25
+ context.putImageData(pixels, 0, 0);
26
+ return noiseTile;
27
+ }
28
+
29
+ function drawBackground(context: RenderContext, width: number, height: number): void {
30
+ const linear = context.createLinearGradient(0, 0, width, height * 0.35);
31
+ linear.addColorStop(0, "#1688e9");
32
+ linear.addColorStop(0.52, "#4aabef");
33
+ linear.addColorStop(1, "#c2e7f8");
34
+ context.fillStyle = linear;
35
+ context.fillRect(0, 0, width, height);
36
+
37
+ const glow = context.createRadialGradient(width * 0.56, height * 0.47, 0, width * 0.56, height * 0.47, Math.max(width, height) * 0.72);
38
+ glow.addColorStop(0, "rgba(255,255,255,0.16)");
39
+ glow.addColorStop(0.7, "rgba(255,255,255,0.02)");
40
+ glow.addColorStop(1, "rgba(2,74,153,0.08)");
41
+ context.fillStyle = glow;
42
+ context.fillRect(0, 0, width, height);
43
+
44
+ const pattern = context.createPattern(getNoiseTile(), "repeat");
45
+ if (pattern) {
46
+ context.save();
47
+ context.globalAlpha = 0.08;
48
+ context.fillStyle = pattern;
49
+ context.fillRect(0, 0, width, height);
50
+ context.restore();
51
+ }
52
+ }
53
+
54
+ export function renderWallpaper(
55
+ context: RenderContext,
56
+ width: number,
57
+ height: number,
58
+ placements: Float32Array,
59
+ logo: CanvasImageSource,
60
+ ): void {
61
+ context.setTransform(1, 0, 0, 1, 0, 0);
62
+ context.clearRect(0, 0, width, height);
63
+ drawBackground(context, width, height);
64
+
65
+ const unit = Math.min(width, height);
66
+ for (let offset = 0; offset < placements.length; offset += PLACEMENT_STRIDE) {
67
+ const x = (placements[offset] ?? 0) * unit;
68
+ const y = (placements[offset + 1] ?? 0) * unit;
69
+ const logoWidth = (placements[offset + 2] ?? 0) * unit;
70
+ const angle = (placements[offset + 3] ?? 0) * (Math.PI / 180);
71
+ context.save();
72
+ context.translate(x, y);
73
+ context.rotate(angle);
74
+ context.shadowColor = "rgba(8, 39, 78, 0.24)";
75
+ context.shadowBlur = Math.max(2, logoWidth * 0.035);
76
+ context.shadowOffsetY = Math.max(2, logoWidth * 0.032);
77
+ context.drawImage(logo, -logoWidth * 0.5, -logoWidth * 0.5, logoWidth, logoWidth);
78
+ context.restore();
79
+ }
80
+ }
src/style.css ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ :root {
2
+ color: #122334;
3
+ background: #edf5fa;
4
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
5
+ font-synthesis: none;
6
+ }
7
+
8
+ * { box-sizing: border-box; }
9
+ body { margin: 0; min-width: 320px; min-height: 100vh; background: radial-gradient(circle at 70% 0%, #fff 0, #edf5fa 42%, #dfedf5 100%); }
10
+ button, input, select { font: inherit; }
11
+ button, select, input { accent-color: #f7a600; }
12
+
13
+ .app-shell { width: min(1500px, 100%); margin: 0 auto; padding: 28px; }
14
+ .hero { display: flex; justify-content: space-between; align-items: end; gap: 24px; margin-bottom: 22px; }
15
+ .hero h1 { margin: 2px 0 6px; font-size: clamp(2rem, 4vw, 3.45rem); line-height: 1; letter-spacing: -0.055em; }
16
+ .hero h1 span { display: inline-block; transform: translateY(0.08em); }
17
+ .hero p { margin: 0; color: #557083; font-size: 1.02rem; }
18
+ .hero .eyebrow { color: #0d82df; font-size: 0.75rem; font-weight: 800; letter-spacing: 0.14em; text-transform: uppercase; }
19
+ .hero a { color: #315469; font-weight: 700; text-decoration: none; border-bottom: 1px solid #9bb3c1; }
20
+
21
+ .workspace { display: grid; grid-template-columns: 330px minmax(0, 1fr); gap: 18px; align-items: start; }
22
+ .controls, .preview-panel { background: rgba(255,255,255,0.88); border: 1px solid rgba(105,144,166,0.24); box-shadow: 0 18px 55px rgba(35,74,99,0.12); backdrop-filter: blur(18px); }
23
+ .controls { border-radius: 20px; padding: 18px; position: sticky; top: 18px; }
24
+ .control-section + .control-section { border-top: 1px solid #dce8ee; margin-top: 18px; padding-top: 16px; }
25
+ .section-heading { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
26
+ .section-heading h2 { font-size: 0.78rem; letter-spacing: 0.11em; text-transform: uppercase; margin: 0; color: #577184; }
27
+ .section-heading span { color: #7b929f; font-size: 0.78rem; }
28
+ label { color: #486273; display: grid; gap: 6px; font-size: 0.79rem; font-weight: 700; }
29
+ select, input[type="number"] { width: 100%; border: 1px solid #c9dbe4; border-radius: 10px; background: #f9fcfd; color: #173348; padding: 9px 10px; outline: none; }
30
+ select:focus, input:focus { border-color: #0d82df; box-shadow: 0 0 0 3px rgba(13,130,223,0.12); }
31
+ .split-inputs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-top: 10px; }
32
+ .range-label { margin-top: 13px; }
33
+ .range-label span { display: flex; justify-content: space-between; }
34
+ .range-label output { color: #0d82df; font-variant-numeric: tabular-nums; }
35
+ input[type="range"] { width: 100%; margin: 0; cursor: ew-resize; }
36
+ .seed-row { display: grid; grid-template-columns: 1fr 42px; gap: 8px; align-items: end; margin-top: 14px; }
37
+ .icon-button, .text-button { border: 0; cursor: pointer; color: #0d82df; background: transparent; font-weight: 800; }
38
+ .icon-button { height: 38px; border-radius: 10px; background: #e8f4fc; font-size: 1.2rem; }
39
+ .text-button { padding: 0; font-size: 0.76rem; }
40
+ .export-button { width: 100%; border: 0; border-radius: 12px; margin-top: 18px; padding: 12px 16px; color: #2d2706; font-weight: 850; cursor: pointer; background: linear-gradient(135deg, #ffd21e, #ffad03); box-shadow: 0 8px 22px rgba(255,173,3,0.25); }
41
+ .export-button:hover { filter: brightness(1.03); transform: translateY(-1px); }
42
+ .export-button:disabled { cursor: wait; opacity: 0.65; transform: none; }
43
+ .export-status { min-height: 2.4em; margin: 9px 2px 0; color: #718894; font-size: 0.72rem; line-height: 1.35; }
44
+
45
+ .preview-panel { overflow: hidden; border-radius: 20px; }
46
+ .preview-toolbar { min-height: 48px; padding: 0 16px; display: flex; align-items: center; justify-content: space-between; color: #587181; font-size: 0.78rem; font-weight: 700; border-bottom: 1px solid #dce8ee; }
47
+ .preview-toolbar span:first-child { display: flex; align-items: center; gap: 8px; }
48
+ .live-dot { width: 8px; height: 8px; border-radius: 50%; background: #1ab76c; box-shadow: 0 0 0 5px rgba(26,183,108,0.12); }
49
+ .canvas-wrap { width: 100%; min-height: 360px; display: grid; place-items: center; background: #d5e3eb; }
50
+ #preview { display: block; width: 100%; height: auto; max-height: calc(100vh - 180px); object-fit: contain; }
51
+
52
+ @media (max-width: 900px) {
53
+ .app-shell { padding: 18px; }
54
+ .workspace { grid-template-columns: 1fr; }
55
+ .controls { position: static; }
56
+ .preview-panel { grid-row: 1; }
57
+ .canvas-wrap { min-height: 220px; }
58
+ #preview { max-height: none; }
59
+ }
60
+
61
+ @media (max-width: 520px) {
62
+ .app-shell { padding: 12px; }
63
+ .hero { align-items: start; }
64
+ .hero a { display: none; }
65
+ .controls, .preview-panel { border-radius: 15px; }
66
+ }
src/vite-env.d.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ /// <reference types="vite/client" />
tsconfig.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "strict": true,
8
+ "noUncheckedIndexedAccess": true,
9
+ "noFallthroughCasesInSwitch": true,
10
+ "noUnusedLocals": true,
11
+ "noUnusedParameters": true,
12
+ "lib": ["ES2022", "DOM", "DOM.Iterable", "WebWorker"],
13
+ "types": ["vitest/globals"],
14
+ "skipLibCheck": true
15
+ },
16
+ "include": ["src"]
17
+ }