@easysociable/cli 0.1.1 → 0.1.3
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/README.md +29 -18
- package/content-engine/fixtures/golden-run-en/draft.md +13 -0
- package/content-engine/fixtures/golden-run-en/idea.md +26 -0
- package/content-engine/fixtures/golden-run-en/pack-tiktok.json +11 -0
- package/content-engine/scripts/contracts.mjs +183 -0
- package/content-engine/scripts/export-contracts.mjs +76 -0
- package/content-engine/scripts/fixture.test.mjs +24 -0
- package/content-engine/scripts/starter.mjs +55 -0
- package/content-engine/scripts/validate-export.mjs +42 -0
- package/content-engine/scripts/validate-pack.mjs +48 -0
- package/content-engine/scripts/validate-run.mjs +100 -0
- package/content-engine/scripts/validate.test.mjs +101 -0
- package/content-engine/scripts/workbench.mjs +135 -0
- package/content-engine/starter/CLAUDE.md +756 -0
- package/content-engine/starter/accounts/README.md +9 -0
- package/content-engine/starter/accounts/_index.md +31 -0
- package/content-engine/starter/accounts/overrides/README.md +3 -0
- package/content-engine/starter/discovery/README.md +18 -0
- package/content-engine/starter/discovery/_index.md +27 -0
- package/content-engine/starter/discovery/_template.md +41 -0
- package/content-engine/starter/engine.json +19 -0
- package/content-engine/starter/exports/README.md +28 -0
- package/content-engine/starter/inbox/README.md +9 -0
- package/content-engine/starter/inbox/_index.md +24 -0
- package/content-engine/starter/inbox/entries/README.md +3 -0
- package/content-engine/starter/platforms/README.md +19 -0
- package/content-engine/starter/platforms/ig.md +69 -0
- package/content-engine/starter/platforms/instagram.md +5 -0
- package/content-engine/starter/platforms/linkedin.md +72 -0
- package/content-engine/starter/platforms/threads.md +69 -0
- package/content-engine/starter/platforms/tiktok.md +71 -0
- package/content-engine/starter/platforms/x.md +78 -0
- package/content-engine/starter/platforms/xiaohongshu.md +19 -0
- package/content-engine/starter/profiles/_index.md +13 -0
- package/content-engine/starter/profiles/default/README.md +11 -0
- package/content-engine/starter/profiles/default/audience.md +38 -0
- package/content-engine/starter/profiles/default/pillars.md +33 -0
- package/content-engine/starter/profiles/default/voice.md +24 -0
- package/content-engine/starter/published/_index.md +20 -0
- package/content-engine/starter/raw/README.md +4 -0
- package/content-engine/starter/raw/_index.md +12 -0
- package/content-engine/starter/runs/README.md +22 -0
- package/content-engine/starter/runs/_index.md +41 -0
- package/content-engine/starter/runs/_template/brief.md +66 -0
- package/content-engine/starter/runs/_template/draft.md +11 -0
- package/content-engine/starter/runs/_template/editor.md +18 -0
- package/content-engine/starter/runs/_template/feedback.md +15 -0
- package/content-engine/starter/runs/_template/idea.md +27 -0
- package/content-engine/starter/runs/_template/pack.md +47 -0
- package/content-engine/starter/runs/_template/packet.md +25 -0
- package/content-engine/starter/runs/_template/rubric.md +19 -0
- package/content-engine/starter/swipe/_index.md +27 -0
- package/content-engine/starter/swipe/_template.md +34 -0
- package/content-engine/starter/topics/README.md +25 -0
- package/content-engine/starter/topics/WATCHLIST.md +65 -0
- package/content-engine/starter/topics/_index.md +44 -0
- package/content-engine/starter/topics/_template-item.md +90 -0
- package/content-engine/starter/topics/items/README.md +4 -0
- package/content-engine/starter/topics/scoring.md +201 -0
- package/content-engine/starter/topics/watchlists/README.md +10 -0
- package/content-engine/starter/topics/watchlists/_shared.md +14 -0
- package/content-engine/starter/topics/watchlists/default.md +6 -0
- package/content-engine/starter/wiki/README.md +13 -0
- package/content-engine/starter/wiki/atoms/_index.md +23 -0
- package/content-engine/starter/wiki/atoms/_template.md +31 -0
- package/content-engine/starter/wiki/claims/_index.md +26 -0
- package/content-engine/starter/wiki/claims/_template.md +38 -0
- package/content-engine/starter/workbench/README.md +17 -0
- package/content-engine/workbench/build.py +1074 -0
- package/content-engine/workbench/serve.py +53 -0
- package/content-engine/workbench/template.html +2119 -0
- package/dist/easysociable.js +804 -2903
- package/dist/easysociable.js.map +1 -1
- package/package.json +19 -13
- package/skills/ONBOARDING.md +20 -14
- package/skills/easysociable-content/SKILL.md +38 -102
- package/skills/easysociable-slideshow/SKILL.md +24 -16
- package/skills/easysociable-slideshow/references/VAULT.md +13 -11
- package/skills/easysociable-template/SKILL.md +26 -24
- package/skills/easysociable-template/references/CATALOG_METADATA.md +2 -2
- package/skills/easysociable-template/references/DESIGN_V2_1.md +53 -2
- package/skills/easysociable-template/references/GATEWAY.md +7 -2
- package/skills/easysociable-template/references/PLATFORM_SPECS.md +11 -3
- package/skills/easysociable-template/references/THEME_TOKENS.md +1 -1
- package/skills/easysociable-template/references/fixtures/image-reference.design-plan.json +74 -0
- package/skills/easysociable-template/references/fixtures/image-reference.visual-qa.json +28 -0
- package/skills/easysociable-template/scripts/generate-theme-from-hex.mjs +0 -0
- package/skills/easysociable-template/scripts/validate-design-v2.mjs +97 -0
- package/skills/easysociable-template/workflows/ASSET_PLAN.md +2 -1
- package/skills/easysociable-template/workflows/AUTHORING_FROM_BRIEF.md +4 -4
- package/skills/easysociable-template/workflows/AUTHORING_FROM_IMAGE.md +66 -16
- package/skills/easysociable-template/workflows/AUTHORING_STUDIO.md +28 -15
- package/skills/easysociable-template/workflows/DESIGN_BRIEF.md +4 -3
- package/skills/easysociable-template/workflows/MULTI_PLATFORM_FAMILY.md +81 -0
- package/skills/easysociable-template/workflows/PUBLISH_FROM_DRAFT.md +1 -1
- package/skills/easysociable-template/workflows/VISUAL_QA.md +10 -0
- package/skills/easysociable-content/platforms/claude.md +0 -5
- package/skills/easysociable-content/platforms/codex.md +0 -5
- package/skills/easysociable-content/platforms/hermes.md +0 -5
- package/skills/easysociable-content/platforms/openclaw.md +0 -5
- package/skills/easysociable-content/references/CLI.md +0 -27
- package/skills/easysociable-content/references/MCP.md +0 -20
- package/skills/easysociable-content/references/PAGE_PACK.md +0 -6
- package/skills/easysociable-content/references/TROUBLESHOOTING.md +0 -11
- package/skills/easysociable-content/references/VAULT.md +0 -20
- package/skills/easysociable-content/scripts/setup.mjs +0 -156
- package/skills/easysociable-content/scripts/setup.test.mjs +0 -71
- package/skills/easysociable-content/scripts/starter.mjs +0 -53
- package/skills/easysociable-content/workflows/ANALYSIS_AND_COVERAGE.md +0 -8
- package/skills/easysociable-content/workflows/CONTENT_EXPORT.md +0 -19
- package/skills/easysociable-content/workflows/DEVICE_TASKS.md +0 -5
- package/skills/easysociable-content/workflows/EXPERIMENTS.md +0 -6
- package/skills/easysociable-content/workflows/MCP_FIRST.md +0 -10
- package/skills/easysociable-content/workflows/PUBLICATION.md +0 -5
- package/skills/easysociable-content/workflows/TOPIC_DRAFT.md +0 -5
- package/skills/easysociable-content/workflows/WORKING_DRAFT.md +0 -10
|
@@ -120,12 +120,20 @@ No platform chrome assumptions. Still pick a concrete preset (`1:1`, `4:5`, `3:4
|
|
|
120
120
|
|
|
121
121
|
---
|
|
122
122
|
|
|
123
|
+
## Layout Family (multi-platform create)
|
|
124
|
+
|
|
125
|
+
Creating a template **defaults** to a Layout Family with one master page + derived presets — see `workflows/MULTI_PLATFORM_FAMILY.md`.
|
|
126
|
+
|
|
127
|
+
Same-preset platforms share one variant tree (**Instagram + Threads → `4:5`**). Do not invent a second 4:5 page for Threads.
|
|
128
|
+
|
|
129
|
+
Typical vertical bucket when master is `3:4` (Xiaohongshu): also derive `4:5`, `9:16`, `1:1`. Absolute layout cannot share one AST across those presets — hand-tune each variant after derive.
|
|
130
|
+
|
|
123
131
|
## Selection rules for Agents
|
|
124
132
|
|
|
125
|
-
1. If user names one platform → set `platforms: [that]`
|
|
126
|
-
2. If user names several
|
|
133
|
+
1. If user names one platform only → set master `platforms: [that]` + that platform’s **Recommended** row; skip family derive unless they later ask for more.
|
|
134
|
+
2. If user names several **or** does not limit platforms → pick a master Recommended canvas, then derive the default vertical targets (`MULTI_PLATFORM_FAMILY.md`).
|
|
127
135
|
3. Never mix presets across slides of one deck without an explicit user choice (IG/Threads/TikTok crop to first slide).
|
|
128
|
-
4.
|
|
136
|
+
4. Each family variant’s P0 `selectionMetadata` must list only platforms that match that page’s `canvasPreset`.
|
|
129
137
|
5. Specs above are **practical authoring targets** (2026 guides + Meta help for IG). Re-verify if a platform announces breaking crop changes.
|
|
130
138
|
|
|
131
139
|
## Sources (research snapshot)
|
|
@@ -15,7 +15,7 @@ Theme is the **skin** (color / font / surface). The page template is the **struc
|
|
|
15
15
|
|
|
16
16
|
## Gateway payload (`theme_schema_v1`)
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Authoring persists Studio preview tokens on **`TemplateLayoutFamily.preview_tokens`** (not a TemplateTheme row). Fill/render uses Brand `themeSnapshot` first, then family preview tokens, then a service default (ADR-0033). When `palette` is present, treat tokens as `theme_schema_v2`. The `colors` object includes six semantic roles: `canvas`, `text`, `accent`, `muted`, `primary`, `border`.
|
|
19
19
|
|
|
20
20
|
```ts
|
|
21
21
|
{
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "design_plan_v2_1",
|
|
3
|
+
"sourceKind": "image_reference",
|
|
4
|
+
"intent": {
|
|
5
|
+
"jobToBeDone": "Rebuild soft-card landing convert poster from reference",
|
|
6
|
+
"primaryPlatform": "instagram",
|
|
7
|
+
"canvas": { "width": 1080, "height": 1350 }
|
|
8
|
+
},
|
|
9
|
+
"philosophy": {
|
|
10
|
+
"movementName": "Soft Ledger",
|
|
11
|
+
"space": "Stacked rounded cards with generous outer margin",
|
|
12
|
+
"color": "Warm cream canvas, dark ink, soft peach accents",
|
|
13
|
+
"rhythm": "Even card gaps; one tip band; bottom CTA",
|
|
14
|
+
"composition": "Title → dual soft cards with figures → tip → CTA",
|
|
15
|
+
"typographyAccent": "Bold title; quieter body in cards",
|
|
16
|
+
"craftBar": "Match reference geometry; image slots for figures; no CSS fake people"
|
|
17
|
+
},
|
|
18
|
+
"concepts": [
|
|
19
|
+
{
|
|
20
|
+
"id": "soft-ledger-rebuild",
|
|
21
|
+
"name": "Soft ledger rebuild",
|
|
22
|
+
"rationale": "Reference already fixes composition",
|
|
23
|
+
"philosophyExpression": "Same stack and figure wells as referenceSpec",
|
|
24
|
+
"visualMotif": "soft rounded cards",
|
|
25
|
+
"layout": "title, two cards, tip, cta",
|
|
26
|
+
"hierarchy": "title > card copy > tip > cta",
|
|
27
|
+
"typography": "bold title, medium body",
|
|
28
|
+
"colorStrategy": "cream + ink from reference",
|
|
29
|
+
"assetStrategy": "two image slots for person illustrations",
|
|
30
|
+
"risks": ["figure crops"]
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"selectedConceptId": "soft-ledger-rebuild",
|
|
34
|
+
"selectionReason": "Single reconstruction of the supplied reference",
|
|
35
|
+
"referenceSpec": {
|
|
36
|
+
"stackOrder": ["title", "soft-card-1", "soft-card-2", "tip", "cta"],
|
|
37
|
+
"margins": "~48px outer",
|
|
38
|
+
"gaps": "~24px between cards",
|
|
39
|
+
"radii": "cards 16px; figure wells 12px",
|
|
40
|
+
"typeScale": "title ~42/bold; body ~22; tip ~18"
|
|
41
|
+
},
|
|
42
|
+
"referenceImageInventory": [
|
|
43
|
+
{
|
|
44
|
+
"label": "Left soft-card person illustration",
|
|
45
|
+
"slotKey": "image_0",
|
|
46
|
+
"imageRole": "hero",
|
|
47
|
+
"boxApprox": "left mid ~280×320"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"label": "Right soft-card person illustration",
|
|
51
|
+
"slotKey": "image_1",
|
|
52
|
+
"imageRole": "hero",
|
|
53
|
+
"boxApprox": "right mid ~280×320"
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"assetPlan": [
|
|
57
|
+
{
|
|
58
|
+
"role": "hero",
|
|
59
|
+
"source": "user_upload",
|
|
60
|
+
"required": true,
|
|
61
|
+
"slotKey": "image_0",
|
|
62
|
+
"description": "Left soft-card person illustration — empty image-slot frame",
|
|
63
|
+
"treatment": "Match reference box; placeholder until account imageId fill"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"role": "hero",
|
|
67
|
+
"source": "user_upload",
|
|
68
|
+
"required": true,
|
|
69
|
+
"slotKey": "image_1",
|
|
70
|
+
"description": "Right soft-card person illustration — empty image-slot frame",
|
|
71
|
+
"treatment": "Match reference box; placeholder until account imageId fill"
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "visual_qa_v2_1",
|
|
3
|
+
"draftId": "draft_fixture_ref",
|
|
4
|
+
"iteration": 0,
|
|
5
|
+
"sourceKind": "image_reference",
|
|
6
|
+
"screenshotEvidence": "/tmp/studio-render-fixture.png",
|
|
7
|
+
"referenceCompare": {
|
|
8
|
+
"performed": true,
|
|
9
|
+
"geometryMatch": true,
|
|
10
|
+
"imageSlotsMatchInventory": true,
|
|
11
|
+
"notes": "Fixture — stack and image_0/image_1 frames match referenceSpec"
|
|
12
|
+
},
|
|
13
|
+
"hardFailures": [],
|
|
14
|
+
"scores": {
|
|
15
|
+
"hierarchy": 18,
|
|
16
|
+
"composition": 14,
|
|
17
|
+
"spacingAlignment": 14,
|
|
18
|
+
"typography": 14,
|
|
19
|
+
"colorContrast": 9,
|
|
20
|
+
"assetRelevance": 9,
|
|
21
|
+
"contentResilience": 8,
|
|
22
|
+
"editability": 5
|
|
23
|
+
},
|
|
24
|
+
"total": 91,
|
|
25
|
+
"verdict": "pass",
|
|
26
|
+
"issues": [],
|
|
27
|
+
"changesMade": []
|
|
28
|
+
}
|
|
File without changes
|
|
@@ -126,6 +126,67 @@ function validateThemeTokensBlock(tokens, label) {
|
|
|
126
126
|
errors.push(`${label}.surfaces.shadow is required`)
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
const REFERENCE_SPEC_FIELDS = ['margins', 'gaps', 'radii', 'typeScale']
|
|
130
|
+
const IMAGE_INVENTORY_ROLES = new Set(['hero', 'screenshot', 'avatar', 'product'])
|
|
131
|
+
const IMAGE_SLOT_SOURCES = new Set(['user_upload', 'brand_kit', 'external_photo', 'generated'])
|
|
132
|
+
const SLOT_KEY_RE = /^image_\d+$/
|
|
133
|
+
|
|
134
|
+
function validateReferenceImageFields(plan, assetPlan) {
|
|
135
|
+
const spec = plan.referenceSpec
|
|
136
|
+
if (!spec || typeof spec !== 'object') {
|
|
137
|
+
errors.push('referenceSpec is required when sourceKind is image_reference')
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
if (!Array.isArray(spec.stackOrder) || spec.stackOrder.length < 1)
|
|
141
|
+
errors.push('referenceSpec.stackOrder must be a non-empty array')
|
|
142
|
+
else {
|
|
143
|
+
spec.stackOrder.forEach((entry, index) => {
|
|
144
|
+
if (!nonEmpty(entry))
|
|
145
|
+
errors.push(`referenceSpec.stackOrder[${index}] must be a non-empty string`)
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
REFERENCE_SPEC_FIELDS.forEach((field) => {
|
|
149
|
+
if (!nonEmpty(spec[field]))
|
|
150
|
+
errors.push(`referenceSpec.${field} is required`)
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const inventory = plan.referenceImageInventory
|
|
155
|
+
if (!Array.isArray(inventory)) {
|
|
156
|
+
errors.push('referenceImageInventory must be an array when sourceKind is image_reference (empty if no pictures)')
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const seenSlotKeys = new Set()
|
|
161
|
+
inventory.forEach((item, index) => {
|
|
162
|
+
if (!nonEmpty(item?.label))
|
|
163
|
+
errors.push(`referenceImageInventory[${index}].label is required`)
|
|
164
|
+
if (!SLOT_KEY_RE.test(item?.slotKey ?? ''))
|
|
165
|
+
errors.push(`referenceImageInventory[${index}].slotKey must match image_N`)
|
|
166
|
+
else if (seenSlotKeys.has(item.slotKey))
|
|
167
|
+
errors.push(`referenceImageInventory duplicate slotKey: ${item.slotKey}`)
|
|
168
|
+
else
|
|
169
|
+
seenSlotKeys.add(item.slotKey)
|
|
170
|
+
if (!IMAGE_INVENTORY_ROLES.has(item?.imageRole))
|
|
171
|
+
errors.push(`referenceImageInventory[${index}].imageRole must be hero|screenshot|avatar|product`)
|
|
172
|
+
if (!nonEmpty(item?.boxApprox))
|
|
173
|
+
errors.push(`referenceImageInventory[${index}].boxApprox is required`)
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
if (!Array.isArray(assetPlan))
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
seenSlotKeys.forEach((slotKey) => {
|
|
180
|
+
const match = assetPlan.find((asset) => asset?.slotKey === slotKey)
|
|
181
|
+
if (!match) {
|
|
182
|
+
errors.push(`assetPlan must include an entry with slotKey ${slotKey} for each inventoried picture`)
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
if (!IMAGE_SLOT_SOURCES.has(match.source))
|
|
186
|
+
errors.push(`assetPlan entry for ${slotKey} must use user_upload|brand_kit|external_photo|generated (not css_svg stand-in)`)
|
|
187
|
+
})
|
|
188
|
+
}
|
|
189
|
+
|
|
129
190
|
function validateDesignPlan(plan) {
|
|
130
191
|
const allowedSourceKinds = new Set(['text_brief', 'image_reference'])
|
|
131
192
|
const allowedRoles = new Set(['background', 'hero', 'decorative', 'avatar', 'logo', 'icon'])
|
|
@@ -206,8 +267,13 @@ function validateDesignPlan(plan) {
|
|
|
206
267
|
errors.push(`assetPlan[${index}].required must be boolean`)
|
|
207
268
|
if (!nonEmpty(asset?.description))
|
|
208
269
|
errors.push(`assetPlan[${index}].description is required`)
|
|
270
|
+
if (asset?.slotKey !== undefined && !SLOT_KEY_RE.test(asset.slotKey))
|
|
271
|
+
errors.push(`assetPlan[${index}].slotKey must match image_N when present`)
|
|
209
272
|
})
|
|
210
273
|
}
|
|
274
|
+
|
|
275
|
+
if (plan.sourceKind === 'image_reference')
|
|
276
|
+
validateReferenceImageFields(plan, plan.assetPlan)
|
|
211
277
|
}
|
|
212
278
|
|
|
213
279
|
function validateThemeTokens(theme) {
|
|
@@ -252,6 +318,33 @@ function validateThemeTokens(theme) {
|
|
|
252
318
|
}
|
|
253
319
|
}
|
|
254
320
|
|
|
321
|
+
function validateReferenceCompare(qa) {
|
|
322
|
+
const compare = qa.referenceCompare
|
|
323
|
+
if (qa.sourceKind !== 'image_reference') {
|
|
324
|
+
if (compare !== undefined && (compare === null || typeof compare !== 'object'))
|
|
325
|
+
errors.push('referenceCompare must be an object when present')
|
|
326
|
+
return
|
|
327
|
+
}
|
|
328
|
+
if (!compare || typeof compare !== 'object') {
|
|
329
|
+
errors.push('referenceCompare is required when sourceKind is image_reference')
|
|
330
|
+
return
|
|
331
|
+
}
|
|
332
|
+
if (typeof compare.performed !== 'boolean')
|
|
333
|
+
errors.push('referenceCompare.performed must be boolean')
|
|
334
|
+
if (typeof compare.geometryMatch !== 'boolean')
|
|
335
|
+
errors.push('referenceCompare.geometryMatch must be boolean')
|
|
336
|
+
if (typeof compare.imageSlotsMatchInventory !== 'boolean')
|
|
337
|
+
errors.push('referenceCompare.imageSlotsMatchInventory must be boolean')
|
|
338
|
+
if (qa.verdict === 'pass') {
|
|
339
|
+
if (compare.performed !== true)
|
|
340
|
+
errors.push('pass verdict for image_reference requires referenceCompare.performed true')
|
|
341
|
+
if (compare.geometryMatch !== true)
|
|
342
|
+
errors.push('pass verdict for image_reference requires referenceCompare.geometryMatch true')
|
|
343
|
+
if (compare.imageSlotsMatchInventory !== true)
|
|
344
|
+
errors.push('pass verdict for image_reference requires referenceCompare.imageSlotsMatchInventory true')
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
255
348
|
function validateVisualQa(qa) {
|
|
256
349
|
const maxima = {
|
|
257
350
|
hierarchy: 20,
|
|
@@ -278,6 +371,8 @@ function validateVisualQa(qa) {
|
|
|
278
371
|
errors.push('changesMade must be an array')
|
|
279
372
|
if (!['pass', 'revise'].includes(qa.verdict))
|
|
280
373
|
errors.push('verdict must be pass or revise')
|
|
374
|
+
if (qa.sourceKind !== undefined && !['text_brief', 'image_reference'].includes(qa.sourceKind))
|
|
375
|
+
errors.push('sourceKind must be text_brief or image_reference when present')
|
|
281
376
|
|
|
282
377
|
let calculatedTotal = 0
|
|
283
378
|
for (const [field, maximum] of Object.entries(maxima)) {
|
|
@@ -298,6 +393,8 @@ function validateVisualQa(qa) {
|
|
|
298
393
|
}
|
|
299
394
|
if (qa.verdict === 'revise' && calculatedTotal >= 80 && qa.hardFailures?.length === 0)
|
|
300
395
|
errors.push('revise verdict conflicts with a passing score and no hardFailures')
|
|
396
|
+
|
|
397
|
+
validateReferenceCompare(qa)
|
|
301
398
|
}
|
|
302
399
|
|
|
303
400
|
if (value?.schemaVersion === 'design_plan_v2_1')
|
|
@@ -24,7 +24,8 @@ Choose the first adequate option:
|
|
|
24
24
|
- Roles: `background | hero | decorative | avatar | logo | icon`.
|
|
25
25
|
- For `external_photo` / `template_icon_catalog` / `gradient_catalog`: discovery → bounded showcase → **user confirm** → materialize. Unselected → no EasySociable write.
|
|
26
26
|
- **`role: icon` ⇒ `source` MUST be `template_icon_catalog`** (unless catalog failed and user approved degrade). Never invent path SVG for icons “to go faster”.
|
|
27
|
-
-
|
|
27
|
+
- **Reference-image path:** every `referenceImageInventory` entry MUST have a matching `assetPlan` row with the same `slotKey`, source `user_upload|brand_kit|external_photo|generated`, and role suited to the picture — not `css_svg` fake people/illustrations. Geometric chrome only for non-pictorial panels/lines/anonymous washes.
|
|
28
|
+
- Never put third-party temporary image URLs into `authoringSource`.
|
|
28
29
|
- Use generated imagery only when it supports the selected concept. Prefer transparent, text-free, crop-safe elements for independent decorations.
|
|
29
30
|
- Never ask image generation to render the final poster, headline, metric, avatar name, or other replaceable content.
|
|
30
31
|
- Keep generated prompts and returned asset IDs/paths as provenance when available.
|
|
@@ -4,12 +4,12 @@ Primary intake when the user describes a single-page template in words (no refer
|
|
|
4
4
|
|
|
5
5
|
## Goal
|
|
6
6
|
|
|
7
|
-
One publishable **single-page** template
|
|
7
|
+
One publishable **single-page** template **Family** (master canvas + multi-platform variants by default): Design Brief → Design Philosophy → 2–3 Concepts → Asset Plan → Theme lock → master layout → Authoring Studio → `MULTI_PLATFORM_FAMILY.md`.
|
|
8
8
|
|
|
9
9
|
## Steps
|
|
10
10
|
|
|
11
|
-
1. Confirm this is **one page**, not a slideshow. If they want a multi-slide deck, stop and hand off (`OUT_OF_SCOPE.md`).
|
|
12
|
-
2. Read `references/PLATFORM_SPECS.md
|
|
11
|
+
1. Confirm this is **one page**, not a slideshow. If they want a multi-slide deck, stop and hand off (`OUT_OF_SCOPE.md`). Multi-platform variants of the same page are expected — that is not a slideshow.
|
|
12
|
+
2. Read `references/PLATFORM_SPECS.md`. Lock **master** platform + recommended canvas, and the default vertical derive targets unless the user limited platforms (`MULTI_PLATFORM_FAMILY.md`).
|
|
13
13
|
3. **Mandatory — V2.1 design plan first** (no reference image):
|
|
14
14
|
- Follow `workflows/DESIGN_BRIEF.md`
|
|
15
15
|
- Follow `workflows/DESIGN_PHILOSOPHY.md`; lock shared `philosophy` (+ companion `design-philosophy.md`)
|
|
@@ -30,7 +30,7 @@ One publishable **single-page** template draft: Design Brief → Design Philosop
|
|
|
30
30
|
- `authoringSource` per `references/AUTHORING_SOURCE.md` (flat tree + text-nesting exception)
|
|
31
31
|
- Theme per `references/THEME_TOKENS.md` — use `themeTokens` from validated `theme-tokens.json` for `theme.create`
|
|
32
32
|
5. Run `scripts/validate-authoring-source.mjs <authoring-source.json>` and fix every error.
|
|
33
|
-
6. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = text_brief`)
|
|
33
|
+
6. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = text_brief`) → **`MULTI_PLATFORM_FAMILY.md`** (derive + hand-tune + QA each variant) before handoff.
|
|
34
34
|
|
|
35
35
|
## Checklist pages
|
|
36
36
|
|
|
@@ -4,48 +4,98 @@ Intake when the user provides a poster / card image to turn into a **single-page
|
|
|
4
4
|
|
|
5
5
|
## Goal
|
|
6
6
|
|
|
7
|
-
Vision/LLM understanding stays **local**.
|
|
7
|
+
Vision/LLM understanding stays **local**. Rebuild the page as a slotted `authoringSource` that **follows the reference as a layout and visual specification**, not as a loose mood board.
|
|
8
|
+
|
|
9
|
+
User always finishes in Authoring Studio. Do **not** claim a pixel-perfect clone is guaranteed under absolute layout — but do **not** treat approximation as permission to invent a different composition.
|
|
10
|
+
|
|
11
|
+
## Non-negotiable (reference path)
|
|
12
|
+
|
|
13
|
+
When `source.kind = image_reference` (user gave a reference image):
|
|
14
|
+
|
|
15
|
+
1. **Spec over vibes.** Target function is **minimize visual distance to the reference** under slot/Theme/flat-tree constraints — not “ship any educational card that validates.”
|
|
16
|
+
2. **Measure before inventing.** Extract and record **`referenceSpec`** in `design-plan.json` (required fields: `stackOrder`, `margins`, `gaps`, `radii`, `typeScale` — contract in `references/DESIGN_V2_1.md`). Validator rejects plans without it.
|
|
17
|
+
3. **Inventory every pictorial region.** Before building nodes, list all **bitmap-like content** in the reference into **`referenceImageInventory`** (see § Image inventory). Each must become an **`image` slot** (or Icon Catalog for true icons — see below). **Forbidden:** replacing those regions with CSS fake faces, emoji, or invented SVG “because it’s faster.”
|
|
18
|
+
4. **Editable copy stays nodes.** Rebuild typography and numbers as text/number slots. Never flatten replaceable text into a baked background image.
|
|
19
|
+
5. **Visual QA must compare to the reference.** Studio screenshot alone is not enough — side-by-side against the reference is required before handoff (`VISUAL_QA.md` § Reference compare). `visual-qa.json` must set `sourceKind: image_reference` and a passing `referenceCompare`.
|
|
20
|
+
|
|
21
|
+
## Image inventory (what must become an image slot)
|
|
22
|
+
|
|
23
|
+
Treat as **pictorial / bitmap content** → **`image` slot** (`image_0`, `image_1`, …) with `imageRoles` set:
|
|
24
|
+
|
|
25
|
+
| Include as image slot | Examples |
|
|
26
|
+
|-----------------------|----------|
|
|
27
|
+
| Photos | People, product, lifestyle |
|
|
28
|
+
| Illustrations / drawn characters | Soft person illustrations, mascots |
|
|
29
|
+
| Screenshots / UI captures | App frames, dashboards |
|
|
30
|
+
| Avatars / logos used as content | Profile photos, brand marks used as imagery |
|
|
31
|
+
| Any non-geometric picture inside a frame | Rounded photo wells, poster insets |
|
|
32
|
+
|
|
33
|
+
**Not** image slots (CSS / Theme / Icon Catalog instead):
|
|
34
|
+
|
|
35
|
+
| Keep as chrome | How |
|
|
36
|
+
|----------------|-----|
|
|
37
|
+
| Solid/gradient panels, hairlines, anonymous discs used only as wash | CSS decoration |
|
|
38
|
+
| Number badges that are pure typography in a circle | decoration + fixed or `text`/`number` if variable |
|
|
39
|
+
| Recognizable **icons/glyphs** (arrows, checks, UI symbols) | **Icon Catalog** (`SURFACE_REVIEW.md` § Icons) — not hand-drawn; not a photo slot unless the reference literally uses a photo there |
|
|
40
|
+
|
|
41
|
+
If unsure whether a region is “illustration” vs “icon”: prefer **image slot** when it looks like artwork/photo; use Icon Catalog when it is a standard UI glyph.
|
|
42
|
+
|
|
43
|
+
### Image slot rules on this path
|
|
44
|
+
|
|
45
|
+
- Each inventoried picture → one `image` slot; set `requiresImage` / `imageRoles` / `slotSummary.image` accordingly.
|
|
46
|
+
- Authoring nodes for those slots: absolute `img` (or image host) with `data-variable="image_N"` — **empty placeholder frame** matching reference geometry (size, radius, position). Do not fill with CSS person-shapes.
|
|
47
|
+
- At create time, placeholders may be empty; fill later via account `imageId` (`FILL_AND_RENDER.md`). Optional: import a cropped stand-in from the reference only when the user supplies/exports that crop as an upload — never keep third-party temp URLs in Authoring Source.
|
|
48
|
+
- Write inventory into `design-plan.json` → `referenceImageInventory` **and** matching `assetPlan[]` rows with the same `slotKey` (see `DESIGN_V2_1.md`). Do not leave inventory only in chat.
|
|
8
49
|
|
|
9
50
|
## Steps
|
|
10
51
|
|
|
11
52
|
1. Confirm single-page intent (not “rebuild this whole carousel as one Agent pack”).
|
|
12
|
-
2. Read `references/PLATFORM_SPECS.md` when mapping detected aspect → platform defaults.
|
|
13
|
-
3.
|
|
53
|
+
2. Read `references/PLATFORM_SPECS.md` when mapping detected aspect → platform defaults. Prefer the platform the user named; otherwise infer from aspect and **confirm**.
|
|
54
|
+
3. **Read the reference pixels with a vision-capable tool** (`read_file` on the image path, or equivalent). Do **not** invent geometry or image inventory from a filename, caption, or memory alone. Then extract **into a written checklist** (show the user before heavy authoring when geometry is ambiguous):
|
|
14
55
|
- Canvas aspect / preset + suggested pixel size; likely `platforms`
|
|
15
56
|
- Likely **pageRoles** (1–2), contentFamily / archetypes, density, visualStyle
|
|
16
|
-
- Layout regions (title,
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
57
|
+
- Layout regions and **stack order** (title, cards, tip, CTA, …)
|
|
58
|
+
- **`referenceSpec`:** margins, gaps, radii, typeScale, stackOrder
|
|
59
|
+
- **`referenceImageInventory`:** every pictorial region → `image_N` + `imageRole` + `boxApprox`
|
|
60
|
+
- **Variable vs fixed:** copy/data → slots; pure chrome → decoration; pictures → image slots; glyphs → Icon Catalog
|
|
61
|
+
- Theme candidates (canvas/text/accent/muted, font feel) sampled from the reference
|
|
62
|
+
4. Reverse-engineer a short shared philosophy (`workflows/DESIGN_PHILOSOPHY.md`) from the reference — movement name + space/color/rhythm/composition/type/craftBar. Write it into `design-plan.json` `philosophy`. Philosophy must **serve the measured layout**, not excuse a different grid.
|
|
63
|
+
5. Treat the reconstruction as the selected concept (one concept is enough), with `philosophyExpression` describing how the rebuilt zones express that philosophy **and** match the reference zones. Then follow `workflows/ASSET_PLAN.md` and `workflows/SURFACE_REVIEW.md`:
|
|
64
|
+
- Pictorial regions → `image` slots (`user_upload` / import) with `assetPlan[].slotKey`, not `css_svg` stand-ins
|
|
65
|
+
- Icons/glyphs → Icon Catalog confirm → `get_icon`
|
|
66
|
+
- Rebuild editable typography, data, and **geometric** decoration with nodes/CSS
|
|
23
67
|
- Never flatten replaceable text or data into a generated image
|
|
24
68
|
- Never keep third-party temporary image URLs in Authoring Source
|
|
25
|
-
- Write
|
|
69
|
+
- Write `referenceSpec` + `referenceImageInventory` + `assetPlan`, then `node scripts/validate-design-v2.mjs design-plan.json` (must pass); `image_reference` may contain one concept
|
|
26
70
|
- When co-creating Theme, run `THEME_FROM_BRAND.md` (sample colors from reference → `brandHex`) and validate `theme-tokens.json`
|
|
27
71
|
6. Build **P0 selection metadata** (`references/CATALOG_METADATA.md`) — infer, then confirm with the user.
|
|
28
72
|
7. Build an explicit **slot table** (`references/SLOTS.md`):
|
|
29
73
|
- Types: `text` | `number` | `image` only
|
|
30
74
|
- Keys may be `text_0`, `image_0`, …
|
|
31
75
|
- Every `text` slot includes `maxLines` (+ `maxCharacters` when known)
|
|
76
|
+
- Every inventoried picture has a corresponding `image_*` entry
|
|
32
77
|
- No `list` type — repeated lines → `text_1`…`text_n` + `fixedItemCount` when checklist/steps
|
|
33
|
-
8. Map regions to **flat absolutely positioned** canvas siblings (no absolute-inside-absolute groups; card chrome = decoration sibling). Set `
|
|
78
|
+
8. Map regions to **flat absolutely positioned** canvas siblings (no absolute-inside-absolute groups; card chrome = decoration sibling). Set `data-variable` / binding on fillable nodes only. **Match the geometry spec** (positions/sizes within a tight tolerance of the measured reference, scaled to the chosen canvas). Put exact geometry and critical typography in inline `style={{...}}`; do not use Tailwind arbitrary-value layout classes. Fill `slotSummary` / `requiresImage` / `imageRoles`.
|
|
34
79
|
9. Propose Theme reuse or `ThemeTokens` create from `theme-tokens.json` (`references/THEME_TOKENS.md`).
|
|
35
|
-
10. Show the user: selection metadata,
|
|
80
|
+
10. Show the user: geometry summary, **image inventory → slot map**, selection metadata, Theme. Call out any region you could not match and why. Proceed when they accept.
|
|
36
81
|
11. Run `scripts/validate-authoring-source.mjs <authoring-source.json>` and fix every error.
|
|
37
|
-
12. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = image_reference`, brief = path or short note)
|
|
82
|
+
12. Continue with `workflows/AUTHORING_STUDIO.md` (`source.kind = image_reference`, brief = path or short note) → **`MULTI_PLATFORM_FAMILY.md`** (derive + hand-tune other presets). Run Visual QA **including reference compare on the master** before handoff; still QA each derived variant for fit/clipping.
|
|
38
83
|
|
|
39
84
|
## Limits (state upfront when relevant)
|
|
40
85
|
|
|
41
|
-
- Complex photography collages, heavy effects, or handwritten type
|
|
86
|
+
- Complex photography collages, heavy effects, or handwritten type may still need Studio polish; still inventory pictures as image slots and match overall geometry.
|
|
42
87
|
- Absolute layout: do not promise empty lines will pack upward.
|
|
43
88
|
- Never Publish from Agent until the user has Saved in Studio and explicitly asked to publish (`PUBLISH_FROM_DRAFT.md`).
|
|
44
89
|
|
|
45
90
|
## Do not
|
|
46
91
|
|
|
47
|
-
- Treat the reference bitmap as the template (must become slotted authoring source)
|
|
92
|
+
- Treat the reference bitmap as the whole template (must become slotted authoring source)
|
|
93
|
+
- Use the reference as a **mood board** while shipping a different composition/density
|
|
94
|
+
- Replace pictorial regions with CSS fake people, emoji, or invented illustrations
|
|
95
|
+
- Skip vision read of the reference / invent inventory from filename or memory alone
|
|
96
|
+
- Skip `referenceSpec` / `referenceImageInventory` or skip image slots for detected pictures
|
|
48
97
|
- Skip reverse-engineered philosophy / `philosophyExpression`
|
|
98
|
+
- Skip reference-compare Visual QA (`sourceKind` + `referenceCompare` on `visual-qa.json`)
|
|
49
99
|
- Copy private Binding/AST formats into Gateway requests
|
|
50
100
|
- Use runtime Tailwind arbitrary-value classes for critical geometry or typography
|
|
51
101
|
- Nest absolute frames inside wrapper cards/groups (flatten to canvas siblings instead)
|
|
@@ -15,6 +15,15 @@ Complete intake first:
|
|
|
15
15
|
- Optional draft-stage `selectionMetadata` (P0 can be completed at publish time)
|
|
16
16
|
- References: `SLOTS.md`, `AUTHORING_SOURCE.md`, `THEME_TOKENS.md`, `CATALOG_METADATA.md`
|
|
17
17
|
|
|
18
|
+
## Layout Family (multi-platform)
|
|
19
|
+
|
|
20
|
+
New drafts create a **Layout Family** (`familyId`) plus a master canvas page.
|
|
21
|
+
**Default create path:** after the master draft exists, follow `MULTI_PLATFORM_FAMILY.md` (derive + hand-tune variants) before handoff unless the user asked for one platform only.
|
|
22
|
+
|
|
23
|
+
- Authoring URL: `/authoring/{draftId}?platform=…`. Top bar **platform chips** switch variants (do not resize one AST).
|
|
24
|
+
- Preview skin = Family `preview_tokens` from `theme.create`; fill/render uses Brand snapshot first (ADR-0033).
|
|
25
|
+
- Legacy drafts without `familyId` auto-wrap on `getDraft` / `listDrafts`. Bulk: `easysociable templates families migrate-legacy --json`.
|
|
26
|
+
|
|
18
27
|
## Flow
|
|
19
28
|
|
|
20
29
|
1. Run the required local preflights (resolve scripts relative to this Skill directory):
|
|
@@ -36,48 +45,52 @@ The JSON file contains the public `name`, `source`, `authoringSource`, optional
|
|
|
36
45
|
`selectionMetadata`, and optional `theme`/`themeId` fields. Do not pass private
|
|
37
46
|
AST or Binding Maps.
|
|
38
47
|
|
|
39
|
-
3. Read `data.id` / `data.templateId` / `data.
|
|
48
|
+
3. Read `data.id` / `data.templateId` / `data.familyId` / `data.authoringStudioUrl` (and `data.variants` when present).
|
|
49
|
+
|
|
50
|
+
4. **Multi-platform (default):** run `workflows/MULTI_PLATFORM_FAMILY.md` — derive target presets, hand-tune each variant, QA each canvas. Skip only when the user asked for a single platform.
|
|
40
51
|
|
|
41
|
-
|
|
52
|
+
5. **Before handoff, run `workflows/VISUAL_QA.md`** on the master (and each variant from step 4). Open the actual Studio render; inspect screenshots, not only source. When a draft fails, fix authoring JSON, validate, update that page id, and re-render:
|
|
42
53
|
|
|
43
54
|
```bash
|
|
44
55
|
node scripts/validate-authoring-source.mjs ./authoring-source.json
|
|
45
|
-
easysociable templates update --id <id> --file ./draft-patch.json --json
|
|
56
|
+
easysociable templates update-draft --id <id> --file ./draft-patch.json --json
|
|
46
57
|
```
|
|
47
58
|
|
|
48
|
-
Stop after two automatic revisions unless the user explicitly requests more. Validate the passing QA artifact:
|
|
59
|
+
Stop after two automatic revisions per page unless the user explicitly requests more. Validate the passing QA artifact:
|
|
49
60
|
|
|
50
61
|
```bash
|
|
51
62
|
node scripts/validate-design-v2.mjs ./visual-qa.json
|
|
52
63
|
```
|
|
53
64
|
|
|
54
|
-
|
|
65
|
+
6. **Required user handoff message** (paste only after QA — customize URLs):
|
|
55
66
|
|
|
56
67
|
```text
|
|
57
|
-
|
|
68
|
+
已创建多平台模板 Family(master + 变体)。请打开 Authoring Studio:
|
|
58
69
|
{authoringStudioUrl}
|
|
59
70
|
|
|
60
|
-
|
|
61
|
-
|
|
71
|
+
顶栏平台芯片可切换画布(如小红书 3:4 / IG·Threads 4:5 / TikTok 9:16 / LinkedIn 1:1)。
|
|
72
|
+
可直接改当前变体的布局、文案和 Theme。满意后点 Save(页面上没有 Publish)。
|
|
73
|
+
保存完成后回复我「publish」或「发布该模板」,我会补齐选型元数据并发布。
|
|
62
74
|
```
|
|
63
75
|
|
|
64
76
|
English equivalent when the session is EN:
|
|
65
77
|
|
|
66
78
|
```text
|
|
67
|
-
|
|
79
|
+
Multi-platform template Family created (master + variants). Open Authoring Studio:
|
|
68
80
|
{authoringStudioUrl}
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
Use the top-bar platform chips to switch canvases (e.g. XHS 3:4 / IG·Threads 4:5 / TikTok 9:16 / LinkedIn 1:1).
|
|
83
|
+
Edit the active variant’s layout, copy, and Theme. When satisfied, click Save (no Publish button).
|
|
84
|
+
Then reply “publish” and I will complete catalog metadata and publish.
|
|
72
85
|
```
|
|
73
86
|
|
|
74
|
-
|
|
87
|
+
7. User previews/edits in the **slim** Authoring Studio:
|
|
75
88
|
- Canvas + right property panel
|
|
76
|
-
- Left: Theme tokens + optional Catalog hints
|
|
77
|
-
- Top bar: **
|
|
89
|
+
- Left: Theme / preview tokens + optional Catalog hints
|
|
90
|
+
- Top bar: **platform chips**, **Save**, **Fork**. Final PNG rendering is delegated to the unified CLI/MCP after the user confirms the saved draft.
|
|
78
91
|
- **No** Publish button; **No** left SidebarMenu (no AI / select-template / typeset rail)
|
|
79
92
|
|
|
80
|
-
|
|
93
|
+
8. When the user says publish → run `workflows/PUBLISH_FROM_DRAFT.md`.
|
|
81
94
|
|
|
82
95
|
## SDK shape
|
|
83
96
|
|
|
@@ -16,11 +16,11 @@ Skip this workflow when `AUTHORING_FROM_IMAGE.md` applies and a reference image
|
|
|
16
16
|
|
|
17
17
|
### 1. Gather intent (batch questions; skip known answers)
|
|
18
18
|
|
|
19
|
-
-
|
|
19
|
+
- **Master** platform + canvas from `references/PLATFORM_SPECS.md`, plus default multi-platform derive targets (`MULTI_PLATFORM_FAMILY.md`) unless the user wants one platform only
|
|
20
20
|
- Page roles (1–2) + contentFamily / archetypes / density (`CATALOG_METADATA.md`)
|
|
21
21
|
- Content job: what must the viewer understand in one glance?
|
|
22
22
|
- Must-have slots: title / metric / image / CTA / fixed N checklist lines…
|
|
23
|
-
- Theme
|
|
23
|
+
- Theme/preview direction (swiss / editorial / bold_promo / minimal / dark). Note brand hex or archetype for `DESIGN_INTELLIGENCE.md` palette seeding → Family `preview_tokens`
|
|
24
24
|
|
|
25
25
|
### 2. Write the Design Brief
|
|
26
26
|
|
|
@@ -31,7 +31,8 @@ Use this structure (Chinese or English to match the user):
|
|
|
31
31
|
|
|
32
32
|
## 1. Job to be done
|
|
33
33
|
- One-sentence purpose of this page
|
|
34
|
-
-
|
|
34
|
+
- Master platform + canvas (preset + pixels) — cite PLATFORM_SPECS recommended row
|
|
35
|
+
- Multi-platform targets (default vertical bucket unless user limited) — `MULTI_PLATFORM_FAMILY.md`
|
|
35
36
|
|
|
36
37
|
## 2. Visual constraints
|
|
37
38
|
- Desired/forbidden moods or brand rules
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Multi-platform Layout Family (default create path)
|
|
2
|
+
|
|
3
|
+
**SoT for creating templates that ship on several platforms at once.**
|
|
4
|
+
One user-facing **Layout Family**; one **master** canvas page; additional **variant** pages per aspect preset. Studio switches variants with platform chips — it does not resize one AST (ADR-0033).
|
|
5
|
+
|
|
6
|
+
Single-page ≠ single-platform. Still never create slideshow/carousel packs (`OUT_OF_SCOPE.md`).
|
|
7
|
+
|
|
8
|
+
## Default when the user asks to create a template
|
|
9
|
+
|
|
10
|
+
Unless they explicitly want **one platform only**:
|
|
11
|
+
|
|
12
|
+
1. Lock a **master** platform + recommended canvas (`PLATFORM_SPECS.md`).
|
|
13
|
+
2. Create the master draft (Family is created with it).
|
|
14
|
+
3. **Derive** missing vertical presets for the family.
|
|
15
|
+
4. **Hand-tune** each variant’s absolute layout (do not ship raw derive output for dense pages).
|
|
16
|
+
5. Visual-QA master + each variant, then hand off Studio with chip switching.
|
|
17
|
+
|
|
18
|
+
### Default vertical bucket (most social covers)
|
|
19
|
+
|
|
20
|
+
| Role | Preset | Pixels | Platforms on that variant |
|
|
21
|
+
|---|---|---|---|
|
|
22
|
+
| Master (typical) | `3:4` | 1080×1440 | `xiaohongshu` |
|
|
23
|
+
| Variant | `4:5` | 1080×1350 | `instagram` + `threads` (shared tree) |
|
|
24
|
+
| Variant | `9:16` | 1080×1920 | `tiktok` |
|
|
25
|
+
| Variant | `1:1` | 1080×1080 | `linkedin` |
|
|
26
|
+
|
|
27
|
+
If the user’s primary platform is already `4:5` / `9:16` / `1:1`, that page is master; derive the other presets in the bucket (see Gateway `defaultDerivePolicyForPreset`).
|
|
28
|
+
|
|
29
|
+
Skip or trim targets only when the user names a subset (“只要 IG 和 LinkedIn”).
|
|
30
|
+
|
|
31
|
+
## CLI
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
# After create-draft, read familyId from the draft view (or families list)
|
|
35
|
+
easysociable templates families get --id <familyId> --json
|
|
36
|
+
|
|
37
|
+
# Create missing presets (idempotent; use --presets to limit)
|
|
38
|
+
easysociable templates families derive --id <familyId> --presets 4:5,9:16,1:1 --json
|
|
39
|
+
|
|
40
|
+
# Hand-tuned layout per variant pageId
|
|
41
|
+
easysociable templates update-draft --id <variantPageId> --file ./variant-4x5.patch.json --json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Gateway equivalents: `GET/POST /v1/template-families/:id`, `POST /v1/template-families/:id/derive`.
|
|
45
|
+
|
|
46
|
+
If CLI `--json` fails Zod on new family fields (`familyId`, `variants`, …) but HTTP succeeded, re-fetch with `families get` / `list-drafts` or raw Gateway — do not assume create/derive failed.
|
|
47
|
+
|
|
48
|
+
## Hand-tune rules (required after derive)
|
|
49
|
+
|
|
50
|
+
`derive` applies pin `v1_vertical` (scale + bottom/center corrections). That is a **starting point**, not shippable layout for dense covers.
|
|
51
|
+
|
|
52
|
+
For **each** variant page:
|
|
53
|
+
|
|
54
|
+
1. Keep the **same slots / copy / icons** as master (same `data-variable` keys).
|
|
55
|
+
2. Rebuild absolute geometry for the target `canvas` (`width`/`height` + root Graphic size must match preset pixels).
|
|
56
|
+
3. Distribute vertical rhythm across the full board: even section gaps; **no** packed header + empty foot.
|
|
57
|
+
4. TikTok `9:16`: keep critical UI out of bottom ~120–160px chrome.
|
|
58
|
+
5. JSX styles must be `style={{ ... }}` (double braces). Python f-strings turn `{{` into `{` — validate with Babel/`validate-authoring-source.mjs` before update.
|
|
59
|
+
6. Per-variant `selectionMetadata.platforms` / `canvasPreset` / `canvas` must match that page only (IG+Threads both listed on the `4:5` page).
|
|
60
|
+
7. Theme/preview tokens stay on the **Family** (`preview_tokens` via `theme.create` on draft) — not a TemplateTheme row (`THEME_TOKENS.md`, ADR-0033).
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
node scripts/validate-authoring-source.mjs ./variant-4x5.authoring.json
|
|
64
|
+
easysociable templates update-draft --id <variantPageId> --file ./variant-4x5.patch.json --json
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Visual QA
|
|
68
|
+
|
|
69
|
+
Run `VISUAL_QA.md` on the **master** and **each derived preset** (screenshot Studio at that draft URL / platform chip). Fail if any variant has clipped content, overlapping bands, or large empty voids with cramped clusters.
|
|
70
|
+
|
|
71
|
+
## Handoff
|
|
72
|
+
|
|
73
|
+
After QA, open the **master** Studio URL and tell the user they can switch platforms with the top-bar chips (each chip is a real canvas variant). Save still applies to the active variant; Agent publish remains post-Save (`PUBLISH_FROM_DRAFT.md`). Include every variant `pageId` + preset in the handoff notes.
|
|
74
|
+
|
|
75
|
+
## Do not
|
|
76
|
+
|
|
77
|
+
- Author one AST and claim it fits 3:4 + 9:16 + 1:1
|
|
78
|
+
- Leave dense pages on raw derive output
|
|
79
|
+
- Put Instagram and Threads on different presets when both are 4:5
|
|
80
|
+
- Create a second Family for the same template identity
|
|
81
|
+
- Treat multi-platform variants as a slideshow / carousel pack
|