@zpress/core 0.1.0 → 0.2.0
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/index.mjs +92 -2
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -819,6 +819,80 @@ function composeBanner(params) {
|
|
|
819
819
|
];
|
|
820
820
|
return sections.filter((s)=>s.length > 0).join('\n');
|
|
821
821
|
}
|
|
822
|
+
const ICON_SIZE = 512;
|
|
823
|
+
const ICON_RADIUS = 32;
|
|
824
|
+
const ICON_PADDING = 64;
|
|
825
|
+
const svg_icon_FIGLET_ROWS = 6;
|
|
826
|
+
const ICON_FALLBACK_FONT_SIZE = 320;
|
|
827
|
+
function buildFigletIcon(params) {
|
|
828
|
+
const artPixelWidth = 7.8 * params.width;
|
|
829
|
+
const artPixelHeight = (svg_icon_FIGLET_ROWS - 1) * 16;
|
|
830
|
+
const usable = ICON_SIZE - 2 * ICON_PADDING;
|
|
831
|
+
const scale = Math.min(usable / artPixelWidth, usable / artPixelHeight);
|
|
832
|
+
const scaledWidth = artPixelWidth * scale;
|
|
833
|
+
const scaledHeight = artPixelHeight * scale;
|
|
834
|
+
const translateX = Math.round((ICON_SIZE - scaledWidth) / 2);
|
|
835
|
+
const translateY = Math.round((ICON_SIZE - scaledHeight) / 2);
|
|
836
|
+
const textLines = params.lines.map((line, i)=>{
|
|
837
|
+
const y = 16 * i;
|
|
838
|
+
return ` <text class="text brand" font-size="13" y="${y}" xml:space="preserve">${escapeXml(line)}</text>`;
|
|
839
|
+
}).join('\n');
|
|
840
|
+
return [
|
|
841
|
+
GENERATED_MARKER,
|
|
842
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${ICON_SIZE} ${ICON_SIZE}">`,
|
|
843
|
+
' <defs>',
|
|
844
|
+
' <style>',
|
|
845
|
+
` .text { font-family: ${FONT_STACK}; }`,
|
|
846
|
+
` .brand { fill: ${COLORS.brand}; }`,
|
|
847
|
+
' </style>',
|
|
848
|
+
' </defs>',
|
|
849
|
+
'',
|
|
850
|
+
' <!-- Background -->',
|
|
851
|
+
` <rect width="${ICON_SIZE}" height="${ICON_SIZE}" rx="${ICON_RADIUS}" ry="${ICON_RADIUS}" fill="${COLORS.base}" />`,
|
|
852
|
+
'',
|
|
853
|
+
' <!-- FIGlet letter -->',
|
|
854
|
+
` <g transform="translate(${translateX}, ${translateY}) scale(${scale.toFixed(4)})">`,
|
|
855
|
+
textLines,
|
|
856
|
+
' </g>',
|
|
857
|
+
'</svg>'
|
|
858
|
+
].join('\n');
|
|
859
|
+
}
|
|
860
|
+
function buildFallbackIcon(params) {
|
|
861
|
+
const centerX = ICON_SIZE / 2;
|
|
862
|
+
const centerY = ICON_SIZE / 2 + 0.35 * ICON_FALLBACK_FONT_SIZE;
|
|
863
|
+
const escaped = escapeXml(params.char);
|
|
864
|
+
return [
|
|
865
|
+
GENERATED_MARKER,
|
|
866
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${ICON_SIZE} ${ICON_SIZE}">`,
|
|
867
|
+
' <defs>',
|
|
868
|
+
' <style>',
|
|
869
|
+
` .text { font-family: ${FONT_STACK}; }`,
|
|
870
|
+
` .brand { fill: ${COLORS.brand}; }`,
|
|
871
|
+
' </style>',
|
|
872
|
+
' </defs>',
|
|
873
|
+
'',
|
|
874
|
+
' <!-- Background -->',
|
|
875
|
+
` <rect width="${ICON_SIZE}" height="${ICON_SIZE}" rx="${ICON_RADIUS}" ry="${ICON_RADIUS}" fill="${COLORS.base}" />`,
|
|
876
|
+
'',
|
|
877
|
+
' <!-- Fallback letter -->',
|
|
878
|
+
` <text class="text brand" font-size="${ICON_FALLBACK_FONT_SIZE}" x="${centerX}" y="${centerY}" text-anchor="middle">${escaped}</text>`,
|
|
879
|
+
'</svg>'
|
|
880
|
+
].join('\n');
|
|
881
|
+
}
|
|
882
|
+
function composeIcon(params) {
|
|
883
|
+
const [firstChar = 'Z'] = [
|
|
884
|
+
...params.title.trimStart()
|
|
885
|
+
];
|
|
886
|
+
const figlet = renderFigletText(firstChar);
|
|
887
|
+
const hasFigletGlyph = figlet.lines.some((line)=>line.trim().length > 0);
|
|
888
|
+
if (hasFigletGlyph) return buildFigletIcon({
|
|
889
|
+
lines: figlet.lines,
|
|
890
|
+
width: figlet.width
|
|
891
|
+
});
|
|
892
|
+
return buildFallbackIcon({
|
|
893
|
+
char: firstChar
|
|
894
|
+
});
|
|
895
|
+
}
|
|
822
896
|
const LOGO_TOP_PAD = 28;
|
|
823
897
|
const LOGO_BOTTOM_PAD = 28;
|
|
824
898
|
const svg_logo_FIGLET_ROWS = 6;
|
|
@@ -921,6 +995,21 @@ function generateLogoSvg(config) {
|
|
|
921
995
|
}
|
|
922
996
|
];
|
|
923
997
|
}
|
|
998
|
+
function generateIconSvg(config) {
|
|
999
|
+
if (0 === config.title.trim().length) return [
|
|
1000
|
+
assetError('empty_title', 'Cannot generate icon: title is empty'),
|
|
1001
|
+
null
|
|
1002
|
+
];
|
|
1003
|
+
return [
|
|
1004
|
+
null,
|
|
1005
|
+
{
|
|
1006
|
+
filename: 'icon.svg',
|
|
1007
|
+
content: composeIcon({
|
|
1008
|
+
title: config.title
|
|
1009
|
+
})
|
|
1010
|
+
}
|
|
1011
|
+
];
|
|
1012
|
+
}
|
|
924
1013
|
async function shouldGenerate(filePath) {
|
|
925
1014
|
const content = await promises.readFile(filePath, 'utf8').catch(()=>null);
|
|
926
1015
|
if (null === content) return true;
|
|
@@ -952,7 +1041,8 @@ async function generateAssets(params) {
|
|
|
952
1041
|
});
|
|
953
1042
|
const generators = [
|
|
954
1043
|
()=>generateBannerSvg(params.config),
|
|
955
|
-
()=>generateLogoSvg(params.config)
|
|
1044
|
+
()=>generateLogoSvg(params.config),
|
|
1045
|
+
()=>generateIconSvg(params.config)
|
|
956
1046
|
];
|
|
957
1047
|
const written = await generators.reduce(async (accPromise, generate)=>{
|
|
958
1048
|
const acc = await accPromise;
|
|
@@ -2817,4 +2907,4 @@ function createPaths(dir) {
|
|
|
2817
2907
|
cacheDir: node_path.resolve(outputRoot, 'cache')
|
|
2818
2908
|
};
|
|
2819
2909
|
}
|
|
2820
|
-
export { configError, config_loadConfig as loadConfig, createPaths, defineConfig, generateAssets, generateBannerSvg, generateLogoSvg, hasGlobChars, loadManifest, resolveEntries, sync, syncError };
|
|
2910
|
+
export { configError, config_loadConfig as loadConfig, createPaths, defineConfig, generateAssets, generateBannerSvg, generateIconSvg, generateLogoSvg, hasGlobChars, loadManifest, resolveEntries, sync, syncError };
|