Files
gh-trabian-fluxwing-skills/skills/fluxwing-screenshot-importer/docs/screenshot-screen-generation.md
2025-11-30 09:02:33 +08:00

6.3 KiB

Screenshot Import Screen Generation

This document provides helper functions for generating screen files (.uxm, .md, .rendered.md) during screenshot import.

Overview

Screen generation creates 3 files:

  1. .uxm - Screen metadata and component references
  2. .md - Template with {{component:id}} references
  3. .rendered.md - Actual ASCII preview with real data

Step 1: Generate Screen Metadata (.uxm)

function generateScreenUxm(mergedData) {
  const screenId = `${mergedData.screen.type}-screen`;
  const screenName = mergedData.screen.name;
  const screenDescription = mergedData.screen.description;
  const allComponentIds = [
    ...mergedData.composition.atomicComponents,
    ...mergedData.composition.compositeComponents
  ];

  return {
    "id": screenId,
    "type": "container",
    "version": "1.0.0",
    "metadata": {
      "name": screenName,
      "description": screenDescription,
      "author": "Fluxwing Screenshot Import",
      "created": new Date().toISOString(),
      "modified": new Date().toISOString(),
      "tags": ["screen", mergedData.screen.type, "imported"],
      "category": "layout"
    },
    "props": {
      "title": screenName,
      "layout": mergedData.screen.layout,
      "components": allComponentIds
    },
    "ascii": {
      "templateFile": `${screenId}.md`,
      "width": 80,
      "height": 40
    }
  };
}

Step 2: Generate Screen Template (.md)

function generateScreenTemplate(screenId, screenName, description, components, mergedData) {
  let markdown = `# ${screenName}\n\n${description}\n\n`;

  markdown += `## Layout\n\n\`\`\`\n`;

  // Reference all components
  for (const compId of components) {
    markdown += `{{component:${compId}}}\n\n`;
  }

  markdown += '\`\`\`\n\n';

  markdown += `## Components Used\n\n`;
  for (const compId of components) {
    const comp = mergedData.components.find(c => c.id === compId);
    const compName = comp ? generateComponentName(compId) : compId;
    markdown += `- \`${compId}\` - ${compName} (${comp?.type || 'unknown'})\n`;
  }

  return markdown;
}

// Helper: Convert kebab-case ID to Title Case name
function generateComponentName(id) {
  return id.split('-').map(word =>
    word.charAt(0).toUpperCase() + word.slice(1)
  ).join(' ');
}

Step 3: Generate Rendered Screen (.rendered.md)

CRITICAL: Embed actual ASCII from component files, NOT {{variables}}

async function generateScreenRendered(screenId, screenName, description, mergedData) {
  let markdown = `# ${screenName}\n\n`;

  markdown += `## Rendered Example\n\n\`\`\`\n`;

  // Build complete rendered layout
  // Stack all components vertically (user can refine layout)
  const allComponentIds = [
    ...mergedData.composition.atomicComponents,
    ...mergedData.composition.compositeComponents
  ];

  for (const compId of allComponentIds) {
    // Read component .md file
    const compMdPath = `./fluxwing/components/${compId}.md`;
    try {
      const compMdContent = await read(compMdPath);

      // Extract default state ASCII (between first ``` pair after "## Default State")
      const asciiMatch = compMdContent.match(/## Default State\n\n```\n([\s\S]*?)\n```/);
      if (asciiMatch) {
        markdown += asciiMatch[1] + '\n\n';
      } else {
        markdown += `[Component ${compId} - ASCII not found]\n\n`;
      }
    } catch (error) {
      markdown += `[Component ${compId} - File not found]\n\n`;
    }
  }

  markdown += '\`\`\`\n\n';

  // Add example data section
  markdown += `**Example Data:**\n`;
  const exampleData = generateExampleData(mergedData.screen.type);
  for (const [key, value] of Object.entries(exampleData)) {
    markdown += `- ${key}: ${value}\n`;
  }

  return markdown;
}

Step 4: Generate Example Data by Screen Type

function generateExampleData(screenType) {
  const examples = {
    "login": {
      "Email": "john.doe@example.com",
      "Password": "••••••••",
      "Button": "Sign In"
    },
    "dashboard": {
      "Revenue": "$24,567",
      "Users": "1,234",
      "Growth": "+12.5%"
    },
    "profile": {
      "Name": "Jane Smith",
      "Role": "Product Manager",
      "Email": "jane.smith@company.com"
    },
    "settings": {
      "Theme": "Dark Mode",
      "Language": "English",
      "Notifications": "Enabled"
    },
    "form": {
      "Field 1": "Example value",
      "Field 2": "Another value"
    }
  };

  return examples[screenType] || examples["form"];
}

Step 5: Write All Files Concurrently

async function writeScreenFiles(screenId, screenUxm, screenMd, screenRendered) {
  // Create screens directory
  await bash('mkdir -p ./fluxwing/screens');

  const screenDir = './fluxwing/screens';
  const uxmPath = `${screenDir}/${screenId}.uxm`;
  const mdPath = `${screenDir}/${screenId}.md`;
  const renderedPath = `${screenDir}/${screenId}.rendered.md`;

  // Write all 3 files concurrently
  await Promise.all([
    write(uxmPath, JSON.stringify(screenUxm, null, 2)),
    write(mdPath, screenMd),
    write(renderedPath, screenRendered)
  ]);

  return { uxmPath, mdPath, renderedPath };
}

Complete Workflow

async function generateScreen(mergedData) {
  const screenId = `${mergedData.screen.type}-screen`;
  const allComponentIds = [
    ...mergedData.composition.atomicComponents,
    ...mergedData.composition.compositeComponents
  ];

  // Generate all content
  const screenUxm = generateScreenUxm(mergedData);
  const screenMd = generateScreenTemplate(
    screenId,
    mergedData.screen.name,
    mergedData.screen.description,
    allComponentIds,
    mergedData
  );
  const screenRendered = await generateScreenRendered(
    screenId,
    mergedData.screen.name,
    mergedData.screen.description,
    mergedData
  );

  // Write all files
  const paths = await writeScreenFiles(screenId, screenUxm, screenMd, screenRendered);

  console.log(`✓ Created: ${paths.uxmPath}`);
  console.log(`✓ Created: ${paths.mdPath}`);
  console.log(`✓ Created: ${paths.renderedPath}`);

  return { screenId, ...paths };
}

Usage

try {
  const screenResult = await generateScreen(mergedData);
  console.log(`✅ Screen generated with ${allComponentIds.length} components`);
} catch (error) {
  console.error(`❌ Failed to generate screen: ${error.message}`);
  throw error;
}