diff --git a/helloprint-presentation-creator/HP_MainTemplate_baseline.pptx b/helloprint-presentation-creator/HP_MainTemplate_baseline.pptx new file mode 100644 index 0000000..bf286f7 Binary files /dev/null and b/helloprint-presentation-creator/HP_MainTemplate_baseline.pptx differ diff --git a/helloprint-presentation-creator/SKILL.md b/helloprint-presentation-creator/SKILL.md new file mode 100644 index 0000000..72587c3 --- /dev/null +++ b/helloprint-presentation-creator/SKILL.md @@ -0,0 +1,546 @@ +--- +name: helloprint-presentation-creator +description: > + Helloprint Presentation Creator — generates on-brand Helloprint .pptx files by copying + slide templates from a curated baseline and swapping text content. Use whenever someone asks + to "make a presentation", "create slides", "build a deck", "make a pptx", or any variation + involving a Helloprint internal or external presentation. Also trigger for: "quarterly review", + "team update", "strategy deck", "board presentation", "pitch deck", "section update slides", + or "present this as slides". +--- + +# Helloprint Presentation Creator + +## HOW IT WORKS + +This skill uses a **template-copy engine**. The baseline PPTX (`HP_MainTemplate_baseline.pptx`) +contains 15 perfectly designed slide templates. The generator copies selected slides from that +file, swaps placeholder text, and outputs a new `.pptx` — preserving all fonts, colors, shapes, +and layouts exactly as designed. + +**Do NOT use python-pptx or pptxgenjs.** The generator is a raw ZIP/XML engine. + +--- + +## SETUP (run once per session) + +Before generating, ensure the generator and baseline are available: + +```python +import os, shutil + +SKILL_DIR = os.path.dirname(os.path.abspath(__file__)) # adapt to actual skill path +BASELINE_SRC = os.path.join(SKILL_DIR, "HP_MainTemplate_baseline.pptx") +GENERATOR_SRC = os.path.join(SKILL_DIR, "generate_presentation.py") + +# Copy to working session directory +shutil.copy(BASELINE_SRC, "/sessions/brave-bold-cerf/hp_baseline.pptx") +shutil.copy(GENERATOR_SRC, "/sessions/brave-bold-cerf/generate_presentation.py") +``` + +Then run the generator: + +```python +import sys +sys.path.insert(0, "/sessions/brave-bold-cerf") +from generate_presentation import generate +``` + +--- + +## THE API + +```python +generate(slide_plan, output_path) +``` + +`slide_plan` is a list of `(template_name, replacements)` tuples, in the order you want slides +to appear in the final deck. + +`replacements` is a list of `(old_text, new_text)` pairs. Pass raw characters — the engine +handles XML escaping. **Never pass `&` in your strings — pass `&` directly.** + +Example: + +```python +generate([ + ("title-dark", [ + ("Q1 2025 Business Review", "My Presentation Title"), + ("Kwartaalresultaten, strategische prioriteiten en roadmap H2", "Subtitle goes here"), + ("Team Strategy & Leadership", "Author Name / Team"), + ]), + ("closing", []), +], "/sessions/brave-bold-cerf/mnt/outputs/MyPresentation.pptx") +``` + +--- + +## AVAILABLE SLIDE TEMPLATES + +15 templates are available. Below is each template's name, purpose, and **exact placeholder +text strings** that can be replaced. Always copy the exact string shown — these are the +literal XML text values in the baseline. + +--- + +### `title-dark` +Dark background title slide. Always first. + +| Placeholder text | What it is | +|---|---| +| `Q1 2025 Business Review` | Main title | +| `Kwartaalresultaten, strategische prioriteiten en roadmap H2` | Subtitle / tagline | +| `Team Strategy & Leadership` | Author / team (bottom right) | + +> Note: The date (`15 april 2025`) is auto-filled from the slide and can optionally be replaced. +> `Making print easier, for everyone, everywhere.` is the permanent brand tagline — leave it. + +--- + +### `hero-split-photo` +Left-side dark photo panel with headline overlay. For bold opening statements. + +| Placeholder text | What it is | +|---|---| +| `ANNUAL REPORT 2024` | Section label (top) | +| `Building Europe's` | Display headline line 1 | +| `largest print` | Display headline line 2 | +| ` platform.` | Display headline line 3 (note leading space) | +| `300+ suppliers · 14 markets · millions of products` | Sub-caption | + +--- + +### `content-bullets-kpis` +White content slide with 3 bullet rows on the left and 4 KPI cards on the right. + +| Placeholder text | What it is | +|---|---| +| `STRATEGIE` | Section label | +| `Drie strategische prioriteiten voor Q2` | Slide title | +| `Revenue Growth` | Bullet 1 title (appears 3× — first occurrence is replaced) | +| ` — target €18M voor FY2025, nu tracking op €16.2M (+12% YoY)` | Bullet 1 body (note leading space) | +| `€16.2M` | KPI 1 number | +| `Q1 Revenue` | KPI 1 label | +| `4.6/5` | KPI 2 number | +| `Supplier NPS` | KPI 2 label | +| `12` | KPI 3 number | +| `AI Skills live` | KPI 3 label | +| `3` | KPI 4 number | +| `New markets H1` | KPI 4 label | + +> **3 distinct bullets** are supported via nth-occurrence tuples `(old, new, n)`. +> Because all 3 bullet rows share identical placeholder text in the baseline, use n=1, 2, 3 +> to target each row individually. The engine automatically adjusts for already-replaced +> occurrences, so n=2 always means "the 2nd bullet", not "the 2nd remaining one". + +```python +("content-bullets-kpis", [ + ("STRATEGIE", "STRATEGIE Q2"), + ("Drie strategische prioriteiten voor Q2", "Drie prioriteiten voor Q2"), + # Bullet 1 + ("Revenue Growth", "Marketplace Expansie", 1), + (" — target €18M voor FY2025, nu tracking op €16.2M (+12% YoY)", + " — uitrol naar IT en ES gepland voor juni, pipeline 40+ leveranciers", 1), + # Bullet 2 + ("Revenue Growth", "AI-First Operations", 2), + (" — target €18M voor FY2025, nu tracking op €16.2M (+12% YoY)", + " — 20 Skills in productie, doel is 35 actieve Skills voor einde Q2", 2), + # Bullet 3 + ("Revenue Growth", "Supplier Kwaliteit", 3), + (" — target €18M voor FY2025, nu tracking op €16.2M (+12% YoY)", + " — NPS van 4.6 naar 4.8 via geautomatiseerde kwaliteitscontrole", 3), + # KPI cards + ("€16.2M", "€14.2M"), ("Q1 Revenue", "Q1 Omzet"), + ... +]), +``` + +> **Character limit:** Bullet title: max **25 chars**. Bullet body (after the dash): max **100 chars**. + +--- + +### `impact-statement` +Full dark-green impact slide. For key takeaways. Max 1 per 5 slides. + +| Placeholder text | What it is | +|---|---| +| `€1B` | Hero number / stat | +| `Disrupting` | Bold word in headline | +| ` the print industry by building Earth's ` | Normal weight headline text | +| `largest infrastructure` | Bold word in headline | +| ` for customised products.` | Normal weight headline ending | + +> Note: `Making print easier, for everyone, everywhere.` is the permanent tagline — leave it. + +--- + +### `chart-kpi-cards` +White slide with chart area on the left and 4 KPI cards on the right. + +| Placeholder text | What it is | +|---|---| +| `FINANCIALS` | Section label | +| `Revenue & Margin — Q1 2025` | Slide title | +| `€12.4M` | KPI 1 number | +| `▲ +18% YoY` | KPI 1 delta (use ▲ for positive, ▼ for negative) | +| `Total Revenue Q1` | KPI 1 label | +| `34.2%` | KPI 2 number | +| `▲ +2.1pp` | KPI 2 delta | +| `Gross Margin` | KPI 2 label | +| `1.2M` | KPI 3 number | +| `▼ -3.1%` | KPI 3 delta | +| `Orders Processed` | KPI 3 label | +| `€10.33` | KPI 4 number | +| `▲ +22%` | KPI 4 delta | +| `Revenue per Order` | KPI 4 label | + +--- + +### `two-column-product` +White slide with text on the left and product image placeholder on the right. +Good for product spotlights or feature introductions. + +| Placeholder text | What it is | +|---|---| +| `PRODUCT LAUNCH` | Section label | +| `Premium Hardcover Boekjes` | Slide title | +| `Professionele uitstraling voor elk merk` | Left column headline | +| `Ons nieuwe hardcover-programma biedt premium kwaliteit...` | Left column body text | +| `Vanaf 1 stuk bestelbaar` | USP bullet 1 | +| `Gratis ontwerpcheck` | USP bullet 2 | +| `FSC-gecertificeerd papier` | USP bullet 3 | + +--- + +### `usp-4-cards` +Light gray background with 4 equal feature/USP cards. + +| Placeholder text | What it is | +|---|---| +| `WAAROM HELLOPRINT` | Section label | +| `Vier redenen om voor ons te kiezen` | Slide title | +| `Laagste prijs` | Card title line 1 (all 4 cards identical in baseline) | +| `garantie` | Card title line 2 (all 4 cards identical in baseline) | +| `All-inclusive prijzen zonder verborgen kosten...` | Card body (all 4 identical in baseline) | +| `30%` | Card stat (all 4 identical in baseline) | +| `goedkoper dan traditioneel` | Card stat label (all 4 identical in baseline) | + +**Per-card content** is supported via nth-occurrence tuples `(old, new, n)`. Because all 4 +cards share identical placeholder text, pass `n=1..4` to target each card individually: + +```python +("usp-4-cards", [ + ("WAAROM HELLOPRINT", "COMMUNITY MOMENTS"), + ("Vier redenen om voor ons te kiezen", "Wat er deze maand gebeurde"), + # Card 1 + ("Laagste prijs", "Armeense lunch", 1), + ("garantie", "Tolma & Ghapama", 1), + ("All-inclusive prijzen zonder verborgen kosten...", "Sona nodigde iedereen uit voor een Armeense lunch op 10 april.", 1), + # Card 2 + ("Laagste prijs", "Paas taart", 2), + ("garantie", "van Marte", 2), + ("All-inclusive prijzen zonder verborgen kosten...", "Marte verraste het team met een zelfgemaakte paastaart.", 2), + # Card 3 + ("Laagste prijs", "Chinese snacks", 3), + ("garantie", "van Irene", 3), + ("All-inclusive prijzen zonder verborgen kosten...", "Irene bracht authentieke Chinese snacks mee voor het hele kantoor.", 3), + # Card 4 + ("Laagste prijs", "Boek tip", 4), + ("garantie", "van Dennis", 4), + ("All-inclusive prijzen zonder verborgen kosten...", "Dennis deelde zijn favoriete boek: Zero to One van Peter Thiel.", 4), + # Remove stat elements (replace with empty or relevant values) + ("30%", ""), + ("goedkoper dan traditioneel", ""), +]), +``` + +> **Character limits:** Card title line 1: max **20 chars**. Card title line 2: max **20 chars**. +> Card body: max **100 chars** (≈2 lines). Exceeding these causes text to overflow the card box. + +--- + +### `team-photo` +Dark slide with full-bleed photo and stat overlay. For team events or company culture. + +| Placeholder text | What it is | +|---|---| +| `TEAM EVENT` | Section label | +| `Rotterdam Summit 2025` | Slide title | +| `150+ Helloprinters. 14 markten.` | Sub-headline | +| `Twee dagen strategie, innovatie en teambuilding.` | Body text | +| `150+` | Stat 1 number | +| `Helloprinters` | Stat 1 label | +| `14` | Stat 2 number | +| `Markets` | Stat 2 label | +| `2` | Stat 3 number | +| `Days` | Stat 3 label | + +--- + +### `process-timeline` +White slide with 4 horizontal process steps. For workflows, onboarding, roadmaps. + +| Placeholder text | What it is | +|---|---| +| `WORKFLOW` | Section label | +| `Van ontwerp tot levering` | Slide title | +| `1` | Step 1 number | +| `Upload` | Step 1 title | +| `Upload je ontwerp of gebruik onze online editor.` | Step 1 description | +| `2` | Step 2 number | +| `Designcheck` | Step 2 title | +| `Automatische controle op resolutie, afloop en kleuren.` | Step 2 description | +| `3` | Step 3 number | +| `Productie` | Step 3 title | +| `We matchen je order aan de beste supplier.` | Step 3 description | +| `4` | Step 4 number | +| `Levering` | Step 4 title | +| `Track & trace tot aan je deur. Gemiddeld 5 werkdagen.` | Step 4 description | +| `Gemiddelde levertijd: ` | Footer note line 1 (note trailing space) | +| `5 werkdagen in NL/BE` | Footer note line 2 | +| `Express: 2 werkdagen →` | Footer note line 3 | + +--- + +### `before-after` +White slide comparing two states side by side (gray left panel, dark right panel). + +| Placeholder text | What it is | +|---|---| +| `IMPACT` | Section label | +| `AI-First Operations: Voor en Na` | Slide title | +| `Handmatig proces` | Left panel title | +| `Content creation duurde 2–4 uur per product per markt. Handmatige keyword research, copy schrijven, vertalen en publiceren.` | Left panel body | +| `4h` | Left stat 1 | +| `per product per markt` | Left stat 1 label | +| `50` | Left stat 2 | +| `producten per week max` | Left stat 2 label | +| `3 FTE` | Left stat 3 | +| `nodig` | Left stat 3 label | +| `Geautomatiseerd` | Right panel title | +| `Claude Skills handelen keyword research, copy generation en Contentful publishing af. Mens reviewt alleen het eindresultaat.` | Right panel body | +| `15min` | Right stat 1 | +| `500+` | Right stat 2 | +| `producten per week` | Right stat 2 label | +| `0.5 FTE` | Right stat 3 | +| `menselijke review` | Right stat 3 label | + +> Note: `⬤ VOORHEEN` and `⬤ NU MET AI SKILLS` are colored dot labels — best left as-is. + +--- + +### `big-number` +Dark background with one giant number. Maximum impact for a single key metric. Max 1 per 5 slides. + +| Placeholder text | What it is | +|---|---| +| `Q1 HIGHLIGHT` | Section label (top right, green) | +| `+42%` | The hero number/stat | +| `Year-over-year growth in new customer acquisition` | Label below the number | +| `Driven by marketplace expansion into 3 new markets and a 28% improvement in organic search visibility across all locales.` | Context paragraph | + +--- + +### `section-divider` +Green full-bleed slide announcing a new section. + +| Placeholder text | What it is | +|---|---| +| `02` | Section number (appears twice: large decorative + small label — both get replaced) | +| `Financial Performance` | Section title | +| `Omzet, marges en key metrics over het afgelopen kwartaal met een vooruitblik op de rest van het jaar.` | Section description | + +> The number `02` appears twice in the XML (visible number + large background decoration). +> Replacing it changes both at once. Always replace with a two-digit number like `01`, `02`, `03`. + +--- + +### `closing` +Dark closing slide with centered logo, tagline, and contact footer. Always last. + +No replacements needed — all text is permanent brand content: +- "Making print easier, for everyone, everywhere." +- helloprint.com | info@helloprint.com | ★★★★★ Uitstekend — 32.000+ reviews + +```python +("closing", []) +``` + +--- + +### `agenda` +White agenda slide with numbered items (up to 5), time allocations, and section label. + +| Placeholder text | What it is | +|---|---| +| `TEAM UPDATE — AI & GROWTH` | Section label (top left, green) | +| `Premium Hardcover Boekjes` | Slide title | +| `Q1 Resultaten` | Item 1 title | +| `Revenue, margins, order volume en key highlights` | Item 1 description | +| `5 min` | Item 1 time (right side) | +| `Financial Performance` | Item 2 title | +| `Deep dive in de cijfers per markt en categorie` | Item 2 description | +| `10 min` | Item 2 time (2nd occurrence) | +| `Product & Platform` | Item 3 title | +| `Nieuwe features, marketplace expansie en tech roadmap` | Item 3 description | +| `AI & Automation` | Item 4 title | +| `Skill updates, voortgang en planning voor HY1` | Item 4 description | + +> Note: Item 5 (`Q&A` / `Open vragen en discussie` / `15 min`) is at the bottom and uses +> unique text — replace as needed. The item numbers (01–05) are visual icons, not plain text. + +--- + +### `testimonial` +Dark background with large quote, avatar initials, name, and title. + +| Placeholder text | What it is | +|---|---| +| `Helloprint heeft ons de flexibiliteit gegeven om snel te schakelen. Van 500 visitekaartjes tot 50.000 brochures — alles wordt op tijd en perfect geleverd.` | Quote text | +| `MV` | Avatar initials | +| `Martine Verhoeven` | Person name | +| `Marketing Director — TechCorp BV` | Person title and company | + +--- + +## RECOMMENDED SLIDE ORDER + +``` +title-dark → agenda → section-divider → [content slides] → closing +``` + +Content slides to mix and match between dividers: +- `content-bullets-kpis` — text + KPIs summary +- `before-after` — transformation/impact +- `big-number` — single hero metric +- `chart-kpi-cards` — data-heavy +- `usp-4-cards` — features/benefits +- `process-timeline` — workflow steps +- `two-column-product` — product/feature spotlight +- `impact-statement` — bold statement +- `testimonial` — social proof +- `team-photo` — culture/team + +**Rule:** No more than 3 content slides in a row without a visual break (section-divider, big-number, or impact-statement). + +--- + +## WRITING STYLE + +All text in presentations must follow Helloprint's tone: **direct, professional, no fluff**. + +- Start with the main message — no preamble +- Short titles (max 8 words) +- Body text max 3 lines per block +- KPI numbers should be specific and real +- Avoid filler words: "We are proud to...", "Please note that..." +- Never use ALL CAPS except section labels (those are auto-uppercase in the template) + +**Language:** Default to Dutch for internal NL/BE audiences, English for international. Match +the language of the brief — don't mix languages in one presentation. + +**Ampersand:** Use `&` in your Python strings. The engine handles XML escaping automatically. +Never write `&` in replacement text. + +**Special characters:** Use Unicode directly — `—` (em dash), `→`, `▲`, `▼`, `✓`, `€`, `+`. + +See `references/writing-style-guide.md` for detailed Helloprint tone of voice guidelines. +See `references/internal-language.md` for internal Helloprint terminology. + +--- + +## FULL EXAMPLE + +```python +import sys, shutil, os + +# 1. Setup +SKILL_DIR = "/path/to/helloprint-presentation-creator" +shutil.copy(f"{SKILL_DIR}/HP_MainTemplate_baseline.pptx", "/sessions/brave-bold-cerf/hp_baseline.pptx") +shutil.copy(f"{SKILL_DIR}/generate_presentation.py", "/sessions/brave-bold-cerf/generate_presentation.py") + +sys.path.insert(0, "/sessions/brave-bold-cerf") +from generate_presentation import generate + +# 2. Build slide plan +plan = [ + ("title-dark", [ + ("Q1 2025 Business Review", "AI & Automation — Q2 2025 Update"), + ("Kwartaalresultaten, strategische prioriteiten en roadmap H2", + "Voortgang, resultaten en roadmap voor het tweede kwartaal"), + ("Team Strategy & Leadership", "AI & Growth Team"), + ]), + ("agenda", [ + ("Premium Hardcover Boekjes", "Wat bespreken we vandaag"), + ("Q1 Resultaten", "AI Skills Voortgang"), + ("Revenue, margins, order volume en key highlights", "20 skills live, impact en learnings Q1"), + ("Financial Performance", "Content Automation"), + ("Deep dive in de cijfers per markt en categorie", "SEO output, kwaliteit en schaalresultaten"), + ]), + ("section-divider", [ + ("02", "01"), + ("Financial Performance", "Content Automation"), + ("Omzet, marges en key metrics over het afgelopen kwartaal met een vooruitblik op de rest van het jaar.", + "Hoe we 500+ pagina's per maand genereren — on-brand en meertalig."), + ]), + ("big-number", [ + ("Q1 HIGHLIGHT", "Q1 RESULTAAT"), + ("+42%", "500+"), + ("Year-over-year growth in new customer acquisition", + "Productpagina's gegenereerd in Q1 via AI Skills"), + ("Driven by marketplace expansion into 3 new markets and a 28% improvement in organic search visibility across all locales.", + "Uitgerold in NL, BE, DE en FR. Gemiddeld 15 min per pagina. Schaalbaar naar alle 14 markten in Q2."), + ]), + ("closing", []), +] + +# 3. Generate +generate(plan, "/sessions/brave-bold-cerf/mnt/outputs/MyPresentation.pptx") +``` + +--- + +## CHARACTER LIMITS + +Text boxes in the baseline have fixed widths. Exceeding these limits causes text to wrap to a +second line or overflow the element, breaking the visual design. **Always stay within these limits.** + +| Template | Field | Max chars | +|---|---|---| +| All slides | Section label (top left, green) | 40 | +| All slides | Slide title | 45 | +| `section-divider` | Section title | 25 | +| `section-divider` | Section description | 130 | +| `usp-4-cards` | Card title line 1 | 20 | +| `usp-4-cards` | Card title line 2 | 20 | +| `usp-4-cards` | Card body text | 100 | +| `agenda` | Item title | 25 | +| `agenda` | Item description | 70 | +| `big-number` | Hero label (below number) | 55 | +| `big-number` | Context paragraph | 200 | +| `process-timeline` | Step title | 15 | +| `process-timeline` | Step description | 80 | +| `testimonial` | Quote text | 210 | +| `testimonial` | Person name | 30 | +| `testimonial` | Person title | 45 | +| `content-bullets-kpis` | Bullet title | 25 | +| `content-bullets-kpis` | Bullet body | 100 | + +> **Template fix recommended:** If you need more room, open `HP_MainTemplate_baseline.pptx` in +> PowerPoint, select the relevant text box, go to **Format Shape → Text Options → enable +> "Shrink text on overflow"**. This makes the layout robust against occasional overruns. +> After editing, save as the new baseline and re-upload to Gitea via skill-sync. + +--- + +## TROUBLESHOOTING + +**Text not replaced?** Check that your `old_text` exactly matches the placeholder (copy-paste +from the table above). The match is case-sensitive and whitespace-sensitive. + +**`&` showing literally?** You passed `&` as new_text — change it to `&`. + +**USP cards all same content?** Known limitation — see usp-4-cards note above. + +**Slide out of order?** The order in `slide_plan` determines the output order, not the +template index. Put slides in the list in the order you want them. diff --git a/helloprint-presentation-creator/generate_presentation.py b/helloprint-presentation-creator/generate_presentation.py new file mode 100644 index 0000000..13c1a4f --- /dev/null +++ b/helloprint-presentation-creator/generate_presentation.py @@ -0,0 +1,550 @@ +""" +Helloprint Presentation Generator — Keep-and-Delete Engine v2 +------------------------------------------------------------- +Strategy: copy full baseline → remove unwanted slides → replace text. +All original file numbers / internal references stay intact. +Unused media files are stripped. Slide order matches slide_plan order. + +NEW in v2: + - Auto page numbering (replaces hardcoded "03" footer numbers) + - Nth-occurrence replacement: (old, new, n) replaces only the nth match + - Duplicate template support: use same template multiple times in one deck + - Media replacement: swap image files via generate(..., media={}) + +Slide index map (0-based, matches presentation order in baseline): + 0 title-dark 8 process-timeline + 1 hero-split-photo 9 before-after + 2 content-bullets-kpis 10 big-number + 3 impact-statement 11 section-divider + 4 chart-kpi-cards 12 closing + 5 two-column-product 13 agenda + 6 usp-4-cards 14 testimonial + 7 team-photo +""" + +import zipfile, re, os +from io import BytesIO + +BASELINE = "/sessions/brave-bold-cerf/hp_baseline.pptx" + +SLIDE_IDX = { + "title-dark": 0, + "hero-split-photo": 1, + "content-bullets-kpis": 2, + "impact-statement": 3, + "chart-kpi-cards": 4, + "two-column-product": 5, + "usp-4-cards": 6, + "team-photo": 7, + "process-timeline": 8, + "before-after": 9, + "big-number": 10, + "section-divider": 11, + "closing": 12, + "agenda": 13, + "testimonial": 14, +} + +SLIDE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" +SLIDE_CT = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml" +IMAGE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" + +# Slides that display a footer page number (sz=600 small text in bottom-right) +SLIDES_WITH_PAGE_NUM = { + "content-bullets-kpis", "chart-kpi-cards", "two-column-product", + "usp-4-cards", "process-timeline", "before-after", "agenda", +} + + +# ── Core helpers ────────────────────────────────────────────────────────────── + +def get_slide_file_order(src_zip): + """Returns list of (slide_path, rels_path, rId) in presentation order.""" + prs_xml = src_zip.read('ppt/presentation.xml').decode('utf-8') + prs_rels = src_zip.read('ppt/_rels/presentation.xml.rels').decode('utf-8') + rid_order = re.findall(r']*r:id="([^"]+)"', prs_xml) + rid_to_target = {} + for m in re.finditer( + r' 2 else None + + old_esc = old.replace('&', '&').replace('<', '<').replace('>', '>') + new_esc = new.replace('&', '&').replace('<', '<').replace('>', '>') + + if occurrence is None: + # Replace all occurrences (and reset any consumed counter for this key) + s = s.replace(f'>{old_esc}<', f'>{new_esc}<') + s = s.replace(f'>{old}<', f'>{new_esc}<') + consumed.pop(old, None) + else: + # Adjust n: subtract how many occurrences of this text were already replaced + already = consumed.get(old, 0) + adjusted = occurrence - already + if adjusted >= 1: + replaced_esc = replace_nth(s, f'>{old_esc}<', f'>{new_esc}<', adjusted) + if replaced_esc != s: + s = replaced_esc + else: + s = replace_nth(s, f'>{old}<', f'>{new_esc}<', adjusted) + consumed[old] = already + 1 + return s.encode('utf-8') + + +def apply_page_number(xml_bytes, page_num): + """ + Replace the footer page number in slide XML. + Footer numbers are identified by font size sz="600" (6pt) — distinct from + larger decorative numbers like agenda item badges (sz="1000"+). + """ + s = xml_bytes.decode('utf-8') + num = f"{page_num:02d}" + + def replacer(m): + # Check the 300 chars before this text run for sz="600" (footer font size) + start = max(0, m.start() - 300) + context = s[start:m.start()] + # Only replace if this is footer-size text + if re.search(r'sz="600"', context): + return f'>{num}<' + return m.group(0) + + s = re.sub(r'>0[1-9]<', replacer, s) + return s.encode('utf-8') + + +# ── Main generator ──────────────────────────────────────────────────────────── + +def generate(slide_plan, output_path, media=None, page_numbers=True): + """ + Generate a Helloprint presentation. + + slide_plan : list of (template_name, replacements) + replacements items: (old, new) or (old, new, n) for nth-occurrence + + output_path : destination .pptx file path + + media : dict mapping baseline image filename → local file path + e.g. {"image20.jpg": "/path/to/office_photo.jpg"} + Use for slides with photo slots: hero-split-photo (image20.jpg), + team-photo (image21.jpg), two-column-product (add your own). + + page_numbers: if True (default), auto-number slide footers + """ + if media is None: + media = {} + + with zipfile.ZipFile(BASELINE, 'r') as src: + slide_order = get_slide_file_order(src) + total = len(slide_order) + + # Find max existing slide number for naming copies + existing_nums = [ + int(re.search(r'slide(\d+)\.xml', s[0]).group(1)) + for s in slide_order + if re.search(r'slide(\d+)\.xml', s[0]) + ] + next_copy_num = max(existing_nums, default=21) + 100 # safe gap + + # ── Build wanted dict + plan_order ──────────────────────────────────── + # wanted maps baseline_idx → (slide_info, replacements, template) + # copies maps copy_rid → (source_slide_info, replacements, template, copy_num) + wanted = {} + copies = {} # extra slides for duplicate templates + plan_order = [] # rIds in slide_plan order (for sldIdLst reordering) + seen_rids = set() + copy_counter = 0 + + for template, replacements in slide_plan: + idx = SLIDE_IDX.get(template) + if idx is None: + print(f" ✗ Unknown template: {template}") + continue + if idx >= total: + print(f" ✗ Index {idx} out of range for {template}") + continue + + if idx not in wanted: + # First use of this template + wanted[idx] = (slide_order[idx], replacements, template) + rId = slide_order[idx][2] + plan_order.append(rId) + seen_rids.add(rId) + else: + # Duplicate — create a copy with a new filename and rId + copy_num = next_copy_num + copy_counter + copy_counter += 1 + copy_rid = f"rIdCopy{copy_num}" + copies[copy_rid] = (slide_order[idx], replacements, template, copy_num) + plan_order.append(copy_rid) + seen_rids.add(copy_rid) + print(f" ↳ Duplicate '{template}' → new slide{copy_num}.xml") + + # Slide number lookup: rId → 1-based position in output + slide_num_map = {rId: i + 1 for i, rId in enumerate(plan_order)} + + # ── Path sets ───────────────────────────────────────────────────────── + keep_paths = {slide_order[i][0] for i in wanted} + keep_rids = {slide_order[i][2] for i in wanted} + drop_paths = ( + {slide_order[i][0] for i in range(total) if i not in wanted} | + {slide_order[i][1] for i in range(total) if i not in wanted} + ) + used_media = collect_used_media(src, drop_paths) + # Also add media from copy sources (they reference same files as originals) + # (already captured above since originals' rels are not in drop_paths) + + # Collect media for copied slides (same source rels → same media) + # Already included in used_media since source slides are in keep_paths. + + out_buf = BytesIO() + with zipfile.ZipFile(out_buf, 'w', zipfile.ZIP_DEFLATED) as dst: + + for item in src.namelist(): + # ── Drop unwanted slide XML + rels ──────────────────────────── + if item in drop_paths: + continue + + # ── Strip unused media; apply optional overrides ─────────────── + if item.startswith('ppt/media/'): + fname = item.split('/')[-1] + if item not in used_media: + continue + if fname in media: + # User-provided replacement image + with open(media[fname], 'rb') as f: + dst.writestr(item, f.read()) + print(f" 🖼 Replaced {fname}") + else: + dst.writestr(item, src.read(item)) + continue + + # ── Process kept slides ─────────────────────────────────────── + if item in keep_paths: + for idx, (info, replacements, template) in wanted.items(): + if info[0] == item: + data = replace_text(src.read(item), replacements) + if page_numbers and template in SLIDES_WITH_PAGE_NUM: + rId = info[2] + pg = slide_num_map.get(rId, 0) + data = apply_page_number(data, pg) + dst.writestr(item, data) + print(f" ✓ {template:30s} ({item})") + break + continue + + # ── Patch ppt/presentation.xml — reorder sldIdLst + add copies ─ + if item == 'ppt/presentation.xml': + data = src.read(item).decode('utf-8') + + # Extract existing sldId elements → map rId → element + rid_to_elem = {} + existing_ids = [] + for m in re.finditer(r'', data): + rid_m = re.search(r'r:id="([^"]+)"', m.group(0)) + id_m = re.search(r'\bid="(\d+)"', m.group(0)) + if rid_m: + rid_to_elem[rid_m.group(1)] = m.group(0) + if id_m: + existing_ids.append(int(id_m.group(1))) + + next_sld_id = max(existing_ids, default=256) + 1 + + # Build sldId elements for copies + for copy_rid, (src_info, _, _, copy_num) in copies.items(): + elem = ( + f'' + ) + rid_to_elem[copy_rid] = elem + next_sld_id += 1 + + # Reorder sldIdLst to match plan_order + ordered = ''.join( + rid_to_elem[rid] for rid in plan_order if rid in rid_to_elem + ) + data = re.sub( + r'()(.*?)()', + lambda m: m.group(1) + ordered + m.group(3), + data, flags=re.DOTALL + ) + dst.writestr(item, data.encode('utf-8')) + continue + + # ── Patch _rels/presentation.xml.rels — add copy relationships ─ + if item == 'ppt/_rels/presentation.xml.rels': + data = src.read(item).decode('utf-8') + + # Remove unwanted slide rels + def keep_rel(m): + rid = re.search(r'Id="([^"]+)"', m.group(0)) + rtype = re.search(r'Type="([^"]+)"', m.group(0)) + if rtype and rtype.group(1) == SLIDE_REL: + return m.group(0) if (rid and rid.group(1) in keep_rids) else '' + return m.group(0) + data = re.sub(r']*/>', keep_rel, data) + + # Add relationships for copied slides + copy_rels = ''.join( + f'' + for copy_rid, (_, _, _, copy_num) in copies.items() + ) + data = data.replace('', copy_rels + '') + dst.writestr(item, data.encode('utf-8')) + continue + + # ── Patch [Content_Types].xml — add copy overrides ──────────── + if item == '[Content_Types].xml': + data = src.read(item).decode('utf-8') + + # Remove unwanted slide overrides + def keep_ct(m): + ct_type = re.search(r'ContentType="([^"]+)"', m.group(0)) + part = re.search(r'PartName="([^"]+)"', m.group(0)) + if ct_type and ct_type.group(1) == SLIDE_CT: + if part: + fname = part.group(1).lstrip('/') + return m.group(0) if fname in keep_paths else '' + return m.group(0) + data = re.sub(r']*/>', keep_ct, data) + + # Add content types for copied slides + copy_cts = ''.join( + f'' + for _, (_, _, _, copy_num) in copies.items() + ) + data = data.replace('', copy_cts + '') + dst.writestr(item, data.encode('utf-8')) + continue + + # ── Copy everything else verbatim ───────────────────────────── + dst.writestr(item, src.read(item)) + + # ── Write copied slide files ────────────────────────────────────── + for copy_rid, (src_info, replacements, template, copy_num) in copies.items(): + src_path = src_info[0] # e.g. ppt/slides/slide12.xml + src_rels = src_info[1] # e.g. ppt/slides/_rels/slide12.xml.rels + dest_path = f'ppt/slides/slide{copy_num}.xml' + dest_rels = f'ppt/slides/_rels/slide{copy_num}.xml.rels' + + data = replace_text(src.read(src_path), replacements) + if page_numbers and template in SLIDES_WITH_PAGE_NUM: + pg = slide_num_map.get(copy_rid, 0) + data = apply_page_number(data, pg) + dst.writestr(dest_path, data) + print(f" ✓ {template:30s} (copy → slide{copy_num}.xml)") + + # Copy the rels file too (same media references) + if src_rels in src.namelist(): + dst.writestr(dest_rels, src.read(src_rels)) + + with open(output_path, 'wb') as f: + f.write(out_buf.getvalue()) + + n_total = len(wanted) + len(copies) + print(f"\n✓ {n_total} slides → {output_path}") + size_mb = os.path.getsize(output_path) / 1024 / 1024 + print(f" File size: {size_mb:.1f} MB") + + +# ── Example: Rotterdam monthly overview ────────────────────────────────────── + +if __name__ == '__main__': + plan = [ + ("title-dark", [ + ("Q1 2025 Business Review", + "Office Rotterdam — Maandoverzicht"), + ("Kwartaalresultaten, strategische prioriteiten en roadmap H2", + "Highlights, events en community · maart–april 2026"), + ("Team Strategy & Leadership", "Office Rotterdam"), + ]), + + ("agenda", [ + ("Premium Hardcover Boekjes", "Wat gebeurde er deze maand"), + ("Q1 Resultaten", "Strategy Days"), + ("Revenue, margins, order volume en key highlights", + "Strategische afstemming op 14 & 15 april"), + ("5 min", "10 min"), + ("Financial Performance", "CEO Bezoek"), + ("Deep dive in de cijfers per markt en categorie", + "17 CEO's bij Michael's AI-talk op 2 april"), + ("Product & Platform", "HelloBoxing"), + ("Nieuwe features, marketplace expansie en tech roadmap", + "Bokslessen terug — elke 2 weken op donderdag"), + ("AI & Automation", "Community Moments"), + ("Skill updates, voortgang en planning voor HY1", + "Eten, cultuur en verbinding in het kantoor"), + ]), + + ("section-divider", [ + ("02", "01"), + ("Financial Performance", "Office Highlights"), + ("Omzet, marges en key metrics over het afgelopen kwartaal met een vooruitblik op de rest van het jaar.", + "De belangrijkste momenten van de afgelopen maand op een rij."), + ]), + + ("process-timeline", [ + ("WORKFLOW", "MAART \u2192 APRIL 2026"), + ("Van ontwerp tot levering", "Een maand vol events en verbinding"), + ("Upload", "Ronald McDonald"), + ("Upload je ontwerp of gebruik onze online editor.", + "Team kookte op 25 maart voor gezinnen bij het Ronald McDonald Huis."), + ("Designcheck", "HelloBoxing"), + ("Automatische controle op resolutie, afloop en kleuren.", + "Bokslessen hernomen \u2014 bi-weekly op donderdag, start 16 april."), + ("Productie", "CEO Bezoek"), + ("We matchen je order aan de beste supplier.", + "17 CEO\u2019s aanwezig op 2 april voor Michael\u2019s AI-talk in the living."), + ("Levering", "Strategy Days"), + ("Track & trace tot aan je deur. Gemiddeld 5 werkdagen.", + "Twee dagen strategische afstemming op 14 & 15 april."), + ("Gemiddelde levertijd: ", "Keynote: "), + ("5 werkdagen in NL/BE", "vrijdag 17 april om 16:30"), + ("Express: 2 werkdagen \u2192", "Met de laatste strategy updates \u2192"), + ]), + + ("big-number", [ + ("Q1 HIGHLIGHT", "CEO BEZOEK"), + ("+42%", "17"), + ("Year-over-year growth in new customer acquisition", + "CEO\u2019s aanwezig bij Michael\u2019s AI-talk in the Rotterdam living"), + ("Driven by marketplace expansion into 3 new markets and a 28% improvement in organic search visibility across all locales.", + "Op 2 april verwelkomde het Rotterdam kantoor 17 CEO\u2019s voor een sessie over AI en de toekomst van ons vak."), + ]), + + # USP cards — each card now has individual content (nth-occurrence targeting) + ("usp-4-cards", [ + ("WAAROM HELLOPRINT", "MENSEN & CULTUUR"), + ("Vier redenen om voor ons te kiezen", + "Community moments deze maand"), + # Card 1 — Armeense lunch + ("Laagste prijs", "Armeense lunch", 1), + ("garantie", "Tolma & Ghapama", 1), + ("All-inclusive prijzen zonder verborgen kosten. Vind je het goedkoper? Wij matchen het.", + "Sona nodigde iedereen uit voor een Armeense lunch op 10 april.", 1), + ("30%", "10 apr", 1), + ("goedkoper dan traditioneel", "Armeens lunchen", 1), + # Card 2 — Easter cake + ("Laagste prijs", "Easter Cake", 2), + ("garantie", "Carrot Cake", 2), + ("All-inclusive prijzen zonder verborgen kosten. Vind je het goedkoper? Wij matchen het.", + "Saskia bakte een worteltaart voor Pasen \u2014 op beiden verdiepingen!",2), + ("30%", "2 apr", 2), + ("goedkoper dan traditioneel", "Paastraktatie", 2), + # Card 3 — Chinese snacks + ("Laagste prijs", "Chinese Snacks", 3), + ("garantie", "Sachima & Mahua", 3), + ("All-inclusive prijzen zonder verborgen kosten. Vind je het goedkoper? Wij matchen het.", + "Yaqi bracht traditionele Chinese snacks mee van haar vakantie.", 3), + ("30%", "16 apr", 3), + ("goedkoper dan traditioneel", "Internationaal genieten", 3), + # Card 4 — Dennis' book + ("Laagste prijs", "Kinderboek Launch", 4), + ("garantie", "My New Parents", 4), + ("All-inclusive prijzen zonder verborgen kosten. Vind je het goedkoper? Wij matchen het.", + "Dennis\u2019 vrouw lanceerde haar eerste kinderboek \u2014 met de hand geschreven en getekend!", + 4), + ("30%", "9 apr", 4), + ("goedkoper dan traditioneel", "NL & EN editie", 4), + ]), + + ("testimonial", [ + ("Helloprint heeft ons de flexibiliteit gegeven om snel te schakelen. Van 500 visitekaartjes tot 50.000 brochures \u2014 alles wordt op tijd en perfect geleverd.", + "Rolling dolma boosts productivity by 89%. No proof\u2026 but worth testing! Kom naar boven om mee te helpen met Tolma en Ghapama voor de Armeense lunch."), + ("Martine Verhoeven", "Sona Hovhannisyan"), + ("Marketing Director \u2014 TechCorp BV", + "People & Culture \u2014 Helloprint Rotterdam"), + ("MV", "SH"), + ]), + + # Second section-divider — duplicate template, now supported + ("section-divider", [ + ("02", "02"), + ("Financial Performance", "Upcoming"), + ("Omzet, marges en key metrics over het afgelopen kwartaal met een vooruitblik op de rest van het jaar.", + "Wat staat er op de planning voor de komende weken."), + ]), + + ("closing", []), + ] + + print("Generating: Office Rotterdam — Maandoverzicht\n") + generate(plan, "/sessions/brave-bold-cerf/mnt/outputs/OfficeRotterdam_Maandoverzicht_Apr2026.pptx") diff --git a/helloprint-presentation-creator/internal-language.md b/helloprint-presentation-creator/internal-language.md new file mode 100644 index 0000000..3848407 --- /dev/null +++ b/helloprint-presentation-creator/internal-language.md @@ -0,0 +1,277 @@ +# Helloprint Internal Language Guide +Source: Helloprint Internal Language Guide v3 (April 2026) — use these terms exactly as defined in all presentations. + +--- + +## 1. People & Roles + +| Term | Definition | +|------|-----------| +| Helloprinter | Employee at Helloprint — the standard internal term for all colleagues | +| CX | Customer Experience — customer service team | +| Supplier Management | Team responsible for suppliers | +| Bespoke team | Team that handles manual quotes for custom orders | +| Process Lead | Project lead / owner of a core Helloprint process | +| RevOps | Revenue Operations — alignment between Sales, Marketing and CX | +| Traffic | CX sub-team responsible for the artwork and file flow | +| CJT | Customer Journey Tech — Helloprint's internal product development team | +| PSM | Procurement & Supplier Management — team responsible for supplier relationships | + +--- + +## 2. Systems & Tools + +| Term | Definition | +|------|-----------| +| Presta | PrestaShop — e-commerce & order management system (transactional source) | +| PCM | Product Content Management — system for core product data & SKU attributes | +| Contentful | CMS for merchant content: titles, descriptions, images | +| HubSpot | CRM and ticketing system for customer service and sales | +| Cube | Semantic layer for certified metrics and dashboards | +| HelloMatch | Tool that determines the best supplier/carrier combination per product SKU | +| BigQuery | Data warehouse — central analytics platform | +| NetSuite | ERP and financial system — invoice processing | +| Supplier Central | Management system for supplier data and IDs | +| Algolia | Search platform for product indices on the webshops | +| Partner HUB | Portal where suppliers receive, confirm and process orders | +| 360 (platform) | Internal operations/artwork platform for order and file management | +| Sendcloud | Carrier/shipping platform for tracking and label management | +| Lox | Logistics platform / carrier partner (used alongside Sendcloud) | +| Jira | Project management tool for sprint planning and issue tracking | +| Confluence | Knowledge management and documentation tool, linked to Jira | +| GitLab | Version control system for code, CI/CD pipelines and MRs | +| Sentry | Error monitoring tool — real-time detection of production issues | +| Marker.io | Visual bug reporting tool: screenshots + annotations directly from browser | +| SmartSpy | Internal monitoring and tracking tool for behavior and conversions | +| ORQ / orq.ai | AI orchestration platform for managing and monitoring AI workflows | +| FastEditor / Design Online | Online design tool for customers to personalise products | +| Logo Editor | Simplified online tool for customers to upload and position their logo | +| Studio Tool | Internal design tool for the bespoke team | +| Drukbaas | Name of a specific supplier/partner within the supplier network | + +--- + +## 3. AI Systems & Agents + +| Term | Definition | +|------|-----------| +| Quotifyer | AI agent that automatically processes bespoke quote requests and creates quotes (also referred to internally as 'bespoke GPT') | +| Anna | AI chatbot for first customer contact; collects information before Quotifyer takes over | +| Content Engine | AI system for generating product content (copy, print icons, SEO) | +| Skill | Claude AI capability that automates a specific internal process | +| MCP | Model Context Protocol — connection between Claude and external tools/systems | +| Knowledge Hub | Internal knowledge base where skills, governance cards & documentation are stored | +| Guardrail | Restriction or rule that keeps an AI system within defined boundaries | +| Engaige | CX platform linked to Quotifyer for re-orders and customer flows | + +--- + +## 4. Processes & Concepts + +| Term | Definition | +|------|-----------| +| Bespoke | Custom order outside the standard catalog; requires a manual quote | +| Handover | Transfer of a ticket or conversation from AI to the human | +| SSOT | Single Source of Truth — one authoritative data source per domain | +| PoC | Proof of Concept — first working version to validate an idea | +| Reprint | Reprint of an order due to a confirmed production or quality error | +| Divert | Rerouting of an order to an alternative supplier | +| Autocheck | Automated file check on upload — verifies print readiness | +| cPDF | Certified/Corrected PDF — print-ready file after prepress processing | +| Prepress | File preparation before production: color conversion, bleed, resolution | +| Goodwill | Customer compensation outside warranty — voucher or refund as a gesture | +| Feature Flag | Mechanism to enable or disable features per segment, market or rollout phase | +| MR | Merge Request — GitLab equivalent of a Pull Request (code review step) | +| Pub/Sub | Google Pub/Sub — asynchronous messaging architecture between microservices | +| Self-Onboarding | Process where new suppliers onboard independently via Partner HUB | +| Print Management | Enterprise segment where Helloprint takes over complete print management for a client | +| Reciprocity | Principle: suppliers receive volume in exchange for pricing agreements | +| Savings | Seasonal cost reduction target from supply-side negotiations | +| Supply Cluster | Geographic or product-based grouping of suppliers | + +--- + +## 5. Planning & Rhythm + +| Term | Definition | +|------|-----------| +| Season | Planning period of ~6 months; replaces 'quarter' in Helloprint terminology | +| HY1 / HY2 | Half Year 1 / Half Year 2 — the two seasons per year (e.g. 'HY1 Season Goals') | +| OKR | Objectives & Key Results — seasonal targets with measurable results | +| Season prep week | Week(s) in which the team prepares for the next season: OKRs, priorities, planning | +| Delete week | Week reserved for clearing technical debt and cleaning up across the organisation | +| Value Stream | Organisational unit or workflow — used in all official templates | +| HelloDays | Internal Helloprint event for company-wide updates, culture and connection | + +--- + +## 6. Document Templates + +Helloprint uses three fixed document templates for developing initiatives and projects: + +| Document | Step | Purpose | +|----------|------|---------| +| Opportunity Brief | Step 1 | Problem statement, business case & success metrics | +| Project Charter | Step 2 | Strategy, solution, scope & milestones | +| Blueprint | Step 3 | Functional + technical specs for development | + +--- + +## 7. Business Model Taxonomy + +Use these terms exactly as defined — in Looker, presentations and reports. + +| Term | Definition | +|------|-----------| +| Business Solutions | Overarching category for all direct customer solutions from Helloprint | +| End Label | Direct Helloprint webshops (helloprint.nl, .fr, .es, Drukzo.nl, etc.) | +| Business Portal | Portal for corporate/enterprise clients with organisation management & budgets | +| Reseller Solutions | Overarching category for all reseller-oriented solutions | +| Connect | B2B platform for resellers: exclusive pricing, broad assortment | +| Print Store / WLS | White Label Shop — reseller-branded webshop on the Helloprint platform | +| Reseller API | Programmatic integration for resellers via helloprintapi.com | +| RMH | Reseller Management Hub — management platform for resellers (resellerhub.io) | +| M&A | Mergers & Acquisitions — revenue distinction: via acquisitions vs. organic growth | +| Organic | Organically grown revenue, separate from acquired brands or shops | + +--- + +## 8. Product Categories (Looker order — use this order consistently) + +| Term | Definition | +|------|-----------| +| Commercial Print | Core products: flyers, folders, business cards, letterheads etc. | +| Signage & Outdoor | Outdoor advertising: banners, roll-ups, name boards, display stands etc. | +| Services | Services such as design, digital proofing etc. | +| Corporate Gifts | Promotional gifts & personalised gift items | +| Labels | Stickers and self-adhesive labels | +| Photo products | Photo products: canvas, photo frames, photo gifts etc. | +| Clothing & Textiles | Branded clothing and textile products | +| Packaging | Packaging products: boxes, bags, labels etc. | + +--- + +## 9. Metrics & KPIs + +| Term | Definition | +|------|-----------| +| GP | Gross Profit — gross profit in euros (absolute, e.g. '€1.63M GP') | +| GPM | Gross Profit Margin — gross profit margin in % (e.g. '30% GPM target') | +| Contribution Margin | Net revenue minus supplier price minus logistics costs per order line | +| AOV | Average Order Value — average value per order | +| NRR | Net Revenue Retention — measure of customer retention and upsell | +| NSM | North Star Metric — the single core indicator measuring organisational success | +| CPA / CPO | Cost Per Acquisition / Cost Per Order — efficiency of marketing spend | +| ROAS | Return on Ad Spend — return on advertising expenditure | +| NPS | Net Promoter Score — customer loyalty measure: 'how likely are you to recommend us?' | +| CSAT | Customer Satisfaction Score — satisfaction score after customer contact | +| OTD | On-Time Dispatch — KPI for timely shipment from the supplier | +| Activation Rate | % of new customers placing a second order — retention KPI | +| Deflection Rate | % of customer queries resolved without a human agent | +| Automation Rate | % of CX interactions fully handled without human intervention | +| Complaint Rate | % of orders with a complaint — supply-side quality KPI | +| Supplier Quality Score | Quality rating per supplier based on complaints and OTD | +| RFM | Recency / Frequency / Monetary — customer segmentation model based on order behaviour | +| YoY | Year over Year — comparison versus the same period last year | +| WoW / MoM | Week-over-Week / Month-over-Month — short-term period comparisons | +| Match rate | % of bespoke tickets successfully matched by Quotifyer | +| Handover rate | % of tickets transferred to the human bespoke team | + +--- + +## 10. Brand & Identity + +| Term | Definition | +|------|-----------| +| Brand Promise | "Making print easier, for everyone, everywhere." | +| Positioning | Building Earth's largest infrastructure for customised products. | +| Personality | Friendly · Pragmatic · Expert help | +| Certifications | B Corp · FSC · ISO · Great Place to Work — as trust signals, not buzzwords | +| Fightclub | Helloprint's external marketing agency | + +--- + +## 11. Shop & Data Taxonomy + +### Country Codes (internal use) + +| Code | Country / Market | +|------|----------------| +| NL | Netherlands — largest market, HQ Rotterdam | +| BE | Belgium (incl. fr.helloprint.be for French-speaking Belgium) | +| UK | United Kingdom (helloprint.co.uk, Quinns) | +| FR | France | +| ES | Spain | +| DE | Germany | +| IT | Italy | +| SE | Sweden | +| IE | Ireland | +| US | United States (helloprint.us — growth market) | +| PL | Poland | +| API | Reseller API channel (no country; orders via helloprintapi.com) | + +### Shop Names by Type + +| Category | Examples | +|----------|---------| +| End Label | helloprint.nl · helloprint.fr · helloprint.es · helloprint.co.uk · helloprint.de · helloprint.it · helloprint.se · helloprint.ie · helloprint.us · fr.helloprint.be · Drukzo.nl · Drukzo.be | +| Connect | connect.helloprint.nl · connect.helloprint.fr · connect.helloprint.es · connect.helloprint.co.uk · connect.helloprint.de · connect.helloprint.it · connect.helloprint.se | +| White Label (WLS) | Drukwerkmax.nl · Drukwerktijger.nl · quinnstheprinters.com · allgifts.nl · ZPressPrint.nl · Drukwerkbestellen.be · Event-print.nl · snelindruk.be · printking.be · uprint.be | +| Reseller API | eur.helloprintapi.com · gbp.helloprintapi.com · usd.helloprintapi.com | +| Reseller Hub | eur.resellerhub.io · gbp.resellerhub.io | + +### Business Model Structure (Looker) + +| Business Model | Sub-model | Description | +|---------------|-----------|-------------| +| Business Solutions | End Label | Direct HP webshops (helloprint.nl etc.) | +| Business Solutions | Business Portal | Enterprise portal for organisations | +| Reseller Solutions | Connect | B2B pricing access for resellers | +| Reseller Solutions | Print Store | White Label Shop (WLS) | +| Reseller Solutions | Reseller API | Programmatic integration | +| Reseller Solutions | Reseller Mgmt. Hub | Management platform (RMH) | + +### Reporting Distinction + +| Label | Definition | +|-------|-----------| +| Organic | Revenue from organically growing shops (no acquisition) | +| M&A | Revenue from acquired brands or shops (Mergers & Acquisitions) | + +--- + +## 12. Operations & Supply Chain + +| Term | Definition | +|------|-----------| +| Partner HUB | Portal where suppliers receive, confirm and process orders | +| Supplier Hub | Overarching supplier platform (synonym for Partner HUB in broader context) | +| Self-Onboarding | Process where new suppliers onboard independently via Partner HUB | +| OTD | On-Time Dispatch — KPI for timely shipment from the printer | +| Reprint | Reprint of an order when a production or quality error is confirmed | +| Divert | Rerouting of an order to an alternative supplier | +| Savings | Seasonal cost reduction target from supply-side negotiations | + +--- + +## 13. Tech & Development + +| Term | Definition | +|------|-----------| +| CJT | Customer Journey Tech — Helloprint's internal product development team | +| MR | Merge Request — GitLab equivalent of a Pull Request (code review step) | +| Feature Flag | Mechanism to enable or disable features per segment, market or rollout phase | +| Pub/Sub | Google Pub/Sub — asynchronous messaging architecture between microservices | +| ORQ / orq.ai | AI orchestration platform for orchestrating and monitoring AI workflows | + +--- + +## 14. Strategy & Initiatives + +| Term | Definition | +|------|-----------| +| Moonshot | Helloprint's long-term ambition: €1B revenue with minimal headcount growth (€1M revenue per FTE) | +| Print Management | Enterprise segment where Helloprint takes over complete print management for a client | +| AI-first | Helloprint's operating model: AI handles the bulk of work, humans focus on judgment and strategy | +| HY1 / HY2 | Half Year 1 / Half Year 2 — the two planning seasons per year | +| Road to Independence | Strategic initiative name — used in leadership presentations | diff --git a/helloprint-presentation-creator/logo_dark.png b/helloprint-presentation-creator/logo_dark.png new file mode 100644 index 0000000..1ac6f18 Binary files /dev/null and b/helloprint-presentation-creator/logo_dark.png differ diff --git a/helloprint-presentation-creator/logo_white.png b/helloprint-presentation-creator/logo_white.png new file mode 100644 index 0000000..aec5d6b Binary files /dev/null and b/helloprint-presentation-creator/logo_white.png differ diff --git a/helloprint-presentation-creator/writing-style-guide.md b/helloprint-presentation-creator/writing-style-guide.md new file mode 100644 index 0000000..e86eaf8 --- /dev/null +++ b/helloprint-presentation-creator/writing-style-guide.md @@ -0,0 +1,206 @@ +# Helloprint Writing Style Guide for Internal Presentations +Source: Helloprint Writing Style Guide (April 2026) — applies to ALL internal presentations. +Scope: Internal only. Not for external customer or partner communications. + +--- + +## 1. Brand Personality — Four Pillars (always active) + +| Pillar | What it means | +|--------|---------------| +| Friendly | Accessible and warm — but never casually informal. Write for busy professionals, not consumers. | +| Pragmatic | No-nonsense. No woolly language, no corporate jargon. If it isn't needed, leave it out. | +| Expert | Knowledge-driven and helpful. Plain language. Don't assume everyone knows everything — but treat the reader as intelligent. | +| Direct | Always lead with the main message. Conclusion at the top — not after three paragraphs of build-up. | + +--- + +## 2. Green List & Red List + +### ✅ Do this +- **Conclusion-first titles.** The slide title is the takeaway, not the topic. Good: "Q1 above target — but one risk needs attention" +- **Specific numbers.** A number makes a claim credible; an adjective does not. Good: "NPS rose from 42 to 57" +- **Honest about limitations and misses.** Internal trust is built through transparency, not spin. If something didn't work, say so directly. +- **Tables for comparisons.** Use tables when comparing 3 or more items across multiple dimensions. +- **One core idea per slide.** If you're unsure whether a slide says two things — split it. +- **Active and personal writing.** 'We decide', 'You do X', 'I propose'. +- **Document metadata at the top.** Always include For / Author / Date / Status. +- **Use → for direction or impact.** 'Days → hours', '42 → 57 NPS', 'Weeks → 1 run'. + +### ❌ Avoid this +- **Topic titles with no point.** Titles like 'Strategic Update' or 'Q1 Review' say nothing. +- **Vague claims.** Words like 'enormous', 'great', 'a lot' without evidence. No number = no argument. +- **Glossing over or omitting bad news.** Internal readers see through it. +- **Nested bullet points deeper than 2 levels.** Restructure instead of nesting. +- **Multiple messages on one slide.** The reader remembers the first or the last — rarely both. +- **Passive and impersonal writing.** 'It has been decided', 'It is being considered', 'One aims to'. +- **Documents without a metadata header.** +- **Adjectives as proof.** 'Significant', 'large', 'many'. Always quantify. + +--- + +## 3. Tone by Context + +| Context | Tone | Real Helloprint Example | +|---------|------|------------------------| +| Strategy / Leadership Deck | Clear, data-first, ambitious but realistic | "Helloprint is at a decisive inflection point. The ambition: €1B in revenue, minimal headcount growth." | +| Kickoff / All-Hands | Energetic and confident — but concrete, never hype | "We are rolling out Claude. Not as a buzzword — as a real competitive advantage." | +| OKR Update / Retrospective | Honest, constructive, data-driven. Own your misses explicitly. | "Goal not reached (❌). Cause: supplier delay. Fix: new ETA 22 Apr, owner: Tim." | +| Operational Update | Concise, status-driven, action-oriented | "Status: 🟢. Blocker: Cube data validation. Owner: Joao. ETA: week 16." | +| Team Email | Warm, direct, goal stated upfront | "Hi everyone — we're taking a major step in our AI-first transformation. Here's what it means for you." | + +--- + +## 4. Standard Presentation Structure + +Use this sequence. Not every deck needs all blocks — but the order is always the same. + +| # | Block | What goes here | +|---|-------|---------------| +| 1 | Metadata | For / Author / Date / Status (Draft / Final) at the top of every document. | +| 2 | Title / Conclusion | The takeaway goes in the title — not the topic. Write the title as if you already know the conclusion. Good: "Q1 above target — structural risk in NL requires a decision" Bad: "Q1 Review" | +| 3 | Executive Summary | Max 3–5 sentences. What's happening, what you're proposing, what you need from the reader. | +| 4 | Data / Core | Tables for comparisons. Bullets for lists of 3+ items. Always concrete numbers. Use → to show direction. | +| 5 | Implication | What does this mean? One sentence, active and assertive. Good: "This requires a decision on X before 30 April." | +| 6 | Next Step | Format: Owner · Action · Deadline. Always specific. No action item without a name on it. | + +--- + +## 5. Language & Style Rules + +**English or Dutch?** Choose one language per document. Dutch for Dutch-only teams. English when the audience is international or at leadership level. Mixing languages within a single sentence is always wrong. + +**Capitalisation & Punctuation:** +- NO ALL CAPS, except abbreviations (OKR, B2B, NPS, FTE) +- NO exclamation marks in titles or conclusions +- Title Case only for section headers, not in running text + +**Emojis — Functional, not Decorative:** +- Use ✅ ❌ 🟢 🟡 🔴 for status in tables or OKR updates +- Maximum 1–2 emojis per document for tone or atmosphere +- Never in a formal conclusion or Executive Summary + +**Bold & Formatting:** +- Bold only for key terms or KPIs +- Italics for quotes or examples +- Never underline (it looks like a hyperlink) +- Never bold and italics at the same time + +--- + +## 6. Before vs After — Concrete Examples + +| Type | ❌ Don't | ✅ Do | +|------|---------|------| +| Slide title | Q1 Strategic Update | Q1 above target — NL margin requires a decision before 30 April | +| Conclusion | The results are positive and we've learned a lot this quarter. | NPS +15pt. Revenue +8% vs target. One critical risk: NL churn up 3%. | +| Action item | We will endeavour to address this in the coming period. | Owner: Derk. Action: complete NL margin analysis. Deadline: 22 April. | +| Limitation | The data is not yet fully available at this point in time. | Cube pricing data is partial. Cross-check with Looker before deciding. | +| Impact | This will save the team a lot of time. | This reduces manual content creation: weeks → hours per market. | + +--- + +## 7. Slide Templates by Type + +### Template 1 — Opening Slide +- **Purpose:** Introduce the presentation and orient the audience immediately. +- **When:** First slide of every presentation. +- **Layout:** Visually dominant, minimal text. Dark background (Thamar Black #191919). +- **Required:** Title (central question or assertion — not the topic) · Subtitle or occasion (max 1 sentence) · Presenter(s) + role · Date and audience (e.g. "MT — 15 April 2026") +- **Optional:** Helloprint logo · Short agenda overview (max 3 items) +- **Example:** TITLE: "Road to Independence — Where do we stand, and what decisions need to be made?" SUBTITLE: MT update Q1 2026 BY: Derk Disselhoff, AI & Growth DATE: 15 April 2026 | Management Team +- **Writing rules:** Title is a question or assertion — never just a topic. No welcome sentence. Get straight to it. + +### Template 2 — Video or Full-Screen Visual Slide +- **Purpose:** Capture attention, set the scene, or demonstrate something without distraction. +- **When:** Opener, interlude, or product/tool demonstration. +- **Layout:** Full screen. Video or image fills the entire canvas. +- **Required:** Nothing — the visual or video is the content. +- **Optional:** Maximum one line of text as context, at the top or bottom (small font, high contrast). Video duration goes in speaker notes — not on the slide. +- **Writing rules:** No title on the slide if the image speaks for itself. If text is needed: maximum 6 words. Put all explanation in the speaker notes. + +### Template 3 — Agenda Slide +- **Purpose:** Show the structure of the presentation so the audience knows what's coming. +- **When:** Directly after the opening slide for presentations of 20+ minutes. +- **Required:** Numbered list of topics (max 6) · Time indication per block (recommended) +- **Example:** 1. Situation & context (5 min) 2. Q1 performance (10 min) 3. Three strategic choices (15 min) 4. Decision & next steps (5 min) +- **Writing rules:** Agenda items are topics — this is the exception to the conclusion-first rule. Short noun or verb phrases. No full sentences. + +### Template 4 — Company Update +- **Purpose:** Deliver a broad organisational update on strategy, direction, or major developments. +- **When:** All-hands, MT update, quarterly update. +- **Required:** Title = the central message (conclusion-first) · What changed or was decided? · What does it mean for the organisation? · What do you need from the audience? +- **Structure:** TITLE: [Central message / decision] CONTEXT: Why is this relevant now? (max 2 sentences) UPDATE: What has concretely changed or been achieved? IMPACT: What does this mean for teams / individuals? NEXT STEP: Owner · Action · Deadline +- **Writing rules:** Warm but to the point. No management-speak ('synergies', 'alignment', 'strategic pillars'). If there's bad news: put it in the title, not buried at the end. + +### Template 5 — Performance Update +- **Purpose:** Report results against targets. +- **When:** Weekly/monthly/quarterly reports, OKR reviews. +- **Required:** Title = the conclusion of the numbers, not 'Performance Update' · Status per KPI: 🟢 on track / 🟡 attention needed / 🔴 action required · Actual vs Target (always both) · Commentary on deviations +- **Structure per KPI:** METRIC: [KPI name] STATUS: 🟢/🟡/🔴 ACTUAL: [Number] TARGET: [Number] TREND: [Comparison with prior period] COMMENTARY: [One sentence on cause or expectation — only for 🟡 and 🔴] +- **Writing rules:** Use → for trends. Never 'the results are positive'. State the number, the target, the gap. For a 🔴: always include the cause and the corrective action. + +### Template 6 — OKR / Goals Update +- **Purpose:** Transparently report progress on objectives and key results. +- **When:** End of a month, quarter, or season. +- **Required per Objective:** Objective (ambition in one sentence) · Key Results with status (✅ / ❌ / 🔄) · Percentage or number achieved · For ❌: cause + corrective action +- **Structure:** OBJECTIVE: [Ambition in one active sentence] KR 1: [Description] | Target: X | Actual: Y | Status: ✅ KR 2: [Description] | Target: X | Actual: Y | Status: 🔄 KR 3: [Description] | Target: X | Actual: Y | Status: ❌ Cause: [one sentence] Fix: [action + owner + date] OVERALL: [X]% achieved +- **Writing rules:** Be honest about misses. ❌ is not a shame — a ❌ without explanation is. Write the Objective actively. No long narratives per KR. Commentary only on deviations. + +### Template 7 — Team Update +- **Purpose:** Share team results and developments with a broader audience. +- **When:** All-hands, MT presentation, season review. +- **Required:** Team name + mission statement (one sentence) · What did we do? (max 5 bullets, facts + numbers) · What did we learn? · Focus for the next period +- **Structure:** TEAM: [Name] MISSION: [One sentence — what is this team's reason for existing?] WHAT WE DID: - [Result 1 + number] WHAT WE LEARNED: - [Insight 1] NEXT PERIOD: - [Focus 1] BLOCKER: [What's blocking us? Who needs to do what to resolve it?] +- **Writing rules:** Mission statement in one sentence, active and specific. Not 'we aim to...'. Results always with a number or comparison. Be honest about blockers. + +### Template 8 — Decision Slide +- **Purpose:** Request or present a decision to the audience. +- **When:** MT updates, stakeholder presentations, go/no-go moments. +- **Required:** Title = the decision being requested (as a question or assertion) · Context (why now?) · Max 3 options with pros/cons · A recommendation · An explicit ask to the audience +- **Structure:** TITLE: [Decision question — e.g. 'Do we proceed with market X?'] CONTEXT: [Why now? Max 2 sentences.] OPTIONS: Option A: [Description] | Pro: ... | Con: ... Option B: [Description] | Pro: ... | Con: ... Option C: Do nothing | Pro: ... | Con: ... RECOMMENDATION: Option [X], because [one-sentence rationale]. ASK: [What do you need? Approval / input / decision?] DEADLINE: [When does the decision need to be made?] +- **Writing rules:** Always give a recommendation. Options without a position is not a decision slide. Always include the 'do nothing' option. State the ask to the audience explicitly. + +### Template 9 — Closing Slide / Next Steps +- **Purpose:** Close the presentation with a clear landing. +- **When:** Last slide of every presentation. +- **Required:** Core message of the presentation (1 sentence — the takeaway) · Next steps: Owner · Action · Deadline +- **Optional:** Open questions for discussion · Contact / follow-up meeting +- **Example:** CORE MESSAGE: 'Q1 is above target, but the NL margin requires a decision before 30 April.' NEXT STEPS: - Derk → Complete NL margin analysis → 22 April - Tim → Escalate with supplier → 18 April +- **Writing rules:** Repeat the core message in one sentence — not the full summary. Every next step has a name on it. No 'thank you for your attention'. Close with the actions. + +--- + +## 8. Brand Visual Identity + +**Colours:** +- Primary: Charmed Green #008539 · Link Green #049E46 · Thamar Black #191919 · Snowflake #F0F0F0 · Orochimaru #D9D9D9 · White #FFFFFF · Dark Green #005c26 +- Secondary: Heroic Red #D64545 · Honey Teriyaki #F36D13 · Ripe Mango #FDC325 · Hampton Beach #9D6639 · Seaside #67A5B1 + +**Font:** Inter (weights 300, 400, 500, 600, 700, 900) + +**Slide layouts:** +- Title slide: Thamar Black (#191919) background, white text, green accent line +- Content slide: White background, green section label, footer with logo and page number +- Impact / Statement slide: Dark Green (#005c26) background — use sparingly, max 1 per 5 slides +- Data / KPI slide: White background, KPI cards with green accent bar on left + +**Google Slides canvas:** 1920×1080 (16:9) + +--- + +## 9. Pre-Flight Checklist — Before You Share + +Run through this before finalising any presentation: + +1. Does the document include For / Author / Date / Status at the top? +2. Is the title of every slide the conclusion, not the topic? (exception: agenda slide) +3. Is the main message in the first sentence? +4. Are all claims backed by specific numbers? +5. Have misses or limitations been reported honestly? +6. Is the language active and personal (no passive constructions)? +7. Are emojis used functionally (not decoratively)? +8. Are ALL CAPS, exclamation marks, and jargon avoided? +9. Is every next step clear: owner · action · deadline? +10. Is the language consistent throughout (English or Dutch — not mixed)? +11. Does each slide type match the correct template from section 7?