@gobing-ai/knowledge-kit 0.0.28 → 0.0.30
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/.claude-plugin/marketplace.json +1 -1
- package/dist/index.js +114 -38
- package/package.json +1 -1
- package/plugins/generations/daily-article-gen/dist/index.js +45 -5
- package/plugins/generations/daily-article-gen/src/index.ts +59 -4
- package/plugins/generations/dailynews-gen/dist/index.js +46 -9
- package/plugins/generations/dailynews-gen/src/index.ts +36 -1
- package/plugins/generations/dailynews-gen/src/script-builder.ts +15 -12
- package/plugins/generations/dailynews-gen/src/script-plan.ts +21 -3
- package/plugins/generations/episode-plan-gen/dist/index.js +55 -4
- package/plugins/generations/episode-plan-gen/src/index.ts +80 -2
- package/plugins/generations/image-gen/assets/fonts/NotoSansSC-Bold.otf +0 -0
- package/plugins/generations/image-gen/assets/fonts/NotoSansSC-Regular.otf +0 -0
- package/plugins/generations/image-gen/assets/fonts/OFL.txt +92 -0
- package/plugins/generations/image-gen/assets/fonts/SOURCE.md +15 -0
- package/plugins/generations/image-gen/dist/index.js +700 -22
- package/plugins/generations/image-gen/package.json +1 -0
- package/plugins/generations/image-gen/presets/formats/daily-news-cover.yaml +2 -2
- package/plugins/generations/image-gen/presets/formats/xhs-card.yaml +19 -11
- package/plugins/generations/image-gen/presets/styles/xhs-knowledge.yaml +2 -1
- package/plugins/generations/image-gen/scripts/compose-e2e-artifact.ts +135 -0
- package/plugins/generations/image-gen/src/bytes.ts +116 -0
- package/plugins/generations/image-gen/src/compose/card.ts +294 -0
- package/plugins/generations/image-gen/src/compose/chromium.ts +147 -0
- package/plugins/generations/image-gen/src/compose/cmap.ts +177 -0
- package/plugins/generations/image-gen/src/index.ts +211 -13
- package/plugins/generations/image-gen/src/job.ts +12 -0
- package/plugins/generations/image-gen/src/presets.ts +53 -13
- package/plugins/generations/image-gen/src/providers/minimax.ts +12 -4
- package/plugins/generations/image-gen/src/providers/types.ts +12 -1
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/daily-voice-style.zh.md +27 -14
- package/plugins/kk/scripts/itc-stages.ts +21 -3
- package/plugins/kk/scripts/kk-workflow-stages.ts +461 -10
- package/plugins/kk/scripts/topic-boundary.json +96 -0
- package/plugins/kk/scripts/validate-voicescript.ts +15 -0
- package/plugins/kk/skills/content-judge/SKILL.md +30 -0
- package/plugins/kk/skills/content-judge/references/workflow-integration.md +9 -2
- package/plugins/kk/skills/content-safety/SKILL.md +163 -0
- package/plugins/kk/skills/content-safety/references/platforms/apple-podcasts.md +36 -0
- package/plugins/kk/skills/content-safety/references/platforms/netease-music.md +31 -0
- package/plugins/kk/skills/content-safety/references/platforms/wechatmp.md +32 -0
- package/plugins/kk/skills/content-safety/references/platforms/xiaoyuzhou.md +35 -0
- package/plugins/kk/skills/publish/SKILL.md +1 -1
- package/plugins/kk/skills/xhs-images/SKILL.md +11 -7
- package/plugins/kk/skills/xhs-images/references/card-layouts.md +4 -4
- package/plugins/kk/workflows/judge-gated-publish-example.yaml +18 -10
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +139 -24
- package/plugins/kk/workflows/kk-itc.yaml +21 -8
- package/plugins/publishings/podcast-pub/dist/index.js +4 -0
- package/plugins/publishings/podcast-pub/src/index.ts +2 -0
- package/plugins/publishings/podcast-pub/src/show-notes.ts +6 -0
- package/plugins/publishings/xhs-pub/dist/index.js +1 -1
- package/plugins/publishings/xhs-pub/src/automation-guard.ts +2 -2
- package/plugins/publishings/xhs-pub/src/index.ts +1 -1
- package/plugins/publishings/xhs-pub/src/profile-check.ts +2 -2
- package/plugins/sp/lib/env.ts +0 -46
- package/plugins/sp/lib/inline-run.generated.mjs +0 -1489
- package/plugins/sp/scripts/batch-preflight.mjs +0 -346
- package/plugins/sp/scripts/batch-preflight.ts +0 -459
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +0 -615
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +0 -846
- package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +0 -223
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +0 -367
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +0 -132
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +0 -169
- package/plugins/sp/scripts/feature-dev-precheck.mjs +0 -171
- package/plugins/sp/scripts/feature-dev-precheck.ts +0 -238
- package/plugins/sp/scripts/feature-sync-bounded.mjs +0 -285
- package/plugins/sp/scripts/feature-sync-bounded.ts +0 -478
- package/plugins/sp/scripts/feature-verification-steps.ts +0 -275
- package/plugins/sp/scripts/history-anatomy-cache.mjs +0 -902
- package/plugins/sp/scripts/history-anatomy-cache.ts +0 -1028
- package/plugins/sp/scripts/idea-handoff.mjs +0 -22
- package/plugins/sp/scripts/idea-handoff.ts +0 -44
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +0 -185
- package/plugins/sp/scripts/inline-run-setup.ts +0 -722
- package/plugins/sp/scripts/pr-reviewing.mjs +0 -769
- package/plugins/sp/scripts/pr-reviewing.ts +0 -925
- package/plugins/sp/scripts/quality-gate.mjs +0 -179
- package/plugins/sp/scripts/quality-gate.ts +0 -217
- package/plugins/sp/scripts/script-contract-check.ts +0 -319
- package/plugins/sp/scripts/stage-registry-adapter.ts +0 -1533
- package/plugins/sp/scripts/surface-drift-inventory.ts +0 -929
- package/plugins/sp/scripts/task-evidence-precheck.ts +0 -181
- package/plugins/sp/scripts/task-size-precheck.ts +0 -175
- package/plugins/sp/scripts/transition-shim-check.ts +0 -238
- package/plugins/sp/scripts/validate-commands.ts +0 -689
- package/plugins/sp/scripts/validate-flag-contracts.ts +0 -878
- package/plugins/sp/scripts/verify-answer-lint.ts +0 -530
- package/plugins/sp/scripts/workflow-step-profile.mjs +0 -316
- package/plugins/sp/scripts/workflow-step-profile.ts +0 -456
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +0 -190
- package/plugins/sp/scripts/wrapup-drift-probe.ts +0 -258
- package/plugins/sp/scripts/wrapup-steps.mjs +0 -444
- package/plugins/sp/scripts/wrapup-steps.ts +0 -552
|
@@ -21809,8 +21809,410 @@ function findProjectRoot(startDir) {
|
|
|
21809
21809
|
}
|
|
21810
21810
|
var init_file_system_node = () => {};
|
|
21811
21811
|
|
|
21812
|
+
// ../../plugins/generations/image-gen/src/bytes.ts
|
|
21813
|
+
import { readFile, writeFile } from "fs/promises";
|
|
21814
|
+
async function writeBytes(path2, bytes) {
|
|
21815
|
+
await writeFile(path2, bytes);
|
|
21816
|
+
}
|
|
21817
|
+
async function readBytes(path2) {
|
|
21818
|
+
return new Uint8Array(await readFile(path2));
|
|
21819
|
+
}
|
|
21820
|
+
function imageDimensions(bytes) {
|
|
21821
|
+
if (isPng(bytes))
|
|
21822
|
+
return pngDimensions(bytes);
|
|
21823
|
+
if (isJpeg(bytes))
|
|
21824
|
+
return jpegDimensions(bytes);
|
|
21825
|
+
if (isWebp(bytes))
|
|
21826
|
+
return webpDimensions(bytes);
|
|
21827
|
+
return null;
|
|
21828
|
+
}
|
|
21829
|
+
function isPng(b) {
|
|
21830
|
+
const PNG_SIG = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
21831
|
+
if (b.byteLength < 24)
|
|
21832
|
+
return false;
|
|
21833
|
+
for (let i = 0;i < 8; i++) {
|
|
21834
|
+
if (b[i] !== PNG_SIG[i])
|
|
21835
|
+
return false;
|
|
21836
|
+
}
|
|
21837
|
+
return true;
|
|
21838
|
+
}
|
|
21839
|
+
function pngDimensions(b) {
|
|
21840
|
+
const view = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
21841
|
+
const ihdrLength = view.getUint32(8, false);
|
|
21842
|
+
if (ihdrLength !== 13 || view.getUint32(12, false) !== 1229472850) {
|
|
21843
|
+
return null;
|
|
21844
|
+
}
|
|
21845
|
+
return { width: view.getUint32(16, false), height: view.getUint32(20, false) };
|
|
21846
|
+
}
|
|
21847
|
+
function isJpeg(b) {
|
|
21848
|
+
const view = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
21849
|
+
return b.byteLength >= 4 && view.getUint16(0, false) === 65496 && view.getUint8(2) === 255;
|
|
21850
|
+
}
|
|
21851
|
+
function jpegDimensions(b) {
|
|
21852
|
+
const view = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
21853
|
+
let at = 2;
|
|
21854
|
+
while (at + 4 <= b.byteLength) {
|
|
21855
|
+
if (view.getUint8(at) !== 255)
|
|
21856
|
+
return null;
|
|
21857
|
+
const marker = view.getUint8(at + 1);
|
|
21858
|
+
at += 2;
|
|
21859
|
+
if (marker === 1 || marker >= 208 && marker <= 215)
|
|
21860
|
+
continue;
|
|
21861
|
+
if (at + 2 > b.byteLength)
|
|
21862
|
+
return null;
|
|
21863
|
+
const length = view.getUint16(at, false);
|
|
21864
|
+
if (length < 2)
|
|
21865
|
+
return null;
|
|
21866
|
+
const isSof = marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204;
|
|
21867
|
+
if (isSof) {
|
|
21868
|
+
if (at + 7 > b.byteLength)
|
|
21869
|
+
return null;
|
|
21870
|
+
return { height: view.getUint16(at + 3, false), width: view.getUint16(at + 5, false) };
|
|
21871
|
+
}
|
|
21872
|
+
at += length;
|
|
21873
|
+
}
|
|
21874
|
+
return null;
|
|
21875
|
+
}
|
|
21876
|
+
function isWebp(b) {
|
|
21877
|
+
if (b.byteLength < 20)
|
|
21878
|
+
return false;
|
|
21879
|
+
const view = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
21880
|
+
return view.getUint32(0, true) === 1179011410 && view.getUint32(8, true) === 1346520407;
|
|
21881
|
+
}
|
|
21882
|
+
function webpDimensions(b) {
|
|
21883
|
+
const view = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
21884
|
+
let at = 12;
|
|
21885
|
+
while (at + 8 <= b.byteLength) {
|
|
21886
|
+
const id = String.fromCharCode(view.getUint8(at), view.getUint8(at + 1), view.getUint8(at + 2), view.getUint8(at + 3));
|
|
21887
|
+
const size = view.getUint32(at + 4, true);
|
|
21888
|
+
const body = at + 8;
|
|
21889
|
+
if (id === "VP8 ") {
|
|
21890
|
+
if (size < 10 || body + 10 > b.byteLength)
|
|
21891
|
+
return null;
|
|
21892
|
+
if (view.getUint8(body + 3) !== 157 || view.getUint8(body + 4) !== 1 || view.getUint8(body + 5) !== 42) {
|
|
21893
|
+
return null;
|
|
21894
|
+
}
|
|
21895
|
+
return {
|
|
21896
|
+
width: view.getUint16(body + 6, true) & 16383,
|
|
21897
|
+
height: view.getUint16(body + 8, true) & 16383
|
|
21898
|
+
};
|
|
21899
|
+
}
|
|
21900
|
+
if (id === "VP8L") {
|
|
21901
|
+
if (size < 5 || body + 5 > b.byteLength || view.getUint8(body) !== 47)
|
|
21902
|
+
return null;
|
|
21903
|
+
const bits = view.getUint32(body + 1, true);
|
|
21904
|
+
return { width: (bits & 16383) + 1, height: (bits >>> 14 & 16383) + 1 };
|
|
21905
|
+
}
|
|
21906
|
+
if (id === "VP8X") {
|
|
21907
|
+
if (size < 10 || body + 10 > b.byteLength)
|
|
21908
|
+
return null;
|
|
21909
|
+
const width = view.getUint8(body + 4) | view.getUint8(body + 5) << 8 | view.getUint8(body + 6) << 16;
|
|
21910
|
+
const height = view.getUint8(body + 7) | view.getUint8(body + 8) << 8 | view.getUint8(body + 9) << 16;
|
|
21911
|
+
return { width: width + 1, height: height + 1 };
|
|
21912
|
+
}
|
|
21913
|
+
at = body + size + size % 2;
|
|
21914
|
+
}
|
|
21915
|
+
return null;
|
|
21916
|
+
}
|
|
21917
|
+
var init_bytes = () => {};
|
|
21918
|
+
|
|
21919
|
+
// ../../plugins/generations/image-gen/src/compose/card.ts
|
|
21920
|
+
function assertCoverage(input, fonts) {
|
|
21921
|
+
const headlineCover = fonts.get(input.compose.fonts.headline);
|
|
21922
|
+
const bodyCover = fonts.get(input.compose.fonts.body);
|
|
21923
|
+
if (headlineCover === undefined) {
|
|
21924
|
+
throw new Error(`compose: headline font '${input.compose.fonts.headline}' not loaded`);
|
|
21925
|
+
}
|
|
21926
|
+
if (bodyCover === undefined) {
|
|
21927
|
+
throw new Error(`compose: body font '${input.compose.fonts.body}' not loaded`);
|
|
21928
|
+
}
|
|
21929
|
+
const checkField = (text, fieldName, covers) => {
|
|
21930
|
+
for (const ch of text) {
|
|
21931
|
+
const cp = ch.codePointAt(0);
|
|
21932
|
+
if (cp === undefined)
|
|
21933
|
+
continue;
|
|
21934
|
+
if (cp <= 32 || cp === 160 || cp >= 8192 && cp <= 8207 || cp === 12288) {
|
|
21935
|
+
continue;
|
|
21936
|
+
}
|
|
21937
|
+
if (!covers(cp)) {
|
|
21938
|
+
const hex3 = `U+${cp.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
21939
|
+
const fontName = fieldName.startsWith("points") ? input.compose.fonts.body : input.compose.fonts.headline;
|
|
21940
|
+
throw new Error(`compose: ${fieldName} has ${hex3} '${ch}' not covered by ${fontName}`);
|
|
21941
|
+
}
|
|
21942
|
+
}
|
|
21943
|
+
};
|
|
21944
|
+
checkField(input.headline, "headline", headlineCover);
|
|
21945
|
+
for (const [i, point] of input.points.entries()) {
|
|
21946
|
+
checkField(point, `points[${i}]`, bodyCover);
|
|
21947
|
+
}
|
|
21948
|
+
}
|
|
21949
|
+
function assertTextFreePrompt(prompt, copy, sources) {
|
|
21950
|
+
const hit = (text) => {
|
|
21951
|
+
const headline = copy.headline.trim();
|
|
21952
|
+
if (headline.length >= 2 && text.includes(headline))
|
|
21953
|
+
return `the headline '${headline}'`;
|
|
21954
|
+
for (const point of copy.points) {
|
|
21955
|
+
const trimmed = point.trim();
|
|
21956
|
+
if (trimmed.length >= 2 && text.includes(trimmed))
|
|
21957
|
+
return `point '${trimmed}'`;
|
|
21958
|
+
}
|
|
21959
|
+
const han = /\p{Script=Han}/u.exec(text);
|
|
21960
|
+
if (han !== null) {
|
|
21961
|
+
const cp = (han[0]?.codePointAt(0) ?? 0).toString(16).toUpperCase().padStart(4, "0");
|
|
21962
|
+
return `Han character '${han[0]}' (U+${cp})`;
|
|
21963
|
+
}
|
|
21964
|
+
return null;
|
|
21965
|
+
};
|
|
21966
|
+
const fail = (source, what2) => {
|
|
21967
|
+
throw new Error(`compose: the illustration prompt must stay text-free \u2014 ${source} contributes ${what2} (card copy is typeset, never prompted)`);
|
|
21968
|
+
};
|
|
21969
|
+
for (const { source, text } of sources) {
|
|
21970
|
+
const what2 = hit(text);
|
|
21971
|
+
if (what2 !== null)
|
|
21972
|
+
fail(source, what2);
|
|
21973
|
+
}
|
|
21974
|
+
const what = hit(prompt);
|
|
21975
|
+
if (what !== null)
|
|
21976
|
+
fail("the composed prompt", what);
|
|
21977
|
+
}
|
|
21978
|
+
function cardHtml(input, fontUrls) {
|
|
21979
|
+
const { compose: cfg, width, height, illustration } = input;
|
|
21980
|
+
const { safeArea, headlineMinPx, bodyMinPx, illustrationHeight, palette } = cfg;
|
|
21981
|
+
const headlineFont = fontUrls.get(cfg.fonts.headline) ?? "";
|
|
21982
|
+
const bodyFont = fontUrls.get(cfg.fonts.body) ?? "";
|
|
21983
|
+
const headlineStartPx = headlineMinPx * 1.5;
|
|
21984
|
+
const textPanelHeight = height - illustrationHeight;
|
|
21985
|
+
const safeWidth = width - safeArea * 2;
|
|
21986
|
+
const illustrationSrc = illustration ? `data:${illustration.mime};base64,${Buffer.from(illustration.bytes).toString("base64")}` : "";
|
|
21987
|
+
const pointsHtml = input.points.map((pt, i) => `<div class="point" data-field="points[${i}]" style="font-size:${bodyMinPx}px;">${escHtml(pt)}</div>`).join(`
|
|
21988
|
+
`);
|
|
21989
|
+
return `<!DOCTYPE html>
|
|
21990
|
+
<html lang="zh-Hans">
|
|
21991
|
+
<head>
|
|
21992
|
+
<meta charset="utf-8">
|
|
21993
|
+
<style>
|
|
21994
|
+
@font-face {
|
|
21995
|
+
font-family: 'NotoSansSC-Bold';
|
|
21996
|
+
src: url('${headlineFont}') format('opentype');
|
|
21997
|
+
font-weight: bold;
|
|
21998
|
+
font-style: normal;
|
|
21999
|
+
}
|
|
22000
|
+
@font-face {
|
|
22001
|
+
font-family: 'NotoSansSC-Regular';
|
|
22002
|
+
src: url('${bodyFont}') format('opentype');
|
|
22003
|
+
font-weight: normal;
|
|
22004
|
+
font-style: normal;
|
|
22005
|
+
}
|
|
22006
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
22007
|
+
html, body {
|
|
22008
|
+
width: ${width}px;
|
|
22009
|
+
height: ${height}px;
|
|
22010
|
+
overflow: hidden;
|
|
22011
|
+
background: ${palette.background};
|
|
22012
|
+
}
|
|
22013
|
+
.card {
|
|
22014
|
+
width: ${width}px;
|
|
22015
|
+
height: ${height}px;
|
|
22016
|
+
display: flex;
|
|
22017
|
+
flex-direction: column;
|
|
22018
|
+
background: ${palette.background};
|
|
22019
|
+
}
|
|
22020
|
+
.illustration-band {
|
|
22021
|
+
width: ${width}px;
|
|
22022
|
+
height: ${illustrationHeight}px;
|
|
22023
|
+
flex-shrink: 0;
|
|
22024
|
+
overflow: hidden;
|
|
22025
|
+
background: ${palette.background};
|
|
22026
|
+
}
|
|
22027
|
+
.illustration-band img {
|
|
22028
|
+
width: 100%;
|
|
22029
|
+
height: 100%;
|
|
22030
|
+
object-fit: cover;
|
|
22031
|
+
display: block;
|
|
22032
|
+
}
|
|
22033
|
+
.text-panel {
|
|
22034
|
+
width: ${width}px;
|
|
22035
|
+
height: ${textPanelHeight}px;
|
|
22036
|
+
flex-shrink: 0;
|
|
22037
|
+
padding: ${safeArea}px;
|
|
22038
|
+
display: flex;
|
|
22039
|
+
flex-direction: column;
|
|
22040
|
+
justify-content: flex-start;
|
|
22041
|
+
overflow: hidden;
|
|
22042
|
+
background: ${palette.background};
|
|
22043
|
+
}
|
|
22044
|
+
.headline {
|
|
22045
|
+
font-family: 'NotoSansSC-Bold', sans-serif;
|
|
22046
|
+
font-weight: bold;
|
|
22047
|
+
color: ${palette.text};
|
|
22048
|
+
line-height: 1.3;
|
|
22049
|
+
margin-bottom: ${Math.round(safeArea / 2)}px;
|
|
22050
|
+
word-break: break-all;
|
|
22051
|
+
white-space: normal;
|
|
22052
|
+
/* font-size is set inline with JS fit-step */
|
|
22053
|
+
font-size: ${headlineStartPx}px;
|
|
22054
|
+
}
|
|
22055
|
+
.points-container {
|
|
22056
|
+
display: flex;
|
|
22057
|
+
flex-direction: column;
|
|
22058
|
+
gap: ${Math.round(bodyMinPx * 0.5)}px;
|
|
22059
|
+
}
|
|
22060
|
+
.point {
|
|
22061
|
+
font-family: 'NotoSansSC-Regular', sans-serif;
|
|
22062
|
+
font-weight: normal;
|
|
22063
|
+
color: ${palette.text};
|
|
22064
|
+
line-height: 1.5;
|
|
22065
|
+
word-break: break-all;
|
|
22066
|
+
white-space: normal;
|
|
22067
|
+
}
|
|
22068
|
+
.safe-area-sentinel {
|
|
22069
|
+
position: absolute;
|
|
22070
|
+
top: ${illustrationHeight + safeArea}px;
|
|
22071
|
+
left: ${safeArea}px;
|
|
22072
|
+
width: ${safeWidth}px;
|
|
22073
|
+
height: ${textPanelHeight - safeArea * 2}px;
|
|
22074
|
+
pointer-events: none;
|
|
22075
|
+
}
|
|
22076
|
+
</style>
|
|
22077
|
+
</head>
|
|
22078
|
+
<body>
|
|
22079
|
+
<div class="card">
|
|
22080
|
+
<div class="illustration-band">
|
|
22081
|
+
${illustrationSrc ? `<img src="${illustrationSrc}" alt="">` : `<div style="width:100%;height:100%;background:${palette.background};"></div>`}
|
|
22082
|
+
</div>
|
|
22083
|
+
<div class="text-panel">
|
|
22084
|
+
<div class="headline" data-field="headline" id="headline">${escHtml(input.headline)}</div>
|
|
22085
|
+
<div class="points-container">
|
|
22086
|
+
${pointsHtml}
|
|
22087
|
+
</div>
|
|
22088
|
+
</div>
|
|
22089
|
+
</div>
|
|
22090
|
+
<div class="safe-area-sentinel" id="safe-area-sentinel"></div>
|
|
22091
|
+
<script>
|
|
22092
|
+
(function () {
|
|
22093
|
+
// Step headline font down from start until it fits, never below floor.
|
|
22094
|
+
var headline = document.getElementById('headline');
|
|
22095
|
+
var headlineMinPx = ${headlineMinPx};
|
|
22096
|
+
var headlineStartPx = ${headlineStartPx};
|
|
22097
|
+
var safeWidth = ${safeWidth};
|
|
22098
|
+
var panelHeight = ${textPanelHeight - safeArea * 2};
|
|
22099
|
+
var size = headlineStartPx;
|
|
22100
|
+
while (size > headlineMinPx) {
|
|
22101
|
+
headline.style.fontSize = size + 'px';
|
|
22102
|
+
if (headline.scrollWidth <= safeWidth && headline.scrollHeight <= panelHeight) break;
|
|
22103
|
+
size -= 4;
|
|
22104
|
+
}
|
|
22105
|
+
headline.style.fontSize = Math.max(size, headlineMinPx) + 'px';
|
|
22106
|
+
})();
|
|
22107
|
+
</script>
|
|
22108
|
+
</body>
|
|
22109
|
+
</html>`;
|
|
22110
|
+
}
|
|
22111
|
+
function escHtml(s) {
|
|
22112
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
22113
|
+
}
|
|
22114
|
+
|
|
22115
|
+
// ../../plugins/generations/image-gen/src/compose/chromium.ts
|
|
22116
|
+
var exports_chromium = {};
|
|
22117
|
+
__export(exports_chromium, {
|
|
22118
|
+
makeChromiumComposer: () => makeChromiumComposer,
|
|
22119
|
+
ChromiumComposer: () => ChromiumComposer
|
|
22120
|
+
});
|
|
22121
|
+
import { join as join3 } from "path";
|
|
22122
|
+
function fontRouteUrls(fontNames) {
|
|
22123
|
+
const map2 = new Map;
|
|
22124
|
+
for (const name of fontNames) {
|
|
22125
|
+
map2.set(name, `${FONT_ROUTE_BASE}${encodeURIComponent(name)}`);
|
|
22126
|
+
}
|
|
22127
|
+
return map2;
|
|
22128
|
+
}
|
|
22129
|
+
|
|
22130
|
+
class ChromiumComposer {
|
|
22131
|
+
async compose(input) {
|
|
22132
|
+
let chromium;
|
|
22133
|
+
try {
|
|
22134
|
+
const pw = await import("playwright");
|
|
22135
|
+
chromium = pw.chromium;
|
|
22136
|
+
} catch (err) {
|
|
22137
|
+
throw new Error(`compose: Playwright is not installed. Run: bunx playwright install chromium
|
|
22138
|
+
(${err instanceof Error ? err.message : String(err)})`);
|
|
22139
|
+
}
|
|
22140
|
+
const fontNames = [input.compose.fonts.headline, input.compose.fonts.body];
|
|
22141
|
+
const fontUrls = fontRouteUrls(fontNames);
|
|
22142
|
+
const html = cardHtml(input, fontUrls);
|
|
22143
|
+
let browser;
|
|
22144
|
+
try {
|
|
22145
|
+
try {
|
|
22146
|
+
browser = await chromium.launch({ headless: true });
|
|
22147
|
+
} catch (err) {
|
|
22148
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22149
|
+
throw new Error(`compose: Chromium browser not found. Run: bunx playwright install chromium
|
|
22150
|
+
(${msg})`);
|
|
22151
|
+
}
|
|
22152
|
+
const page = await browser.newPage({
|
|
22153
|
+
viewport: { width: input.width, height: input.height },
|
|
22154
|
+
deviceScaleFactor: 1
|
|
22155
|
+
});
|
|
22156
|
+
await page.route(`${FONT_ROUTE_BASE}**`, async (route) => {
|
|
22157
|
+
const url2 = new URL(route.request().url());
|
|
22158
|
+
const fontName = decodeURIComponent(url2.pathname.slice(1));
|
|
22159
|
+
const fontPath = join3(FONTS_DIR, fontName);
|
|
22160
|
+
try {
|
|
22161
|
+
const bytes = await readBytes(fontPath);
|
|
22162
|
+
await route.fulfill({
|
|
22163
|
+
status: 200,
|
|
22164
|
+
contentType: "font/otf",
|
|
22165
|
+
body: Buffer.from(bytes)
|
|
22166
|
+
});
|
|
22167
|
+
} catch {
|
|
22168
|
+
await route.abort();
|
|
22169
|
+
}
|
|
22170
|
+
});
|
|
22171
|
+
await page.setContent(html, { waitUntil: "domcontentloaded" });
|
|
22172
|
+
await page.evaluate("document.fonts.ready");
|
|
22173
|
+
const { safeArea, illustrationHeight } = input.compose;
|
|
22174
|
+
const safeLeft = safeArea;
|
|
22175
|
+
const safeTop = illustrationHeight + safeArea;
|
|
22176
|
+
const safeRight = input.width - safeArea;
|
|
22177
|
+
const safeBottom = input.height - safeArea;
|
|
22178
|
+
const overflowExpr = `
|
|
22179
|
+
(function(safeLeft, safeTop, safeRight, safeBottom) {
|
|
22180
|
+
var results = [];
|
|
22181
|
+
var els = Array.from(document.querySelectorAll('[data-field]'));
|
|
22182
|
+
for (var i = 0; i < els.length; i++) {
|
|
22183
|
+
var el = els[i];
|
|
22184
|
+
var field = el.dataset.field || '';
|
|
22185
|
+
var rect = el.getBoundingClientRect();
|
|
22186
|
+
if (rect.left < safeLeft || rect.top < safeTop || rect.right > safeRight || rect.bottom > safeBottom) {
|
|
22187
|
+
results.push({ field: field, rect: { top: rect.top, left: rect.left, bottom: rect.bottom, right: rect.right } });
|
|
22188
|
+
}
|
|
22189
|
+
}
|
|
22190
|
+
return results;
|
|
22191
|
+
})(${safeLeft}, ${safeTop}, ${safeRight}, ${safeBottom})
|
|
22192
|
+
`;
|
|
22193
|
+
const overflows = await page.evaluate(overflowExpr);
|
|
22194
|
+
if (overflows.length > 0) {
|
|
22195
|
+
const fields = overflows.map((o) => o.field).join(", ");
|
|
22196
|
+
throw new Error(`compose: text overflows the safe area for field(s): ${fields} (safe area: ${safeArea}px inset)`);
|
|
22197
|
+
}
|
|
22198
|
+
const screenshot = await page.screenshot({ type: "png", animations: "disabled" });
|
|
22199
|
+
return new Uint8Array(screenshot);
|
|
22200
|
+
} finally {
|
|
22201
|
+
await browser?.close();
|
|
22202
|
+
}
|
|
22203
|
+
}
|
|
22204
|
+
}
|
|
22205
|
+
function makeChromiumComposer() {
|
|
22206
|
+
return new ChromiumComposer;
|
|
22207
|
+
}
|
|
22208
|
+
var FONTS_DIR, FONT_ROUTE_BASE = "http://kk-fonts.local/";
|
|
22209
|
+
var init_chromium = __esm(() => {
|
|
22210
|
+
init_bytes();
|
|
22211
|
+
FONTS_DIR = join3(import.meta.dir, "..", "..", "assets", "fonts");
|
|
22212
|
+
});
|
|
22213
|
+
|
|
21812
22214
|
// ../../plugins/generations/image-gen/src/index.ts
|
|
21813
|
-
import { dirname as dirname3 } from "path";
|
|
22215
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
21814
22216
|
import { parseArgs } from "util";
|
|
21815
22217
|
|
|
21816
22218
|
// ../../packages/kk-core/src/kinds.ts
|
|
@@ -21908,14 +22310,141 @@ init_config();
|
|
|
21908
22310
|
if (false) {}
|
|
21909
22311
|
// ../../plugins/generations/image-gen/src/index.ts
|
|
21910
22312
|
init_dist();
|
|
22313
|
+
init_bytes();
|
|
21911
22314
|
|
|
21912
|
-
// ../../plugins/generations/image-gen/src/
|
|
21913
|
-
|
|
21914
|
-
|
|
21915
|
-
|
|
22315
|
+
// ../../plugins/generations/image-gen/src/compose/cmap.ts
|
|
22316
|
+
function u16(view, offset) {
|
|
22317
|
+
return view.getUint16(offset, false);
|
|
22318
|
+
}
|
|
22319
|
+
function u32(view, offset) {
|
|
22320
|
+
return view.getUint32(offset, false);
|
|
22321
|
+
}
|
|
22322
|
+
function tag(view, offset) {
|
|
22323
|
+
return String.fromCharCode(view.getUint8(offset), view.getUint8(offset + 1), view.getUint8(offset + 2), view.getUint8(offset + 3));
|
|
22324
|
+
}
|
|
22325
|
+
function assertBytes(view, offset, needed, context2) {
|
|
22326
|
+
if (offset + needed > view.byteLength) {
|
|
22327
|
+
throw new Error(`compose: truncated font \u2014 expected ${needed} bytes at offset ${offset} (${context2})`);
|
|
22328
|
+
}
|
|
22329
|
+
}
|
|
22330
|
+
function findCmapTable(view) {
|
|
22331
|
+
assertBytes(view, 0, 12, "sfnt header");
|
|
22332
|
+
const magic = u32(view, 0);
|
|
22333
|
+
if (magic !== 65536 && magic !== 1330926671 && magic !== 1953658213) {
|
|
22334
|
+
throw new Error(`compose: not a valid sfnt font (magic 0x${magic.toString(16).padStart(8, "0")})`);
|
|
22335
|
+
}
|
|
22336
|
+
const numTables = u16(view, 4);
|
|
22337
|
+
assertBytes(view, 12, numTables * 16, "sfnt table directory");
|
|
22338
|
+
for (let i = 0;i < numTables; i++) {
|
|
22339
|
+
const base = 12 + i * 16;
|
|
22340
|
+
if (tag(view, base) === "cmap") {
|
|
22341
|
+
const offset = u32(view, base + 8);
|
|
22342
|
+
const length = u32(view, base + 12);
|
|
22343
|
+
return { offset, length };
|
|
22344
|
+
}
|
|
22345
|
+
}
|
|
22346
|
+
throw new Error("compose: truncated font \u2014 cmap table not found in sfnt directory");
|
|
22347
|
+
}
|
|
22348
|
+
function coverageFormat4(view, subtableOffset) {
|
|
22349
|
+
assertBytes(view, subtableOffset, 14, "cmap format 4 header");
|
|
22350
|
+
const length = u16(view, subtableOffset + 2);
|
|
22351
|
+
assertBytes(view, subtableOffset, length, "cmap format 4 body");
|
|
22352
|
+
const segCount = u16(view, subtableOffset + 6) / 2;
|
|
22353
|
+
const endCodesBase = subtableOffset + 14;
|
|
22354
|
+
assertBytes(view, endCodesBase, segCount * 2 + 2 + segCount * 2 + segCount * 2 + segCount * 2, "cmap format 4 segments");
|
|
22355
|
+
const startCodesBase = endCodesBase + segCount * 2 + 2;
|
|
22356
|
+
const idDeltaBase = startCodesBase + segCount * 2;
|
|
22357
|
+
const idRangeOffsetBase = idDeltaBase + segCount * 2;
|
|
22358
|
+
return (cp) => {
|
|
22359
|
+
if (cp > 65535)
|
|
22360
|
+
return false;
|
|
22361
|
+
let lo = 0;
|
|
22362
|
+
let hi = segCount - 1;
|
|
22363
|
+
while (lo <= hi) {
|
|
22364
|
+
const mid = lo + hi >>> 1;
|
|
22365
|
+
const endCode = u16(view, endCodesBase + mid * 2);
|
|
22366
|
+
if (endCode < cp) {
|
|
22367
|
+
lo = mid + 1;
|
|
22368
|
+
} else {
|
|
22369
|
+
hi = mid - 1;
|
|
22370
|
+
}
|
|
22371
|
+
}
|
|
22372
|
+
if (lo >= segCount)
|
|
22373
|
+
return false;
|
|
22374
|
+
const startCode = u16(view, startCodesBase + lo * 2);
|
|
22375
|
+
if (cp < startCode)
|
|
22376
|
+
return false;
|
|
22377
|
+
const idRangeOffset = u16(view, idRangeOffsetBase + lo * 2);
|
|
22378
|
+
if (idRangeOffset === 0) {
|
|
22379
|
+
const idDelta = u16(view, idDeltaBase + lo * 2);
|
|
22380
|
+
return (cp + idDelta & 65535) !== 0;
|
|
22381
|
+
}
|
|
22382
|
+
const glyphIdAddr = idRangeOffsetBase + lo * 2 + idRangeOffset + (cp - startCode) * 2;
|
|
22383
|
+
if (glyphIdAddr + 2 > view.byteLength)
|
|
22384
|
+
return false;
|
|
22385
|
+
const glyphId = u16(view, glyphIdAddr);
|
|
22386
|
+
return glyphId !== 0;
|
|
22387
|
+
};
|
|
22388
|
+
}
|
|
22389
|
+
function coverageFormat12(view, subtableOffset) {
|
|
22390
|
+
assertBytes(view, subtableOffset, 16, "cmap format 12 header");
|
|
22391
|
+
const numGroups = u32(view, subtableOffset + 12);
|
|
22392
|
+
assertBytes(view, subtableOffset + 16, numGroups * 12, "cmap format 12 groups");
|
|
22393
|
+
const groupsBase = subtableOffset + 16;
|
|
22394
|
+
return (cp) => {
|
|
22395
|
+
let lo = 0;
|
|
22396
|
+
let hi = numGroups - 1;
|
|
22397
|
+
while (lo <= hi) {
|
|
22398
|
+
const mid = lo + hi >>> 1;
|
|
22399
|
+
const base = groupsBase + mid * 12;
|
|
22400
|
+
const startChar = u32(view, base);
|
|
22401
|
+
const endChar = u32(view, base + 4);
|
|
22402
|
+
if (endChar < cp) {
|
|
22403
|
+
lo = mid + 1;
|
|
22404
|
+
} else if (startChar > cp) {
|
|
22405
|
+
hi = mid - 1;
|
|
22406
|
+
} else {
|
|
22407
|
+
const startGlyph = u32(view, base + 8);
|
|
22408
|
+
return startGlyph + (cp - startChar) !== 0;
|
|
22409
|
+
}
|
|
22410
|
+
}
|
|
22411
|
+
return false;
|
|
22412
|
+
};
|
|
21916
22413
|
}
|
|
21917
|
-
|
|
21918
|
-
|
|
22414
|
+
function glyphCoverage(fontBytes) {
|
|
22415
|
+
if (fontBytes.byteLength < 12) {
|
|
22416
|
+
throw new Error("compose: truncated font \u2014 too small for sfnt header");
|
|
22417
|
+
}
|
|
22418
|
+
const view = new DataView(fontBytes.buffer, fontBytes.byteOffset, fontBytes.byteLength);
|
|
22419
|
+
const { offset: cmapOffset } = findCmapTable(view);
|
|
22420
|
+
assertBytes(view, cmapOffset, 4, "cmap table header");
|
|
22421
|
+
const numSubtables = u16(view, cmapOffset + 2);
|
|
22422
|
+
assertBytes(view, cmapOffset + 4, numSubtables * 8, "cmap encoding records");
|
|
22423
|
+
let fmt4Offset;
|
|
22424
|
+
let fmt12Offset;
|
|
22425
|
+
for (let i = 0;i < numSubtables; i++) {
|
|
22426
|
+
const recBase = cmapOffset + 4 + i * 8;
|
|
22427
|
+
const platformId = u16(view, recBase);
|
|
22428
|
+
const encodingId = u16(view, recBase + 2);
|
|
22429
|
+
const subtableOffset = cmapOffset + u32(view, recBase + 4);
|
|
22430
|
+
if (subtableOffset + 2 > view.byteLength)
|
|
22431
|
+
continue;
|
|
22432
|
+
const fmt = u16(view, subtableOffset);
|
|
22433
|
+
if (fmt === 12 && (platformId === 3 || platformId === 0)) {
|
|
22434
|
+
fmt12Offset = subtableOffset;
|
|
22435
|
+
}
|
|
22436
|
+
if (fmt === 4 && (platformId === 3 || platformId === 0) && encodingId !== 10) {
|
|
22437
|
+
if (fmt4Offset === undefined)
|
|
22438
|
+
fmt4Offset = subtableOffset;
|
|
22439
|
+
}
|
|
22440
|
+
}
|
|
22441
|
+
if (fmt12Offset !== undefined) {
|
|
22442
|
+
return coverageFormat12(view, fmt12Offset);
|
|
22443
|
+
}
|
|
22444
|
+
if (fmt4Offset !== undefined) {
|
|
22445
|
+
return coverageFormat4(view, fmt4Offset);
|
|
22446
|
+
}
|
|
22447
|
+
throw new Error("compose: truncated font \u2014 no usable cmap subtable (format 4 or 12) found");
|
|
21919
22448
|
}
|
|
21920
22449
|
|
|
21921
22450
|
// ../../plugins/generations/image-gen/src/job.ts
|
|
@@ -21993,6 +22522,15 @@ var PresetSourceSchema = exports_external.object({
|
|
|
21993
22522
|
extractedAt: exports_external.string().optional(),
|
|
21994
22523
|
confidence: exports_external.enum(["high", "medium", "low"]).optional()
|
|
21995
22524
|
});
|
|
22525
|
+
var ComposeConfigSchema = exports_external.object({
|
|
22526
|
+
engine: exports_external.literal("text-card"),
|
|
22527
|
+
safeArea: exports_external.number(),
|
|
22528
|
+
headlineMinPx: exports_external.number(),
|
|
22529
|
+
bodyMinPx: exports_external.number(),
|
|
22530
|
+
illustrationHeight: exports_external.number(),
|
|
22531
|
+
palette: exports_external.object({ background: exports_external.string(), text: exports_external.string(), accent: exports_external.string() }),
|
|
22532
|
+
fonts: exports_external.object({ headline: exports_external.string(), body: exports_external.string() })
|
|
22533
|
+
});
|
|
21996
22534
|
var FormatPresetSchema = exports_external.object({
|
|
21997
22535
|
name: exports_external.string(),
|
|
21998
22536
|
description: exports_external.string(),
|
|
@@ -22004,7 +22542,10 @@ var FormatPresetSchema = exports_external.object({
|
|
|
22004
22542
|
keywords: exports_external.array(exports_external.string()),
|
|
22005
22543
|
outputFilename: exports_external.string(),
|
|
22006
22544
|
variables: exports_external.record(exports_external.string(), exports_external.object({ description: exports_external.string(), default: exports_external.string() })),
|
|
22007
|
-
template: exports_external.string()
|
|
22545
|
+
template: exports_external.string(),
|
|
22546
|
+
compose: ComposeConfigSchema.optional()
|
|
22547
|
+
}).refine((data) => data.compose === undefined || data.width !== undefined && data.height !== undefined, {
|
|
22548
|
+
message: "A format preset with a compose block requires both width and height to be declared"
|
|
22008
22549
|
});
|
|
22009
22550
|
var StylePresetSchema = exports_external.object({
|
|
22010
22551
|
name: exports_external.string(),
|
|
@@ -22157,6 +22698,7 @@ function loadPresets() {
|
|
|
22157
22698
|
import { Buffer as Buffer3 } from "buffer";
|
|
22158
22699
|
|
|
22159
22700
|
// ../../plugins/generations/image-gen/src/providers/refs.ts
|
|
22701
|
+
init_bytes();
|
|
22160
22702
|
import { extname as extname2 } from "path";
|
|
22161
22703
|
function mimeForPath(path2) {
|
|
22162
22704
|
const ext = extname2(path2).toLowerCase();
|
|
@@ -22178,6 +22720,7 @@ async function refToDataUri(path2) {
|
|
|
22178
22720
|
}
|
|
22179
22721
|
|
|
22180
22722
|
// ../../plugins/generations/image-gen/src/providers/seedream.ts
|
|
22723
|
+
init_bytes();
|
|
22181
22724
|
import { Buffer as Buffer2 } from "buffer";
|
|
22182
22725
|
|
|
22183
22726
|
// ../../plugins/generations/image-gen/src/providers/timeouts.ts
|
|
@@ -22376,9 +22919,11 @@ var createAgnes = (options) => {
|
|
|
22376
22919
|
};
|
|
22377
22920
|
|
|
22378
22921
|
// ../../plugins/generations/image-gen/src/providers/azure.ts
|
|
22922
|
+
init_bytes();
|
|
22379
22923
|
import { extname as extname3 } from "path";
|
|
22380
22924
|
|
|
22381
22925
|
// ../../plugins/generations/image-gen/src/providers/openai.ts
|
|
22926
|
+
init_bytes();
|
|
22382
22927
|
import { Buffer as Buffer4 } from "buffer";
|
|
22383
22928
|
import { basename as basename2 } from "path";
|
|
22384
22929
|
var DEFAULT_MODEL3 = "gpt-image-2";
|
|
@@ -22788,6 +23333,7 @@ var createAzure = (options) => {
|
|
|
22788
23333
|
};
|
|
22789
23334
|
|
|
22790
23335
|
// ../../plugins/generations/image-gen/src/providers/codex-cli.ts
|
|
23336
|
+
init_bytes();
|
|
22791
23337
|
import { spawn } from "child_process";
|
|
22792
23338
|
import { randomBytes } from "crypto";
|
|
22793
23339
|
import { access as access2, mkdir, rm, writeFile as writeFile2 } from "fs/promises";
|
|
@@ -23312,6 +23858,7 @@ import { execFileSync } from "child_process";
|
|
|
23312
23858
|
import { randomUUID } from "crypto";
|
|
23313
23859
|
import { tmpdir as tmpdir2 } from "os";
|
|
23314
23860
|
import { join as join2 } from "path";
|
|
23861
|
+
init_bytes();
|
|
23315
23862
|
var GOOGLE_MULTIMODAL_MODELS = [
|
|
23316
23863
|
"gemini-3-pro-image",
|
|
23317
23864
|
"gemini-3.1-flash-image",
|
|
@@ -23766,15 +24313,19 @@ var createJimeng = (options) => {
|
|
|
23766
24313
|
};
|
|
23767
24314
|
|
|
23768
24315
|
// ../../plugins/generations/image-gen/src/providers/minimax.ts
|
|
24316
|
+
init_bytes();
|
|
23769
24317
|
import { Buffer as Buffer7 } from "buffer";
|
|
23770
24318
|
var DEFAULT_MODEL9 = "image-01";
|
|
23771
24319
|
var MAX_REFERENCE_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
23772
24320
|
var SUPPORTED_ASPECT_RATIOS = new Set(["1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]);
|
|
24321
|
+
function isAcceptablePixelSize(width, height) {
|
|
24322
|
+
return width >= 512 && width <= 2048 && height >= 512 && height <= 2048 && width % 8 === 0 && height % 8 === 0;
|
|
24323
|
+
}
|
|
23773
24324
|
function validatePixelSize(width, height) {
|
|
23774
|
-
if (width
|
|
23775
|
-
|
|
23776
|
-
|
|
23777
|
-
|
|
24325
|
+
if (!isAcceptablePixelSize(width, height)) {
|
|
24326
|
+
if (width < 512 || width > 2048 || height < 512 || height > 2048) {
|
|
24327
|
+
throw new Error("MiniMax custom size must keep width and height between 512 and 2048.");
|
|
24328
|
+
}
|
|
23778
24329
|
throw new Error("MiniMax custom size requires width and height divisible by 8.");
|
|
23779
24330
|
}
|
|
23780
24331
|
}
|
|
@@ -23856,6 +24407,7 @@ var createMinimax = (options) => {
|
|
|
23856
24407
|
defaultModel: () => DEFAULT_MODEL9,
|
|
23857
24408
|
isAvailable: () => readEnv("MINIMAX_API_KEY") !== "",
|
|
23858
24409
|
supportsReferenceImages: () => true,
|
|
24410
|
+
exactSize: (model, width, height) => model === "image-01" && isAcceptablePixelSize(width, height),
|
|
23859
24411
|
validate(req) {
|
|
23860
24412
|
requestBody(req.prompt, req.model, req);
|
|
23861
24413
|
},
|
|
@@ -24896,6 +25448,65 @@ async function processGeneratorIO(inputPath, outputPath, options = {}) {
|
|
|
24896
25448
|
}
|
|
24897
25449
|
if (spec.job === "illustration")
|
|
24898
25450
|
assertAnchorPlan(doc2?.body ?? "", positions, prompts.length);
|
|
25451
|
+
let _chromiumComposer;
|
|
25452
|
+
const resolveComposer = async () => {
|
|
25453
|
+
if (options.composer !== undefined)
|
|
25454
|
+
return options.composer;
|
|
25455
|
+
if (_chromiumComposer !== undefined)
|
|
25456
|
+
return _chromiumComposer;
|
|
25457
|
+
const { makeChromiumComposer: makeChromiumComposer2 } = await Promise.resolve().then(() => (init_chromium(), exports_chromium));
|
|
25458
|
+
_chromiumComposer = makeChromiumComposer2();
|
|
25459
|
+
return _chromiumComposer;
|
|
25460
|
+
};
|
|
25461
|
+
if (format.compose !== undefined) {
|
|
25462
|
+
const cfg = format.compose;
|
|
25463
|
+
if (spec.job !== "free") {
|
|
25464
|
+
throw new Error(`compose: format '${format.name}' has a compose block but the job is '${spec.job}' \u2014 compose formats require job: free`);
|
|
25465
|
+
}
|
|
25466
|
+
const vars = spec.vars ?? {};
|
|
25467
|
+
const headline = vars.headline ?? format.variables.headline?.default ?? "";
|
|
25468
|
+
if (!headline.trim()) {
|
|
25469
|
+
throw new Error(`compose: format '${format.name}' requires a non-empty vars.headline`);
|
|
25470
|
+
}
|
|
25471
|
+
const pointsRaw = vars.points ?? format.variables.points?.default ?? "";
|
|
25472
|
+
const points = pointsRaw.split(`
|
|
25473
|
+
`).filter((l) => l.trim() !== "");
|
|
25474
|
+
const promptVars = { subject: doc2?.body ?? "", ...spec.vars ?? {} };
|
|
25475
|
+
const textFreeSources = [
|
|
25476
|
+
{ source: "the format template", text: format.template },
|
|
25477
|
+
{
|
|
25478
|
+
source: `the style preset '${style.name}'`,
|
|
25479
|
+
text: [style.modifiers, style.negative].filter((part) => part !== "").join(", ")
|
|
25480
|
+
},
|
|
25481
|
+
{ source: "spec.extra", text: spec.extra ?? "" },
|
|
25482
|
+
...Object.entries(promptVars).filter(([name]) => name !== "headline" && name !== "points" && name !== "index").map(([name, value]) => ({ source: `vars.${name}`, text: String(value) }))
|
|
25483
|
+
];
|
|
25484
|
+
assertTextFreePrompt(prompts[0] ?? "", { headline, points }, textFreeSources);
|
|
25485
|
+
const fontsDir = join4(import.meta.dir, "..", "assets", "fonts");
|
|
25486
|
+
const fontNames = [cfg.fonts.headline, cfg.fonts.body];
|
|
25487
|
+
const fontBytesMap = new Map;
|
|
25488
|
+
for (const name of fontNames) {
|
|
25489
|
+
fontBytesMap.set(name, new Uint8Array(await readBytes(join4(fontsDir, name))));
|
|
25490
|
+
}
|
|
25491
|
+
const coverageMap = new Map;
|
|
25492
|
+
for (const [name, bytes] of fontBytesMap) {
|
|
25493
|
+
coverageMap.set(name, glyphCoverage(bytes));
|
|
25494
|
+
}
|
|
25495
|
+
if (format.width == null || format.height == null) {
|
|
25496
|
+
throw new Error(`image-gen: format '${format.name}' declares no canvas size; compose requires width and height`);
|
|
25497
|
+
}
|
|
25498
|
+
const cardInput = {
|
|
25499
|
+
headline,
|
|
25500
|
+
points,
|
|
25501
|
+
role: vars.role ?? format.variables.role?.default ?? "content",
|
|
25502
|
+
compose: cfg,
|
|
25503
|
+
width: format.width,
|
|
25504
|
+
height: format.height
|
|
25505
|
+
};
|
|
25506
|
+
assertCoverage(cardInput, coverageMap);
|
|
25507
|
+
const composer2 = await resolveComposer();
|
|
25508
|
+
await composer2.compose(cardInput);
|
|
25509
|
+
}
|
|
24899
25510
|
const resolveProvider = options.providerResolver ?? defaultProviderResolver;
|
|
24900
25511
|
const primary = resolveProvider(spec);
|
|
24901
25512
|
const built = new Map;
|
|
@@ -24960,15 +25571,17 @@ async function processGeneratorIO(inputPath, outputPath, options = {}) {
|
|
|
24960
25571
|
}
|
|
24961
25572
|
const provider = at === 0 ? primary : createCandidate(name);
|
|
24962
25573
|
const model = at === 0 ? spec.model ?? provider.defaultModel() : provider.defaultModel();
|
|
25574
|
+
const exact = format.width !== undefined && format.height !== undefined && provider.exactSize?.(model, format.width, format.height) === true;
|
|
24963
25575
|
const request = {
|
|
24964
25576
|
prompt,
|
|
24965
25577
|
model,
|
|
24966
|
-
aspectRatio: format.aspectRatio,
|
|
25578
|
+
...exact ? { width: format.width, height: format.height } : { aspectRatio: format.aspectRatio },
|
|
24967
25579
|
...spec.referenceImages !== undefined && spec.referenceImages.length > 0 ? { referenceImages: spec.referenceImages } : {}
|
|
24968
25580
|
};
|
|
24969
25581
|
try {
|
|
24970
25582
|
provider.validate?.(request);
|
|
24971
|
-
|
|
25583
|
+
const result = await provider.generate(request);
|
|
25584
|
+
return { result, sizeRequest: exact ? "exact" : "aspect" };
|
|
24972
25585
|
} catch (err) {
|
|
24973
25586
|
if (spec.fallback === undefined) {
|
|
24974
25587
|
throw err;
|
|
@@ -24991,7 +25604,15 @@ async function processGeneratorIO(inputPath, outputPath, options = {}) {
|
|
|
24991
25604
|
};
|
|
24992
25605
|
let body = doc2?.body ?? "";
|
|
24993
25606
|
for (const [index, prompt] of prompts.entries()) {
|
|
24994
|
-
const result = await generateWithFallback(index + 1, prompt);
|
|
25607
|
+
const { result, sizeRequest } = await generateWithFallback(index + 1, prompt);
|
|
25608
|
+
const delivered = imageDimensions(result.bytes);
|
|
25609
|
+
if (format.width !== undefined && format.height !== undefined) {
|
|
25610
|
+
if (delivered === null) {
|
|
25611
|
+
echoError(`WARN image-gen: ${result.provider}/${result.model} delivered unparseable image bytes; dimensions omitted`);
|
|
25612
|
+
} else if (delivered.width !== format.width || delivered.height !== format.height) {
|
|
25613
|
+
echoError(`WARN image-gen: ${result.provider}/${result.model} delivered ${delivered.width}x${delivered.height}; format '${format.name}' declares ${format.width}x${format.height}`);
|
|
25614
|
+
}
|
|
25615
|
+
}
|
|
24995
25616
|
let anchor;
|
|
24996
25617
|
if (spec.job === "illustration") {
|
|
24997
25618
|
anchor = positions?.[index];
|
|
@@ -25003,17 +25624,74 @@ async function processGeneratorIO(inputPath, outputPath, options = {}) {
|
|
|
25003
25624
|
throw new Error(`Illustration anchor must occur exactly once in the source body, found ${parts.length - 1}: '${anchor.slice(0, 50)}'`);
|
|
25004
25625
|
}
|
|
25005
25626
|
}
|
|
25006
|
-
|
|
25007
|
-
|
|
25008
|
-
|
|
25627
|
+
let artifactPathValue;
|
|
25628
|
+
let artifactBytes;
|
|
25629
|
+
let artifactWidth;
|
|
25630
|
+
let artifactHeight;
|
|
25631
|
+
let illustrationMeta;
|
|
25632
|
+
if (format.compose !== undefined) {
|
|
25633
|
+
const illustrationArtifactPath = artifactPath(outputPath, stem, `${format.name}-illustration`, index + 1, result.extension);
|
|
25634
|
+
await writeBytes(illustrationArtifactPath, result.bytes);
|
|
25635
|
+
written.push(illustrationArtifactPath);
|
|
25636
|
+
const vars = spec.vars ?? {};
|
|
25637
|
+
const headline = vars.headline ?? format.variables.headline?.default ?? "";
|
|
25638
|
+
const pointsRaw = vars.points ?? format.variables.points?.default ?? "";
|
|
25639
|
+
const points = pointsRaw.split(`
|
|
25640
|
+
`).filter((l) => l.trim() !== "");
|
|
25641
|
+
if (format.width == null || format.height == null) {
|
|
25642
|
+
throw new Error(`image-gen: format '${format.name}' declares no canvas size; compose requires width and height`);
|
|
25643
|
+
}
|
|
25644
|
+
const cardInput = {
|
|
25645
|
+
headline,
|
|
25646
|
+
points,
|
|
25647
|
+
role: vars.role ?? format.variables.role?.default ?? "content",
|
|
25648
|
+
compose: format.compose,
|
|
25649
|
+
width: format.width,
|
|
25650
|
+
height: format.height,
|
|
25651
|
+
illustration: { bytes: result.bytes, mime: `image/${result.extension}` }
|
|
25652
|
+
};
|
|
25653
|
+
const composer2 = await resolveComposer();
|
|
25654
|
+
const cardBytes = await composer2.compose(cardInput);
|
|
25655
|
+
const cardPath = artifactPath(outputPath, stem, format.name, index + 1, "png");
|
|
25656
|
+
await writeBytes(cardPath, cardBytes);
|
|
25657
|
+
written.push(cardPath);
|
|
25658
|
+
const dims = imageDimensions(cardBytes);
|
|
25659
|
+
if (dims === null) {
|
|
25660
|
+
throw new Error(`compose: composed card dimensions are unparseable; format '${format.name}' requires ${format.width}x${format.height}`);
|
|
25661
|
+
}
|
|
25662
|
+
if (dims.width !== format.width || dims.height !== format.height) {
|
|
25663
|
+
throw new Error(`compose: card dimensions ${dims.width}x${dims.height} do not match format canvas ${format.width}x${format.height}`);
|
|
25664
|
+
}
|
|
25665
|
+
artifactPathValue = cardPath;
|
|
25666
|
+
artifactBytes = cardBytes.byteLength;
|
|
25667
|
+
artifactWidth = dims.width;
|
|
25668
|
+
artifactHeight = dims.height;
|
|
25669
|
+
illustrationMeta = {
|
|
25670
|
+
path: illustrationArtifactPath,
|
|
25671
|
+
bytes: result.bytes.byteLength,
|
|
25672
|
+
...delivered !== null ? { width: delivered.width, height: delivered.height } : {}
|
|
25673
|
+
};
|
|
25674
|
+
} else {
|
|
25675
|
+
const path2 = artifactPath(outputPath, stem, format.name, index + 1, result.extension);
|
|
25676
|
+
await writeBytes(path2, result.bytes);
|
|
25677
|
+
written.push(path2);
|
|
25678
|
+
artifactPathValue = path2;
|
|
25679
|
+
artifactBytes = result.bytes.byteLength;
|
|
25680
|
+
artifactWidth = delivered?.width;
|
|
25681
|
+
artifactHeight = delivered?.height;
|
|
25682
|
+
}
|
|
25009
25683
|
const artifact = {
|
|
25010
|
-
path:
|
|
25684
|
+
path: artifactPathValue,
|
|
25011
25685
|
format: format.name,
|
|
25012
25686
|
style: style.name,
|
|
25013
25687
|
provider: result.provider,
|
|
25014
25688
|
model: result.model,
|
|
25015
25689
|
prompt,
|
|
25016
|
-
bytes:
|
|
25690
|
+
bytes: artifactBytes,
|
|
25691
|
+
sizeRequest,
|
|
25692
|
+
...artifactWidth !== undefined ? { width: artifactWidth } : {},
|
|
25693
|
+
...artifactHeight !== undefined ? { height: artifactHeight } : {},
|
|
25694
|
+
...illustrationMeta !== undefined ? { illustration: illustrationMeta } : {}
|
|
25017
25695
|
};
|
|
25018
25696
|
if (spec.seed !== undefined) {
|
|
25019
25697
|
artifact.seed = spec.seed;
|
|
@@ -25024,10 +25702,10 @@ async function processGeneratorIO(inputPath, outputPath, options = {}) {
|
|
|
25024
25702
|
const altText = `Illustration ${index + 1}`;
|
|
25025
25703
|
const markdown = `${anchor}
|
|
25026
25704
|
|
|
25027
|
-
`;
|
|
25028
25706
|
captions.push({
|
|
25029
25707
|
index: index + 1,
|
|
25030
|
-
path:
|
|
25708
|
+
path: artifactPathValue,
|
|
25031
25709
|
caption: anchor,
|
|
25032
25710
|
altText,
|
|
25033
25711
|
anchor
|