@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.
Files changed (2) hide show
  1. package/dist/index.mjs +92 -2
  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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zpress/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",