diff --git a/plugins/linkedin-studio/commands/newsletter.md b/plugins/linkedin-studio/commands/newsletter.md index 20c5697..e7d4345 100644 --- a/plugins/linkedin-studio/commands/newsletter.md +++ b/plugins/linkedin-studio/commands/newsletter.md @@ -31,9 +31,9 @@ This command is **fundamentally different** from the short-form commands: phases* (fact-check sweep + persona sweep + hook-gate), NOT by the short-form `PreToolUse` content-gatekeeper/voice-guardian hooks (those stay short-form-only). - **State lives in the series folder, not the plugin.** Production state for an - edition lives in the resolved **series root** (Step 0) — by default the - maskinrommet series folder - (`${LTL_SERIES_ROOT:-/Users/ktg/repos/maskinrommet/serier}//`), per + edition lives in the resolved **series root** (Step 0) — by default a per-slug + folder under your series base + (`${LTL_SERIES_ROOT:-$HOME/linkedin-series}//`), per decision G, but any explicit path works (Step 0 resolution order). The plugin ships the *schema* (`config/edition-state.template.json`) and this command; the edition's actual state + drafts live with the series. @@ -135,13 +135,14 @@ the edition left off before doing anything. `), use it verbatim. This is how the edition is produced for any repo, a throwaway fixture, or a non-default location. - Otherwise derive it from the series slug under the **default series base**, - `${LTL_SERIES_ROOT:-/Users/ktg/repos/maskinrommet/serier}//`. The - `LTL_SERIES_ROOT` env-var overrides the base without editing this command; - the maskinrommet path is the default, not the only path. + `${LTL_SERIES_ROOT:-$HOME/linkedin-series}//`. The + `LTL_SERIES_ROOT` env-var overrides the base without editing this command + (and `LTL_BRAND` re-brands the rendered output — empty by default); the + default base is a default, not the only path. - If neither a path nor a resolvable slug is available, ask once which series (or series-root path) this edition belongs to. All later steps treat `` as this resolved series root; nothing below - re-hardcodes the maskinrommet path. + re-hardcodes a specific series path. 2. **Read edition-state** (`/linkedin/edition-state.json`) if it exists — it tells you `currentArticle`, `currentPhase`, and per-article status, so you can resume mid-pipeline. The schema is documented in diff --git a/plugins/linkedin-studio/config/edition-state.template.json b/plugins/linkedin-studio/config/edition-state.template.json index f9f2530..7354ce4 100644 --- a/plugins/linkedin-studio/config/edition-state.template.json +++ b/plugins/linkedin-studio/config/edition-state.template.json @@ -1,7 +1,7 @@ { "_doc": { "purpose": "Schema for edition-state.json — deterministic resumption state for a newsletter edition in production. Holds the current phase + per-article status so /linkedin:newsletter (Step 0) can resume exactly where a prior session stopped.", - "decision": "G — production state lives in the serie-mappe (e.g. /Users/ktg/repos/maskinrommet/serier//linkedin/edition-state.json), NOT in the plugin. This file is the schema-defining TEMPLATE only; copy + fill it in the serie-mappe when producing an edition.", + "decision": "G — production state lives in the series folder (e.g. $LTL_SERIES_ROOT//linkedin/edition-state.json, default $HOME/linkedin-series//...), NOT in the plugin. This file is the schema-defining TEMPLATE only; copy + fill it in the series folder when producing an edition.", "complements": "edition-config.json (static: calendar, freshness, captions) and /STATE.md (human-readable narrative state, overwritten each phase per the ONE-system continuity rule — there is no edition-HANDOVER.md). edition-state.json is the machine-readable companion: deterministic resumption + the durable fact-check log, immutable rules, and persona verdicts that the old HANDOVER §4/§5 used to carry.", "lifecycle": "/linkedin:newsletter reads this in Step 0 and rewrites it at every phase transition. Article keys mirror edition-config.json (zero-padded strings: \"01\", \"02\", ..., or \"samle\").", "phases": [ diff --git a/plugins/linkedin-studio/config/image-credit-caption.template.md b/plugins/linkedin-studio/config/image-credit-caption.template.md index ea75301..a8d7058 100644 --- a/plugins/linkedin-studio/config/image-credit-caption.template.md +++ b/plugins/linkedin-studio/config/image-credit-caption.template.md @@ -59,5 +59,5 @@ som *også* legger en feed-cover trenger likevel en rad over. ## Samle-post -Ev. Maskinrommet-/serie-badge (egen asset) → ingen credit. Lenken til serien ligger i +Ev. serie-badge (egen asset) → ingen credit. Lenken til serien ligger i første kommentar, ikke i bildet. diff --git a/plugins/linkedin-studio/render/build-carousel.mjs b/plugins/linkedin-studio/render/build-carousel.mjs index 623d5b6..8375479 100644 --- a/plugins/linkedin-studio/render/build-carousel.mjs +++ b/plugins/linkedin-studio/render/build-carousel.mjs @@ -4,7 +4,7 @@ // Hver "## SLIDE N — ..." blir én portrett-side (1080×1350, 4:5) i PDF-en. // Designet typografisk deck — speiler avis-identiteten (Newsreader/Inter, off-white, // oxblood). Cover (slide 1) + CTA (siste slide) = oxblood-bokstøtter; de øvrige lyse -// med bolk-kicker + footer (Maskinrommet + teller). Ingen per-slide AI-foto. +// med bolk-kicker + footer (valgfri brand + teller). Ingen per-slide AI-foto. // Krever: weasyprint på PATH. Ingen npm-avhengigheter. import fs from "node:fs"; @@ -14,6 +14,11 @@ import { execFileSync } from "node:child_process"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); +// Valgfri brand i footer + cover-eyebrow-fallback. Tom som standard (generisk); +// sett LTL_BRAND til serie-/publikasjonsnavnet for å re-brande (samme mønster +// som LTL_SERIES_ROOT). Tom brand → footer uten brand-span, tom eyebrow-fallback. +const BRAND = process.env.LTL_BRAND || ""; + // --------------------------------------------------------------------------- // weasyprint graceful degradation (S1, correction #3) // Detekterer weasyprint på PATH. Returnerer et skip-signal (kaster ALDRI) når @@ -229,7 +234,7 @@ function slideHtml(slide, idx, total, eyebrows) { } const counter = `${idx + 1} / ${total}`; - const footer = ``; + const footer = ``; return `
${head} @@ -279,7 +284,7 @@ function main() { continue; } const eyebrows = { - cover: meta.cover_eyebrow || "Maskinrommet", + cover: meta.cover_eyebrow || BRAND, cta: meta.cta_eyebrow || "Kom i gang", }; const dir = path.dirname(inPath); diff --git a/plugins/linkedin-studio/render/build-linkedin.mjs b/plugins/linkedin-studio/render/build-linkedin.mjs index 90c3205..fc6abaa 100644 --- a/plugins/linkedin-studio/render/build-linkedin.mjs +++ b/plugins/linkedin-studio/render/build-linkedin.mjs @@ -29,6 +29,11 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url)); const OUT_ROOT = path.join(process.cwd(), "linkedin"); const DELINGSTEKST_FILE = path.join(OUT_ROOT, "edition-delingstekst.md"); +// Valgfri brand på samle-postens tittel. Tom som standard (generisk plugin); +// sett LTL_BRAND til serie-/publikasjonsnavnet for å re-brande (samme mønster +// som LTL_SERIES_ROOT). Tom brand → ren «Samle-post»-tittel. +const BRAND = process.env.LTL_BRAND || ""; + // --------------------------------------------------------------------------- // EDITION-KONFIG (HANDOVER §13 / DREIEBOK / image-credit-caption.md) // Per-serie verdier (kalender, ferskvare, cover-credit, captions) leses fra @@ -347,7 +352,7 @@ export function samlePost(share, config = EMPTY_CONFIG) {
${esc(share ? share.kommentar : "—")}
[LENKE] = index/kanonisk hjem (fromaitochitta.com hvis live) ELLER Del 1-editionen som inngang. Velg det som faktisk er publisert.
`; - return shell("Samle-post · Maskinrommet", inner); + return shell(BRAND ? `Samle-post · ${BRAND}` : "Samle-post", inner); } // ---------------------------------------------------------------------------