@olwiba/dx 0.0.17 → 0.0.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ascii-gif.d.ts +5 -0
- package/dist/ascii-gif.js +40 -11
- package/dist/ascii-gif.js.map +1 -1
- package/dist/cli.js +96 -29
- package/dist/generate-assets.d.ts +1 -0
- package/dist/generate-assets.js +56 -18
- package/dist/generate-assets.js.map +1 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
3
|
-
import path, { join } from 'path';
|
|
3
|
+
import path, { join, resolve } from 'path';
|
|
4
4
|
import { createRequire } from 'module';
|
|
5
5
|
import { createInterface } from 'readline/promises';
|
|
6
6
|
import { stdout, stdin } from 'process';
|
|
@@ -1368,6 +1368,23 @@ async function generateAsciiGif(options) {
|
|
|
1368
1368
|
const layout = composeAsciiText(font, options.text);
|
|
1369
1369
|
const bounds = getLayoutBounds(layout);
|
|
1370
1370
|
const accentColumns = getAsciiAccentColumns(font, options.text, options.accent ?? "");
|
|
1371
|
+
let colAccentMap;
|
|
1372
|
+
let accentColorList;
|
|
1373
|
+
let sparklingAccents;
|
|
1374
|
+
if (options.accents && options.accents.length > 0) {
|
|
1375
|
+
colAccentMap = /* @__PURE__ */ new Map();
|
|
1376
|
+
sparklingAccents = /* @__PURE__ */ new Set();
|
|
1377
|
+
for (let i = 0; i < options.accents.length; i++) {
|
|
1378
|
+
const cols = getAsciiAccentColumns(font, options.text, options.accents[i].text);
|
|
1379
|
+
for (const col of cols) colAccentMap.set(col, i);
|
|
1380
|
+
if (options.accents[i].sparkle) sparklingAccents.add(i);
|
|
1381
|
+
}
|
|
1382
|
+
accentColorList = options.accents.map((a) => a.color);
|
|
1383
|
+
} else {
|
|
1384
|
+
colAccentMap = new Map([...accentColumns].map((col) => [col, 0]));
|
|
1385
|
+
accentColorList = [options.accentColor ?? options.color ?? "#38bdf8"];
|
|
1386
|
+
sparklingAccents = /* @__PURE__ */ new Set();
|
|
1387
|
+
}
|
|
1371
1388
|
const width = Math.ceil(bounds.cols * ASCII_CHAR_WIDTH * scale) + padding * 2;
|
|
1372
1389
|
const height = bounds.rows * ASCII_CHAR_HEIGHT * scale + padding * 2;
|
|
1373
1390
|
const frameCount = Math.max(1, Math.round(fps * duration));
|
|
@@ -1410,11 +1427,11 @@ async function generateAsciiGif(options) {
|
|
|
1410
1427
|
writeFileSync(options.outputPath, bytes2);
|
|
1411
1428
|
return;
|
|
1412
1429
|
}
|
|
1413
|
-
const palette =
|
|
1430
|
+
const palette = createMultiAccentPalette({
|
|
1414
1431
|
backgroundColor: options.backgroundColor ?? "#0a0a0a",
|
|
1415
1432
|
blendColor: options.blendColor ?? options.backgroundColor ?? "#0a0a0a",
|
|
1416
1433
|
color: options.color ?? "#e5e5e5",
|
|
1417
|
-
|
|
1434
|
+
accentColors: accentColorList,
|
|
1418
1435
|
levels,
|
|
1419
1436
|
transparent: options.transparent ?? false
|
|
1420
1437
|
});
|
|
@@ -1422,7 +1439,8 @@ async function generateAsciiGif(options) {
|
|
|
1422
1439
|
frames.push(
|
|
1423
1440
|
renderIndexedAsciiFrame({
|
|
1424
1441
|
layout,
|
|
1425
|
-
|
|
1442
|
+
colAccentMap,
|
|
1443
|
+
sparklingAccents,
|
|
1426
1444
|
phase: frame / frameCount,
|
|
1427
1445
|
width,
|
|
1428
1446
|
height,
|
|
@@ -1449,9 +1467,11 @@ function renderIndexedAsciiFrame(options) {
|
|
|
1449
1467
|
const charWidth = Math.round(ASCII_CHAR_WIDTH * options.scale);
|
|
1450
1468
|
const charHeight = Math.round(ASCII_CHAR_HEIGHT * options.scale);
|
|
1451
1469
|
for (const cell of options.layout.cells) {
|
|
1452
|
-
const
|
|
1470
|
+
const accentIdx = options.colAccentMap.get(cell.col);
|
|
1471
|
+
const isGlitter = accentIdx !== void 0 && options.sparklingAccents.has(accentIdx) && cell.ch === "\u2588";
|
|
1472
|
+
const intensity = isGlitter ? getGlitterIntensity(cell, options.phase * Math.PI * 2) : getAsciiCellIntensity(cell, { phase: options.phase });
|
|
1453
1473
|
const level = Math.max(0, Math.min(options.levels - 1, Math.round(intensity * (options.levels - 1))));
|
|
1454
|
-
const offset =
|
|
1474
|
+
const offset = accentIdx !== void 0 ? 1 + (accentIdx + 1) * options.levels : 1;
|
|
1455
1475
|
const colorIndex = offset + level;
|
|
1456
1476
|
const x0 = options.padding + Math.round((cell.col - options.bounds.minCol) * ASCII_CHAR_WIDTH * options.scale);
|
|
1457
1477
|
const y0 = options.padding + (cell.row - options.bounds.minRow) * charHeight;
|
|
@@ -1489,6 +1509,13 @@ function renderIndexedAsciiFrameWithRowColors(options) {
|
|
|
1489
1509
|
}
|
|
1490
1510
|
return pixels;
|
|
1491
1511
|
}
|
|
1512
|
+
function getGlitterIntensity(cell, loop) {
|
|
1513
|
+
const seed = (cell.col * 7.13 + cell.row * 13.37) % (Math.PI * 2);
|
|
1514
|
+
const glitter = (Math.sin(loop * 3.5 + seed) + 1) / 2;
|
|
1515
|
+
const flow = Math.sin(cell.col * 0.08 + loop) * 0.055 + Math.sin(cell.row * 0.22 - loop * 0.75) * 0.04;
|
|
1516
|
+
const sparkBoost = Math.pow(glitter, 3) * 0.42;
|
|
1517
|
+
return Math.max(0.55, Math.min(1, 0.68 + flow + sparkBoost));
|
|
1518
|
+
}
|
|
1492
1519
|
function createRowColorPalette(options) {
|
|
1493
1520
|
const bg = parseColor(options.backgroundColor);
|
|
1494
1521
|
const blend = parseColor(options.blendColor);
|
|
@@ -1529,21 +1556,23 @@ function resolveFont(font) {
|
|
|
1529
1556
|
if (font in asciiFonts) return asciiFonts[font];
|
|
1530
1557
|
return readFileSync(font, "utf-8");
|
|
1531
1558
|
}
|
|
1532
|
-
function
|
|
1559
|
+
function createMultiAccentPalette(options) {
|
|
1533
1560
|
const bg = parseColor(options.backgroundColor);
|
|
1534
1561
|
const blend = parseColor(options.blendColor);
|
|
1535
1562
|
const base = parseColor(options.color);
|
|
1536
|
-
const accent = parseColor(options.accentColor);
|
|
1537
1563
|
const table = [bg.r, bg.g, bg.b];
|
|
1538
1564
|
for (let level = 0; level < options.levels; level++) {
|
|
1539
1565
|
const alpha = level / (options.levels - 1);
|
|
1540
1566
|
const mixed = mix(blend, base, alpha);
|
|
1541
1567
|
table.push(mixed.r, mixed.g, mixed.b);
|
|
1542
1568
|
}
|
|
1543
|
-
for (
|
|
1544
|
-
const
|
|
1545
|
-
|
|
1546
|
-
|
|
1569
|
+
for (const accentHex of options.accentColors) {
|
|
1570
|
+
const accent = parseColor(accentHex);
|
|
1571
|
+
for (let level = 0; level < options.levels; level++) {
|
|
1572
|
+
const alpha = level / (options.levels - 1);
|
|
1573
|
+
const mixed = mix(blend, accent, alpha);
|
|
1574
|
+
table.push(mixed.r, mixed.g, mixed.b);
|
|
1575
|
+
}
|
|
1547
1576
|
}
|
|
1548
1577
|
while (table.length < 256 * 3) table.push(0, 0, 0);
|
|
1549
1578
|
return { table: Uint8Array.from(table.slice(0, 256 * 3)) };
|
|
@@ -1680,13 +1709,16 @@ var generate_assets_exports = {};
|
|
|
1680
1709
|
__export(generate_assets_exports, {
|
|
1681
1710
|
generateAssets: () => generateAssets
|
|
1682
1711
|
});
|
|
1712
|
+
function detectIconMode(icon) {
|
|
1713
|
+
return icon.endsWith(".svg") || icon.includes("/") || icon.includes("\\") ? "svg" : "lucide";
|
|
1714
|
+
}
|
|
1683
1715
|
function toKebabCase(name) {
|
|
1684
1716
|
return name.replace(/([A-Z])/g, (_, ch) => `-${ch.toLowerCase()}`).replace(/^-/, "").toLowerCase();
|
|
1685
1717
|
}
|
|
1686
1718
|
function escapeXml(str) {
|
|
1687
1719
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
1688
1720
|
}
|
|
1689
|
-
function
|
|
1721
|
+
function getLucideInner(iconName) {
|
|
1690
1722
|
const kebab = toKebabCase(iconName);
|
|
1691
1723
|
const req = createRequire(import.meta.url);
|
|
1692
1724
|
let iconPath;
|
|
@@ -1700,7 +1732,15 @@ function getLucideIconInner(iconName) {
|
|
|
1700
1732
|
const svg = readFileSync(iconPath, "utf-8");
|
|
1701
1733
|
return svg.replace(/<svg[^>]*>/, "").replace(/<\/svg>/, "").replace(/\sstroke="currentColor"/g, "").replace(/\sfill="none"/g, "").trim();
|
|
1702
1734
|
}
|
|
1703
|
-
function
|
|
1735
|
+
function getSvgInner(svgPath) {
|
|
1736
|
+
const abs = resolve(process.cwd(), svgPath);
|
|
1737
|
+
const svg = readFileSync(abs, "utf-8");
|
|
1738
|
+
const vbMatch = svg.match(/viewBox="0 0 (\d+\.?\d*) \d/);
|
|
1739
|
+
const viewBox = vbMatch ? parseFloat(vbMatch[1]) : 24;
|
|
1740
|
+
const inner = svg.replace(/<svg[^>]*>/, "").replace(/<\/svg>/, "").trim();
|
|
1741
|
+
return { inner, viewBox };
|
|
1742
|
+
}
|
|
1743
|
+
function buildIconSvgLucide(inner, color, size) {
|
|
1704
1744
|
const pad = Math.round(size * 0.18);
|
|
1705
1745
|
const area = size - pad * 2;
|
|
1706
1746
|
const scale = area / 24;
|
|
@@ -1713,7 +1753,19 @@ function buildIconSvg(inner, color, size) {
|
|
|
1713
1753
|
</g>
|
|
1714
1754
|
</svg>`;
|
|
1715
1755
|
}
|
|
1716
|
-
function
|
|
1756
|
+
function buildIconSvgCustom(inner, viewBox, color, size) {
|
|
1757
|
+
const pad = Math.round(size * 0.08);
|
|
1758
|
+
const area = size - pad * 2;
|
|
1759
|
+
const scale = area / viewBox;
|
|
1760
|
+
const rx = Math.round(size * 0.16);
|
|
1761
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}">
|
|
1762
|
+
<rect width="${size}" height="${size}" rx="${rx}" fill="${color}"/>
|
|
1763
|
+
<g transform="translate(${pad} ${pad}) scale(${scale})">
|
|
1764
|
+
${inner}
|
|
1765
|
+
</g>
|
|
1766
|
+
</svg>`;
|
|
1767
|
+
}
|
|
1768
|
+
function buildOgSvgLucide(inner, name, color) {
|
|
1717
1769
|
const iconPx = 180;
|
|
1718
1770
|
const iconX = Math.round((1200 - iconPx) / 2);
|
|
1719
1771
|
const iconY = 160;
|
|
@@ -1734,6 +1786,18 @@ function buildOgSvg(inner, name, color) {
|
|
|
1734
1786
|
<text x="600" y="470" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="72" font-weight="700" fill="white" text-anchor="middle" letter-spacing="-1">${escapeXml(name)}</text>
|
|
1735
1787
|
</svg>`;
|
|
1736
1788
|
}
|
|
1789
|
+
function buildOgSvgCustom(inner, viewBox, color) {
|
|
1790
|
+
const s = 470;
|
|
1791
|
+
const x = Math.round((1200 - s) / 2);
|
|
1792
|
+
const y = Math.round((630 - s) / 2);
|
|
1793
|
+
const scale = s / viewBox;
|
|
1794
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630">
|
|
1795
|
+
<rect width="1200" height="630" fill="${color}"/>
|
|
1796
|
+
<g transform="translate(${x} ${y}) scale(${scale})">
|
|
1797
|
+
${inner}
|
|
1798
|
+
</g>
|
|
1799
|
+
</svg>`;
|
|
1800
|
+
}
|
|
1737
1801
|
async function generateAssets(config) {
|
|
1738
1802
|
const { name, icon, color, outputDir } = config;
|
|
1739
1803
|
let sharpFn;
|
|
@@ -1747,23 +1811,31 @@ async function generateAssets(config) {
|
|
|
1747
1811
|
mkdirSync(outputDir, { recursive: true });
|
|
1748
1812
|
const faviconDir = join(outputDir, "favicon");
|
|
1749
1813
|
mkdirSync(faviconDir, { recursive: true });
|
|
1750
|
-
const
|
|
1814
|
+
const mode = detectIconMode(icon);
|
|
1751
1815
|
const written = [];
|
|
1816
|
+
let inner;
|
|
1817
|
+
let viewBox = 24;
|
|
1818
|
+
if (mode === "svg") {
|
|
1819
|
+
const parsed = getSvgInner(icon);
|
|
1820
|
+
inner = parsed.inner;
|
|
1821
|
+
viewBox = parsed.viewBox;
|
|
1822
|
+
} else {
|
|
1823
|
+
inner = getLucideInner(icon);
|
|
1824
|
+
}
|
|
1825
|
+
const buildIcon = (size) => mode === "svg" ? buildIconSvgCustom(inner, viewBox, color, size) : buildIconSvgLucide(inner, color, size);
|
|
1752
1826
|
for (const size of FAVICON_SIZES) {
|
|
1753
|
-
const
|
|
1754
|
-
const png = await sharpFn(Buffer.from(svg)).png().toBuffer();
|
|
1827
|
+
const png = await sharpFn(Buffer.from(buildIcon(size))).png().toBuffer();
|
|
1755
1828
|
const dest = join(faviconDir, `favicon-${size}.png`);
|
|
1756
1829
|
writeFileSync(dest, png);
|
|
1757
1830
|
written.push(dest);
|
|
1758
1831
|
}
|
|
1759
1832
|
for (const [filename, size] of NAMED_ICONS) {
|
|
1760
|
-
const
|
|
1761
|
-
const png = await sharpFn(Buffer.from(svg)).png().toBuffer();
|
|
1833
|
+
const png = await sharpFn(Buffer.from(buildIcon(size))).png().toBuffer();
|
|
1762
1834
|
const dest = join(outputDir, filename);
|
|
1763
1835
|
writeFileSync(dest, png);
|
|
1764
1836
|
written.push(dest);
|
|
1765
1837
|
}
|
|
1766
|
-
const ogSvg =
|
|
1838
|
+
const ogSvg = mode === "svg" ? buildOgSvgCustom(inner, viewBox, color) : buildOgSvgLucide(inner, name, color);
|
|
1767
1839
|
const ogPng = await sharpFn(Buffer.from(ogSvg)).png().toBuffer();
|
|
1768
1840
|
const ogDest = join(outputDir, "og-image.png");
|
|
1769
1841
|
writeFileSync(ogDest, ogPng);
|
|
@@ -1772,16 +1844,11 @@ async function generateAssets(config) {
|
|
|
1772
1844
|
name,
|
|
1773
1845
|
short_name: name,
|
|
1774
1846
|
theme_color: color,
|
|
1775
|
-
background_color: "#09090b",
|
|
1847
|
+
background_color: mode === "svg" ? color : "#09090b",
|
|
1776
1848
|
display: "standalone",
|
|
1777
1849
|
icons: [
|
|
1778
1850
|
{ src: "/android-chrome-192x192.png", sizes: "192x192", type: "image/png" },
|
|
1779
|
-
{
|
|
1780
|
-
src: "/android-chrome-512x512.png",
|
|
1781
|
-
sizes: "512x512",
|
|
1782
|
-
type: "image/png",
|
|
1783
|
-
purpose: "maskable"
|
|
1784
|
-
}
|
|
1851
|
+
{ src: "/android-chrome-512x512.png", sizes: "512x512", type: "image/png", purpose: "maskable" }
|
|
1785
1852
|
]
|
|
1786
1853
|
};
|
|
1787
1854
|
const manifestDest = join(outputDir, "manifest.json");
|
|
@@ -1795,7 +1862,7 @@ async function generateAssets(config) {
|
|
|
1795
1862
|
var FAVICON_SIZES, NAMED_ICONS;
|
|
1796
1863
|
var init_generate_assets = __esm({
|
|
1797
1864
|
"src/generate-assets.ts"() {
|
|
1798
|
-
FAVICON_SIZES = [16, 32, 48, 64];
|
|
1865
|
+
FAVICON_SIZES = [16, 32, 48, 64, 192, 512];
|
|
1799
1866
|
NAMED_ICONS = [
|
|
1800
1867
|
["apple-touch-icon.png", 180],
|
|
1801
1868
|
["android-chrome-192x192.png", 192],
|
package/dist/generate-assets.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
2
2
|
import { createRequire } from 'module';
|
|
3
|
-
import { join } from 'path';
|
|
3
|
+
import { join, resolve } from 'path';
|
|
4
4
|
|
|
5
5
|
// src/generate-assets.ts
|
|
6
|
+
function detectIconMode(icon) {
|
|
7
|
+
return icon.endsWith(".svg") || icon.includes("/") || icon.includes("\\") ? "svg" : "lucide";
|
|
8
|
+
}
|
|
6
9
|
function toKebabCase(name) {
|
|
7
10
|
return name.replace(/([A-Z])/g, (_, ch) => `-${ch.toLowerCase()}`).replace(/^-/, "").toLowerCase();
|
|
8
11
|
}
|
|
9
12
|
function escapeXml(str) {
|
|
10
13
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
11
14
|
}
|
|
12
|
-
function
|
|
15
|
+
function getLucideInner(iconName) {
|
|
13
16
|
const kebab = toKebabCase(iconName);
|
|
14
17
|
const req = createRequire(import.meta.url);
|
|
15
18
|
let iconPath;
|
|
@@ -23,7 +26,15 @@ function getLucideIconInner(iconName) {
|
|
|
23
26
|
const svg = readFileSync(iconPath, "utf-8");
|
|
24
27
|
return svg.replace(/<svg[^>]*>/, "").replace(/<\/svg>/, "").replace(/\sstroke="currentColor"/g, "").replace(/\sfill="none"/g, "").trim();
|
|
25
28
|
}
|
|
26
|
-
function
|
|
29
|
+
function getSvgInner(svgPath) {
|
|
30
|
+
const abs = resolve(process.cwd(), svgPath);
|
|
31
|
+
const svg = readFileSync(abs, "utf-8");
|
|
32
|
+
const vbMatch = svg.match(/viewBox="0 0 (\d+\.?\d*) \d/);
|
|
33
|
+
const viewBox = vbMatch ? parseFloat(vbMatch[1]) : 24;
|
|
34
|
+
const inner = svg.replace(/<svg[^>]*>/, "").replace(/<\/svg>/, "").trim();
|
|
35
|
+
return { inner, viewBox };
|
|
36
|
+
}
|
|
37
|
+
function buildIconSvgLucide(inner, color, size) {
|
|
27
38
|
const pad = Math.round(size * 0.18);
|
|
28
39
|
const area = size - pad * 2;
|
|
29
40
|
const scale = area / 24;
|
|
@@ -36,7 +47,19 @@ function buildIconSvg(inner, color, size) {
|
|
|
36
47
|
</g>
|
|
37
48
|
</svg>`;
|
|
38
49
|
}
|
|
39
|
-
function
|
|
50
|
+
function buildIconSvgCustom(inner, viewBox, color, size) {
|
|
51
|
+
const pad = Math.round(size * 0.08);
|
|
52
|
+
const area = size - pad * 2;
|
|
53
|
+
const scale = area / viewBox;
|
|
54
|
+
const rx = Math.round(size * 0.16);
|
|
55
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}">
|
|
56
|
+
<rect width="${size}" height="${size}" rx="${rx}" fill="${color}"/>
|
|
57
|
+
<g transform="translate(${pad} ${pad}) scale(${scale})">
|
|
58
|
+
${inner}
|
|
59
|
+
</g>
|
|
60
|
+
</svg>`;
|
|
61
|
+
}
|
|
62
|
+
function buildOgSvgLucide(inner, name, color) {
|
|
40
63
|
const iconPx = 180;
|
|
41
64
|
const iconX = Math.round((1200 - iconPx) / 2);
|
|
42
65
|
const iconY = 160;
|
|
@@ -57,7 +80,19 @@ function buildOgSvg(inner, name, color) {
|
|
|
57
80
|
<text x="600" y="470" font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif" font-size="72" font-weight="700" fill="white" text-anchor="middle" letter-spacing="-1">${escapeXml(name)}</text>
|
|
58
81
|
</svg>`;
|
|
59
82
|
}
|
|
60
|
-
|
|
83
|
+
function buildOgSvgCustom(inner, viewBox, color) {
|
|
84
|
+
const s = 470;
|
|
85
|
+
const x = Math.round((1200 - s) / 2);
|
|
86
|
+
const y = Math.round((630 - s) / 2);
|
|
87
|
+
const scale = s / viewBox;
|
|
88
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630">
|
|
89
|
+
<rect width="1200" height="630" fill="${color}"/>
|
|
90
|
+
<g transform="translate(${x} ${y}) scale(${scale})">
|
|
91
|
+
${inner}
|
|
92
|
+
</g>
|
|
93
|
+
</svg>`;
|
|
94
|
+
}
|
|
95
|
+
var FAVICON_SIZES = [16, 32, 48, 64, 192, 512];
|
|
61
96
|
var NAMED_ICONS = [
|
|
62
97
|
["apple-touch-icon.png", 180],
|
|
63
98
|
["android-chrome-192x192.png", 192],
|
|
@@ -76,23 +111,31 @@ async function generateAssets(config) {
|
|
|
76
111
|
mkdirSync(outputDir, { recursive: true });
|
|
77
112
|
const faviconDir = join(outputDir, "favicon");
|
|
78
113
|
mkdirSync(faviconDir, { recursive: true });
|
|
79
|
-
const
|
|
114
|
+
const mode = detectIconMode(icon);
|
|
80
115
|
const written = [];
|
|
116
|
+
let inner;
|
|
117
|
+
let viewBox = 24;
|
|
118
|
+
if (mode === "svg") {
|
|
119
|
+
const parsed = getSvgInner(icon);
|
|
120
|
+
inner = parsed.inner;
|
|
121
|
+
viewBox = parsed.viewBox;
|
|
122
|
+
} else {
|
|
123
|
+
inner = getLucideInner(icon);
|
|
124
|
+
}
|
|
125
|
+
const buildIcon = (size) => mode === "svg" ? buildIconSvgCustom(inner, viewBox, color, size) : buildIconSvgLucide(inner, color, size);
|
|
81
126
|
for (const size of FAVICON_SIZES) {
|
|
82
|
-
const
|
|
83
|
-
const png = await sharpFn(Buffer.from(svg)).png().toBuffer();
|
|
127
|
+
const png = await sharpFn(Buffer.from(buildIcon(size))).png().toBuffer();
|
|
84
128
|
const dest = join(faviconDir, `favicon-${size}.png`);
|
|
85
129
|
writeFileSync(dest, png);
|
|
86
130
|
written.push(dest);
|
|
87
131
|
}
|
|
88
132
|
for (const [filename, size] of NAMED_ICONS) {
|
|
89
|
-
const
|
|
90
|
-
const png = await sharpFn(Buffer.from(svg)).png().toBuffer();
|
|
133
|
+
const png = await sharpFn(Buffer.from(buildIcon(size))).png().toBuffer();
|
|
91
134
|
const dest = join(outputDir, filename);
|
|
92
135
|
writeFileSync(dest, png);
|
|
93
136
|
written.push(dest);
|
|
94
137
|
}
|
|
95
|
-
const ogSvg =
|
|
138
|
+
const ogSvg = mode === "svg" ? buildOgSvgCustom(inner, viewBox, color) : buildOgSvgLucide(inner, name, color);
|
|
96
139
|
const ogPng = await sharpFn(Buffer.from(ogSvg)).png().toBuffer();
|
|
97
140
|
const ogDest = join(outputDir, "og-image.png");
|
|
98
141
|
writeFileSync(ogDest, ogPng);
|
|
@@ -101,16 +144,11 @@ async function generateAssets(config) {
|
|
|
101
144
|
name,
|
|
102
145
|
short_name: name,
|
|
103
146
|
theme_color: color,
|
|
104
|
-
background_color: "#09090b",
|
|
147
|
+
background_color: mode === "svg" ? color : "#09090b",
|
|
105
148
|
display: "standalone",
|
|
106
149
|
icons: [
|
|
107
150
|
{ src: "/android-chrome-192x192.png", sizes: "192x192", type: "image/png" },
|
|
108
|
-
{
|
|
109
|
-
src: "/android-chrome-512x512.png",
|
|
110
|
-
sizes: "512x512",
|
|
111
|
-
type: "image/png",
|
|
112
|
-
purpose: "maskable"
|
|
113
|
-
}
|
|
151
|
+
{ src: "/android-chrome-512x512.png", sizes: "512x512", type: "image/png", purpose: "maskable" }
|
|
114
152
|
]
|
|
115
153
|
};
|
|
116
154
|
const manifestDest = join(outputDir, "manifest.json");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generate-assets.ts"],"names":[],"mappings":"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","file":"generate-assets.js","sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { join } from \"node:path\"\n\nexport interface GenerateAssetsConfig {\n name: string\n icon: string\n color: string\n outputDir: string\n}\n\nexport interface GenerateAssetsResult {\n files: string[]\n}\n\nfunction toKebabCase(name: string): string {\n return name\n .replace(/([A-Z])/g, (_, ch: string) => `-${ch.toLowerCase()}`)\n .replace(/^-/, \"\")\n .toLowerCase()\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n}\n\nfunction getLucideIconInner(iconName: string): string {\n const kebab = toKebabCase(iconName)\n const req = createRequire(import.meta.url)\n let iconPath: string\n try {\n iconPath = req.resolve(`lucide-static/icons/${kebab}.svg`)\n } catch {\n throw new Error(\n `Unknown Lucide icon: \"${iconName}\" (tried \"${kebab}.svg\"). ` +\n `Browse icons at https://lucide.dev/icons`,\n )\n }\n const svg = readFileSync(iconPath, \"utf-8\")\n // Strip outer <svg> wrapper; clear inherited stroke/fill so parent <g> controls them\n return svg\n .replace(/<svg[^>]*>/, \"\")\n .replace(/<\\/svg>/, \"\")\n .replace(/\\sstroke=\"currentColor\"/g, \"\")\n .replace(/\\sfill=\"none\"/g, \"\")\n .trim()\n}\n\nfunction buildIconSvg(inner: string, color: string, size: number): string {\n const pad = Math.round(size * 0.18)\n const area = size - pad * 2\n const scale = area / 24\n const rx = Math.round(size * 0.16)\n // Inverse-scale stroke so it renders at ~2px regardless of size\n const sw = (2 / scale).toFixed(4)\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\">\n <rect width=\"${size}\" height=\"${size}\" rx=\"${rx}\" fill=\"${color}\"/>\n <g transform=\"translate(${pad} ${pad}) scale(${scale})\" stroke=\"white\" fill=\"none\" stroke-width=\"${sw}\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n ${inner}\n </g>\n</svg>`\n}\n\nfunction buildOgSvg(inner: string, name: string, color: string): string {\n const iconPx = 180\n const iconX = Math.round((1200 - iconPx) / 2) // 510\n const iconY = 160\n const scale = iconPx / 24\n // ~2.5px strokes at final 180px icon size\n const sw = (2.5 / scale).toFixed(4)\n\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\">\n <defs>\n <radialGradient id=\"rg\" cx=\"50%\" cy=\"40%\" r=\"65%\">\n <stop offset=\"0%\" stop-color=\"${color}\" stop-opacity=\"0.22\"/>\n <stop offset=\"100%\" stop-color=\"#000000\" stop-opacity=\"0\"/>\n </radialGradient>\n </defs>\n <rect width=\"1200\" height=\"630\" fill=\"#09090b\"/>\n <rect width=\"1200\" height=\"630\" fill=\"url(#rg)\"/>\n <g transform=\"translate(${iconX} ${iconY}) scale(${scale})\" stroke=\"${color}\" fill=\"none\" stroke-width=\"${sw}\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n ${inner}\n </g>\n <text x=\"600\" y=\"470\" font-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif\" font-size=\"72\" font-weight=\"700\" fill=\"white\" text-anchor=\"middle\" letter-spacing=\"-1\">${escapeXml(name)}</text>\n</svg>`\n}\n\nconst FAVICON_SIZES = [16, 32, 48, 64] as const\n\nconst NAMED_ICONS: Array<[string, number]> = [\n [\"apple-touch-icon.png\", 180],\n [\"android-chrome-192x192.png\", 192],\n [\"android-chrome-512x512.png\", 512],\n]\n\nexport async function generateAssets(\n config: GenerateAssetsConfig,\n): Promise<GenerateAssetsResult> {\n const { name, icon, color, outputDir } = config\n\n let sharpFn: typeof import(\"sharp\")\n try {\n sharpFn = (await import(\"sharp\")).default\n } catch {\n throw new Error(\n \"sharp is required for asset generation.\\n\" +\n \"Install it in your project: bun add sharp lucide-static\",\n )\n }\n\n mkdirSync(outputDir, { recursive: true })\n\n const faviconDir = join(outputDir, \"favicon\")\n mkdirSync(faviconDir, { recursive: true })\n\n const inner = getLucideIconInner(icon)\n const written: string[] = []\n\n // favicon/favicon-{size}.png\n for (const size of FAVICON_SIZES) {\n const svg = buildIconSvg(inner, color, size)\n const png = await sharpFn(Buffer.from(svg)).png().toBuffer()\n const dest = join(faviconDir, `favicon-${size}.png`)\n writeFileSync(dest, png)\n written.push(dest)\n }\n\n // apple-touch-icon, android-chrome-*\n for (const [filename, size] of NAMED_ICONS) {\n const svg = buildIconSvg(inner, color, size)\n const png = await sharpFn(Buffer.from(svg)).png().toBuffer()\n const dest = join(outputDir, filename)\n writeFileSync(dest, png)\n written.push(dest)\n }\n\n // og-image.png\n const ogSvg = buildOgSvg(inner, name, color)\n const ogPng = await sharpFn(Buffer.from(ogSvg)).png().toBuffer()\n const ogDest = join(outputDir, \"og-image.png\")\n writeFileSync(ogDest, ogPng)\n written.push(ogDest)\n\n // manifest.json\n const manifest = {\n name,\n short_name: name,\n theme_color: color,\n background_color: \"#09090b\",\n display: \"standalone\",\n icons: [\n { src: \"/android-chrome-192x192.png\", sizes: \"192x192\", type: \"image/png\" },\n {\n src: \"/android-chrome-512x512.png\",\n sizes: \"512x512\",\n type: \"image/png\",\n purpose: \"maskable\",\n },\n ],\n }\n const manifestDest = join(outputDir, \"manifest.json\")\n writeFileSync(manifestDest, JSON.stringify(manifest, null, 2) + \"\\n\")\n written.push(manifestDest)\n\n // robots.txt\n const robotsDest = join(outputDir, \"robots.txt\")\n writeFileSync(robotsDest, \"User-agent: *\\nAllow: /\\n\")\n written.push(robotsDest)\n\n return { files: written }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/generate-assets.ts"],"names":[],"mappings":"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","file":"generate-assets.js","sourcesContent":["import { mkdirSync, readFileSync, writeFileSync } from \"node:fs\"\nimport { createRequire } from \"node:module\"\nimport { join, resolve } from \"node:path\"\n\nexport interface GenerateAssetsConfig {\n name: string\n /** Lucide icon name (e.g. \"Zap\") OR path to an SVG file (e.g. \"./logo.svg\") */\n icon: string\n color: string\n outputDir: string\n}\n\nexport interface GenerateAssetsResult {\n files: string[]\n}\n\ntype IconMode = \"lucide\" | \"svg\"\n\nfunction detectIconMode(icon: string): IconMode {\n return icon.endsWith(\".svg\") || icon.includes(\"/\") || icon.includes(\"\\\\\") ? \"svg\" : \"lucide\"\n}\n\nfunction toKebabCase(name: string): string {\n return name\n .replace(/([A-Z])/g, (_, ch: string) => `-${ch.toLowerCase()}`)\n .replace(/^-/, \"\")\n .toLowerCase()\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\")\n}\n\nfunction getLucideInner(iconName: string): string {\n const kebab = toKebabCase(iconName)\n const req = createRequire(import.meta.url)\n let iconPath: string\n try {\n iconPath = req.resolve(`lucide-static/icons/${kebab}.svg`)\n } catch {\n throw new Error(\n `Unknown Lucide icon: \"${iconName}\" (tried \"${kebab}.svg\"). ` +\n `Browse icons at https://lucide.dev/icons`,\n )\n }\n const svg = readFileSync(iconPath, \"utf-8\")\n return svg\n .replace(/<svg[^>]*>/, \"\")\n .replace(/<\\/svg>/, \"\")\n .replace(/\\sstroke=\"currentColor\"/g, \"\")\n .replace(/\\sfill=\"none\"/g, \"\")\n .trim()\n}\n\nfunction getSvgInner(svgPath: string): { inner: string; viewBox: number } {\n const abs = resolve(process.cwd(), svgPath)\n const svg = readFileSync(abs, \"utf-8\")\n const vbMatch = svg.match(/viewBox=\"0 0 (\\d+\\.?\\d*) \\d/)\n const viewBox = vbMatch ? parseFloat(vbMatch[1]) : 24\n const inner = svg\n .replace(/<svg[^>]*>/, \"\")\n .replace(/<\\/svg>/, \"\")\n .trim()\n return { inner, viewBox }\n}\n\nfunction buildIconSvgLucide(inner: string, color: string, size: number): string {\n const pad = Math.round(size * 0.18)\n const area = size - pad * 2\n const scale = area / 24\n const rx = Math.round(size * 0.16)\n const sw = (2 / scale).toFixed(4)\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\">\n <rect width=\"${size}\" height=\"${size}\" rx=\"${rx}\" fill=\"${color}\"/>\n <g transform=\"translate(${pad} ${pad}) scale(${scale})\" stroke=\"white\" fill=\"none\" stroke-width=\"${sw}\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n ${inner}\n </g>\n</svg>`\n}\n\nfunction buildIconSvgCustom(inner: string, viewBox: number, color: string, size: number): string {\n const pad = Math.round(size * 0.08)\n const area = size - pad * 2\n const scale = area / viewBox\n const rx = Math.round(size * 0.16)\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\">\n <rect width=\"${size}\" height=\"${size}\" rx=\"${rx}\" fill=\"${color}\"/>\n <g transform=\"translate(${pad} ${pad}) scale(${scale})\">\n ${inner}\n </g>\n</svg>`\n}\n\nfunction buildOgSvgLucide(inner: string, name: string, color: string): string {\n const iconPx = 180\n const iconX = Math.round((1200 - iconPx) / 2)\n const iconY = 160\n const scale = iconPx / 24\n const sw = (2.5 / scale).toFixed(4)\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\">\n <defs>\n <radialGradient id=\"rg\" cx=\"50%\" cy=\"40%\" r=\"65%\">\n <stop offset=\"0%\" stop-color=\"${color}\" stop-opacity=\"0.22\"/>\n <stop offset=\"100%\" stop-color=\"#000000\" stop-opacity=\"0\"/>\n </radialGradient>\n </defs>\n <rect width=\"1200\" height=\"630\" fill=\"#09090b\"/>\n <rect width=\"1200\" height=\"630\" fill=\"url(#rg)\"/>\n <g transform=\"translate(${iconX} ${iconY}) scale(${scale})\" stroke=\"${color}\" fill=\"none\" stroke-width=\"${sw}\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n ${inner}\n </g>\n <text x=\"600\" y=\"470\" font-family=\"-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif\" font-size=\"72\" font-weight=\"700\" fill=\"white\" text-anchor=\"middle\" letter-spacing=\"-1\">${escapeXml(name)}</text>\n</svg>`\n}\n\nfunction buildOgSvgCustom(inner: string, viewBox: number, color: string): string {\n const s = 470\n const x = Math.round((1200 - s) / 2)\n const y = Math.round((630 - s) / 2)\n const scale = s / viewBox\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\">\n <rect width=\"1200\" height=\"630\" fill=\"${color}\"/>\n <g transform=\"translate(${x} ${y}) scale(${scale})\">\n ${inner}\n </g>\n</svg>`\n}\n\nconst FAVICON_SIZES = [16, 32, 48, 64, 192, 512] as const\n\nconst NAMED_ICONS: Array<[string, number]> = [\n [\"apple-touch-icon.png\", 180],\n [\"android-chrome-192x192.png\", 192],\n [\"android-chrome-512x512.png\", 512],\n]\n\nexport async function generateAssets(\n config: GenerateAssetsConfig,\n): Promise<GenerateAssetsResult> {\n const { name, icon, color, outputDir } = config\n\n let sharpFn: typeof import(\"sharp\")\n try {\n sharpFn = (await import(\"sharp\")).default\n } catch {\n throw new Error(\n \"sharp is required for asset generation.\\n\" +\n \"Install it in your project: bun add sharp lucide-static\",\n )\n }\n\n mkdirSync(outputDir, { recursive: true })\n const faviconDir = join(outputDir, \"favicon\")\n mkdirSync(faviconDir, { recursive: true })\n\n const mode = detectIconMode(icon)\n const written: string[] = []\n\n let inner: string\n let viewBox = 24\n\n if (mode === \"svg\") {\n const parsed = getSvgInner(icon)\n inner = parsed.inner\n viewBox = parsed.viewBox\n } else {\n inner = getLucideInner(icon)\n }\n\n const buildIcon = (size: number) =>\n mode === \"svg\"\n ? buildIconSvgCustom(inner, viewBox, color, size)\n : buildIconSvgLucide(inner, color, size)\n\n for (const size of FAVICON_SIZES) {\n const png = await sharpFn(Buffer.from(buildIcon(size))).png().toBuffer()\n const dest = join(faviconDir, `favicon-${size}.png`)\n writeFileSync(dest, png)\n written.push(dest)\n }\n\n for (const [filename, size] of NAMED_ICONS) {\n const png = await sharpFn(Buffer.from(buildIcon(size))).png().toBuffer()\n const dest = join(outputDir, filename)\n writeFileSync(dest, png)\n written.push(dest)\n }\n\n const ogSvg =\n mode === \"svg\"\n ? buildOgSvgCustom(inner, viewBox, color)\n : buildOgSvgLucide(inner, name, color)\n\n const ogPng = await sharpFn(Buffer.from(ogSvg)).png().toBuffer()\n const ogDest = join(outputDir, \"og-image.png\")\n writeFileSync(ogDest, ogPng)\n written.push(ogDest)\n\n const manifest = {\n name,\n short_name: name,\n theme_color: color,\n background_color: mode === \"svg\" ? color : \"#09090b\",\n display: \"standalone\",\n icons: [\n { src: \"/android-chrome-192x192.png\", sizes: \"192x192\", type: \"image/png\" },\n { src: \"/android-chrome-512x512.png\", sizes: \"512x512\", type: \"image/png\", purpose: \"maskable\" },\n ],\n }\n const manifestDest = join(outputDir, \"manifest.json\")\n writeFileSync(manifestDest, JSON.stringify(manifest, null, 2) + \"\\n\")\n written.push(manifestDest)\n\n const robotsDest = join(outputDir, \"robots.txt\")\n writeFileSync(robotsDest, \"User-agent: *\\nAllow: /\\n\")\n written.push(robotsDest)\n\n return { files: written }\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -19,10 +19,23 @@ type DevPlugin = {
|
|
|
19
19
|
apply: "serve";
|
|
20
20
|
configureServer: (server: unknown) => void;
|
|
21
21
|
};
|
|
22
|
+
declare function hexToAnsi24(hex: string): string | null;
|
|
22
23
|
declare function printBanner(input: string | BannerSegment[] | BannerOptions): Promise<void>;
|
|
23
24
|
declare function createTsupBannerHook(project: string | BannerOptions | LegacyBannerOptions): () => Promise<void>;
|
|
24
25
|
declare function createDevBannerPlugin(project: string | BannerOptions | LegacyBannerOptions): DevPlugin;
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Probe for the first free port from `start` up. Vite's own auto-increment
|
|
29
|
+
* doesn't kick in reliably under the TanStack Start plugin, so dev servers
|
|
30
|
+
* resolve the port themselves before passing it to `server.port`.
|
|
31
|
+
*/
|
|
32
|
+
declare function findFreePort(start: number, attempts?: number): Promise<number>;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the dev server port for a preferred base port, logging when the
|
|
35
|
+
* preferred port is taken and a fallback is used.
|
|
36
|
+
*/
|
|
37
|
+
declare function resolveDevPort(preferred: number, attempts?: number): Promise<number>;
|
|
38
|
+
|
|
26
39
|
type ProjectBrandAccent = {
|
|
27
40
|
hex: string;
|
|
28
41
|
lightOklch?: string;
|
|
@@ -49,4 +62,4 @@ declare function createProjectDevBannerPlugin(project: ProjectConfig): {
|
|
|
49
62
|
};
|
|
50
63
|
declare function createProjectTsupBannerHook(project: ProjectConfig): () => Promise<void>;
|
|
51
64
|
|
|
52
|
-
export { type BannerSegment, type ProjectBannerSegment, type ProjectBrandAccent, type ProjectConfig, createDevBannerPlugin, createProjectDevBannerPlugin, createProjectTsupBannerHook, createTsupBannerHook, printBanner };
|
|
65
|
+
export { type BannerSegment, type ProjectBannerSegment, type ProjectBrandAccent, type ProjectConfig, createDevBannerPlugin, createProjectDevBannerPlugin, createProjectTsupBannerHook, createTsupBannerHook, findFreePort, hexToAnsi24, printBanner, resolveDevPort };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createServer } from 'net';
|
|
2
|
+
|
|
1
3
|
var __defProp = Object.defineProperty;
|
|
2
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
5
|
var __esm = (fn, res) => function __init() {
|
|
@@ -1428,6 +1430,23 @@ function createDevBannerPlugin(project) {
|
|
|
1428
1430
|
}
|
|
1429
1431
|
};
|
|
1430
1432
|
}
|
|
1433
|
+
async function findFreePort(start, attempts = 20) {
|
|
1434
|
+
for (let port = start; port < start + attempts; port++) {
|
|
1435
|
+
const free = await new Promise((resolve) => {
|
|
1436
|
+
const probe = createServer().once("error", () => resolve(false)).once("listening", () => probe.close(() => resolve(true)));
|
|
1437
|
+
probe.listen(port);
|
|
1438
|
+
});
|
|
1439
|
+
if (free) return port;
|
|
1440
|
+
}
|
|
1441
|
+
throw new Error(`No free port found in range ${start}-${start + attempts - 1}`);
|
|
1442
|
+
}
|
|
1443
|
+
async function resolveDevPort(preferred, attempts = 20) {
|
|
1444
|
+
const port = await findFreePort(preferred, attempts);
|
|
1445
|
+
if (port !== preferred) {
|
|
1446
|
+
console.log(`Port ${preferred} is in use \u2014 dev server will use ${port} instead.`);
|
|
1447
|
+
}
|
|
1448
|
+
return port;
|
|
1449
|
+
}
|
|
1431
1450
|
|
|
1432
1451
|
// src/project-brand.ts
|
|
1433
1452
|
function resolveProjectBannerSegments(segments, accentHex) {
|
|
@@ -1449,6 +1468,6 @@ function createProjectTsupBannerHook(project) {
|
|
|
1449
1468
|
return createTsupBannerHook(getProjectBanner(project));
|
|
1450
1469
|
}
|
|
1451
1470
|
|
|
1452
|
-
export { createDevBannerPlugin, createProjectDevBannerPlugin, createProjectTsupBannerHook, createTsupBannerHook, printBanner };
|
|
1471
|
+
export { createDevBannerPlugin, createProjectDevBannerPlugin, createProjectTsupBannerHook, createTsupBannerHook, findFreePort, hexToAnsi24, printBanner, resolveDevPort };
|
|
1453
1472
|
//# sourceMappingURL=index.js.map
|
|
1454
1473
|
//# sourceMappingURL=index.js.map
|