mirror of
https://github.com/ZSeven-W/openpencil.git
synced 2026-05-31 19:04:29 +07:00
Some checks failed
Rust check (native) / ubuntu-latest / 1.94 (push) Failing after 2s
Rust check (native) / cargo-deny (native) (push) Failing after 1s
Rust check (native) / diagnostics golden drift (push) Failing after 2s
Rust multi-platform build / linux-x86_64 (push) Failing after 1s
Rust multi-platform build / wasm32-unknown-unknown / op-host-web (compile guard) (push) Failing after 2s
Rust multi-platform build / android-aarch64 (cargo check only) (push) Failing after 2s
Rust multi-platform build / android-x86_64 (cargo check only) (push) Failing after 2s
WASM bundle check (kickoff §1.2) / cargo check --target wasm32-unknown-unknown (push) Failing after 2s
WASM bundle check (kickoff §1.2) / cargo-deny --target wasm32-unknown-unknown check bans (push) Failing after 1s
Rust check (native) / macos-latest / 1.94 (push) Has been cancelled
Rust check (native) / windows-latest / 1.94 (push) Has been cancelled
Rust multi-platform build / linux-aarch64 (push) Has been cancelled
Rust multi-platform build / macos-aarch64 (push) Has been cancelled
Rust multi-platform build / windows-x86_64 (push) Has been cancelled
Rust multi-platform build / macos-x86_64 (push) Has been cancelled
Rust multi-platform build / windows-aarch64 (push) Has been cancelled
Rust multi-platform build / ios-aarch64 (cargo check only) (push) Has been cancelled
Rust multi-platform build / ios-aarch64-sim (cargo check only) (push) Has been cancelled
152 lines
4.7 KiB
JavaScript
152 lines
4.7 KiB
JavaScript
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { createRequire } from 'node:module';
|
|
|
|
const require = createRequire(import.meta.url);
|
|
|
|
const SETS = ['lucide', 'feather', 'simple-icons'];
|
|
const OUT = path.resolve('crates/op-editor-ui/assets/iconify-catalog.json');
|
|
|
|
function attr(tag, name) {
|
|
const match = tag.match(new RegExp(`\\b${name}="([^"]*)"`));
|
|
return match ? match[1] : undefined;
|
|
}
|
|
|
|
function num(value, fallback = 0) {
|
|
if (value === undefined || value === '') return fallback;
|
|
const parsed = Number(value);
|
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
}
|
|
|
|
function circlePath(cx, cy, r) {
|
|
return `M${cx - r} ${cy}A${r} ${r} 0 1 0 ${cx + r} ${cy}A${r} ${r} 0 1 0 ${cx - r} ${cy}Z`;
|
|
}
|
|
|
|
function ellipsePath(cx, cy, rx, ry) {
|
|
return `M${cx - rx} ${cy}A${rx} ${ry} 0 1 0 ${cx + rx} ${cy}A${rx} ${ry} 0 1 0 ${cx - rx} ${cy}Z`;
|
|
}
|
|
|
|
function rectPath(x, y, w, h, r) {
|
|
if (r <= 0) return `M${x} ${y}H${x + w}V${y + h}H${x}Z`;
|
|
const rr = Math.min(r, w / 2, h / 2);
|
|
return [
|
|
`M${x + rr} ${y}`,
|
|
`H${x + w - rr}`,
|
|
`A${rr} ${rr} 0 0 1 ${x + w} ${y + rr}`,
|
|
`V${y + h - rr}`,
|
|
`A${rr} ${rr} 0 0 1 ${x + w - rr} ${y + h}`,
|
|
`H${x + rr}`,
|
|
`A${rr} ${rr} 0 0 1 ${x} ${y + h - rr}`,
|
|
`V${y + rr}`,
|
|
`A${rr} ${rr} 0 0 1 ${x + rr} ${y}`,
|
|
'Z',
|
|
].join('');
|
|
}
|
|
|
|
function pointsPath(points, close) {
|
|
const nums = points
|
|
.trim()
|
|
.split(/[\s,]+/)
|
|
.map(Number)
|
|
.filter(Number.isFinite);
|
|
if (nums.length < 4) return null;
|
|
const pairs = [];
|
|
for (let i = 0; i + 1 < nums.length; i += 2) pairs.push([nums[i], nums[i + 1]]);
|
|
return `M${pairs.map((p) => `${p[0]} ${p[1]}`).join('L')}${close ? 'Z' : ''}`;
|
|
}
|
|
|
|
function pathsFromBody(body) {
|
|
const paths = [];
|
|
for (const match of body.matchAll(/<path\b[^>]*?\bd="([^"]+)"[^>]*>/gi)) {
|
|
paths.push(match[1]);
|
|
}
|
|
for (const match of body.matchAll(/<line\b[^>]*>/gi)) {
|
|
const tag = match[0];
|
|
paths.push(
|
|
`M${num(attr(tag, 'x1'))} ${num(attr(tag, 'y1'))}L${num(attr(tag, 'x2'))} ${num(attr(tag, 'y2'))}`,
|
|
);
|
|
}
|
|
for (const match of body.matchAll(/<circle\b[^>]*>/gi)) {
|
|
const tag = match[0];
|
|
paths.push(circlePath(num(attr(tag, 'cx')), num(attr(tag, 'cy')), num(attr(tag, 'r'))));
|
|
}
|
|
for (const match of body.matchAll(/<ellipse\b[^>]*>/gi)) {
|
|
const tag = match[0];
|
|
paths.push(
|
|
ellipsePath(
|
|
num(attr(tag, 'cx')),
|
|
num(attr(tag, 'cy')),
|
|
num(attr(tag, 'rx')),
|
|
num(attr(tag, 'ry')),
|
|
),
|
|
);
|
|
}
|
|
for (const match of body.matchAll(/<rect\b[^>]*>/gi)) {
|
|
const tag = match[0];
|
|
paths.push(
|
|
rectPath(
|
|
num(attr(tag, 'x')),
|
|
num(attr(tag, 'y')),
|
|
num(attr(tag, 'width')),
|
|
num(attr(tag, 'height')),
|
|
num(attr(tag, 'rx'), num(attr(tag, 'ry'))),
|
|
),
|
|
);
|
|
}
|
|
for (const match of body.matchAll(/<polyline\b[^>]*?\bpoints="([^"]+)"[^>]*>/gi)) {
|
|
const d = pointsPath(match[1], false);
|
|
if (d) paths.push(d);
|
|
}
|
|
for (const match of body.matchAll(/<polygon\b[^>]*?\bpoints="([^"]+)"[^>]*>/gi)) {
|
|
const d = pointsPath(match[1], true);
|
|
if (d) paths.push(d);
|
|
}
|
|
for (let i = 1; i < paths.length; i += 1) {
|
|
if (paths[i].startsWith('m')) paths[i] = `M${paths[i].slice(1)}`;
|
|
}
|
|
return paths;
|
|
}
|
|
|
|
function styleFromBody(body) {
|
|
const hasStroke = /\bstroke=|\bstroke-width=|\bstroke-linecap=|\bfill="none"/i.test(body);
|
|
const hasFill = /\bfill="(?!none)[^"]*"/i.test(body);
|
|
return hasStroke && !hasFill ? 'stroke' : 'fill';
|
|
}
|
|
|
|
const icons = [];
|
|
for (const collection of SETS) {
|
|
const data = require(`../node_modules/@iconify-json/${collection}/icons.json`);
|
|
const names = Object.keys(data.icons).sort((a, b) => a.localeCompare(b));
|
|
for (const name of names) {
|
|
const icon = data.icons[name];
|
|
const paths = pathsFromBody(icon.body);
|
|
if (paths.length === 0) continue;
|
|
icons.push({
|
|
collection,
|
|
name,
|
|
width: icon.width ?? data.width ?? 24,
|
|
height: icon.height ?? data.height ?? 24,
|
|
style: styleFromBody(icon.body),
|
|
d: paths.join(' '),
|
|
});
|
|
}
|
|
}
|
|
|
|
for (const [alias, target] of Object.entries({ home: 'house', unlock: 'lock-open' })) {
|
|
if (
|
|
!icons.some((icon) => icon.collection === 'lucide' && icon.name === alias) &&
|
|
icons.some((icon) => icon.collection === 'lucide' && icon.name === target)
|
|
) {
|
|
const source = icons.find((icon) => icon.collection === 'lucide' && icon.name === target);
|
|
icons.push({ ...source, name: alias });
|
|
}
|
|
}
|
|
|
|
icons.sort((a, b) => {
|
|
const setDelta = SETS.indexOf(a.collection) - SETS.indexOf(b.collection);
|
|
return setDelta || a.name.localeCompare(b.name);
|
|
});
|
|
|
|
fs.mkdirSync(path.dirname(OUT), { recursive: true });
|
|
fs.writeFileSync(OUT, `${JSON.stringify({ icons })}\n`);
|
|
console.log(`wrote ${icons.length} icons to ${OUT}`);
|