opencrush 0.3.20 → 0.3.22
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 -55
- 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,68 +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
|
|
245657
|
+
function buildTextOverlay(data, overlayW) {
|
|
245658
245658
|
const S2 = SCALE;
|
|
245659
|
-
const
|
|
245660
|
-
const availW =
|
|
245661
|
-
let y2 = 50 * S2;
|
|
245662
|
-
const nameY = y2;
|
|
245663
|
-
y2 += 56 * S2;
|
|
245659
|
+
const px = 50 * S2;
|
|
245660
|
+
const availW = overlayW - px - 40 * S2;
|
|
245664
245661
|
const meta = [data.age, data.location].filter(Boolean).join(" \xB7 ");
|
|
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));
|
|
245665
|
+
const nameY = y2;
|
|
245666
|
+
y2 += 72 * S2;
|
|
245665
245667
|
const metaY = y2;
|
|
245666
|
-
if (meta) y2 +=
|
|
245668
|
+
if (meta) y2 += 44 * S2;
|
|
245667
245669
|
const jobY = y2;
|
|
245668
|
-
if (data.job) y2 +=
|
|
245669
|
-
y2 +=
|
|
245670
|
+
if (data.job) y2 += 40 * S2;
|
|
245671
|
+
y2 += 20 * S2;
|
|
245670
245672
|
const tagStartY = y2;
|
|
245671
245673
|
let inlineTags = "";
|
|
245672
|
-
let offsetX =
|
|
245674
|
+
let offsetX = px;
|
|
245673
245675
|
let rowY = tagStartY;
|
|
245674
|
-
const rowH =
|
|
245676
|
+
const rowH = 38 * S2;
|
|
245675
245677
|
let currentRow = 1;
|
|
245676
245678
|
const maxRows = 2;
|
|
245677
|
-
const charW =
|
|
245678
|
-
const padW =
|
|
245679
|
+
const charW = 8 * S2;
|
|
245680
|
+
const padW = 24 * S2;
|
|
245679
245681
|
for (const tag of data.tags) {
|
|
245680
245682
|
const maxChars = Math.floor((availW - padW) / charW);
|
|
245681
245683
|
const label = tag.length > maxChars ? tag.slice(0, maxChars - 1) + "\u2026" : tag;
|
|
245682
245684
|
const w2 = Math.ceil(label.length * charW + padW);
|
|
245683
|
-
if (offsetX + w2 >
|
|
245685
|
+
if (offsetX + w2 > px + availW) {
|
|
245684
245686
|
if (currentRow >= maxRows) break;
|
|
245685
245687
|
currentRow++;
|
|
245686
|
-
offsetX =
|
|
245688
|
+
offsetX = px;
|
|
245687
245689
|
rowY += rowH;
|
|
245688
245690
|
}
|
|
245689
245691
|
inlineTags += `
|
|
245690
|
-
<rect x="${offsetX}" y="${rowY -
|
|
245691
|
-
<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>
|
|
245692
245694
|
`;
|
|
245693
|
-
offsetX += w2 +
|
|
245695
|
+
offsetX += w2 + 10 * S2;
|
|
245694
245696
|
}
|
|
245695
|
-
const vibeY = tagStartY + currentRow * rowH +
|
|
245696
|
-
const jobMax = Math.floor(availW / (
|
|
245697
|
-
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));
|
|
245698
245700
|
const jobText = data.job.length > jobMax ? data.job.slice(0, jobMax - 1) + "\u2026" : data.job;
|
|
245699
245701
|
const vibeText = data.vibe.length > vibeMax ? data.vibe.slice(0, vibeMax - 1) + "\u2026" : data.vibe;
|
|
245700
|
-
return `<svg width="${
|
|
245701
|
-
|
|
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)" />
|
|
245702
245705
|
|
|
245703
|
-
|
|
245706
|
+
<!-- Name -->
|
|
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>
|
|
245704
245708
|
|
|
245705
|
-
|
|
245709
|
+
<!-- Meta -->
|
|
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>` : ""}
|
|
245706
245711
|
|
|
245707
|
-
|
|
245712
|
+
<!-- Job -->
|
|
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>` : ""}
|
|
245708
245714
|
|
|
245715
|
+
<!-- Tags -->
|
|
245709
245716
|
${inlineTags}
|
|
245710
245717
|
|
|
245711
|
-
|
|
245718
|
+
<!-- Vibe -->
|
|
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>` : ""}
|
|
245712
245720
|
|
|
245713
245721
|
<!-- Branding -->
|
|
245714
|
-
<text x="${
|
|
245715
|
-
<text x="${
|
|
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>
|
|
245716
245724
|
</svg>`;
|
|
245717
245725
|
}
|
|
245718
245726
|
async function generateCard(characterName) {
|
|
@@ -245733,39 +245741,34 @@ async function generateCard(characterName) {
|
|
|
245733
245741
|
}
|
|
245734
245742
|
}
|
|
245735
245743
|
if (!refImagePath) return generateFallbackCard(charDir, characterName, data);
|
|
245736
|
-
const
|
|
245737
|
-
const
|
|
245738
|
-
const
|
|
245739
|
-
const
|
|
245740
|
-
const panelH = H2 - portraitH;
|
|
245741
|
-
const portrait = await sharp(refImagePath).resize(W2, portraitH, { fit: "cover", position: "top" }).png().toBuffer();
|
|
245744
|
+
const portraitW = Math.round(H2 * 4 / 5);
|
|
245745
|
+
const overlayW = W2 - portraitW + Math.round(40 * SCALE);
|
|
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();
|
|
245742
245748
|
const fadeMask = Buffer.from(
|
|
245743
|
-
`<svg width="${
|
|
245749
|
+
`<svg width="${portraitW}" height="${H2}">
|
|
245744
245750
|
<defs>
|
|
245745
|
-
<linearGradient id="fade" x1="0" y1="0" x2="
|
|
245751
|
+
<linearGradient id="fade" x1="0" y1="0" x2="1" y2="0">
|
|
245746
245752
|
<stop offset="0%" stop-color="white" />
|
|
245747
|
-
<stop offset="
|
|
245753
|
+
<stop offset="65%" stop-color="white" />
|
|
245748
245754
|
<stop offset="100%" stop-color="black" />
|
|
245749
245755
|
</linearGradient>
|
|
245750
245756
|
</defs>
|
|
245751
|
-
<rect width="${
|
|
245757
|
+
<rect width="${portraitW}" height="${H2}" fill="url(#fade)" />
|
|
245752
245758
|
</svg>`
|
|
245753
245759
|
);
|
|
245754
245760
|
const maskedPortrait = await sharp(portrait).composite([{ input: fadeMask, blend: "dest-in" }]).png().toBuffer();
|
|
245755
|
-
const textSvg =
|
|
245756
|
-
const
|
|
245757
|
-
const
|
|
245758
|
-
create: { width: W2, height: H2, channels: 3, background: { r: bgR, g: bgG, b: bgB } }
|
|
245759
|
-
}).png().toBuffer();
|
|
245760
|
-
const card = sharp(bg).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([
|
|
245761
245764
|
{ input: maskedPortrait, left: 0, top: 0 },
|
|
245762
|
-
{ input:
|
|
245765
|
+
{ input: textOverlay, left: W2 - overlayW, top: 0 }
|
|
245763
245766
|
]);
|
|
245764
245767
|
const outputPath = (0, import_path12.join)(charDir, "card.png");
|
|
245765
245768
|
await card.png({ quality: 95 }).toFile(outputPath);
|
|
245766
245769
|
console.log(source_default.green(`
|
|
245767
245770
|
Card generated: characters/${characterName}/card.png`));
|
|
245768
|
-
console.log(source_default.gray(` ${W2}x${H2} PNG (9
|
|
245771
|
+
console.log(source_default.gray(` ${W2}x${H2} PNG (16:9)
|
|
245769
245772
|
`));
|
|
245770
245773
|
return outputPath;
|
|
245771
245774
|
}
|
|
@@ -245773,13 +245776,13 @@ async function generateFallbackCard(charDir, characterName, data) {
|
|
|
245773
245776
|
const sharp = (await import("sharp")).default;
|
|
245774
245777
|
const S2 = SCALE;
|
|
245775
245778
|
const svg = `<svg width="${W2}" height="${H2}" xmlns="http://www.w3.org/2000/svg">
|
|
245776
|
-
<defs><linearGradient id="bg" x1="0" y1="0" x2="
|
|
245779
|
+
<defs><linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
|
245777
245780
|
<stop offset="0%" stop-color="#1a1a2e" /><stop offset="100%" stop-color="#0f0f1a" />
|
|
245778
245781
|
</linearGradient></defs>
|
|
245779
245782
|
<rect width="${W2}" height="${H2}" fill="url(#bg)" />
|
|
245780
|
-
<text x="${W2 / 2}" y="${H2 / 2 - 20 * S2}" font-family="system-ui" font-size="${
|
|
245781
|
-
<text x="${W2 / 2}" y="${H2 / 2 +
|
|
245782
|
-
<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>
|
|
245783
245786
|
</svg>`;
|
|
245784
245787
|
const outputPath = (0, import_path12.join)(charDir, "card.png");
|
|
245785
245788
|
await sharp(Buffer.from(svg)).resize(W2, H2).png().toFile(outputPath);
|
|
@@ -245797,8 +245800,8 @@ var init_card = __esm({
|
|
|
245797
245800
|
import_gray_matter3 = __toESM(require_gray_matter());
|
|
245798
245801
|
init_source();
|
|
245799
245802
|
SCALE = 2;
|
|
245800
|
-
W2 =
|
|
245801
|
-
H2 =
|
|
245803
|
+
W2 = 1920 * SCALE;
|
|
245804
|
+
H2 = 1080 * SCALE;
|
|
245802
245805
|
}
|
|
245803
245806
|
});
|
|
245804
245807
|
|