opencrush 0.3.19 → 0.3.21
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 +55 -71
- 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 >
|
|
245630
|
+
const tags = splitRespectingParens(hobbiesRaw).map((t2) => t2.trim()).filter(Boolean).map((t2) => t2.length > 35 ? 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)) {
|
|
@@ -245651,84 +245651,76 @@ function splitRespectingParens(str2) {
|
|
|
245651
245651
|
if (current) result.push(current);
|
|
245652
245652
|
return result;
|
|
245653
245653
|
}
|
|
245654
|
-
function
|
|
245654
|
+
function esc(str2) {
|
|
245655
245655
|
return str2.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
245656
245656
|
}
|
|
245657
|
-
function buildTextOverlay(data,
|
|
245657
|
+
function buildTextOverlay(data, overlayW) {
|
|
245658
245658
|
const S2 = SCALE;
|
|
245659
|
-
const
|
|
245660
|
-
const
|
|
245661
|
-
const availW = panelW - textX - 30 * S2;
|
|
245659
|
+
const px = 70 * S2;
|
|
245660
|
+
const availW = overlayW - px - 60 * S2;
|
|
245662
245661
|
const meta = [data.age, data.location].filter(Boolean).join(" \xB7 ");
|
|
245663
|
-
const
|
|
245664
|
-
const blockH =
|
|
245665
|
-
|
|
245666
|
-
let y2 = startY;
|
|
245662
|
+
const lines = 1 + (meta ? 1 : 0) + (data.job ? 1 : 0) + (data.tags.length > 0 ? 2 : 0) + (data.vibe ? 1 : 0);
|
|
245663
|
+
const blockH = lines * 44 * S2;
|
|
245664
|
+
let y2 = Math.max(70 * S2, Math.round((H2 - blockH) / 2));
|
|
245667
245665
|
const nameY = y2;
|
|
245668
|
-
y2 +=
|
|
245666
|
+
y2 += 72 * S2;
|
|
245669
245667
|
const metaY = y2;
|
|
245670
|
-
if (meta) y2 +=
|
|
245668
|
+
if (meta) y2 += 44 * S2;
|
|
245671
245669
|
const jobY = y2;
|
|
245672
|
-
if (data.job) y2 +=
|
|
245673
|
-
y2 +=
|
|
245670
|
+
if (data.job) y2 += 40 * S2;
|
|
245671
|
+
y2 += 20 * S2;
|
|
245674
245672
|
const tagStartY = y2;
|
|
245675
245673
|
let inlineTags = "";
|
|
245676
|
-
let offsetX =
|
|
245674
|
+
let offsetX = px;
|
|
245677
245675
|
let rowY = tagStartY;
|
|
245678
|
-
const rowH =
|
|
245676
|
+
const rowH = 38 * S2;
|
|
245679
245677
|
let currentRow = 1;
|
|
245680
245678
|
const maxRows = 2;
|
|
245681
|
-
const charW =
|
|
245682
|
-
const padW =
|
|
245679
|
+
const charW = 8 * S2;
|
|
245680
|
+
const padW = 24 * S2;
|
|
245683
245681
|
for (const tag of data.tags) {
|
|
245684
245682
|
const maxChars = Math.floor((availW - padW) / charW);
|
|
245685
245683
|
const label = tag.length > maxChars ? tag.slice(0, maxChars - 1) + "\u2026" : tag;
|
|
245686
245684
|
const w2 = Math.ceil(label.length * charW + padW);
|
|
245687
|
-
if (offsetX + w2 >
|
|
245685
|
+
if (offsetX + w2 > px + availW) {
|
|
245688
245686
|
if (currentRow >= maxRows) break;
|
|
245689
245687
|
currentRow++;
|
|
245690
|
-
offsetX =
|
|
245688
|
+
offsetX = px;
|
|
245691
245689
|
rowY += rowH;
|
|
245692
245690
|
}
|
|
245693
245691
|
inlineTags += `
|
|
245694
|
-
<rect x="${offsetX}" y="${rowY -
|
|
245695
|
-
<text x="${offsetX +
|
|
245692
|
+
<rect x="${offsetX}" y="${rowY - 18 * S2}" width="${w2}" height="${32 * S2}" rx="${16 * S2}" fill="rgba(255,255,255,0.10)" />
|
|
245693
|
+
<text x="${offsetX + 12 * S2}" y="${rowY + 4 * S2}" font-family="system-ui, -apple-system, sans-serif" font-size="${16 * S2}" fill="rgba(255,255,255,0.8)">${esc(label)}</text>
|
|
245696
245694
|
`;
|
|
245697
|
-
offsetX += w2 +
|
|
245695
|
+
offsetX += w2 + 10 * S2;
|
|
245698
245696
|
}
|
|
245699
|
-
const vibeY = tagStartY + currentRow * rowH +
|
|
245700
|
-
const jobMax = Math.floor(availW / (
|
|
245701
|
-
const vibeMax = Math.floor(availW / (
|
|
245697
|
+
const vibeY = tagStartY + currentRow * rowH + 32 * S2;
|
|
245698
|
+
const jobMax = Math.floor(availW / (9 * S2));
|
|
245699
|
+
const vibeMax = Math.floor(availW / (8.5 * S2));
|
|
245702
245700
|
const jobText = data.job.length > jobMax ? data.job.slice(0, jobMax - 1) + "\u2026" : data.job;
|
|
245703
245701
|
const vibeText = data.vibe.length > vibeMax ? data.vibe.slice(0, vibeMax - 1) + "\u2026" : data.vibe;
|
|
245704
|
-
return `<svg width="${
|
|
245705
|
-
<!--
|
|
245706
|
-
<
|
|
245707
|
-
<linearGradient id="panel" x1="0" y1="0" x2="1" y2="0">
|
|
245708
|
-
<stop offset="0%" stop-color="rgba(${bgR},${bgG},${bgB},0.92)" />
|
|
245709
|
-
<stop offset="100%" stop-color="rgba(${bgR},${bgG},${bgB},0.80)" />
|
|
245710
|
-
</linearGradient>
|
|
245711
|
-
</defs>
|
|
245712
|
-
<rect width="${panelW}" height="${H2}" fill="url(#panel)" />
|
|
245702
|
+
return `<svg width="${overlayW}" height="${H2}" xmlns="http://www.w3.org/2000/svg">
|
|
245703
|
+
<!-- Semi-transparent dark overlay for readability -->
|
|
245704
|
+
<rect width="${overlayW}" height="${H2}" fill="rgba(0,0,0,0.55)" />
|
|
245713
245705
|
|
|
245714
245706
|
<!-- Name -->
|
|
245715
|
-
<text x="${
|
|
245707
|
+
<text x="${px}" y="${nameY}" font-family="system-ui, -apple-system, sans-serif" font-size="${60 * S2}" font-weight="bold" fill="white">${esc(data.name)}</text>
|
|
245716
245708
|
|
|
245717
245709
|
<!-- Meta -->
|
|
245718
|
-
${meta ? `<text x="${
|
|
245710
|
+
${meta ? `<text x="${px}" y="${metaY}" font-family="system-ui, -apple-system, sans-serif" font-size="${22 * S2}" fill="rgba(255,255,255,0.65)">${esc(meta)}</text>` : ""}
|
|
245719
245711
|
|
|
245720
245712
|
<!-- Job -->
|
|
245721
|
-
${data.job ? `<text x="${
|
|
245713
|
+
${data.job ? `<text x="${px}" y="${jobY}" font-family="system-ui, -apple-system, sans-serif" font-size="${18 * S2}" fill="rgba(255,255,255,0.45)">${esc(jobText)}</text>` : ""}
|
|
245722
245714
|
|
|
245723
245715
|
<!-- Tags -->
|
|
245724
245716
|
${inlineTags}
|
|
245725
245717
|
|
|
245726
245718
|
<!-- Vibe -->
|
|
245727
|
-
${data.vibe ? `<text x="${
|
|
245719
|
+
${data.vibe ? `<text x="${px}" y="${vibeY}" font-family="system-ui, -apple-system, sans-serif" font-size="${18 * S2}" fill="rgba(255,255,255,0.50)" font-style="italic">${esc(vibeText)}</text>` : ""}
|
|
245728
245720
|
|
|
245729
|
-
<!--
|
|
245730
|
-
<text x="${
|
|
245731
|
-
<text x="${
|
|
245721
|
+
<!-- Branding -->
|
|
245722
|
+
<text x="${px}" y="${H2 - 44 * S2}" font-family="system-ui, -apple-system, sans-serif" font-size="${18 * S2}" font-weight="bold" fill="#ff69b4">Opencrush</text>
|
|
245723
|
+
<text x="${overlayW - 60 * S2}" y="${H2 - 44 * S2}" font-family="system-ui, -apple-system, sans-serif" font-size="${13 * S2}" fill="rgba(255,255,255,0.2)" text-anchor="end">github.com/heloraai/Opencrush</text>
|
|
245732
245724
|
</svg>`;
|
|
245733
245725
|
}
|
|
245734
245726
|
async function generateCard(characterName) {
|
|
@@ -245748,20 +245740,13 @@ async function generateCard(characterName) {
|
|
|
245748
245740
|
break;
|
|
245749
245741
|
}
|
|
245750
245742
|
}
|
|
245751
|
-
if (!refImagePath)
|
|
245752
|
-
|
|
245753
|
-
|
|
245754
|
-
const
|
|
245755
|
-
const
|
|
245756
|
-
const bgR = Math.round(cr2 * 0.18), bgG = Math.round(cg * 0.18), bgB = Math.round(cb * 0.18);
|
|
245757
|
-
const bgSolid = await sharp({
|
|
245758
|
-
create: { width: W2, height: H2, channels: 3, background: { r: bgR, g: bgG, b: bgB } }
|
|
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();
|
|
245743
|
+
if (!refImagePath) return generateFallbackCard(charDir, characterName, data);
|
|
245744
|
+
const portraitW = Math.round(W2 * 0.55);
|
|
245745
|
+
const overlayW = Math.round(W2 * 0.5);
|
|
245746
|
+
const bgBlur = await sharp(refImagePath).resize(W2, H2, { fit: "cover", position: "center" }).blur(50).modulate({ brightness: 0.3, saturation: 0.5 }).png().toBuffer();
|
|
245747
|
+
const portrait = await sharp(refImagePath).resize(portraitW, H2, { fit: "cover", position: "top" }).png().toBuffer();
|
|
245763
245748
|
const fadeMask = Buffer.from(
|
|
245764
|
-
`<svg width="${portraitW}" height="${
|
|
245749
|
+
`<svg width="${portraitW}" height="${H2}">
|
|
245765
245750
|
<defs>
|
|
245766
245751
|
<linearGradient id="fade" x1="0" y1="0" x2="1" y2="0">
|
|
245767
245752
|
<stop offset="0%" stop-color="white" />
|
|
@@ -245769,39 +245754,38 @@ async function generateCard(characterName) {
|
|
|
245769
245754
|
<stop offset="100%" stop-color="black" />
|
|
245770
245755
|
</linearGradient>
|
|
245771
245756
|
</defs>
|
|
245772
|
-
<rect width="${portraitW}" height="${
|
|
245757
|
+
<rect width="${portraitW}" height="${H2}" fill="url(#fade)" />
|
|
245773
245758
|
</svg>`
|
|
245774
245759
|
);
|
|
245775
245760
|
const maskedPortrait = await sharp(portrait).composite([{ input: fadeMask, blend: "dest-in" }]).png().toBuffer();
|
|
245776
|
-
const
|
|
245777
|
-
const
|
|
245778
|
-
const
|
|
245779
|
-
const card = sharp(bgSolid).composite([
|
|
245761
|
+
const textSvg = buildTextOverlay(data, overlayW);
|
|
245762
|
+
const textOverlay = await sharp(Buffer.from(textSvg)).resize(overlayW, H2).png().toBuffer();
|
|
245763
|
+
const card = sharp(bgBlur).composite([
|
|
245780
245764
|
{ input: maskedPortrait, left: 0, top: 0 },
|
|
245781
|
-
{ input:
|
|
245765
|
+
{ input: textOverlay, left: W2 - overlayW, top: 0 }
|
|
245782
245766
|
]);
|
|
245783
245767
|
const outputPath = (0, import_path12.join)(charDir, "card.png");
|
|
245784
245768
|
await card.png({ quality: 95 }).toFile(outputPath);
|
|
245785
245769
|
console.log(source_default.green(`
|
|
245786
245770
|
Card generated: characters/${characterName}/card.png`));
|
|
245787
|
-
console.log(source_default.gray(` ${W2}x${H2} PNG
|
|
245771
|
+
console.log(source_default.gray(` ${W2}x${H2} PNG (16:9)
|
|
245788
245772
|
`));
|
|
245789
245773
|
return outputPath;
|
|
245790
245774
|
}
|
|
245791
245775
|
async function generateFallbackCard(charDir, characterName, data) {
|
|
245792
245776
|
const sharp = (await import("sharp")).default;
|
|
245793
245777
|
const S2 = SCALE;
|
|
245794
|
-
const
|
|
245795
|
-
<defs><linearGradient id="bg" x1="0" y1="0" x2="
|
|
245796
|
-
<stop offset="0%" stop-color="#1a1a2e" /><stop offset="100%" stop-color="#
|
|
245778
|
+
const svg = `<svg width="${W2}" height="${H2}" xmlns="http://www.w3.org/2000/svg">
|
|
245779
|
+
<defs><linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
245780
|
+
<stop offset="0%" stop-color="#1a1a2e" /><stop offset="100%" stop-color="#0f0f1a" />
|
|
245797
245781
|
</linearGradient></defs>
|
|
245798
245782
|
<rect width="${W2}" height="${H2}" fill="url(#bg)" />
|
|
245799
|
-
<text x="${W2 / 2}" y="${H2 / 2 - 20 * S2}" font-family="system-ui" font-size="${
|
|
245800
|
-
<text x="${W2 / 2}" y="${H2 / 2 +
|
|
245801
|
-
<text x="${
|
|
245783
|
+
<text x="${W2 / 2}" y="${H2 / 2 - 20 * S2}" font-family="system-ui" font-size="${60 * S2}" font-weight="bold" fill="white" text-anchor="middle">${esc(data.name)}</text>
|
|
245784
|
+
<text x="${W2 / 2}" y="${H2 / 2 + 50 * S2}" font-family="system-ui" font-size="${20 * S2}" fill="#999" text-anchor="middle">${esc([data.age, data.location, data.job].filter(Boolean).join(" \xB7 "))}</text>
|
|
245785
|
+
<text x="${40 * S2}" y="${H2 - 30 * S2}" font-family="system-ui" font-size="${18 * S2}" font-weight="bold" fill="#ff69b4">Opencrush</text>
|
|
245802
245786
|
</svg>`;
|
|
245803
245787
|
const outputPath = (0, import_path12.join)(charDir, "card.png");
|
|
245804
|
-
await sharp(Buffer.from(
|
|
245788
|
+
await sharp(Buffer.from(svg)).resize(W2, H2).png().toFile(outputPath);
|
|
245805
245789
|
console.log(source_default.green(`
|
|
245806
245790
|
Card generated: characters/${characterName}/card.png`));
|
|
245807
245791
|
console.log(source_default.gray(` ${W2}x${H2} PNG (no reference image)
|
|
@@ -245816,8 +245800,8 @@ var init_card = __esm({
|
|
|
245816
245800
|
import_gray_matter3 = __toESM(require_gray_matter());
|
|
245817
245801
|
init_source();
|
|
245818
245802
|
SCALE = 2;
|
|
245819
|
-
W2 =
|
|
245820
|
-
H2 =
|
|
245803
|
+
W2 = 1920 * SCALE;
|
|
245804
|
+
H2 = 1080 * SCALE;
|
|
245821
245805
|
}
|
|
245822
245806
|
});
|
|
245823
245807
|
|