Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions src/app/new/NewDeckGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
PageWorkbar,
} from '@/components';

import { SAMPLE_MARKDOWN } from '@/editor/sample-deck';
import { TEMPLATE_PRESETS, type TemplatePreset } from '@/app/templates/template-presets';

const STARTER_MARKDOWN = `---
Expand Down Expand Up @@ -57,18 +56,21 @@ const BLANK_TEMPLATE: TemplatePreset = {
id: 'blank',
name: 'Blank',
vibe: 'Start from scratch',
category: 'case-study',
styleId: 'modern',
paletteId: 'electric',
density: 'comfortable',
mode: 'light',
seed: STARTER_MARKDOWN,
slideCount: 3,
};

export function NewDeckGallery() {
const router = useRouter();

const create = async (preset: TemplatePreset) => {
const deck = await createDeck({
source: STARTER_MARKDOWN,
source: preset.seed,
theme: {
styleId: preset.styleId,
paletteId: preset.paletteId,
Expand Down Expand Up @@ -128,7 +130,7 @@ function BlankCard({ onClick }: { onClick: () => void }) {
function TemplateCard({ preset, onClick }: { preset: TemplatePreset; onClick: () => void }) {
const previewDeck = useMemo(() => {
try {
const parsed = parseDeck(SAMPLE_MARKDOWN, {
const parsed = parseDeck(preset.seed, {
theme: {
styleId: preset.styleId,
paletteId: preset.paletteId,
Expand Down Expand Up @@ -177,8 +179,8 @@ function TemplateCard({ preset, onClick }: { preset: TemplatePreset; onClick: ()
);
}

function templateSlug(id: string): 'pitch' | 'editorial' | 'other' {
if (id.includes('pitch')) return 'pitch';
if (id.includes('editorial')) return 'editorial';
function templateSlug(id: string): 'case-study-pro' | 'case-study-editorial' | 'other' {
if (id === 'case-study-pro') return 'case-study-pro';
if (id === 'case-study-editorial') return 'case-study-editorial';
return 'other';
}
16 changes: 0 additions & 16 deletions src/app/opengraph-image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,6 @@ export default function OpenGraphImage() {
Open-source slide deck builder. Switch themes instantly. Export to PDF.
</div>
</div>

<div style={{ display: 'flex', alignItems: 'center', gap: 16 }}>
<div
style={{
padding: '12px 24px',
borderRadius: 999,
background: '#6366f1',
color: '#fff',
fontSize: 24,
fontWeight: 600,
}}
>
stackdeck.octifytechnologies.com
</div>
<div style={{ color: '#7d7896', fontSize: 22 }}>No backend. No accounts. No lock-in.</div>
</div>
</div>,
{ ...size },
);
Expand Down
27 changes: 14 additions & 13 deletions src/app/templates/TemplatesGallery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ import {
PageWorkbar,
} from '@/components';

import { SAMPLE_MARKDOWN } from '@/editor/sample-deck';

import { TEMPLATE_PRESETS, type TemplatePreset } from './template-presets';

export function TemplatesGallery() {
const router = useRouter();

const applyTemplate = async (preset: TemplatePreset) => {
const deck = await createDeck({
source: SAMPLE_MARKDOWN,
source: preset.seed,
theme: {
styleId: preset.styleId,
paletteId: preset.paletteId,
Expand All @@ -47,7 +45,7 @@ export function TemplatesGallery() {
back={{ href: '/', label: 'Library', ariaLabel: 'Back to library' }}
title="Templates"
count={`${TEMPLATE_PRESETS.length} ${TEMPLATE_PRESETS.length === 1 ? 'preset' : 'presets'}`}
subtitle="Hand-crafted theme combinations. Pick one to create a new deck."
subtitle="Premium starting points. Each is a fully composed deck, ready to customize."
/>

<PageMain>
Expand All @@ -64,7 +62,7 @@ export function TemplatesGallery() {
function TemplateCard({ preset, onClick }: { preset: TemplatePreset; onClick: () => void }) {
const previewDeck = useMemo(() => {
try {
const parsed = parseDeck(SAMPLE_MARKDOWN, {
const parsed = parseDeck(preset.seed, {
theme: {
styleId: preset.styleId,
paletteId: preset.paletteId,
Expand All @@ -73,7 +71,7 @@ function TemplateCard({ preset, onClick }: { preset: TemplatePreset; onClick: ()
},
});
const planned = planDeck(parsed);
return { ok: true as const, deck: { ...planned, slides: planned.slides.slice(0, 1) } };
return { ok: true as const, deck: { ...planned, slides: planned.slides.slice(0, 3) } };
} catch (e) {
const message = e instanceof ParseError ? e.message : (e as Error).message;
return { ok: false as const, error: message };
Expand All @@ -85,16 +83,19 @@ function TemplateCard({ preset, onClick }: { preset: TemplatePreset; onClick: ()
return (
<button
type="button"
className="surface-card template-card"
className="surface-card template-card template-card--multi"
data-template={templateAttr}
onClick={onClick}
>
<div className="template-card__preview">
<div className="template-card__scaler">
<div className="template-card__preview template-card__preview--multi">
<div className="template-card__scaler template-card__scaler--multi">
{previewDeck.ok ? <DeckRenderer deck={previewDeck.deck} /> : null}
</div>
<span className="template-card__chip" data-template={templateAttr}>
{preset.name}
{preset.category}
</span>
<span className="template-card__count" aria-label={`${preset.slideCount} slides`}>
{preset.slideCount} slides
</span>
</div>
<div className="template-card__meta">
Expand All @@ -113,8 +114,8 @@ function TemplateCard({ preset, onClick }: { preset: TemplatePreset; onClick: ()
);
}

function templateSlug(id: string): 'pitch' | 'editorial' | 'other' {
if (id.includes('pitch')) return 'pitch';
if (id.includes('editorial')) return 'editorial';
function templateSlug(id: string): 'case-study-pro' | 'case-study-editorial' | 'other' {
if (id === 'case-study-pro') return 'case-study-pro';
if (id === 'case-study-editorial') return 'case-study-editorial';
return 'other';
}
100 changes: 100 additions & 0 deletions src/app/templates/seeds/case-study-editorial.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
export const CASE_STUDY_EDITORIAL_MARKDOWN = `---
title: A quieter signal
footer: Selene & Co · Editorial case study · 2026
---

