@easysociable/cli 0.1.23 → 0.1.24
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/package.json
CHANGED
|
@@ -5,8 +5,8 @@ description: >
|
|
|
5
5
|
EasySociable Studio. Research facts, pick a proven hook and structure
|
|
6
6
|
(Formula), lay pages out from reference designs (the user's, a Formula's,
|
|
7
7
|
or the catalog's, shuffled), render-check every page locally, then create the Run and hand the user a Studio link.
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Never block on a question: a missing Brand shows as a bound sample the user
|
|
9
|
+
fills once in Studio. Final pixels come only from Studio Download.
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# EasySociable Slideshow Skill
|
|
@@ -16,10 +16,11 @@ enough to post, where every word is still editable. The output is editable,
|
|
|
16
16
|
so **a wrong guess is cheap and a question is expensive**: decide platform,
|
|
17
17
|
page count, arc, style and copy yourself; the user fixes details in Studio.
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
What only the user has never blocks the Run; it becomes a visible, replaceable
|
|
20
|
+
stand-in they fill in Studio:
|
|
20
21
|
|
|
21
|
-
- their Brand (
|
|
22
|
-
- their own material (screenshots, photos)
|
|
22
|
+
- their Brand (name, handle, avatar) → bound samples (step 1);
|
|
23
|
+
- their own material (screenshots, photos) → image placeholders (step 5).
|
|
23
24
|
|
|
24
25
|
## Command output (`--json`)
|
|
25
26
|
|
|
@@ -49,22 +50,25 @@ Read results from these paths; don't guess:
|
|
|
49
50
|
- `authenticated: true` → say once: "This Run will be created as
|
|
50
51
|
`<account.email>` — open Studio with the same account."
|
|
51
52
|
|
|
52
|
-
### 1. Brand (ask
|
|
53
|
+
### 1. Brand (never ask)
|
|
53
54
|
|
|
54
55
|
`easysociable brands list --json`.
|
|
55
56
|
|
|
56
57
|
- A Brand exists → use it (the most recently updated if several), silently.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
58
|
+
Its colours and fonts style the Run (`--brand-id` in step 7).
|
|
59
|
+
- None, but the prompt gives a name / handle / avatar → create it:
|
|
60
|
+
`easysociable brands create --file brand.json`
|
|
61
|
+
(`{ "name", "handle", "colors", "logoAssetId" }`, colours = the deck style's
|
|
62
|
+
default palette in `references/REFERENCES.md`). An avatar: upload it first
|
|
63
|
+
(`images upload --path <file> --json`) and pass `data[0].asset.id` as
|
|
64
|
+
`logoAssetId`.
|
|
65
|
+
- None and nothing given → no Brand, no question. The style's palette goes in
|
|
66
|
+
`theme.json`.
|
|
67
|
+
|
|
68
|
+
Either way every Run carries the Brand spots (cover signature, CTA handle),
|
|
69
|
+
bound with `data-brand`; whatever the Brand lacks shows the sample `Your Name`
|
|
70
|
+
/ `@yourhandle` / sample avatar (`references/REFERENCES.md` rule 5). Studio
|
|
71
|
+
asks the user for exactly those fields and updates every page when they fill them.
|
|
68
72
|
|
|
69
73
|
### 2. Facts and evidence
|
|
70
74
|
|
|
@@ -229,7 +233,11 @@ easysociable slideshows create --file pack.json --brand-id <brandId> --json
|
|
|
229
233
|
# no Brand: --theme theme.json instead of --brand-id
|
|
230
234
|
```
|
|
231
235
|
|
|
232
|
-
Reply once with: the `studioUrl`, the arc in one line,
|
|
236
|
+
Reply once with: the `studioUrl`, the arc in one line, any Brand sample
|
|
237
|
+
left ("The name, handle and avatar on pages 1 and 6 are samples: fill them
|
|
238
|
+
once in the bar at the top of Studio, or in Brands at
|
|
239
|
+
https://my.easysociable.com/brands, and every page updates"; name only the
|
|
240
|
+
fields that are samples), the sources (facts and
|
|
233
241
|
each evidence image: "p1 image: <url>"), and each image placeholder to replace (page + what to upload). Tell them to edit in
|
|
234
242
|
Studio and click **Download** for the PNG ZIP.
|
|
235
243
|
|
|
@@ -36,13 +36,23 @@ Roles overlap in the catalog, so pass related ones together: `hook,cover` ·
|
|
|
36
36
|
`easysociable icons search --query <word>` on a tinted block. Never a grey placeholder or an
|
|
37
37
|
empty box on the cover. If nothing fits, delete the area and give the
|
|
38
38
|
headline its space.
|
|
39
|
-
5. Brand chrome
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
5. Brand chrome — every Run carries the Brand, bound so one Brand edit updates
|
|
40
|
+
every page. The cover has a signature (avatar + name + handle); the CTA has
|
|
41
|
+
avatar + handle next to the action. Inner pages keep only the Brand spots
|
|
42
|
+
their reference already has (never add chrome by habit). Never leave a
|
|
43
|
+
reference's original name or handle (e.g. "Will.AI.m") on a page.
|
|
44
|
+
- The element holding only the name gets `data-brand="name"`, only the
|
|
45
|
+
handle `data-brand="handle"`, the avatar / logo `<img>` `data-brand="avatar"`.
|
|
46
|
+
- Value: the Brand's `name`, `@handle`, `logoUrl`. A field the Brand lacks
|
|
47
|
+
(or no Brand at all) → the sample: `Your Name`, `@yourhandle`, and the
|
|
48
|
+
sample avatar below, still bound. Studio swaps each sample the moment the
|
|
49
|
+
user fills that field, and asks for it until then.
|
|
50
|
+
- Size the spots for a real name (up to ~22 characters) — `check-pages`
|
|
51
|
+
renders them with a long one.
|
|
52
|
+
|
|
53
|
+
Sample avatar (`objectFit: 'cover'`, round it with `borderRadius: '50%'`):
|
|
54
|
+
`data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxNjAnIGhlaWdodD0nMTYwJyB2aWV3Qm94PScwIDAgMTYwIDE2MCc+PHJlY3Qgd2lkdGg9JzE2MCcgaGVpZ2h0PScxNjAnIGZpbGw9JyNDOUNERDMnLz48Y2lyY2xlIGN4PSc4MCcgY3k9JzYyJyByPScyOCcgZmlsbD0nI0Y0RjVGNycvPjxwYXRoIGQ9J00yOCAxNjBjMi0zNCAyNC01MiA1Mi01MnM1MCAxOCA1MiA1MnonIGZpbGw9JyNGNEY1RjcnLz48L3N2Zz4=`
|
|
55
|
+
|
|
46
56
|
6. Accent colour only on large text (≥ 36px, or ≥ 28px bold) and blocks. Small
|
|
47
57
|
labels use the text or muted colour.
|
|
48
58
|
7. Semantic `data-type` values (`title`, `subtitle`, `kicker`, `metric`,
|
|
@@ -24,6 +24,8 @@ const brandRaw = themeFile ? JSON.parse(readFileSync(themeFile, 'utf8')) : null
|
|
|
24
24
|
const brand = brandRaw?.data ?? brandRaw
|
|
25
25
|
const c = brand?.colors ?? brand?.color ?? {}
|
|
26
26
|
// Studio default theme (apps/easysociable/app/lib/themeTokens.ts DEFAULT_AUTHORING_THEME_TOKENS).
|
|
27
|
+
// Brand spots render with the real Brand, or a long stand-in so a real name fits later.
|
|
28
|
+
const BRAND = { name: brand?.name || 'Alexandra Montgomery', handle: brand?.handle ? `@${brand.handle.replace(/^@/, '')}` : '@alexandra.montgomery', avatar: brand?.logoUrl || null }
|
|
27
29
|
const colors = { canvas: c.canvas ?? '#0f1419', text: c.text ?? '#e8eef5', muted: c.textMuted ?? c.muted ?? '#8b9bb0', accent: c.accent ?? '#5b8cff', primary: c.primary ?? c.accent ?? '#5b8cff' }
|
|
28
30
|
|
|
29
31
|
const chrome = [process.env.CHROME_PATH,
|
|
@@ -57,6 +59,7 @@ for (const [i, src] of SOURCES.entries()) {
|
|
|
57
59
|
const box = document.createElement('div')
|
|
58
60
|
box.setAttribute('data-authoring-theme', '')
|
|
59
61
|
box.innerHTML = toHtml(src.json)
|
|
62
|
+
for (const el of box.querySelectorAll('[data-brand]')) { const f = el.dataset.brand; if (f === 'avatar') { if (BRAND.avatar) el.src = BRAND.avatar } else if (BRAND[f]) el.textContent = BRAND[f] }
|
|
60
63
|
if (GRID) { box.style.cssText = 'width:' + src.width * S + 'px;height:' + src.height * S + 'px;overflow:hidden'; box.firstElementChild.style.transform = 'scale(' + S + ')'; box.firstElementChild.style.transformOrigin = '0 0' }
|
|
61
64
|
stage.appendChild(box)
|
|
62
65
|
}
|
|
@@ -112,7 +115,7 @@ for (const page of pack.pages) if (!page.layout?.studioJson) console.log(`page $
|
|
|
112
115
|
const COLS = 4
|
|
113
116
|
const html = `<!doctype html><meta charset="utf-8"><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=block">
|
|
114
117
|
<style>${themeCss}</style><div id="stage"></div><pre id="report"></pre>
|
|
115
|
-
<script>const SOURCES = ${JSON.stringify(pages).replace(/</g, '\\u003c')}, COLS = ${COLS};${pageScript}</script>`
|
|
118
|
+
<script>const SOURCES = ${JSON.stringify(pages).replace(/</g, '\\u003c')}, COLS = ${COLS}, BRAND = ${JSON.stringify(BRAND).replace(/</g, '\\u003c')};${pageScript}</script>`
|
|
116
119
|
const file = join(outDir, 'pages.html')
|
|
117
120
|
writeFileSync(file, html)
|
|
118
121
|
const W = Math.max(...pages.map(p => p.width)), Hs = pages.map(p => p.height)
|
|
@@ -124,9 +127,13 @@ const rows = Math.ceil(pages.length / COLS)
|
|
|
124
127
|
execFileSync(chrome, [...base, `--window-size=${Math.min(COLS, pages.length) * (W / 2 + 16) + 16},${rows * (Math.max(...Hs) / 2 + 16) + 16}`, `--screenshot=${join(outDir, 'grid.png')}`, pathToFileURL(file).href + '#grid'], { stdio: 'ignore' })
|
|
125
128
|
const raw = dom.match(/<pre id="report">([\s\S]*?)<\/pre>/)?.[1]?.replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&')
|
|
126
129
|
const reports = raw ? JSON.parse(raw) : pages.map(() => ({ errors: ['no report (page script did not finish)'], warnings: [] }))
|
|
130
|
+
// Every Run carries the Brand: a cover signature and a CTA handle (references/REFERENCES.md rule 5).
|
|
131
|
+
const brandSpots = { 0: ['name', 'handle', 'avatar'], [pages.length - 1]: ['handle', 'avatar'] }
|
|
127
132
|
let failed = false
|
|
128
133
|
for (const [i, page] of pages.entries()) {
|
|
129
134
|
const report = reports[i]
|
|
135
|
+
for (const f of (brandSpots[i] ?? []).filter(f => !page.json.includes(`data-brand="${f}"`)))
|
|
136
|
+
report.errors.push(`no Brand ${f}: the ${i ? 'last' : 'first'} page needs a data-brand="${f}" element`)
|
|
130
137
|
if (report.errors.length) failed = true
|
|
131
138
|
console.log(`page ${page.n} (${page.role}): ${report.errors.length ? 'FAIL' : 'ok'}`)
|
|
132
139
|
for (const e of report.errors) console.log(` ERROR ${e}`)
|