@cmo-so/cli 0.12.8 → 0.12.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cmo.mjs +173 -17
- package/package.json +1 -1
package/dist/cmo.mjs
CHANGED
|
@@ -14658,6 +14658,54 @@ function replace(blocks, ref, next, synced = {}) {
|
|
|
14658
14658
|
function sectionAt(blocks, ref, synced = {}) {
|
|
14659
14659
|
return pick(locate(blocks), ref, synced);
|
|
14660
14660
|
}
|
|
14661
|
+
function widthValue(width) {
|
|
14662
|
+
const w = String(width ?? "").trim();
|
|
14663
|
+
if (w === "content") return null;
|
|
14664
|
+
if (w === "wide") return "var(--wp--style--global--wide-size)";
|
|
14665
|
+
if (/^\d{2,4}(\.\d+)?(px|rem|em|vw|%)$/.test(w)) return w;
|
|
14666
|
+
throw new Error('width: "content", "wide" or a CSS length such as 1240px.');
|
|
14667
|
+
}
|
|
14668
|
+
var NARROW_PX = 700;
|
|
14669
|
+
var sizePx = (v) => {
|
|
14670
|
+
const m = /^(\d+(?:\.\d+)?)(px|rem|em)$/.exec(v);
|
|
14671
|
+
return m ? Number(m[1]) * (m[2] === "px" ? 1 : 16) : null;
|
|
14672
|
+
};
|
|
14673
|
+
function setSectionWidth(b, width) {
|
|
14674
|
+
const size = widthValue(width);
|
|
14675
|
+
const before = [];
|
|
14676
|
+
const visit = (x, top) => {
|
|
14677
|
+
let changed = false;
|
|
14678
|
+
for (const c of x.inner) if (visit(c, false)) changed = true;
|
|
14679
|
+
const lay = x.attrs.layout;
|
|
14680
|
+
const constrained = lay?.type === "constrained";
|
|
14681
|
+
const cur = lay?.contentSize;
|
|
14682
|
+
const px = cur ? sizePx(cur) : null;
|
|
14683
|
+
const eligible = constrained && (!cur || cur.includes("--wp--style--global") || px == null || px >= NARROW_PX);
|
|
14684
|
+
if (eligible || top && !lay && (x.name === "core/group" || x.name === "core/cover")) {
|
|
14685
|
+
if ((cur ?? null) !== size) {
|
|
14686
|
+
before.push(cur ?? "content");
|
|
14687
|
+
const next = { ...lay ?? {}, type: "constrained" };
|
|
14688
|
+
if (size) next.contentSize = size;
|
|
14689
|
+
else delete next.contentSize;
|
|
14690
|
+
x.attrs = { ...x.attrs, layout: next };
|
|
14691
|
+
changed = true;
|
|
14692
|
+
}
|
|
14693
|
+
}
|
|
14694
|
+
if (changed) delete x.raw;
|
|
14695
|
+
return changed;
|
|
14696
|
+
};
|
|
14697
|
+
visit(b, true);
|
|
14698
|
+
return { containers: before.length, before };
|
|
14699
|
+
}
|
|
14700
|
+
function refreshRaw(list3) {
|
|
14701
|
+
let any = false;
|
|
14702
|
+
for (const b of list3) {
|
|
14703
|
+
const inner = refreshRaw(b.inner);
|
|
14704
|
+
if (inner) delete b.raw;
|
|
14705
|
+
if (inner || b.raw == null) any = true;
|
|
14706
|
+
}
|
|
14707
|
+
return any;
|
|
14708
|
+
}
|
|
14661
14709
|
var escRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14662
14710
|
function applyEdits(b, e) {
|
|
14663
14711
|
let m = serializeBlock(b);
|
|
@@ -15195,6 +15243,15 @@ async function styleGet(site) {
|
|
|
15195
15243
|
fonts: (user.settings?.typography?.fontFamilies?.theme ?? []).map((f) => f.slug)
|
|
15196
15244
|
},
|
|
15197
15245
|
contrast: contrastReport(Object.fromEntries([...palette.values()].map((c) => [c.slug, c.color]))),
|
|
15246
|
+
layout: {
|
|
15247
|
+
content: user.settings?.layout?.contentSize ?? base.settings?.layout?.contentSize ?? null,
|
|
15248
|
+
wide: user.settings?.layout?.wideSize ?? base.settings?.layout?.wideSize ?? null,
|
|
15249
|
+
padding: {
|
|
15250
|
+
left: user.styles?.spacing?.padding?.left ?? base.styles?.spacing?.padding?.left ?? null,
|
|
15251
|
+
right: user.styles?.spacing?.padding?.right ?? base.styles?.spacing?.padding?.right ?? null
|
|
15252
|
+
}
|
|
15253
|
+
},
|
|
15254
|
+
css: user.styles?.css ?? "",
|
|
15198
15255
|
_palette: [...palette.values()],
|
|
15199
15256
|
_fonts: [...fonts.values()],
|
|
15200
15257
|
_user: user
|
|
@@ -15235,6 +15292,17 @@ function contrastReport(colors) {
|
|
|
15235
15292
|
});
|
|
15236
15293
|
}
|
|
15237
15294
|
async function styleSet(site, change, force = false) {
|
|
15295
|
+
const len = (v, what) => {
|
|
15296
|
+
const t = String(v ?? "").trim();
|
|
15297
|
+
if (!/^(0|\d{1,4}(\.\d+)?(px|rem|em|vw|%)|var\(--wp--preset--spacing--[a-z0-9-]+\)|clamp\([0-9a-z.,%+\-\s()]+\))$/i.test(
|
|
15298
|
+
t
|
|
15299
|
+
))
|
|
15300
|
+
throw new Error(`${what}: "${t}" isn't a CSS length like 1240px, 2rem or var(--wp--preset--spacing--50).`);
|
|
15301
|
+
return t;
|
|
15302
|
+
};
|
|
15303
|
+
for (const css of [change.css, change.css_append])
|
|
15304
|
+
if (css != null && /<\/?style|<\?|<script|@import\s+url\(\s*['"]?(?!https:)/i.test(css))
|
|
15305
|
+
throw new Error("css: plain CSS only (no <style>, <script>, PHP or non-https imports).");
|
|
15238
15306
|
const cur = await styleGet(site);
|
|
15239
15307
|
const palette = cur._palette.map((c) => ({ ...c }));
|
|
15240
15308
|
for (const [slug, color] of Object.entries(change.colors ?? {})) {
|
|
@@ -15259,15 +15327,48 @@ async function styleSet(site, change, force = false) {
|
|
|
15259
15327
|
);
|
|
15260
15328
|
const user = cur._user;
|
|
15261
15329
|
const settings2 = { ...user.settings ?? {} };
|
|
15330
|
+
const styles = { ...user.styles ?? {} };
|
|
15262
15331
|
if (change.colors) settings2.color = { ...settings2.color ?? {}, palette: { theme: palette } };
|
|
15263
15332
|
if (change.fonts)
|
|
15264
15333
|
settings2.typography = { ...settings2.typography ?? {}, fontFamilies: { theme: fonts } };
|
|
15334
|
+
if (change.layout) {
|
|
15335
|
+
const layout = { ...settings2.layout ?? {} };
|
|
15336
|
+
if (change.layout.content != null) layout.contentSize = len(change.layout.content, "layout.content");
|
|
15337
|
+
if (change.layout.wide != null) layout.wideSize = len(change.layout.wide, "layout.wide");
|
|
15338
|
+
settings2.layout = layout;
|
|
15339
|
+
}
|
|
15340
|
+
if (change.padding) {
|
|
15341
|
+
const spacing = { ...styles.spacing ?? {} };
|
|
15342
|
+
const pad = { ...spacing.padding ?? {} };
|
|
15343
|
+
if (change.padding.left != null) pad.left = len(change.padding.left, "padding.left");
|
|
15344
|
+
if (change.padding.right != null) pad.right = len(change.padding.right, "padding.right");
|
|
15345
|
+
styles.spacing = { ...spacing, padding: pad };
|
|
15346
|
+
settings2.useRootPaddingAwareAlignments = true;
|
|
15347
|
+
}
|
|
15348
|
+
if (change.css != null) styles.css = change.css;
|
|
15349
|
+
if (change.css_append)
|
|
15350
|
+
styles.css = `${String(styles.css ?? "").trimEnd()}
|
|
15351
|
+
|
|
15352
|
+
${change.css_append.trim()}
|
|
15353
|
+
`.trimStart();
|
|
15265
15354
|
await call(site, {
|
|
15266
15355
|
method: "POST",
|
|
15267
15356
|
path: `/wp/v2/global-styles/${cur.id}`,
|
|
15268
|
-
body: { settings: settings2, styles
|
|
15357
|
+
body: { settings: settings2, styles }
|
|
15269
15358
|
});
|
|
15359
|
+
const changedLayout = change.layout || change.padding;
|
|
15270
15360
|
return {
|
|
15361
|
+
...changedLayout ? {
|
|
15362
|
+
layout: {
|
|
15363
|
+
before: cur.layout,
|
|
15364
|
+
after: {
|
|
15365
|
+
content: settings2.layout?.contentSize ?? cur.layout.content,
|
|
15366
|
+
wide: settings2.layout?.wideSize ?? cur.layout.wide,
|
|
15367
|
+
padding: change.padding ? { ...cur.layout.padding, ...change.padding } : cur.layout.padding
|
|
15368
|
+
}
|
|
15369
|
+
}
|
|
15370
|
+
} : {},
|
|
15371
|
+
...change.css != null || change.css_append ? { css: { before: cur.css.length, after: String(styles.css ?? "").length, before_text: cur.css } } : {},
|
|
15271
15372
|
colors: Object.fromEntries(
|
|
15272
15373
|
Object.entries(change.colors ?? {}).map(([k, v]) => [k, { before: cur.colors[k], after: v }])
|
|
15273
15374
|
),
|
|
@@ -16386,7 +16487,7 @@ var siteTools = [
|
|
|
16386
16487
|
}),
|
|
16387
16488
|
tool({
|
|
16388
16489
|
name: "wp_style_get",
|
|
16389
|
-
description: "The
|
|
16490
|
+
description: "The site's look: colours (by name: canvas, panel, soft, ink, muted, olive, accent\u2026), fonts (display, body), layout widths (content, wide) and page padding, the extra CSS, and the text contrast of each colour pair.",
|
|
16390
16491
|
parameters: { type: "object", properties: {} },
|
|
16391
16492
|
group: "site",
|
|
16392
16493
|
need: READ,
|
|
@@ -16394,7 +16495,14 @@ var siteTools = [
|
|
|
16394
16495
|
gates: ["WP"],
|
|
16395
16496
|
run: async (ctx) => {
|
|
16396
16497
|
const s = await styleGet(siteOf(ctx));
|
|
16397
|
-
return {
|
|
16498
|
+
return {
|
|
16499
|
+
colors: s.colors,
|
|
16500
|
+
fonts: s.fonts,
|
|
16501
|
+
layout: s.layout,
|
|
16502
|
+
css: s.css,
|
|
16503
|
+
customized: s.customized,
|
|
16504
|
+
contrast: s.contrast
|
|
16505
|
+
};
|
|
16398
16506
|
}
|
|
16399
16507
|
})
|
|
16400
16508
|
];
|
|
@@ -16836,12 +16944,15 @@ var sectionTools = [
|
|
|
16836
16944
|
}),
|
|
16837
16945
|
tool({
|
|
16838
16946
|
name: "wp_section_update",
|
|
16839
|
-
description:
|
|
16947
|
+
description: 'Change one section. width: line its content up with the header and footer ("wide") or another width, one section or all. edits: change text, links or pictures inside it and keep its design (works on any section, the homepage too). slots: write the section again from new content (generated types; give the complete content). faq: add, remove (1-based numbers) or replace questions.',
|
|
16840
16948
|
parameters: {
|
|
16841
16949
|
type: "object",
|
|
16842
16950
|
properties: {
|
|
16843
16951
|
page: PAGE,
|
|
16844
|
-
section: str2(
|
|
16952
|
+
section: str2('Section id from wp_page_get (s2, m4) or its type (faq); "all" with width only.'),
|
|
16953
|
+
width: str2(
|
|
16954
|
+
`The section's inner width: "content" (the site's content width), "wide" (the site's wide width, as the header and footer) or a CSS length (1240px). Changes the section and its containers wider than 700px; with section "all", every section on the page.`
|
|
16955
|
+
),
|
|
16845
16956
|
edits: EDITS,
|
|
16846
16957
|
slots: obj("Complete new content for the section (same type unless type is given)."),
|
|
16847
16958
|
markup: str2(
|
|
@@ -16870,6 +16981,19 @@ var sectionTools = [
|
|
|
16870
16981
|
),
|
|
16871
16982
|
run: (ctx, a) => changePage(ctx, a, (blocks, kit) => {
|
|
16872
16983
|
const ref = String(a.section);
|
|
16984
|
+
if (a.width != null && a.width !== "") {
|
|
16985
|
+
const targets = ref === "all" ? locate(blocks) : [sectionAt(blocks, ref, kit.syncedTitles)];
|
|
16986
|
+
const changed = {};
|
|
16987
|
+
for (const t of targets) {
|
|
16988
|
+
if (t.block.name === "core/block") continue;
|
|
16989
|
+
const r = setSectionWidth(t.block, a.width);
|
|
16990
|
+
if (r.containers) changed[t.id] = r;
|
|
16991
|
+
}
|
|
16992
|
+
refreshRaw(blocks);
|
|
16993
|
+
if (!Object.keys(changed).length) throw new Error("Those sections already have that width.");
|
|
16994
|
+
return { blocks, note: { width: String(a.width), sections: changed } };
|
|
16995
|
+
}
|
|
16996
|
+
if (ref === "all") throw new Error('section "all" works with width only.');
|
|
16873
16997
|
const target = sectionAt(blocks, ref, kit.syncedTitles);
|
|
16874
16998
|
const type = classify(target.block, target.region, kit.syncedTitles);
|
|
16875
16999
|
let next;
|
|
@@ -17775,23 +17899,41 @@ var elementTools = [
|
|
|
17775
17899
|
}),
|
|
17776
17900
|
tool({
|
|
17777
17901
|
name: "wp_style_set",
|
|
17778
|
-
description: "Change brand colours (by name, hex)
|
|
17902
|
+
description: "Change the whole site's look: brand colours (by name, hex), fonts (by name, CSS font-family), layout widths (content: the width sections and text use; wide: the header, footer and wide blocks), the left and right page padding, and extra CSS. Saved in the site's Global Styles (works on any block theme; returns before and after). Refuses colours that would make text hard to read (contrast under 4.5:1). One section's width: wp_section_update width.",
|
|
17779
17903
|
parameters: {
|
|
17780
17904
|
type: "object",
|
|
17781
17905
|
properties: {
|
|
17782
17906
|
colors: obj('e.g. { "olive": "#4F5B48", "accent": "#A8834F" }.'),
|
|
17783
|
-
fonts: obj('e.g. { "display": "\\"Playfair Display\\", Georgia, serif" }.')
|
|
17907
|
+
fonts: obj('e.g. { "display": "\\"Playfair Display\\", Georgia, serif" }.'),
|
|
17908
|
+
layout: obj('Widths, e.g. { "content": "1240px", "wide": "1240px" }.', {
|
|
17909
|
+
content: str2("Content width (CSS length)."),
|
|
17910
|
+
wide: str2("Wide width (CSS length).")
|
|
17911
|
+
}),
|
|
17912
|
+
padding: obj('Left and right page padding, e.g. { "left": "24px", "right": "24px" }.', {
|
|
17913
|
+
left: str2(""),
|
|
17914
|
+
right: str2("")
|
|
17915
|
+
}),
|
|
17916
|
+
css: str2("Replace the site's extra CSS (Global Styles \u2192 Additional CSS); plain CSS only."),
|
|
17917
|
+
css_append: str2("Add this CSS after the site's extra CSS.")
|
|
17784
17918
|
}
|
|
17785
17919
|
},
|
|
17786
17920
|
group: "site",
|
|
17787
17921
|
need: WRITE,
|
|
17788
17922
|
risk: "W3",
|
|
17789
17923
|
gates: [...GATES],
|
|
17790
|
-
confirm: (_a3, lang) => say(
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17924
|
+
confirm: (_a3, lang) => say(
|
|
17925
|
+
lang,
|
|
17926
|
+
"Change the website's look for every page (colours, fonts, widths, padding or extra CSS); the result shows before and after.",
|
|
17927
|
+
"\u4FEE\u6539\u7DB2\u7AD9\u6240\u6709\u9801\u9762\u7684\u5916\u89C0\uFF08\u984F\u8272\u3001\u5B57\u9AD4\u3001\u5BEC\u5EA6\u3001\u908A\u8DDD\u6216\u984D\u5916 CSS\uFF09\uFF1B\u7D50\u679C\u6703\u5217\u51FA\u4FEE\u6539\u524D\u5F8C\u3002"
|
|
17928
|
+
),
|
|
17929
|
+
run: async (ctx, a) => styleSet(siteOf(ctx), {
|
|
17930
|
+
colors: a.colors,
|
|
17931
|
+
fonts: a.fonts,
|
|
17932
|
+
layout: a.layout,
|
|
17933
|
+
padding: a.padding,
|
|
17934
|
+
css: typeof a.css === "string" ? a.css : void 0,
|
|
17935
|
+
css_append: typeof a.css_append === "string" ? a.css_append : void 0
|
|
17936
|
+
})
|
|
17795
17937
|
})
|
|
17796
17938
|
];
|
|
17797
17939
|
function checkMarkup(markup, what) {
|
|
@@ -21766,7 +21908,7 @@ var program = new Command();
|
|
|
21766
21908
|
// package.json
|
|
21767
21909
|
var package_default = {
|
|
21768
21910
|
name: "@cmo-so/cli",
|
|
21769
|
-
version: "0.12.
|
|
21911
|
+
version: "0.12.9",
|
|
21770
21912
|
description: "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
|
|
21771
21913
|
type: "module",
|
|
21772
21914
|
license: "MIT",
|
|
@@ -32813,6 +32955,16 @@ function registerWp(program3) {
|
|
|
32813
32955
|
o.preview ? { tool: "wp_section_update", args } : args
|
|
32814
32956
|
);
|
|
32815
32957
|
});
|
|
32958
|
+
section.command("width <page> <section> <width>").description(
|
|
32959
|
+
'line sections up: width "wide" (as the header and footer), "content" or 1240px; section "all" = every section'
|
|
32960
|
+
).option("--preview", "show the result, save nothing").action(async (p, s, w, o, c) => {
|
|
32961
|
+
const args = { page: p, section: s, width: w };
|
|
32962
|
+
await run(
|
|
32963
|
+
g(c),
|
|
32964
|
+
o.preview ? "wp_preview" : "wp_section_update",
|
|
32965
|
+
o.preview ? { tool: "wp_section_update", args } : args
|
|
32966
|
+
);
|
|
32967
|
+
});
|
|
32816
32968
|
pos(section.command("move <page> <section>").description("move a section")).action(
|
|
32817
32969
|
(p, s, o, c) => run(g(c), "wp_section_move", { page: p, section: s, ...posArgs(o) })
|
|
32818
32970
|
);
|
|
@@ -32878,12 +33030,16 @@ ${table(r.issues, ["severity", "code", "path", "detail"])}`;
|
|
|
32878
33030
|
await run(g(c), part === "header" ? "wp_header_set" : "wp_footer_set", args);
|
|
32879
33031
|
});
|
|
32880
33032
|
}
|
|
32881
|
-
const style = wp.command("style").description("brand colours and
|
|
33033
|
+
const style = wp.command("style").description("brand colours, fonts, layout widths, page padding and extra CSS");
|
|
32882
33034
|
style.command("get", { isDefault: true }).action((_o, c) => run(g(c), "wp_style_get", {}));
|
|
32883
|
-
style.command("set [colors...]").description(`e.g. cmo wp style set olive=#4F5B48 --font 'display="Playfair Display", serif'`).option("--font <slug=family...>").action(
|
|
32884
|
-
(colors, o, c) => run(g(c), "wp_style_set", {
|
|
33035
|
+
style.command("set [colors...]").description(`e.g. cmo wp style set olive=#4F5B48 --font 'display="Playfair Display", serif'`).option("--font <slug=family...>").option("--content <width>", "content width, e.g. 1240px").option("--wide <width>", "wide width (header, footer, wide blocks)").option("--padding <length>", "left and right page padding, e.g. 24px").option("--css-file <file>", "replace the site's extra CSS with this file").option("--css-append <file>", "add this CSS file after the site's extra CSS").action(
|
|
33036
|
+
async (colors, o, c) => run(g(c), "wp_style_set", {
|
|
32885
33037
|
colors: colors.length ? Object.fromEntries(pairs(colors)) : void 0,
|
|
32886
|
-
fonts: o.font ? Object.fromEntries(pairs(o.font)) : void 0
|
|
33038
|
+
fonts: o.font ? Object.fromEntries(pairs(o.font)) : void 0,
|
|
33039
|
+
layout: o.content || o.wide ? { content: o.content, wide: o.wide } : void 0,
|
|
33040
|
+
padding: o.padding ? { left: o.padding, right: o.padding } : void 0,
|
|
33041
|
+
css: o.cssFile ? await readFile2(o.cssFile, "utf8") : void 0,
|
|
33042
|
+
css_append: o.cssAppend ? await readFile2(o.cssAppend, "utf8") : void 0
|
|
32887
33043
|
})
|
|
32888
33044
|
);
|
|
32889
33045
|
const media = wp.command("media").description("pictures in the media library");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmo-so/cli",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.9",
|
|
4
4
|
"description": "CMO.so on the command line: your websites, ideas, blog posts, LinkedIn queue and results, plus a local MCP server for Claude Desktop, Cursor and other MCP clients.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|