::cover
# A quieter signal.

The story of how a 47-year-old publishing house rebuilt its newsroom, one workflow at a time.

Selene & Co · Editorial case study
::

::slide

::section
# The brief.
::

::slide

::pull-quote
> We did not need a new tool. We needed a newsroom that trusted itself again.
> -- Editor in chief, Selene Press
::

::slide

::scope-strip{industry="Independent publishing" region="London and New York" timeframe="11 months, 2025–2026"}
::

::slide

::section
# The work.
::

::slide

::approach
We spent the first six weeks not writing code. We sat in editorial standups, watched four issues ship, and counted the number of times the same document was re-typed into a different system. The number was twenty-three. That number became the brief.

The redesign collapsed the chain to a single document, with branches for print, web, and the audio edition. Editors stayed in prose. Producers picked up the rest downstream.
::

::slide

::before-after
**Before**

Reporters wrote in Word. Sub-editors re-typed into a CMS. Designers pulled from a third tool. The audio team rebuilt every script from scratch. Every issue was four versions of the same words.

:::

**After**

A single document is the source of truth. Print, web, and audio each pull a designed view. Reporters watch their words flow into every channel without lifting a finger.
::

::slide

::kpi-grid{source="Selene & Co internal time-tracking, average over the last quarter of engagement"}
::stat{value="63%" label="Less time per issue" delta="-9.2 hr" trend="down"}
::stat{value="3.4x" label="Audio editions shipped" delta="+2.4x" trend="up"}
::stat{value="91" label="Editor NPS" delta="+38" trend="up"}
::

