From 580e798e16346eb894eb899edac82d2efdf40adc Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Sat, 18 Apr 2026 22:32:53 -0500 Subject: initial commit --- src/lib/export.ts | 326 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 src/lib/export.ts (limited to 'src/lib/export.ts') diff --git a/src/lib/export.ts b/src/lib/export.ts new file mode 100644 index 0000000..9e1a7f1 --- /dev/null +++ b/src/lib/export.ts @@ -0,0 +1,326 @@ +import type { BrandInputs, BrandOutputs } from '../types'; + +export function toMarkdown(inputs: BrandInputs, outputs: BrandOutputs): string { + const lines: string[] = []; + + lines.push(`# Brand Package — ${inputs.name || 'Untitled'}`); + lines.push(''); + lines.push(`*Generated ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}*`); + lines.push(''); + lines.push('---'); + lines.push(''); + + lines.push('## Overview'); + lines.push(''); + lines.push(outputs.overview); + lines.push(''); + + lines.push('## Positioning'); + lines.push(''); + lines.push(outputs.positioning); + lines.push(''); + + lines.push('## Tone & Voice'); + lines.push(''); + lines.push(`**Attributes:** ${outputs.tone.attributes.join(', ')}`); + lines.push(''); + lines.push(`**Voice:** ${outputs.tone.voiceNotes}`); + lines.push(''); + if (outputs.tone.avoidList.length > 0) { + lines.push(`**Avoid:** ${outputs.tone.avoidList.join(', ')}`); + lines.push(''); + } + if (outputs.tone.examplePhrases.length > 0) { + lines.push('**Example phrases:**'); + outputs.tone.examplePhrases.forEach(p => lines.push(`- ${p}`)); + lines.push(''); + } + + lines.push('## Messaging'); + lines.push(''); + lines.push('### Titles'); + outputs.titles.forEach((t, i) => lines.push(`${i + 1}. ${t}`)); + lines.push(''); + lines.push('### Subtitles'); + outputs.subtitles.forEach((s, i) => lines.push(`${i + 1}. ${s}`)); + lines.push(''); + lines.push('### Taglines'); + outputs.taglines.forEach((t, i) => lines.push(`${i + 1}. ${t}`)); + lines.push(''); + + lines.push('## Color Palette'); + lines.push(''); + outputs.palette.swatches.forEach(s => { + lines.push(`- **${s.name}** — \`${s.hex}\` *(${s.role})*`); + }); + lines.push(''); + + lines.push('## Typography'); + lines.push(''); + lines.push(`**Primary:** ${outputs.typography.primary}`); + if (outputs.typography.secondary !== outputs.typography.primary) + lines.push(`**Secondary:** ${outputs.typography.secondary}`); + lines.push(`**Monospace:** ${outputs.typography.mono}`); + lines.push(''); + lines.push(`*${outputs.typography.pairNote}*`); + lines.push(''); + lines.push('| Style | Size | Weight | Usage |'); + lines.push('|-------|------|--------|-------|'); + outputs.typography.scale.forEach(t => { + lines.push(`| ${t.label} | ${t.size} | ${t.weight} | ${t.usage} |`); + }); + lines.push(''); + + lines.push('## Visual Direction'); + lines.push(''); + outputs.visualDirections.forEach(dir => { + lines.push(`### ${dir.name}`); + lines.push(''); + lines.push(dir.description); + lines.push(''); + lines.push(`**Palette:** ${dir.palette}`); + lines.push(''); + lines.push(`**Typography:** ${dir.typography}`); + lines.push(''); + lines.push(`**References:** ${dir.references}`); + lines.push(''); + }); + + lines.push('## Logo Concepts'); + lines.push(''); + outputs.logoConcepts.forEach(lc => { + lines.push(`### ${lc.title}`); + lines.push(''); + lines.push(lc.concept); + lines.push(''); + lines.push(`**Mark:** ${lc.mark}`); + lines.push(''); + lines.push(`**Execution:** ${lc.execution}`); + lines.push(''); + }); + + lines.push('## Usage Examples'); + lines.push(''); + outputs.usageExamples.forEach(ex => { + lines.push(`### ${ex.context}`); + lines.push(''); + lines.push('```'); + lines.push(ex.text); + lines.push('```'); + lines.push(''); + }); + + lines.push('## Constraints'); + lines.push(''); + outputs.constraints.forEach(c => lines.push(`- ${c}`)); + lines.push(''); + + lines.push('---'); + lines.push(''); + lines.push('*Brand Workbench*'); + + return lines.join('\n'); +} + +export function toJSON(inputs: BrandInputs, outputs: BrandOutputs): string { + return JSON.stringify( + { + project: { + name: inputs.name, + category: inputs.category, + purpose: inputs.purpose, + audience: inputs.audience, + }, + brand: outputs, + meta: { + generated: new Date().toISOString(), + version: '1.0', + }, + }, + null, + 2 + ); +} + +export function toHTML(inputs: BrandInputs, outputs: BrandOutputs): string { + const date = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }); + const esc = (s: string) => s.replace(/&/g, '&').replace(//g, '>'); + + const swatchesHtml = outputs.palette.swatches.map(s => ` +
+
+
${esc(s.name)}
+
${esc(s.hex)}
+
`).join(''); + + const scaleRows = outputs.typography.scale.map(t => ` + ${esc(t.label)}${esc(t.size)}${esc(t.weight)}${esc(t.usage)}`).join(''); + + const dirsHtml = outputs.visualDirections.map(d => ` +
+

${esc(d.name)}

+

${esc(d.description)}

+
+
Palette
${esc(d.palette)}
+
Typography
${esc(d.typography)}
+
References
${esc(d.references)}
+
+
`).join(''); + + const logosHtml = outputs.logoConcepts.map(l => ` +
+

${esc(l.title)}

+

${esc(l.concept)}

+
+
Mark
${esc(l.mark)}
+
Execution
${esc(l.execution)}
+
+
`).join(''); + + const usageHtml = outputs.usageExamples.map(ex => ` +
+
${esc(ex.context)}
+
${esc(ex.text)}
+
`).join(''); + + const constraintsHtml = outputs.constraints.map(c => `
  • ${esc(c)}
  • `).join('\n'); + + const secondaryRow = outputs.typography.secondary !== outputs.typography.primary + ? `Secondary${esc(outputs.typography.secondary)}` : ''; + + return ` + + + + +${esc(inputs.name || 'Brand')} — Brand Guidelines + + + +
    +
    +

    ${esc(inputs.name || 'Brand')} Guidelines

    +
    Generated ${date}${inputs.category ? ` · ${esc(inputs.category)}` : ''}${inputs.audience ? ` · ${esc(inputs.audience)}` : ''}
    +
    + +
    +

    Overview

    +

    ${esc(outputs.overview)}

    +
    + +
    +

    Positioning

    +

    ${esc(outputs.positioning)}

    +
    + +
    +

    Tone & Voice

    +
    ${outputs.tone.attributes.map(a => `${esc(a)}`).join('')}
    +
    Voice${esc(outputs.tone.voiceNotes)}
    + ${outputs.tone.avoidList.length ? `
    Avoid${esc(outputs.tone.avoidList.join(', '))}
    ` : ''} +
    + +
    +

    Messaging

    +
    +

    Titles

    +
      ${outputs.titles.map(t => `
    1. ${esc(t)}
    2. `).join('')}
    +
    +
    +
    +

    Taglines

    +
      ${outputs.taglines.map(t => `
    1. ${esc(t)}
    2. `).join('')}
    +
    +
    + +
    +

    Color Palette

    +
    ${swatchesHtml}
    +
    + +
    +

    Typography

    + + + ${secondaryRow} + +
    Primary${esc(outputs.typography.primary)}
    Monospace${esc(outputs.typography.mono)}
    +

    ${esc(outputs.typography.pairNote)}

    +
    + + + ${scaleRows} +
    StyleSizeWeightUsage
    +
    + +
    +

    Visual Direction

    + ${dirsHtml} +
    + +
    +

    Logo Concepts

    + ${logosHtml} +
    + +
    +

    Usage Examples

    + ${usageHtml} +
    + +
    +

    Constraints

    +
      ${constraintsHtml}
    +
    +
    + +`; +} + +export function downloadFile(content: string, filename: string, mimeType: string): void { + const blob = new Blob([content], { type: mimeType }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = filename; + a.click(); + URL.revokeObjectURL(url); +} -- cgit v1.2.3