opencrush 0.3.19 → 0.3.20
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.js +58 -77
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -245627,7 +245627,7 @@ function parseIdentity(identityPath) {
|
|
|
245627
245627
|
const job = jobRaw.split(/[—–\.\+]/).map((s2) => s2.trim()).filter(Boolean)[0] ?? "";
|
|
245628
245628
|
const hobbiesMatch = content.match(/\*\*Hobbies:\*\*\s*(.+)/i);
|
|
245629
245629
|
const hobbiesRaw = (hobbiesMatch == null ? void 0 : hobbiesMatch[1]) ?? "";
|
|
245630
|
-
const tags = splitRespectingParens(hobbiesRaw).map((t2) => t2.trim()).filter(Boolean).map((t2) => t2.length > 30 ? t2.split(/[,(]/)[0].trim() : t2).filter((t2) => t2.length > 1).slice(0,
|
|
245630
|
+
const tags = splitRespectingParens(hobbiesRaw).map((t2) => t2.trim()).filter(Boolean).map((t2) => t2.length > 30 ? t2.split(/[,(]/)[0].trim() : t2).filter((t2) => t2.length > 1).slice(0, 6);
|
|
245631
245631
|
let vibe = "";
|
|
245632
245632
|
const soulPath = identityPath.replace("IDENTITY.md", "SOUL.md");
|
|
245633
245633
|
if ((0, import_fs18.existsSync)(soulPath)) {
|
|
@@ -245654,81 +245654,65 @@ function splitRespectingParens(str2) {
|
|
|
245654
245654
|
function escapeXml(str2) {
|
|
245655
245655
|
return str2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
245656
245656
|
}
|
|
245657
|
-
function
|
|
245657
|
+
function buildTextPanel(data, panelH, bgR, bgG, bgB) {
|
|
245658
245658
|
const S2 = SCALE;
|
|
245659
|
-
const
|
|
245660
|
-
const
|
|
245661
|
-
|
|
245662
|
-
const meta = [data.age, data.location].filter(Boolean).join(" \xB7 ");
|
|
245663
|
-
const lineCount = 1 + (meta ? 1 : 0) + (data.job ? 1 : 0) + 1 + (data.tags.length > 0 ? 1 : 0) + (data.vibe ? 1 : 0);
|
|
245664
|
-
const blockH = lineCount * 32 * S2;
|
|
245665
|
-
const startY = Math.max(50 * S2, Math.round((H2 - blockH) / 2) - 20 * S2);
|
|
245666
|
-
let y2 = startY;
|
|
245659
|
+
const padX = 48 * S2;
|
|
245660
|
+
const availW = W2 - padX * 2;
|
|
245661
|
+
let y2 = 50 * S2;
|
|
245667
245662
|
const nameY = y2;
|
|
245668
|
-
y2 +=
|
|
245663
|
+
y2 += 56 * S2;
|
|
245664
|
+
const meta = [data.age, data.location].filter(Boolean).join(" \xB7 ");
|
|
245669
245665
|
const metaY = y2;
|
|
245670
|
-
if (meta) y2 +=
|
|
245666
|
+
if (meta) y2 += 36 * S2;
|
|
245671
245667
|
const jobY = y2;
|
|
245672
|
-
if (data.job) y2 +=
|
|
245673
|
-
y2 +=
|
|
245668
|
+
if (data.job) y2 += 34 * S2;
|
|
245669
|
+
y2 += 16 * S2;
|
|
245674
245670
|
const tagStartY = y2;
|
|
245675
245671
|
let inlineTags = "";
|
|
245676
|
-
let offsetX =
|
|
245672
|
+
let offsetX = padX;
|
|
245677
245673
|
let rowY = tagStartY;
|
|
245678
|
-
const rowH =
|
|
245674
|
+
const rowH = 32 * S2;
|
|
245679
245675
|
let currentRow = 1;
|
|
245680
245676
|
const maxRows = 2;
|
|
245681
|
-
const charW = 6.
|
|
245682
|
-
const padW =
|
|
245677
|
+
const charW = 6.5 * S2;
|
|
245678
|
+
const padW = 18 * S2;
|
|
245683
245679
|
for (const tag of data.tags) {
|
|
245684
245680
|
const maxChars = Math.floor((availW - padW) / charW);
|
|
245685
245681
|
const label = tag.length > maxChars ? tag.slice(0, maxChars - 1) + "\u2026" : tag;
|
|
245686
245682
|
const w2 = Math.ceil(label.length * charW + padW);
|
|
245687
|
-
if (offsetX + w2 >
|
|
245683
|
+
if (offsetX + w2 > padX + availW) {
|
|
245688
245684
|
if (currentRow >= maxRows) break;
|
|
245689
245685
|
currentRow++;
|
|
245690
|
-
offsetX =
|
|
245686
|
+
offsetX = padX;
|
|
245691
245687
|
rowY += rowH;
|
|
245692
245688
|
}
|
|
245693
245689
|
inlineTags += `
|
|
245694
|
-
<rect x="${offsetX}" y="${rowY -
|
|
245695
|
-
<text x="${offsetX +
|
|
245690
|
+
<rect x="${offsetX}" y="${rowY - 14 * S2}" width="${w2}" height="${26 * S2}" rx="${13 * S2}" fill="rgba(255,255,255,0.10)" />
|
|
245691
|
+
<text x="${offsetX + 9 * S2}" y="${rowY + 3 * S2}" font-family="system-ui, -apple-system, sans-serif" font-size="${12 * S2}" fill="#ccc">${escapeXml(label)}</text>
|
|
245696
245692
|
`;
|
|
245697
|
-
offsetX += w2 +
|
|
245693
|
+
offsetX += w2 + 7 * S2;
|
|
245698
245694
|
}
|
|
245699
|
-
const vibeY = tagStartY + currentRow * rowH +
|
|
245700
|
-
const jobMax = Math.floor(availW / (7 * S2));
|
|
245701
|
-
const vibeMax = Math.floor(availW / (
|
|
245695
|
+
const vibeY = tagStartY + currentRow * rowH + 24 * S2;
|
|
245696
|
+
const jobMax = Math.floor(availW / (7.5 * S2));
|
|
245697
|
+
const vibeMax = Math.floor(availW / (7 * S2));
|
|
245702
245698
|
const jobText = data.job.length > jobMax ? data.job.slice(0, jobMax - 1) + "\u2026" : data.job;
|
|
245703
245699
|
const vibeText = data.vibe.length > vibeMax ? data.vibe.slice(0, vibeMax - 1) + "\u2026" : data.vibe;
|
|
245704
|
-
return `<svg width="${
|
|
245705
|
-
|
|
245706
|
-
|
|
245707
|
-
|
|
245708
|
-
|
|
245709
|
-
|
|
245710
|
-
|
|
245711
|
-
</
|
|
245712
|
-
|
|
245713
|
-
|
|
245714
|
-
<!-- Name -->
|
|
245715
|
-
<text x="${textX}" y="${nameY}" font-family="system-ui, -apple-system, sans-serif" font-size="${42 * S2}" font-weight="bold" fill="white">${escapeXml(data.name)}</text>
|
|
245716
|
-
|
|
245717
|
-
<!-- Meta -->
|
|
245718
|
-
${meta ? `<text x="${textX}" y="${metaY}" font-family="system-ui, -apple-system, sans-serif" font-size="${16 * S2}" fill="rgba(255,255,255,0.7)">${escapeXml(meta)}</text>` : ""}
|
|
245719
|
-
|
|
245720
|
-
<!-- Job -->
|
|
245721
|
-
${data.job ? `<text x="${textX}" y="${jobY}" font-family="system-ui, -apple-system, sans-serif" font-size="${13 * S2}" fill="rgba(255,255,255,0.5)">${escapeXml(jobText)}</text>` : ""}
|
|
245722
|
-
|
|
245723
|
-
<!-- Tags -->
|
|
245700
|
+
return `<svg width="${W2}" height="${panelH}" xmlns="http://www.w3.org/2000/svg">
|
|
245701
|
+
<rect width="${W2}" height="${panelH}" fill="rgb(${bgR},${bgG},${bgB})" />
|
|
245702
|
+
|
|
245703
|
+
<text x="${padX}" y="${nameY}" font-family="system-ui, -apple-system, sans-serif" font-size="${48 * S2}" font-weight="bold" fill="white">${escapeXml(data.name)}</text>
|
|
245704
|
+
|
|
245705
|
+
${meta ? `<text x="${padX}" y="${metaY}" font-family="system-ui, -apple-system, sans-serif" font-size="${17 * S2}" fill="rgba(255,255,255,0.65)">${escapeXml(meta)}</text>` : ""}
|
|
245706
|
+
|
|
245707
|
+
${data.job ? `<text x="${padX}" y="${jobY}" font-family="system-ui, -apple-system, sans-serif" font-size="${14 * S2}" fill="rgba(255,255,255,0.45)">${escapeXml(jobText)}</text>` : ""}
|
|
245708
|
+
|
|
245724
245709
|
${inlineTags}
|
|
245725
245710
|
|
|
245726
|
-
|
|
245727
|
-
${data.vibe ? `<text x="${textX}" y="${vibeY}" font-family="system-ui, -apple-system, sans-serif" font-size="${13 * S2}" fill="rgba(255,255,255,0.55)" font-style="italic">${escapeXml(vibeText)}</text>` : ""}
|
|
245711
|
+
${data.vibe ? `<text x="${padX}" y="${vibeY}" font-family="system-ui, -apple-system, sans-serif" font-size="${14 * S2}" fill="rgba(255,255,255,0.50)" font-style="italic">${escapeXml(vibeText)}</text>` : ""}
|
|
245728
245712
|
|
|
245729
|
-
<!--
|
|
245730
|
-
<text x="${
|
|
245731
|
-
<text x="${
|
|
245713
|
+
<!-- Branding -->
|
|
245714
|
+
<text x="${padX}" y="${panelH - 30 * S2}" font-family="system-ui, -apple-system, sans-serif" font-size="${15 * S2}" font-weight="bold" fill="#ff69b4">Opencrush</text>
|
|
245715
|
+
<text x="${W2 - padX}" y="${panelH - 30 * S2}" font-family="system-ui, -apple-system, sans-serif" font-size="${11 * S2}" fill="rgba(255,255,255,0.25)" text-anchor="end">github.com/heloraai/Opencrush</text>
|
|
245732
245716
|
</svg>`;
|
|
245733
245717
|
}
|
|
245734
245718
|
async function generateCard(characterName) {
|
|
@@ -245748,52 +245732,49 @@ async function generateCard(characterName) {
|
|
|
245748
245732
|
break;
|
|
245749
245733
|
}
|
|
245750
245734
|
}
|
|
245751
|
-
if (!refImagePath)
|
|
245752
|
-
return generateFallbackCard(charDir, characterName, data);
|
|
245753
|
-
}
|
|
245735
|
+
if (!refImagePath) return generateFallbackCard(charDir, characterName, data);
|
|
245754
245736
|
const { data: colorData } = await sharp(refImagePath).resize(1, 1, { fit: "cover" }).raw().toBuffer({ resolveWithObject: true });
|
|
245755
245737
|
const [cr2, cg, cb] = [colorData[0], colorData[1], colorData[2]];
|
|
245756
|
-
const bgR = Math.round(cr2 * 0.
|
|
245757
|
-
const
|
|
245758
|
-
|
|
245759
|
-
}).png().toBuffer();
|
|
245760
|
-
const portraitH = H2;
|
|
245761
|
-
const portraitW = Math.round(portraitH * 4 / 5);
|
|
245762
|
-
const portrait = await sharp(refImagePath).resize(portraitW, portraitH, { fit: "cover", position: "top" }).png().toBuffer();
|
|
245738
|
+
const bgR = Math.round(cr2 * 0.15), bgG = Math.round(cg * 0.15), bgB = Math.round(cb * 0.15);
|
|
245739
|
+
const portraitH = Math.round(W2 * 5 / 4);
|
|
245740
|
+
const panelH = H2 - portraitH;
|
|
245741
|
+
const portrait = await sharp(refImagePath).resize(W2, portraitH, { fit: "cover", position: "top" }).png().toBuffer();
|
|
245763
245742
|
const fadeMask = Buffer.from(
|
|
245764
|
-
`<svg width="${
|
|
245743
|
+
`<svg width="${W2}" height="${portraitH}">
|
|
245765
245744
|
<defs>
|
|
245766
|
-
<linearGradient id="fade" x1="0" y1="0" x2="
|
|
245745
|
+
<linearGradient id="fade" x1="0" y1="0" x2="0" y2="1">
|
|
245767
245746
|
<stop offset="0%" stop-color="white" />
|
|
245768
|
-
<stop offset="
|
|
245747
|
+
<stop offset="80%" stop-color="white" />
|
|
245769
245748
|
<stop offset="100%" stop-color="black" />
|
|
245770
245749
|
</linearGradient>
|
|
245771
245750
|
</defs>
|
|
245772
|
-
<rect width="${
|
|
245751
|
+
<rect width="${W2}" height="${portraitH}" fill="url(#fade)" />
|
|
245773
245752
|
</svg>`
|
|
245774
245753
|
);
|
|
245775
245754
|
const maskedPortrait = await sharp(portrait).composite([{ input: fadeMask, blend: "dest-in" }]).png().toBuffer();
|
|
245776
|
-
const
|
|
245777
|
-
const
|
|
245778
|
-
const
|
|
245779
|
-
|
|
245755
|
+
const textSvg = buildTextPanel(data, panelH, bgR, bgG, bgB);
|
|
245756
|
+
const textPanel = await sharp(Buffer.from(textSvg)).resize(W2, panelH).png().toBuffer();
|
|
245757
|
+
const bg = await sharp({
|
|
245758
|
+
create: { width: W2, height: H2, channels: 3, background: { r: bgR, g: bgG, b: bgB } }
|
|
245759
|
+
}).png().toBuffer();
|
|
245760
|
+
const card = sharp(bg).composite([
|
|
245780
245761
|
{ input: maskedPortrait, left: 0, top: 0 },
|
|
245781
|
-
{ input: textPanel, left:
|
|
245762
|
+
{ input: textPanel, left: 0, top: portraitH }
|
|
245782
245763
|
]);
|
|
245783
245764
|
const outputPath = (0, import_path12.join)(charDir, "card.png");
|
|
245784
245765
|
await card.png({ quality: 95 }).toFile(outputPath);
|
|
245785
245766
|
console.log(source_default.green(`
|
|
245786
245767
|
Card generated: characters/${characterName}/card.png`));
|
|
245787
|
-
console.log(source_default.gray(` ${W2}x${H2} PNG
|
|
245768
|
+
console.log(source_default.gray(` ${W2}x${H2} PNG (9:16)
|
|
245788
245769
|
`));
|
|
245789
245770
|
return outputPath;
|
|
245790
245771
|
}
|
|
245791
245772
|
async function generateFallbackCard(charDir, characterName, data) {
|
|
245792
245773
|
const sharp = (await import("sharp")).default;
|
|
245793
245774
|
const S2 = SCALE;
|
|
245794
|
-
const
|
|
245795
|
-
<defs><linearGradient id="bg" x1="0" y1="0" x2="0
|
|
245796
|
-
<stop offset="0%" stop-color="#1a1a2e" /><stop offset="100%" stop-color="#
|
|
245775
|
+
const svg = `<svg width="${W2}" height="${H2}" xmlns="http://www.w3.org/2000/svg">
|
|
245776
|
+
<defs><linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
|
|
245777
|
+
<stop offset="0%" stop-color="#1a1a2e" /><stop offset="100%" stop-color="#0f0f1a" />
|
|
245797
245778
|
</linearGradient></defs>
|
|
245798
245779
|
<rect width="${W2}" height="${H2}" fill="url(#bg)" />
|
|
245799
245780
|
<text x="${W2 / 2}" y="${H2 / 2 - 20 * S2}" font-family="system-ui" font-size="${48 * S2}" font-weight="bold" fill="white" text-anchor="middle">${escapeXml(data.name)}</text>
|
|
@@ -245801,7 +245782,7 @@ async function generateFallbackCard(charDir, characterName, data) {
|
|
|
245801
245782
|
<text x="${36 * S2}" y="${H2 - 20 * S2}" font-family="system-ui" font-size="${14 * S2}" font-weight="bold" fill="#ff69b4">Opencrush</text>
|
|
245802
245783
|
</svg>`;
|
|
245803
245784
|
const outputPath = (0, import_path12.join)(charDir, "card.png");
|
|
245804
|
-
await sharp(Buffer.from(
|
|
245785
|
+
await sharp(Buffer.from(svg)).resize(W2, H2).png().toFile(outputPath);
|
|
245805
245786
|
console.log(source_default.green(`
|
|
245806
245787
|
Card generated: characters/${characterName}/card.png`));
|
|
245807
245788
|
console.log(source_default.gray(` ${W2}x${H2} PNG (no reference image)
|
|
@@ -245816,8 +245797,8 @@ var init_card = __esm({
|
|
|
245816
245797
|
import_gray_matter3 = __toESM(require_gray_matter());
|
|
245817
245798
|
init_source();
|
|
245818
245799
|
SCALE = 2;
|
|
245819
|
-
W2 =
|
|
245820
|
-
H2 =
|
|
245800
|
+
W2 = 1080 * SCALE;
|
|
245801
|
+
H2 = 1920 * SCALE;
|
|
245821
245802
|
}
|
|
245822
245803
|
});
|
|
245823
245804
|
|