diff --git a/src/composables/usePngExport.ts b/src/composables/usePngExport.ts index 305b2ac..c6adf14 100644 --- a/src/composables/usePngExport.ts +++ b/src/composables/usePngExport.ts @@ -2,14 +2,18 @@ import { ref } from 'vue' type Toast = { kind: 'ok' | 'err'; text: string } | null -/** Slugified, stable download name derived from the plan title. */ -export function exportFilename(title: string): string { - const slug = title +/** Lowercase, dash-collapsed slug of a plan title — no extension, no fallback. */ +export function slugify(title: string): string { + return title .toLowerCase() .trim() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') - return `macroplan-${slug || 'plan'}.png` +} + +/** Slugified, stable download name derived from the plan title. */ +export function exportFilename(title: string): string { + return `macroplan-${slugify(title) || 'plan'}.png` } /** diff --git a/src/composables/useSourceExport.test.ts b/src/composables/useSourceExport.test.ts new file mode 100644 index 0000000..5e9ba32 --- /dev/null +++ b/src/composables/useSourceExport.test.ts @@ -0,0 +1,13 @@ +import { describe, it, expect } from 'vitest' +import { sourceFilename } from './useSourceExport' + +describe('sourceFilename', () => { + it('slugifies the plan title into a .toml name', () => { + expect(sourceFilename('Q3 — Checkout revamp')).toBe('macroplan-q3-checkout-revamp.toml') + }) + + it('falls back to a generic name when the title has no usable characters', () => { + expect(sourceFilename('')).toBe('macroplan-plan.toml') + expect(sourceFilename('—— ··')).toBe('macroplan-plan.toml') + }) +}) diff --git a/src/composables/useSourceExport.ts b/src/composables/useSourceExport.ts new file mode 100644 index 0000000..328832e --- /dev/null +++ b/src/composables/useSourceExport.ts @@ -0,0 +1,17 @@ +import { slugify } from './usePngExport' + +/** Slugified, stable download name for a plan's TOML source. */ +export function sourceFilename(title: string): string { + return `macroplan-${slugify(title) || 'plan'}.toml` +} + +/** Download a plan's TOML source as a .toml file (client-side, no backend). */ +export function downloadSource(source: string, filename: string): void { + const blob = new Blob([source], { type: 'text/plain;charset=utf-8' }) + const url = URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = filename + a.click() + URL.revokeObjectURL(url) +}