@cliangdev/conductor 0.21.0 → 0.22.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.
- package/assets/claude/skills/conductor-creative/SKILL.md +144 -0
- package/assets/claude/skills/conductor-publisher/SKILL.md +4 -0
- package/assets/cli-manifest.json +94 -0
- package/dist/commands/creative.d.ts +3 -0
- package/dist/commands/creative.d.ts.map +1 -0
- package/dist/commands/creative.js +60 -0
- package/dist/commands/creative.js.map +1 -0
- package/dist/creative/assertions.js +221 -0
- package/dist/creative/font.js +39 -0
- package/dist/creative/frame.css +126 -0
- package/dist/creative/frame.html +29 -0
- package/dist/creative/frame.js +76 -0
- package/dist/creative/job/render.mjs +203 -0
- package/dist/creative/job/server.mjs +72 -0
- package/dist/creative/job/transport.mjs +167 -0
- package/dist/creative/layouts/bleed/layout.css +47 -0
- package/dist/creative/layouts/bleed/layout.json +17 -0
- package/dist/creative/layouts/card/layout.css +24 -0
- package/dist/creative/layouts/card/layout.json +17 -0
- package/dist/creative/layouts/index.js +62 -0
- package/dist/creative/layouts/index.json +1 -0
- package/dist/creative/layouts/split/layout.css +22 -0
- package/dist/creative/layouts/split/layout.json +17 -0
- package/dist/creative/layouts/stacked/layout.css +40 -0
- package/dist/creative/layouts/stacked/layout.json +23 -0
- package/dist/creative/placements.js +200 -0
- package/dist/creative/placements.json +232 -0
- package/dist/creative/render.js +457 -0
- package/dist/creative/sheet.html +27 -0
- package/dist/creative/sheet.js +94 -0
- package/dist/creative/styles.css +314 -0
- package/dist/creative/tokens.css +33 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/creative-browser.d.ts +25 -0
- package/dist/lib/creative-browser.d.ts.map +1 -0
- package/dist/lib/creative-browser.js +36 -0
- package/dist/lib/creative-browser.js.map +1 -0
- package/dist/lib/creative-id.d.ts +8 -0
- package/dist/lib/creative-id.d.ts.map +1 -0
- package/dist/lib/creative-id.js +14 -0
- package/dist/lib/creative-id.js.map +1 -0
- package/dist/lib/creative-render.d.ts +32 -0
- package/dist/lib/creative-render.d.ts.map +1 -0
- package/dist/lib/creative-render.js +71 -0
- package/dist/lib/creative-render.js.map +1 -0
- package/dist/lib/image-dimensions.d.ts +20 -0
- package/dist/lib/image-dimensions.d.ts.map +1 -0
- package/dist/lib/image-dimensions.js +115 -0
- package/dist/lib/image-dimensions.js.map +1 -0
- package/dist/lib/plugin-assets.d.ts.map +1 -1
- package/dist/lib/plugin-assets.js +1 -0
- package/dist/lib/plugin-assets.js.map +1 -1
- package/dist/mcp/index.d.ts +18 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +284 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/tools/creatives.d.ts +93 -0
- package/dist/mcp/tools/creatives.d.ts.map +1 -0
- package/dist/mcp/tools/creatives.js +291 -0
- package/dist/mcp/tools/creatives.js.map +1 -0
- package/dist/mcp/tools/insights.d.ts.map +1 -1
- package/dist/mcp/tools/insights.js +14 -0
- package/dist/mcp/tools/insights.js.map +1 -1
- package/package.json +4 -3
- package/scripts/build-creative.mjs +93 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Creative contact sheet</title>
|
|
6
|
+
<link rel="stylesheet" href="styles.css">
|
|
7
|
+
<style>
|
|
8
|
+
html, body { margin: 0; background: #F2F2F7; font-family: system-ui, -apple-system, sans-serif; }
|
|
9
|
+
#sheet { display: inline-flex; gap: 24px; align-items: flex-start; padding: 24px; }
|
|
10
|
+
.cell { display: flex; flex-direction: column; gap: 8px; align-items: center; }
|
|
11
|
+
.shell { position: relative; overflow: hidden; border-radius: 6px; box-shadow: 0 3px 12px rgba(0,0,0,0.16); }
|
|
12
|
+
.cap { font-size: 13px; font-weight: 700; color: #717171; }
|
|
13
|
+
</style>
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<!--
|
|
17
|
+
Preview-only contact sheet: every enabled placement of one creative (or
|
|
18
|
+
every beat of a story/carousel), scaled down onto one page, for a quick
|
|
19
|
+
look without downloading full-size PNGs one at a time. Rendered by
|
|
20
|
+
job/render.mjs when a render's `previewOnly` flag is set, and uploaded as
|
|
21
|
+
the single frame placementKey "sheet". Reads its spec from
|
|
22
|
+
window.__RENDER_SPEC__ — see sheet.js.
|
|
23
|
+
-->
|
|
24
|
+
<div id="sheet"></div>
|
|
25
|
+
<script type="module" src="sheet.js"></script>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* sheet.js — builds a contact sheet: every enabled placement of a creative,
|
|
2
|
+
* or every beat of its story/carousel sequence, scaled down onto one page.
|
|
3
|
+
* Preview only — never screenshotted per-placement, never assertion-checked
|
|
4
|
+
* (a bad frame still shows up in the sheet so a human can see what broke).
|
|
5
|
+
*
|
|
6
|
+
* Spec shape (set by job/render.mjs via page.addInitScript before this page
|
|
7
|
+
* loads):
|
|
8
|
+
* {
|
|
9
|
+
* creative, brand, // see README.md's Creative/Brand shapes
|
|
10
|
+
* placementKeys: [...], // the render's placements (spec.placements from
|
|
11
|
+
* // the backend); ignored when the creative has a
|
|
12
|
+
* // sequence, which always renders every beat
|
|
13
|
+
* placements?, layouts?, // registry overrides (default: the shipped ones)
|
|
14
|
+
* }
|
|
15
|
+
*
|
|
16
|
+
* Result contract: window.__RENDER_RESULT = { ok: true, width, height } |
|
|
17
|
+
* { ok: false, error }; window.__ready = true once the sheet has been built,
|
|
18
|
+
* fitted and scaled.
|
|
19
|
+
*/
|
|
20
|
+
import { resolveAd, resolveSequence, renderBoard, fitAll } from './render.js';
|
|
21
|
+
import { placements as DEFAULT_PLACEMENTS } from './placements.js';
|
|
22
|
+
import { layouts as DEFAULT_LAYOUTS } from './layouts/index.js';
|
|
23
|
+
import { ensureBrandFont } from './font.js';
|
|
24
|
+
|
|
25
|
+
const SHEET_SCALE = 0.34;
|
|
26
|
+
|
|
27
|
+
function finish(result) {
|
|
28
|
+
window.__RENDER_RESULT = result;
|
|
29
|
+
window.__ready = true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function main() {
|
|
33
|
+
const spec = window.__RENDER_SPEC__;
|
|
34
|
+
if (!spec || !spec.creative) {
|
|
35
|
+
finish({ ok: false, error: 'no render spec provided (window.__RENDER_SPEC__)' });
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const placementsReg = spec.placements || DEFAULT_PLACEMENTS;
|
|
39
|
+
const layoutsReg = spec.layouts || DEFAULT_LAYOUTS;
|
|
40
|
+
const sheet = document.getElementById('sheet');
|
|
41
|
+
const boards = [];
|
|
42
|
+
|
|
43
|
+
function addCell(ad, placementKey, label) {
|
|
44
|
+
const placement = placementsReg[placementKey];
|
|
45
|
+
if (!placement) throw new Error(`unknown placement "${placementKey}"`);
|
|
46
|
+
const cell = document.createElement('div');
|
|
47
|
+
cell.className = 'cell';
|
|
48
|
+
const shell = document.createElement('div');
|
|
49
|
+
shell.className = 'shell';
|
|
50
|
+
shell.style.width = Math.round(placement.w * SHEET_SCALE) + 'px';
|
|
51
|
+
shell.style.height = Math.round(placement.h * SHEET_SCALE) + 'px';
|
|
52
|
+
const board = renderBoard(ad, placementKey, placementsReg, layoutsReg, spec.brand || {});
|
|
53
|
+
shell.appendChild(board);
|
|
54
|
+
cell.appendChild(shell);
|
|
55
|
+
const cap = document.createElement('div');
|
|
56
|
+
cap.className = 'cap';
|
|
57
|
+
cap.textContent = label;
|
|
58
|
+
cell.appendChild(cap);
|
|
59
|
+
sheet.appendChild(cell);
|
|
60
|
+
boards.push(board);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await ensureBrandFont(spec.brand);
|
|
65
|
+
const isSequence = spec.creative.sequenceKind && Array.isArray(spec.creative.sequence) && spec.creative.sequence.length;
|
|
66
|
+
if (isSequence) {
|
|
67
|
+
const frames = resolveSequence(spec.creative, placementsReg, layoutsReg);
|
|
68
|
+
const key = spec.creative.sequenceKind === 'carousel' ? spec.creative.carouselRatio || '4x5' : 'story';
|
|
69
|
+
frames.forEach((frame, i) => addCell(frame, key, `${i + 1} of ${frames.length}`));
|
|
70
|
+
} else {
|
|
71
|
+
const ad = resolveAd(spec.creative, placementsReg, layoutsReg);
|
|
72
|
+
const keys = Array.isArray(spec.placementKeys) && spec.placementKeys.length
|
|
73
|
+
? spec.placementKeys
|
|
74
|
+
: Object.keys(placementsReg).filter((k) => placementsReg[k].default);
|
|
75
|
+
keys.forEach((key) => addCell(ad, key, (placementsReg[key] && placementsReg[key].label) || key));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Fit every board at its TRUE, unscaled size first (a CSS transform
|
|
79
|
+
// shrinks the measured rect and would throw fitBoard's line-count math
|
|
80
|
+
// off), then apply the display scale — the same order mount.js uses.
|
|
81
|
+
await fitAll(sheet, placementsReg);
|
|
82
|
+
boards.forEach((board) => {
|
|
83
|
+
board.style.transformOrigin = 'top left';
|
|
84
|
+
board.style.transform = `scale(${SHEET_SCALE})`;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const rect = sheet.getBoundingClientRect();
|
|
88
|
+
finish({ ok: true, width: Math.round(rect.width), height: Math.round(rect.height) });
|
|
89
|
+
} catch (err) {
|
|
90
|
+
finish({ ok: false, error: String((err && err.message) || err) });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
main();
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/* styles.css — the single stylesheet a consumer needs: tokens.css + frame.css
|
|
2
|
+
* + every layouts/<name>/layout.css, concatenated in registry order so a
|
|
3
|
+
* layout can rely on frame.css cascading first. This file is generated by
|
|
4
|
+
* hand-concatenation (no build step); if you edit tokens.css, frame.css, or
|
|
5
|
+
* a layout.css, re-run the same concatenation (or copy the change into both
|
|
6
|
+
* the source file and here) and let test/registry.test.mjs catch drift on
|
|
7
|
+
* the individual files it checks. A consumer that only ever wants one board
|
|
8
|
+
* on the page can import this single file; components/layouts.js callers
|
|
9
|
+
* that want to import files individually still can (see README).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* ── from tokens.css ── */
|
|
13
|
+
/* tokens.css — neutral fallback tokens for the creative artboard.
|
|
14
|
+
*
|
|
15
|
+
* This package renders no brand: every color and font here is a plain,
|
|
16
|
+
* unbranded default so a board with no brand tokens applied still renders
|
|
17
|
+
* something legible. The real values come from a workspace's Brand Kit,
|
|
18
|
+
* applied at render time as inline custom properties on the board's own root
|
|
19
|
+
* element (see render.js's applyBrandTokens / mount.js's mountBoard) — never
|
|
20
|
+
* written here and never on `:root`, so two different kits can render on the
|
|
21
|
+
* same page (e.g. two creatives side by side) without colliding.
|
|
22
|
+
*
|
|
23
|
+
* Scoped to .cc-board so it cannot collide with the host app's own styles.
|
|
24
|
+
* All custom properties are prefixed --cc- for the same reason.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
.cc-board {
|
|
28
|
+
--cc-accent: #6B7280; /* neutral accent; a kit's `tokens.accent` overrides this */
|
|
29
|
+
--cc-accent-2: #4B5563; /* a kit's `tokens.accent2` overrides this */
|
|
30
|
+
|
|
31
|
+
--cc-d-bg: #1C1C1E; /* dark artboard background and scrim end stop */
|
|
32
|
+
--cc-d-ink: #FFFFFF; /* headline on dark */
|
|
33
|
+
|
|
34
|
+
--cc-l-bg: #F2F2F7; /* light artboard background */
|
|
35
|
+
--cc-l-card: #FFFFFF; /* light-ground card/chip background */
|
|
36
|
+
--cc-ink: #222222; /* headline on light */
|
|
37
|
+
--cc-ink-2: #717171; /* body on light */
|
|
38
|
+
|
|
39
|
+
--cc-sky: #4AADDB; /* safe-zone review guide only, never in an export */
|
|
40
|
+
|
|
41
|
+
--cc-elev-overlay: 0 8px 24px rgba(0,0,0,0.12);
|
|
42
|
+
--cc-elev-high: 0 8px 32px rgba(0,0,0,0.10);
|
|
43
|
+
|
|
44
|
+
--cc-font: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ── from frame.css ── */
|
|
48
|
+
/* frame.css — the shared creative artboard rules: everything a layout does
|
|
49
|
+
* not own. Ratios and layouts are both data, not hardcoded here.
|
|
50
|
+
*
|
|
51
|
+
* Placements (artboard sizes and per-placement numbers: lockup, body, cta,
|
|
52
|
+
* card padding) live in placements.json / placements.js. render.js reads
|
|
53
|
+
* them and sets CSS custom properties on the board (--cc-board-w,
|
|
54
|
+
* --cc-lockup-gap, --cc-body-size, ...).
|
|
55
|
+
*
|
|
56
|
+
* Layouts (structural families: stacked, bleed, card, split, and any added
|
|
57
|
+
* later) live in layouts/<name>/layout.json + layout.css. This file keeps
|
|
58
|
+
* only what every layout shares: the artboard, the lockup, the headline,
|
|
59
|
+
* the body, the cta row, sequence anchoring, and the safe-zone review
|
|
60
|
+
* guide.
|
|
61
|
+
*
|
|
62
|
+
* NOTHING structural here may be position:absolute. A PNG exporter that
|
|
63
|
+
* screenshots this DOM (the future render job) can drop absolutely
|
|
64
|
+
* positioned layers, so every photo and scrim stays in normal flow: photos
|
|
65
|
+
* are painted as background-image on the artboard or a band, scrims are
|
|
66
|
+
* background-image on the text panel.
|
|
67
|
+
*
|
|
68
|
+
* This file has no @import and no font-face: fonts are brand data (a kit's
|
|
69
|
+
* `fontUrl`), loaded by mount.js's loadFont(), never bundled here.
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/* ── Artboard ─────────────────────────────────────────────────────────────── */
|
|
73
|
+
.cc-board {
|
|
74
|
+
position: relative;
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
box-sizing: border-box;
|
|
77
|
+
font-family: var(--cc-font);
|
|
78
|
+
background-color: var(--cc-d-bg);
|
|
79
|
+
-webkit-font-smoothing: antialiased;
|
|
80
|
+
flex: none;
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
width: var(--cc-board-w);
|
|
84
|
+
height: var(--cc-board-h);
|
|
85
|
+
}
|
|
86
|
+
.cc-board--light { background-color: var(--cc-l-bg); }
|
|
87
|
+
|
|
88
|
+
.cc-board__panel {
|
|
89
|
+
position: relative; /* keeps the panel above the glow */
|
|
90
|
+
box-sizing: border-box;
|
|
91
|
+
display: flex; flex-direction: column; align-items: flex-start;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ── Sequence frames (stories and carousels) ──────────────────────────────── */
|
|
95
|
+
/* Anchored to the bottom on every frame so the eye does not have to re-find the
|
|
96
|
+
copy between taps or swipes. The CTA appears only on the last frame. */
|
|
97
|
+
.cc-board--seq .cc-lockup { position: absolute; top: var(--cc-safe-top, 48px); left: 88px; }
|
|
98
|
+
|
|
99
|
+
/* ── Lockup ───────────────────────────────────────────────────────────────── */
|
|
100
|
+
/* Renders only when the brand kit has a mark and/or a wordmark image; a kit
|
|
101
|
+
with neither renders no lockup at all (see render.js's buildLockup). */
|
|
102
|
+
.cc-lockup { display: inline-flex; align-items: center; flex: none; align-self: flex-start; gap: var(--cc-lockup-gap); }
|
|
103
|
+
.cc-lockup__icon { display: block; width: var(--cc-icon-size); height: var(--cc-icon-size); border-radius: var(--cc-icon-radius); }
|
|
104
|
+
.cc-lockup__mark { display: block; height: var(--cc-mark-height); }
|
|
105
|
+
.cc-lockup--spacer { visibility: hidden; }
|
|
106
|
+
|
|
107
|
+
/* Chip variant: a white pill behind the lockup, for busy photography (ported
|
|
108
|
+
from nexus's schema.json `lockup: "chip"` / render.js). `--cc-l-card` and
|
|
109
|
+
`--cc-elev-overlay` are tokens.css's neutral light-ground surface and
|
|
110
|
+
shadow — brand-agnostic, so a white pill works over any brand's photo. */
|
|
111
|
+
.cc-lockup--chip {
|
|
112
|
+
border-radius: 999px;
|
|
113
|
+
background: var(--cc-l-card);
|
|
114
|
+
box-shadow: var(--cc-elev-overlay);
|
|
115
|
+
padding: var(--cc-chip-pad);
|
|
116
|
+
gap: var(--cc-chip-gap);
|
|
117
|
+
}
|
|
118
|
+
.cc-lockup--chip .cc-lockup__icon {
|
|
119
|
+
width: var(--cc-chip-icon, var(--cc-icon-size));
|
|
120
|
+
height: var(--cc-chip-icon, var(--cc-icon-size));
|
|
121
|
+
border-radius: var(--cc-chip-icon-radius, var(--cc-icon-radius));
|
|
122
|
+
}
|
|
123
|
+
.cc-lockup--chip .cc-lockup__mark { height: var(--cc-chip-mark, var(--cc-mark-height)); }
|
|
124
|
+
|
|
125
|
+
/* ── Headline ─────────────────────────────────────────────────────────────── */
|
|
126
|
+
.cc-headline {
|
|
127
|
+
margin: 0;
|
|
128
|
+
font-size: var(--cc-h-size);
|
|
129
|
+
line-height: var(--cc-h-leading, 1.04);
|
|
130
|
+
font-weight: 800;
|
|
131
|
+
letter-spacing: var(--cc-h-track, -2.3px);
|
|
132
|
+
color: var(--cc-d-ink);
|
|
133
|
+
text-wrap: balance;
|
|
134
|
+
}
|
|
135
|
+
.cc-board--light .cc-headline { color: var(--cc-ink); }
|
|
136
|
+
.cc-headline em { font-style: italic; color: var(--cc-accent); }
|
|
137
|
+
|
|
138
|
+
/* ── Body ─────────────────────────────────────────────────────────────────── */
|
|
139
|
+
.cc-body {
|
|
140
|
+
margin: 0;
|
|
141
|
+
font-weight: 500;
|
|
142
|
+
text-wrap: pretty;
|
|
143
|
+
font-size: var(--cc-body-size);
|
|
144
|
+
line-height: var(--cc-body-lh);
|
|
145
|
+
max-width: var(--cc-body-max-w);
|
|
146
|
+
color: var(--cc-body-color);
|
|
147
|
+
}
|
|
148
|
+
.cc-board--light .cc-body { font-size: var(--cc-light-body-size, var(--cc-body-size)); color: var(--cc-ink-2); }
|
|
149
|
+
|
|
150
|
+
/* ── CTA row ──────────────────────────────────────────────────────────────── */
|
|
151
|
+
/* Renders only when the kit has a badge image and/or a ctaClaim string; a kit
|
|
152
|
+
with neither renders no cta row at all (see render.js's buildCta). */
|
|
153
|
+
.cc-cta { display: flex; align-items: center; flex-wrap: wrap; flex: none; gap: var(--cc-cta-gap); margin-top: var(--cc-cta-margin-top, 0); }
|
|
154
|
+
|
|
155
|
+
/* The badge is brand artwork (a kit's `logos.badge`): sized by height only in
|
|
156
|
+
render.js, its own intrinsic aspect ratio preserved. Nothing here may
|
|
157
|
+
restyle it — no border, no border-radius, no filter, no shadow, no overlay —
|
|
158
|
+
since a store badge (Apple's, Google's, or a kit's own) typically comes with
|
|
159
|
+
guidelines against exactly that. */
|
|
160
|
+
.cc-badge { display: block; flex: none; width: auto; }
|
|
161
|
+
|
|
162
|
+
.cc-cta__note { font-weight: 600; font-size: var(--cc-note-size); color: var(--cc-note-color); }
|
|
163
|
+
.cc-board--light .cc-cta__note { color: var(--cc-ink-2); }
|
|
164
|
+
|
|
165
|
+
/* ── Safe zones (review guide only, never exported) ───────────────────────── */
|
|
166
|
+
.cc-safe { position: absolute; inset: 0; pointer-events: none; }
|
|
167
|
+
.cc-safe__top { position: absolute; top: 0; left: 0; right: 0; height: var(--cc-safe-top);
|
|
168
|
+
background: rgba(74,173,219,0.28); border-bottom: 2px dashed var(--cc-sky); }
|
|
169
|
+
.cc-safe__bottom { position: absolute; bottom: 0; left: 0; right: 0; height: var(--cc-safe-bottom);
|
|
170
|
+
background: rgba(74,173,219,0.28); border-top: 2px dashed var(--cc-sky); }
|
|
171
|
+
.cc-safe__right { position: absolute; bottom: var(--cc-safe-bottom); top: var(--cc-safe-top); right: 0;
|
|
172
|
+
width: var(--cc-safe-right);
|
|
173
|
+
background: rgba(74,173,219,0.20); border-left: 2px dashed var(--cc-sky); }
|
|
174
|
+
|
|
175
|
+
/* ── from layouts/stacked/layout.css ── */
|
|
176
|
+
/* stacked — photo band pinned top over a solid text panel. */
|
|
177
|
+
|
|
178
|
+
.cc-board__band {
|
|
179
|
+
position: relative;
|
|
180
|
+
flex: 0 0 auto;
|
|
181
|
+
height: var(--cc-band-h);
|
|
182
|
+
overflow: hidden;
|
|
183
|
+
margin-bottom: -2px; /* hides a subpixel seam against the panel below */
|
|
184
|
+
}
|
|
185
|
+
.cc-board__band img {
|
|
186
|
+
position: absolute; inset: 0;
|
|
187
|
+
width: 100%; height: 100%;
|
|
188
|
+
object-fit: cover;
|
|
189
|
+
object-position: var(--cc-focal, 50% 30%);
|
|
190
|
+
}
|
|
191
|
+
.cc-board__scrim {
|
|
192
|
+
position: absolute; left: 0; right: 0; bottom: 0; height: 220px;
|
|
193
|
+
background: linear-gradient(to top,
|
|
194
|
+
var(--cc-d-bg) 0%, var(--cc-d-bg) 18%,
|
|
195
|
+
rgba(28,28,30,0.75) 42%, rgba(28,28,30,0.25) 72%, rgba(28,28,30,0) 100%);
|
|
196
|
+
}
|
|
197
|
+
.cc-board__glow {
|
|
198
|
+
position: absolute; right: -200px; bottom: -260px;
|
|
199
|
+
width: 720px; height: 720px; border-radius: 50%;
|
|
200
|
+
background: radial-gradient(circle, color-mix(in srgb, var(--cc-accent) 13%, transparent), transparent 68%);
|
|
201
|
+
pointer-events: none;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.cc-board--stacked .cc-board__panel { flex: 1 1 auto; min-height: 0; }
|
|
205
|
+
.cc-r-9x16.cc-board--stacked .cc-board__panel,
|
|
206
|
+
.cc-r-story.cc-board--stacked .cc-board__panel {
|
|
207
|
+
padding: 88px 88px var(--cc-pad-b, var(--cc-safe-bottom, 430px));
|
|
208
|
+
justify-content: space-between;
|
|
209
|
+
gap: 26px;
|
|
210
|
+
}
|
|
211
|
+
.cc-r-4x5.cc-board--stacked .cc-board__panel {
|
|
212
|
+
padding: 0 88px 88px;
|
|
213
|
+
justify-content: center;
|
|
214
|
+
gap: 20px;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* ── from layouts/bleed/layout.css ── */
|
|
218
|
+
/* bleed — photo covers the whole artboard, text sits over a scrim. */
|
|
219
|
+
|
|
220
|
+
.cc-board--bleed {
|
|
221
|
+
background-image: var(--cc-photo);
|
|
222
|
+
background-size: cover;
|
|
223
|
+
background-position: var(--cc-focal, 50% 50%);
|
|
224
|
+
justify-content: flex-end;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* Fill variant (9x16, 4x5, story): the scrim covers the artboard and the
|
|
228
|
+
copy is distributed from top to bottom over it. */
|
|
229
|
+
.cc-board__panel--fill {
|
|
230
|
+
flex: 1 1 auto;
|
|
231
|
+
height: 100%;
|
|
232
|
+
justify-content: space-between;
|
|
233
|
+
}
|
|
234
|
+
.cc-r-9x16 .cc-board__panel--fill,
|
|
235
|
+
.cc-r-story .cc-board__panel--fill {
|
|
236
|
+
padding: 48px 88px var(--cc-safe-bottom, 430px);
|
|
237
|
+
background-image: linear-gradient(to top,
|
|
238
|
+
rgba(0,0,0,0.94) 0%, rgba(0,0,0,0.88) 42%,
|
|
239
|
+
rgba(0,0,0,0.68) 62%, rgba(0,0,0,0.34) 80%, rgba(0,0,0,0.55) 100%);
|
|
240
|
+
}
|
|
241
|
+
.cc-r-4x5 .cc-board__panel--fill {
|
|
242
|
+
padding: 48px 88px 96px;
|
|
243
|
+
background-image: linear-gradient(to top,
|
|
244
|
+
rgba(0,0,0,0.94) 0%, rgba(0,0,0,0.88) 48%,
|
|
245
|
+
rgba(0,0,0,0.62) 70%, rgba(0,0,0,0.30) 86%, rgba(0,0,0,0.50) 100%);
|
|
246
|
+
}
|
|
247
|
+
/* The chip's own padding already reads as a visual left inset over a full-bleed photo; nudge it back
|
|
248
|
+
flush with the panel's other content (ported from nexus's bleed layout.css). */
|
|
249
|
+
.cc-board__panel--fill .cc-lockup--chip { margin-left: -40px; }
|
|
250
|
+
|
|
251
|
+
/* Sequence frames (stories and carousels): anchored bottom so the eye does
|
|
252
|
+
not have to re-find the copy between taps or swipes. */
|
|
253
|
+
.cc-board--seq .cc-board__panel--fill { justify-content: flex-end; gap: 28px; }
|
|
254
|
+
.cc-board--story.cc-board--bleed .cc-board__panel--fill { padding-top: var(--cc-safe-top, 250px); }
|
|
255
|
+
|
|
256
|
+
/* Bottom variant (every 1x1, forced via perPlacement). */
|
|
257
|
+
.cc-board__panel--bottom {
|
|
258
|
+
flex: 0 0 auto;
|
|
259
|
+
padding: 220px 72px 48px;
|
|
260
|
+
gap: 14px;
|
|
261
|
+
background-image: linear-gradient(to top,
|
|
262
|
+
rgba(28,28,30,0.97) 0%, rgba(28,28,30,0.95) 42%,
|
|
263
|
+
rgba(28,28,30,0.74) 66%, rgba(28,28,30,0.26) 88%, rgba(28,28,30,0) 100%);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* ── from layouts/card/layout.css ── */
|
|
267
|
+
/* card — the light layout: inset rounded photo card on a light page. */
|
|
268
|
+
|
|
269
|
+
.cc-board--card { padding: var(--cc-card-pad); gap: var(--cc-card-gap); }
|
|
270
|
+
|
|
271
|
+
.cc-board__card {
|
|
272
|
+
position: relative;
|
|
273
|
+
width: 100%;
|
|
274
|
+
height: var(--cc-band-h);
|
|
275
|
+
flex: 0 0 auto;
|
|
276
|
+
border-radius: 24px;
|
|
277
|
+
overflow: hidden;
|
|
278
|
+
box-shadow: var(--cc-elev-high);
|
|
279
|
+
}
|
|
280
|
+
.cc-board__card img {
|
|
281
|
+
position: absolute; inset: 0;
|
|
282
|
+
width: 100%; height: 100%;
|
|
283
|
+
object-fit: cover;
|
|
284
|
+
object-position: var(--cc-focal, 50% 40%);
|
|
285
|
+
}
|
|
286
|
+
.cc-board--card .cc-board__panel {
|
|
287
|
+
flex: 1 1 auto; min-height: 0;
|
|
288
|
+
justify-content: center;
|
|
289
|
+
gap: 28px;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* ── from layouts/split/layout.css ── */
|
|
293
|
+
/* split — photo on the top half, plain panel below, no scrim. Proves a fourth
|
|
294
|
+
layout needs only this folder plus an entry in layouts/index.json. */
|
|
295
|
+
|
|
296
|
+
.cc-board--split .cc-board__band {
|
|
297
|
+
position: relative;
|
|
298
|
+
flex: 0 0 auto;
|
|
299
|
+
height: var(--cc-band-h);
|
|
300
|
+
overflow: hidden;
|
|
301
|
+
}
|
|
302
|
+
.cc-board--split .cc-board__band img {
|
|
303
|
+
position: absolute; inset: 0;
|
|
304
|
+
width: 100%; height: 100%;
|
|
305
|
+
object-fit: cover;
|
|
306
|
+
object-position: var(--cc-focal, 50% 30%);
|
|
307
|
+
}
|
|
308
|
+
.cc-board--split .cc-board__panel {
|
|
309
|
+
flex: 1 1 auto; min-height: 0;
|
|
310
|
+
padding: 72px 88px;
|
|
311
|
+
justify-content: center;
|
|
312
|
+
gap: 24px;
|
|
313
|
+
background-color: var(--cc-d-bg);
|
|
314
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* tokens.css — neutral fallback tokens for the creative artboard.
|
|
2
|
+
*
|
|
3
|
+
* This package renders no brand: every color and font here is a plain,
|
|
4
|
+
* unbranded default so a board with no brand tokens applied still renders
|
|
5
|
+
* something legible. The real values come from a workspace's Brand Kit,
|
|
6
|
+
* applied at render time as inline custom properties on the board's own root
|
|
7
|
+
* element (see render.js's applyBrandTokens / mount.js's mountBoard) — never
|
|
8
|
+
* written here and never on `:root`, so two different kits can render on the
|
|
9
|
+
* same page (e.g. two creatives side by side) without colliding.
|
|
10
|
+
*
|
|
11
|
+
* Scoped to .cc-board so it cannot collide with the host app's own styles.
|
|
12
|
+
* All custom properties are prefixed --cc- for the same reason.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
.cc-board {
|
|
16
|
+
--cc-accent: #6B7280; /* neutral accent; a kit's `tokens.accent` overrides this */
|
|
17
|
+
--cc-accent-2: #4B5563; /* a kit's `tokens.accent2` overrides this */
|
|
18
|
+
|
|
19
|
+
--cc-d-bg: #1C1C1E; /* dark artboard background and scrim end stop */
|
|
20
|
+
--cc-d-ink: #FFFFFF; /* headline on dark */
|
|
21
|
+
|
|
22
|
+
--cc-l-bg: #F2F2F7; /* light artboard background */
|
|
23
|
+
--cc-l-card: #FFFFFF; /* light-ground card/chip background */
|
|
24
|
+
--cc-ink: #222222; /* headline on light */
|
|
25
|
+
--cc-ink-2: #717171; /* body on light */
|
|
26
|
+
|
|
27
|
+
--cc-sky: #4AADDB; /* safe-zone review guide only, never in an export */
|
|
28
|
+
|
|
29
|
+
--cc-elev-overlay: 0 8px 24px rgba(0,0,0,0.12);
|
|
30
|
+
--cc-elev-high: 0 8px 32px rgba(0,0,0,0.10);
|
|
31
|
+
|
|
32
|
+
--cc-font: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
33
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ import { registerConfig } from './commands/config.js';
|
|
|
14
14
|
import { registerDashboard } from './commands/dashboard.js';
|
|
15
15
|
import { registerStatus } from './commands/status.js';
|
|
16
16
|
import { registerLint } from './commands/lint.js';
|
|
17
|
+
import { registerCreative } from './commands/creative.js';
|
|
17
18
|
const program = new Command();
|
|
18
19
|
program
|
|
19
20
|
.name('conductor')
|
|
@@ -30,5 +31,6 @@ registerConfig(program);
|
|
|
30
31
|
registerDashboard(program);
|
|
31
32
|
registerStatus(program);
|
|
32
33
|
registerLint(program);
|
|
34
|
+
registerCreative(program);
|
|
33
35
|
program.parse();
|
|
34
36
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC9C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC9C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEzD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;AAE7B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,qDAAqD,CAAC;KAClE,OAAO,CAAC,OAAO,CAAC,CAAA;AAEnB,aAAa,CAAC,OAAO,CAAC,CAAA;AACtB,cAAc,CAAC,OAAO,CAAC,CAAA;AACvB,YAAY,CAAC,OAAO,CAAC,CAAA;AACrB,cAAc,CAAC,OAAO,CAAC,CAAA;AACvB,aAAa,CAAC,OAAO,CAAC,CAAA;AACtB,YAAY,CAAC,OAAO,CAAC,CAAA;AACrB,WAAW,CAAC,OAAO,CAAC,CAAA;AACpB,cAAc,CAAC,OAAO,CAAC,CAAA;AACvB,iBAAiB,CAAC,OAAO,CAAC,CAAA;AAC1B,cAAc,CAAC,OAAO,CAAC,CAAA;AACvB,YAAY,CAAC,OAAO,CAAC,CAAA;AACrB,gBAAgB,CAAC,OAAO,CAAC,CAAA;AAEzB,OAAO,CAAC,KAAK,EAAE,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finds a locally-installed browser to render Creatives with, using `playwright-core` (a dependency
|
|
3
|
+
* of this package, not the browser-bundling `playwright`). `conductor-creative/job/render.mjs`'s own
|
|
4
|
+
* CLI bootstrap depends on full `playwright` (it ships its own Chromium download) because it's meant
|
|
5
|
+
* to run inside a container built for that purpose; the CLI/MCP server run on a user's own machine,
|
|
6
|
+
* so they discover whatever Chromium-family browser is already there instead of bundling one — the
|
|
7
|
+
* same shape nexus-marketing's `rexipe-social` MCP server used.
|
|
8
|
+
*
|
|
9
|
+
* `playwright-core` itself is lazy-imported (see `findBrowserFactory`) so requiring it doesn't add
|
|
10
|
+
* to MCP server startup cost for sessions that never call a creative tool.
|
|
11
|
+
*/
|
|
12
|
+
import type { Browser } from 'playwright-core';
|
|
13
|
+
export type BrowserFactory = () => Promise<Browser>;
|
|
14
|
+
export declare const CHROME_INSTALL_HINT: string;
|
|
15
|
+
/**
|
|
16
|
+
* Returns a factory that, when the render core first asks for a browser, launches the first one it can
|
|
17
|
+
* find in the order above. Launching is deferred to that call on purpose: `run()` fetches the spec before
|
|
18
|
+
* it asks for a browser, and a browser launched ahead of a spec fetch that then fails is never closed —
|
|
19
|
+
* it keeps the CLI process alive and leaks a Chrome process from the MCP server.
|
|
20
|
+
*
|
|
21
|
+
* The factory throws `CHROME_INSTALL_HINT` plus what each attempt failed with when nothing launches;
|
|
22
|
+
* `run()` catches that and reports it as the render's failure.
|
|
23
|
+
*/
|
|
24
|
+
export declare function findBrowserFactory(log?: (...args: unknown[]) => void): Promise<BrowserFactory>;
|
|
25
|
+
//# sourceMappingURL=creative-browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creative-browser.d.ts","sourceRoot":"","sources":["../../src/lib/creative-browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,iBAAiB,CAAA;AAE7D,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;AAEnD,eAAO,MAAM,mBAAmB,QAI4C,CAAA;AAa5E;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,GAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAe9G"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export const CHROME_INSTALL_HINT = 'No installed browser could be launched for creative rendering. Try one of:\n' +
|
|
2
|
+
' - Google Chrome (launched via Playwright channel "chrome")\n' +
|
|
3
|
+
' - Microsoft Edge (launched via Playwright channel "msedge")\n' +
|
|
4
|
+
' - a Playwright-managed Chromium: run `npx playwright install chromium`';
|
|
5
|
+
const ATTEMPTS = [
|
|
6
|
+
{ label: 'system Chrome (channel: chrome)', options: { channel: 'chrome' } },
|
|
7
|
+
{ label: 'system Edge (channel: msedge)', options: { channel: 'msedge' } },
|
|
8
|
+
{ label: 'Playwright-managed Chromium', options: {} },
|
|
9
|
+
];
|
|
10
|
+
/**
|
|
11
|
+
* Returns a factory that, when the render core first asks for a browser, launches the first one it can
|
|
12
|
+
* find in the order above. Launching is deferred to that call on purpose: `run()` fetches the spec before
|
|
13
|
+
* it asks for a browser, and a browser launched ahead of a spec fetch that then fails is never closed —
|
|
14
|
+
* it keeps the CLI process alive and leaks a Chrome process from the MCP server.
|
|
15
|
+
*
|
|
16
|
+
* The factory throws `CHROME_INSTALL_HINT` plus what each attempt failed with when nothing launches;
|
|
17
|
+
* `run()` catches that and reports it as the render's failure.
|
|
18
|
+
*/
|
|
19
|
+
export async function findBrowserFactory(log = () => { }) {
|
|
20
|
+
const { chromium } = await import('playwright-core');
|
|
21
|
+
return async () => {
|
|
22
|
+
const errors = [];
|
|
23
|
+
for (const attempt of ATTEMPTS) {
|
|
24
|
+
try {
|
|
25
|
+
const browser = await chromium.launch(attempt.options);
|
|
26
|
+
log(`using ${attempt.label}`);
|
|
27
|
+
return browser;
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
errors.push(`${attempt.label}: ${err instanceof Error ? err.message : String(err)}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
throw new Error(`${CHROME_INSTALL_HINT}\n\nTried:\n${errors.map((e) => ` - ${e}`).join('\n')}`);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=creative-browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creative-browser.js","sourceRoot":"","sources":["../../src/lib/creative-browser.ts"],"names":[],"mappings":"AAeA,MAAM,CAAC,MAAM,mBAAmB,GAC9B,8EAA8E;IAC9E,gEAAgE;IAChE,iEAAiE;IACjE,0EAA0E,CAAA;AAO5E,MAAM,QAAQ,GAAc;IAC1B,EAAE,KAAK,EAAE,iCAAiC,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;IAC5E,EAAE,KAAK,EAAE,+BAA+B,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;IAC1E,EAAE,KAAK,EAAE,6BAA6B,EAAE,OAAO,EAAE,EAAE,EAAE;CACtD,CAAA;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAoC,GAAG,EAAE,GAAE,CAAC;IACnF,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAA;IACpD,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;gBACtD,GAAG,CAAC,SAAS,OAAO,CAAC,KAAK,EAAE,CAAC,CAAA;gBAC7B,OAAO,OAAO,CAAA;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACtF,CAAC;QACH,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,GAAG,mBAAmB,eAAe,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IAClG,CAAC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creatives are shown to people by display id — "12a", "12b" — on the web page, in list_creatives and in
|
|
3
|
+
* the copyable "render this" commands, while the API addresses them by UUID. Tools and the CLI accept
|
|
4
|
+
* either and resolve a display id here, so a model or a person can pass what they were shown.
|
|
5
|
+
*/
|
|
6
|
+
import type { Config } from '../mcp/config.js';
|
|
7
|
+
export declare function resolveCreativeId(idOrDisplayId: string, config: Config): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=creative-id.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creative-id.d.ts","sourceRoot":"","sources":["../../src/lib/creative-id.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAM9C,wBAAsB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU9F"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { apiGet } from '../mcp/api.js';
|
|
2
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3
|
+
const DISPLAY_ID = /^\d+[a-z]$/i;
|
|
4
|
+
export async function resolveCreativeId(idOrDisplayId, config) {
|
|
5
|
+
const value = idOrDisplayId.trim();
|
|
6
|
+
if (UUID.test(value) || !DISPLAY_ID.test(value))
|
|
7
|
+
return value;
|
|
8
|
+
const creatives = await apiGet(`/api/v2/projects/${config.projectId}/marketing/creatives`, config);
|
|
9
|
+
const match = creatives.find((c) => (c.displayId ?? '').toLowerCase() === value.toLowerCase());
|
|
10
|
+
if (!match)
|
|
11
|
+
throw new Error(`No Creative ${value} in this workspace — call list_creatives for the ids that exist.`);
|
|
12
|
+
return match.id;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=creative-id.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creative-id.js","sourceRoot":"","sources":["../../src/lib/creative-id.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAEtC,MAAM,IAAI,GAAG,iEAAiE,CAAA;AAC9E,MAAM,UAAU,GAAG,aAAa,CAAA;AAEhC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,aAAqB,EAAE,MAAc;IAC3E,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,EAAE,CAAA;IAClC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC7D,MAAM,SAAS,GAAG,MAAM,MAAM,CAC5B,oBAAoB,MAAM,CAAC,SAAS,sBAAsB,EAC1D,MAAM,CACP,CAAA;IACD,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;IAC9F,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,eAAe,KAAK,kEAAkE,CAAC,CAAA;IACnH,OAAO,KAAK,CAAC,EAAE,CAAA;AACjB,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Config } from '../mcp/config.js';
|
|
2
|
+
export interface RenderFrame {
|
|
3
|
+
id?: string;
|
|
4
|
+
placementKey: string;
|
|
5
|
+
platform?: string;
|
|
6
|
+
sequenceIndex?: number | null;
|
|
7
|
+
url?: string;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
sizeBytes?: number;
|
|
11
|
+
warnings?: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface RenderCreativeParams {
|
|
14
|
+
creativeId: string;
|
|
15
|
+
previewOnly?: boolean;
|
|
16
|
+
renderer?: string;
|
|
17
|
+
workflowRunId?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface RenderCreativeResult {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
renderId?: string;
|
|
22
|
+
state?: string;
|
|
23
|
+
error?: string | null;
|
|
24
|
+
frames: RenderFrame[];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Renders a Creative end to end: finds a local browser, runs the render core against the external
|
|
28
|
+
* API, and reads back the stored render (frames with signed URLs, final state) whether or not the
|
|
29
|
+
* render itself succeeded — a FAILED render still has an id and an error message worth returning.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderCreative(params: RenderCreativeParams, config: Config, log?: (...args: unknown[]) => void): Promise<RenderCreativeResult>;
|
|
32
|
+
//# sourceMappingURL=creative-render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"creative-render.d.ts","sourceRoot":"","sources":["../../src/lib/creative-render.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAI9C,MAAM,WAAW,WAAW;IAC1B,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAA;IACX,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,EAAE,WAAW,EAAE,CAAA;CACtB;AAwDD;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,oBAAoB,EAC5B,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAe,GAC3C,OAAO,CAAC,oBAAoB,CAAC,CAiC/B"}
|