::slide

::testimonial{name="Anouk Devereux" role="Editor in chief" company="Selene Press"}
> We did not become a tech company. We became a newsroom that knew where its time was going. The difference is in the work, not the tools.
::

::slide

::big-number{value="20,000 hrs" label="Editorial time returned annually" delta="+20k" trend="up" source="Calculated from time-tracking before and after launch, full editorial staff"}
::

::slide

::section
# The takeaway.
::

::slide

::pull-quote
> Software at its best disappears. The byline stays.
::

::slide

::tear-sheet{client="Selene & Co" engagement="Newsroom systems redesign" outcome="63% time reclaimed, single-source workflow across print, web, audio" date="2025–2026"}
::

::slide

::contact{name="Marin Aalto" role="Practice lead, editorial systems" email="marin@stackdeck.studio" url="stackdeck.studio"}
::
`;
97 changes: 97 additions & 0 deletions src/app/templates/seeds/case-study-pro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
export const CASE_STUDY_PRO_MARKDOWN = `---
title: How Northwind cut deploy time from 12 minutes to 90 seconds
footer: Northwind × stackdeck · Confidential · Q2 2026
---

::cover
# Northwind ships ten times a day.

A 9-week engagement that turned a brittle release pipeline into a competitive advantage.
::

::slide

::scope-strip{industry="B2B SaaS, logistics" region="North America" timeframe="9 weeks, Q2 2026"}
::

::slide

::big-number{value="92%" label="Reduction in time-to-deploy" delta="11min" trend="down" source="Northwind internal CI metrics, 2026"}
::

::slide

::section
# The brief.
::

::slide

::problem
A 12-minute pipeline ran on every push, failed 8% of runs, and turned every release into a small ceremony. Engineers were batching changes to avoid the wait, which made each merge bigger and riskier than the last.
::

::slide

::approach
We re-architected the pipeline around three ideas: parallel test sharding, layer-aware Docker caching, and a lightweight gatekeeper that blocks only on what actually broke. We left the unit-test contract alone so the team did not have to relearn anything.
::

::slide

::section
# What changed.
::

::slide

::kpi-grid{source="Pipeline observability, weekly avg over the last 4 weeks of engagement"}
::stat{value="90s" label="Median pipeline" delta="-11m" trend="down"}
::stat{value="0.4%" label="Failure rate" delta="-7.6pp" trend="down"}
::stat{value="14x" label="Releases per week" delta="+12.6" trend="up"}
::stat{value="$1.8M" label="Recovered eng time / yr" delta="+$1.8M" trend="up"}
::

::slide

::before-after
**Before**

A single linear job. 12 minutes. 8% failure rate. Engineers waited, batched, pushed bigger PRs. Friday afternoon deploys felt like a coin flip.

:::

**After**

Sharded jobs run in parallel with smart caching. 90 seconds median. 0.4% failure. Engineers ship as they finish, multiple times a day, without thinking about it.
::

::slide

::testimonial{name="Priya Mehta" role="VP Engineering" company="Northwind"}
> Stackdeck took the part of our day we hated and made it disappear. Our team's mood is different. Our release calendar is different. The product moves faster.
::

::slide

::pull-quote
> The pipeline used to be the meeting. Now nobody mentions it.
> -- Engineering all-hands, week 9
::

::slide

::tear-sheet{client="Northwind Logistics" engagement="Pipeline overhaul" outcome="92% faster deploys, 14x release frequency" date="Q2 2026"}
::

::slide

::section
# What's next.
::

::slide

::contact{name="Riley Chen" role="Principal, stackdeck" email="riley@stackdeck.studio" phone="+1 415 555 0188" url="stackdeck.studio"}
::
`;
Loading
Loading