Added helloprint-presentation-creator skill
This commit is contained in:
Binary file not shown.
@@ -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.
|
||||
@@ -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'<p:sldId\b[^>]*r:id="([^"]+)"', prs_xml)
|
||||
rid_to_target = {}
|
||||
for m in re.finditer(
|
||||
r'<Relationship\s+Id="([^"]+)"\s+Type="([^"]+)"\s+Target="([^"]+)"', prs_rels
|
||||
):
|
||||
if m.group(2) == SLIDE_REL:
|
||||
rid_to_target[m.group(1)] = m.group(3)
|
||||
order = []
|
||||
for rid in rid_order:
|
||||
if rid in rid_to_target:
|
||||
target = rid_to_target[rid]
|
||||
slide_path = 'ppt/' + target
|
||||
rels_path = re.sub(
|
||||
r'ppt/slides/(slide\d+\.xml)',
|
||||
r'ppt/slides/_rels/\1.rels',
|
||||
slide_path
|
||||
)
|
||||
order.append((slide_path, rels_path, rid))
|
||||
return order
|
||||
|
||||
|
||||
def collect_used_media(src_zip, drop_paths):
|
||||
"""
|
||||
Collect media files referenced from any rels file NOT in drop_paths.
|
||||
Returns set of ZIP paths like 'ppt/media/image1.png'.
|
||||
"""
|
||||
used = set()
|
||||
for name in src_zip.namelist():
|
||||
if name in drop_paths or not name.endswith('.rels'):
|
||||
continue
|
||||
try:
|
||||
rels_xml = src_zip.read(name).decode('utf-8')
|
||||
except Exception:
|
||||
continue
|
||||
rels_dir = name.rsplit('/', 1)[0]
|
||||
part_dir = rels_dir.rsplit('/', 1)[0]
|
||||
for m in re.finditer(r'Target="([^"]+)"', rels_xml):
|
||||
target = m.group(1)
|
||||
if target.startswith('http') or target.startswith('#'):
|
||||
continue
|
||||
resolved = os.path.normpath(part_dir + '/' + target).replace('\\', '/')
|
||||
if '/media/' in resolved:
|
||||
used.add(resolved)
|
||||
return used
|
||||
|
||||
|
||||
def replace_nth(s, old, new, n):
|
||||
"""Replace only the nth occurrence (1-indexed) of old with new."""
|
||||
count, idx = 0, 0
|
||||
while True:
|
||||
pos = s.find(old, idx)
|
||||
if pos == -1:
|
||||
break
|
||||
count += 1
|
||||
if count == n:
|
||||
return s[:pos] + new + s[pos + len(old):]
|
||||
idx = pos + 1
|
||||
return s # nth occurrence not found — leave unchanged
|
||||
|
||||
|
||||
def replace_text(xml_bytes, replacements):
|
||||
"""
|
||||
Apply text replacements to slide XML.
|
||||
replacements: list of (old, new) or (old, new, n)
|
||||
- (old, new) → replaces ALL occurrences
|
||||
- (old, new, n) → replaces the nth occurrence (1-indexed, logical card/row number)
|
||||
|
||||
Handles XML escaping automatically — pass raw & characters, not &
|
||||
|
||||
nth-occurrence tracking: when multiple entries target the same old_text with
|
||||
different n values (e.g. cards 1-4), the engine adjusts for already-consumed
|
||||
occurrences so n=2 always means 'the 2nd card', not 'the 2nd remaining one'.
|
||||
"""
|
||||
s = xml_bytes.decode('utf-8')
|
||||
# Track how many nth-replacements have been applied per old_text,
|
||||
# so later entries with higher n are adjusted to target the correct occurrence.
|
||||
consumed: dict = {}
|
||||
|
||||
for entry in replacements:
|
||||
old, new = entry[0], entry[1]
|
||||
occurrence = entry[2] if len(entry) > 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'<p:sldId\b[^/]*/>', 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'<p:sldId id="{next_sld_id}"'
|
||||
f' r:id="{copy_rid}"/>'
|
||||
)
|
||||
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'(<p:sldIdLst>)(.*?)(</p:sldIdLst>)',
|
||||
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'<Relationship[^>]*/>', keep_rel, data)
|
||||
|
||||
# Add relationships for copied slides
|
||||
copy_rels = ''.join(
|
||||
f'<Relationship Id="{copy_rid}" '
|
||||
f'Type="{SLIDE_REL}" '
|
||||
f'Target="slides/slide{copy_num}.xml"/>'
|
||||
for copy_rid, (_, _, _, copy_num) in copies.items()
|
||||
)
|
||||
data = data.replace('</Relationships>', copy_rels + '</Relationships>')
|
||||
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'<Override[^>]*/>', keep_ct, data)
|
||||
|
||||
# Add content types for copied slides
|
||||
copy_cts = ''.join(
|
||||
f'<Override PartName="/ppt/slides/slide{copy_num}.xml"'
|
||||
f' ContentType="{SLIDE_CT}"/>'
|
||||
for _, (_, _, _, copy_num) in copies.items()
|
||||
)
|
||||
data = data.replace('</Types>', copy_cts + '</Types>')
|
||||
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")
|
||||
@@ -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 |
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -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?
|
||||
Reference in New Issue
Block a user