Panel fixes: tall card row_span, room tab styling, vacuum card image+value, AC airflow labels, AC fan speed sections (auto 3-btn + manual 5-step slider), light subtitle fallback, curtain pane dividers, dimmable light presets, LED panel brightness + reorder. New assets: 34 ui-icons (climate modes, cover, effects, battery, led-strip, switch states), 3 logos, 90 app surface PNGs, design system specs (components, ux-mechanics, tokens). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
119 lines
3.4 KiB
JavaScript
119 lines
3.4 KiB
JavaScript
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { spawnSync } from 'node:child_process';
|
|
import { pathToFileURL } from 'node:url';
|
|
|
|
const root = path.resolve(import.meta.dirname, '..');
|
|
const out = path.join(root, '.build/assets');
|
|
const sources = path.join(root, '.build/vector-sources');
|
|
|
|
export async function generateVectorAssets() {
|
|
await fs.mkdir(sources, { recursive: true });
|
|
const assets = [];
|
|
for (const file of await fs.readdir(path.join(root, 'assets/logos'))) {
|
|
if (!file.endsWith('.svg')) continue;
|
|
const name = path.parse(file).name;
|
|
const expanded = ![
|
|
'apple-tv',
|
|
'disney-plus',
|
|
'prime-video',
|
|
'das-erste',
|
|
'netflix',
|
|
'youtube',
|
|
'arte',
|
|
'zdf',
|
|
'jellyfin',
|
|
'stremio',
|
|
].includes(name);
|
|
assets.push({
|
|
source: path.join(root, 'assets/logos', file),
|
|
output: path.join(out, 'logos', `${name}.lvec`),
|
|
monochrome: [
|
|
'apple-tv',
|
|
'disney-plus',
|
|
'prime-video',
|
|
'das-erste',
|
|
'hbo-max',
|
|
'paramount-plus',
|
|
'twitch',
|
|
'dazn',
|
|
'rakuten-tv',
|
|
'apple-music',
|
|
'amazon-music',
|
|
'bbc-iplayer',
|
|
'itvx',
|
|
'channel-4',
|
|
'now-tv',
|
|
'joyn',
|
|
'waipu-tv',
|
|
'zattoo',
|
|
'skyshowtime',
|
|
'movistar-plus',
|
|
'nrk-tv',
|
|
'stremio',
|
|
'home-assistant',
|
|
'moonlight',
|
|
].includes(name),
|
|
light_ink: expanded && !['live-tv', 'browser'].includes(name),
|
|
trim: expanded,
|
|
reference_size: 1536,
|
|
});
|
|
}
|
|
|
|
// Standalone SVG sources preserve the shipped Lucide and input artwork.
|
|
// No React renderer, JSX loader or JavaScript icon dependency is needed.
|
|
for (const file of await fs.readdir(path.join(root, 'assets/ui-icons'))) {
|
|
if (!file.endsWith('.svg')) continue;
|
|
const name = path.parse(file).name;
|
|
assets.push({
|
|
source: path.join(root, 'assets/ui-icons', file),
|
|
output: path.join(out, 'ui-icons', `${name}.lvec`),
|
|
reference_size: 256,
|
|
});
|
|
}
|
|
|
|
const manifest = path.join(sources, 'manifest.json');
|
|
await fs.writeFile(manifest, JSON.stringify(assets));
|
|
const result = spawnSync(
|
|
'cargo',
|
|
[
|
|
'run',
|
|
'--locked',
|
|
'--release',
|
|
'--manifest-path',
|
|
path.join(root, 'tools/vector-assets/Cargo.toml'),
|
|
'--',
|
|
manifest,
|
|
],
|
|
{
|
|
cwd: root,
|
|
env: {
|
|
...process.env,
|
|
CARGO_TARGET_DIR: path.join(root, '.build/vector-assets-target'),
|
|
},
|
|
stdio: 'inherit',
|
|
timeout: 120000,
|
|
},
|
|
);
|
|
if (result.error) throw result.error;
|
|
if (result.status !== 0) throw new Error('Native SVG geometry compilation failed.');
|
|
await fs.writeFile(
|
|
path.join(out, 'vector-artwork.json'),
|
|
JSON.stringify({
|
|
version: 1,
|
|
assets: assets.map((asset) => path.relative(out, asset.output)),
|
|
}),
|
|
);
|
|
// These directories contain generated artwork only. Do not package stale raster
|
|
// versions alongside the meshes when reusing an older native asset cache.
|
|
for (const directory of ['logos', 'ui-icons']) {
|
|
for (const file of await fs.readdir(path.join(out, directory))) {
|
|
if (file.endsWith('.png')) await fs.rm(path.join(out, directory, file));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
|
|
await generateVectorAssets();
|
|
}
|