Designer Workflow
Table of Contents
Designer Workflow
From HTML/Figma to WordPress Theme in Minutes
Jankx 2.0 được thiết kế đặc biệt cho designers, cho phép chuyển đổi nhanh từ HTML có sẵn và Figma designs sang WordPress theme hoàn chỉnh.
🎨 Designer-Centric Features
Design-to-Code Workflow
┌─────────────────────────────────────┐
│ Design Input │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Figma │ │ HTML │ │
│ │ Design │ │ Template │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Jankx 2.0 Tools │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Design │ │ Code │ │
│ │ Parser │ │ Generator │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ WordPress Output │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Gutenberg │ │ Theme │ │
│ │ Blocks │ │ Files │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────┘
🔧 Design Parser
Figma Design Parser
<?php
namespace Jankx\Designer\Parser;
class FigmaDesignParser
{
private $figmaApi;
private $designData;
public function __construct(string $figmaToken)
{
$this->figmaApi = new FigmaAPI($figmaToken);
}
public function parseFigmaFile(string $fileId): DesignData
{
$this->designData = $this->figmaApi->getFile($fileId);
return new DesignData([
'colors' => $this->extractColors(),
'typography' => $this->extractTypography(),
'components' => $this->extractComponents(),
'layouts' => $this->extractLayouts(),
'spacing' => $this->extractSpacing(),
]);
}
private function extractColors(): array
{
$colors = [];
foreach ($this->designData['styles'] as $style) {
if ($style['styleType'] === 'FILL') {
$colors[$style['name']] = [
'value' => $style['description'],
'type' => 'color',
'figma_id' => $style['key'],
];
}
}
return $colors;
}
private function extractTypography(): array
{
$typography = [];
foreach ($this->designData['styles'] as $style) {
if ($style['styleType'] === 'TEXT') {
$typography[$style['name']] = [
'font_family' => $style['style']['fontFamily'],
'font_size' => $style['style']['fontSize'],
'font_weight' => $style['style']['fontWeight'],
'line_height' => $style['style']['lineHeightPx'],
'figma_id' => $style['key'],
];
}
}
return $typography;
}
private function extractComponents(): array
{
$components = [];
foreach ($this->designData['components'] as $component) {
$components[$component['name']] = [
'id' => $component['key'],
'name' => $component['name'],
'description' => $component['description'] ?? '',
'properties' => $this->extractComponentProperties($component),
'variants' => $this->extractComponentVariants($component),
];
}
return $components;
}
private function extractLayouts(): array
{
$layouts = [];
foreach ($this->designData['pages'] as $page) {
$layouts[$page['name']] = [
'id' => $page['id'],
'name' => $page['name'],
'frames' => $this->extractFrames($page),
'components' => $this->extractPageComponents($page),
];
}
return $layouts;
}
private function extractSpacing(): array
{
$spacing = [];
// Extract spacing from design tokens
foreach ($this->designData['styles'] as $style) {
if (strpos($style['name'], 'spacing') !== false) {
$spacing[$style['name']] = [
'value' => $style['description'],
'type' => 'spacing',
'figma_id' => $style['key'],
];
}
}
return $spacing;
}
}
HTML Template Parser
<?php
namespace Jankx\Designer\Parser;
class HTMLTemplateParser
{
private $htmlContent;
private $parsedData;
public function parseHTMLFile(string $filePath): DesignData
{
$this->htmlContent = file_get_contents($filePath);
return new DesignData([
'colors' => $this->extractColorsFromCSS(),
'typography' => $this->extractTypographyFromCSS(),
'components' => $this->extractComponentsFromHTML(),
'layouts' => $this->extractLayoutsFromHTML(),
'assets' => $this->extractAssetsFromHTML(),
]);
}
private function extractColorsFromCSS(): array
{
$colors = [];
// Extract CSS custom properties
preg_match_all('/--([^:]+):\s*([^;]+);/', $this->htmlContent, $matches);
for ($i = 0; $i < count($matches[1]); $i++) {
$name = trim($matches[1][$i]);
$value = trim($matches[2][$i]);
if (preg_match('/^#[0-9a-fA-F]{3,6}$/', $value) ||
preg_match('/^rgb\(/', $value) ||
preg_match('/^hsl\(/', $value)) {
$colors[$name] = [
'value' => $value,
'type' => 'color',
'source' => 'css',
];
}
}
return $colors;
}
private function extractTypographyFromCSS(): array
{
$typography = [];
// Extract font families
preg_match_all('/font-family:\s*([^;]+);/', $this->htmlContent, $matches);
foreach ($matches[1] as $fontFamily) {
$fontFamily = trim($fontFamily);
if (!in_array($fontFamily, $typography)) {
$typography[] = $fontFamily;
}
}
return $typography;
}
private function extractComponentsFromHTML(): array
{
$components = [];
// Extract reusable components
preg_match_all('/<([a-z]+)[^>]*class="([^"]*component[^"]*)"[^>]*>/i', $this->htmlContent, $matches);
for ($i = 0; $i < count($matches[1]); $i++) {
$tag = $matches[1][$i];
$classes = $matches[2][$i];
$componentName = $this->extractComponentName($classes);
if ($componentName) {
$components[$componentName] = [
'tag' => $tag,
'classes' => $classes,
'html' => $this->extractComponentHTML($componentName),
];
}
}
return $components;
}
private function extractLayoutsFromHTML(): array
{
$layouts = [];
// Extract layout sections
preg_match_all('/<([a-z]+)[^>]*class="([^"]*layout[^"]*)"[^>]*>/i', $this->htmlContent, $matches);
for ($i = 0; $i < count($matches[1]); $i++) {
$tag = $matches[1][$i];
$classes = $matches[2][$i];
$layoutName = $this->extractLayoutName($classes);
if ($layoutName) {
$layouts[$layoutName] = [
'tag' => $tag,
'classes' => $classes,
'html' => $this->extractLayoutHTML($layoutName),
];
}
}
return $layouts;
}
private function extractAssetsFromHTML(): array
{
$assets = [];
// Extract images
preg_match_all('/<img[^>]*src="([^"]*)"[^>]*>/i', $this->htmlContent, $matches);
$assets['images'] = array_unique($matches[1]);
// Extract CSS files
preg_match_all('/<link[^>]*href="([^"]*\.css)"[^>]*>/i', $this->htmlContent, $matches);
$assets['css'] = array_unique($matches[1]);
// Extract JS files
preg_match_all('/<script[^>]*src="([^"]*\.js)"[^>]*>/i', $this->htmlContent, $matches);
$assets['js'] = array_unique($matches[1]);
return $assets;
}
}
🎯 Code Generator
WordPress Theme Generator
<?php
namespace Jankx\Designer\Generator;
class WordPressThemeGenerator
{
private $designData;
private $outputPath;
public function __construct(DesignData $designData, string $outputPath)
{
$this->designData = $designData;
$this->outputPath = $outputPath;
}
public function generateTheme(): void
{
// Generate theme structure
$this->createThemeStructure();
// Generate style.css
$this->generateStyleCSS();
// Generate functions.php
$this->generateFunctionsPHP();
// Generate Gutenberg blocks
$this->generateGutenbergBlocks();
// Generate templates
$this->generateTemplates();
// Generate assets
$this->generateAssets();
// Generate configuration
$this->generateConfiguration();
}
private function createThemeStructure(): void
{
$directories = [
'assets/css',
'assets/js',
'assets/images',
'templates',
'includes',
'gutenberg/blocks',
'config',
];
foreach ($directories as $dir) {
$path = $this->outputPath . '/' . $dir;
if (!is_dir($path)) {
mkdir($path, 0755, true);
}
}
}
private function generateStyleCSS(): void
{
$css = $this->generateThemeCSS();
file_put_contents($this->outputPath . '/style.css', $css);
}
private function generateThemeCSS(): string
{
$css = "/*\n";
$css .= "Theme Name: Generated Theme\n";
$css .= "Description: Theme generated from design data\n";
$css .= "Version: 1.0.0\n";
$css .= "*/\n\n";
// Add design tokens
$css .= $this->generateDesignTokens();
// Add component styles
$css .= $this->generateComponentStyles();
// Add layout styles
$css .= $this->generateLayoutStyles();
return $css;
}
private function generateDesignTokens(): string
{
$css = ":root {\n";
// Add colors
foreach ($this->designData->getColors() as $name => $color) {
$css .= " --color-{$name}: {$color['value']};\n";
}
// Add typography
foreach ($this->designData->getTypography() as $name => $typography) {
$css .= " --font-{$name}-family: {$typography['font_family']};\n";
$css .= " --font-{$name}-size: {$typography['font_size']}px;\n";
$css .= " --font-{$name}-weight: {$typography['font_weight']};\n";
}
// Add spacing
foreach ($this->designData->getSpacing() as $name => $spacing) {
$css .= " --spacing-{$name}: {$spacing['value']};\n";
}
$css .= "}\n\n";
return $css;
}
private function generateComponentStyles(): string
{
$css = '';
foreach ($this->designData->getComponents() as $name => $component) {
$css .= ".jankx-component-{$name} {\n";
// Add component-specific styles
if (isset($component['styles'])) {
foreach ($component['styles'] as $property => $value) {
$css .= " {$property}: {$value};\n";
}
}
$css .= "}\n\n";
}
return $css;
}
private function generateLayoutStyles(): string
{
$css = '';
foreach ($this->designData->getLayouts() as $name => $layout) {
$css .= ".jankx-layout-{$name} {\n";
// Add layout-specific styles
if (isset($layout['styles'])) {
foreach ($layout['styles'] as $property => $value) {
$css .= " {$property}: {$value};\n";
}
}
$css .= "}\n\n";
}
return $css;
}
private function generateFunctionsPHP(): void
{
$php = $this->generateThemeFunctions();
file_put_contents($this->outputPath . '/functions.php', $php);
}
private function generateThemeFunctions(): string
{
$php = "<?php\n\n";
$php .= "// Theme functions generated from design data\n\n";
// Add theme setup
$php .= "function jankx_theme_setup() {\n";
$php .= " add_theme_support('post-thumbnails');\n";
$php .= " add_theme_support('title-tag');\n";
$php .= " add_theme_support('html5', [\n";
$php .= " 'search-form',\n";
$php .= " 'comment-form',\n";
$php .= " 'comment-list',\n";
$php .= " 'gallery',\n";
$php .= " 'caption',\n";
$php .= " ]);\n";
$php .= "}\n";
$php .= "add_action('after_setup_theme', 'jankx_theme_setup');\n\n";
// Add enqueue scripts
$php .= "function jankx_enqueue_scripts() {\n";
$php .= " wp_enqueue_style('jankx-style', get_stylesheet_uri());\n";
$php .= " wp_enqueue_script('jankx-script', get_template_directory_uri() . '/assets/js/main.js', [], '1.0.0', true);\n";
$php .= "}\n";
$php .= "add_action('wp_enqueue_scripts', 'jankx_enqueue_scripts');\n\n";
return $php;
}
private function generateGutenbergBlocks(): void
{
foreach ($this->designData->getComponents() as $name => $component) {
$this->generateBlock($name, $component);
}
}
private function generateBlock(string $name, array $component): void
{
$blockDir = $this->outputPath . "/gutenberg/blocks/{$name}";
if (!is_dir($blockDir)) {
mkdir($blockDir, 0755, true);
}
// Generate block.json
$blockJson = $this->generateBlockJSON($name, $component);
file_put_contents($blockDir . '/block.json', $blockJson);
// Generate block class
$blockClass = $this->generateBlockClass($name, $component);
file_put_contents($blockDir . "/{$name}.php", $blockClass);
// Generate block template
$blockTemplate = $this->generateBlockTemplate($name, $component);
file_put_contents($blockDir . "/{$name}.html", $blockTemplate);
}
private function generateBlockJSON(string $name, array $component): string
{
$json = [
'apiVersion' => 2,
'name' => "jankx/{$name}",
'title' => ucfirst($name),
'category' => 'jankx',
'icon' => 'admin-generic',
'description' => $component['description'] ?? "Generated {$name} component",
'supports' => [
'html' => false,
'anchor' => true,
],
'attributes' => $this->generateBlockAttributes($component),
'textdomain' => 'jankx',
'editorScript' => "file:./index.js",
'editorStyle' => "file:./index.css",
'style' => "file:./style-index.css",
];
return json_encode($json, JSON_PRETTY_PRINT);
}
private function generateBlockAttributes(array $component): array
{
$attributes = [];
if (isset($component['properties'])) {
foreach ($component['properties'] as $prop => $config) {
$attributes[$prop] = [
'type' => $config['type'] ?? 'string',
'default' => $config['default'] ?? '',
];
}
}
return $attributes;
}
private function generateBlockClass(string $name, array $component): string
{
$className = ucfirst($name) . 'Block';
$php = "<?php\n\n";
$php .= "namespace Jankx\\Gutenberg\\Blocks;\n\n";
$php .= "class {$className} extends \\Jankx\\Gutenberg\\Block\n";
$php .= "{\n";
$php .= " protected \$name = '{$name}';\n\n";
$php .= " public function render(\$attributes, \$content) {\n";
$php .= " return \$this->renderTemplate('{$name}', \$attributes);\n";
$php .= " }\n";
$php .= "}\n";
return $php;
}
private function generateBlockTemplate(string $name, array $component): string
{
$html = "<div class=\"jankx-block jankx-block-{$name}\">\n";
if (isset($component['html'])) {
$html .= $component['html'];
} else {
$html .= " <div class=\"jankx-block-content\">\n";
$html .= " <h1 id="speed-optimization">Speed Optimization</h1>
<blockquote>
<p><strong>Optimized Design-to-Website Conversion</strong></p>
</blockquote>
<p>Jankx 2.0 được tối ưu hóa để chuyển đổi design thành website một cách hiệu quả với độ chính xác cao.</p>
<h2 id="-performance-optimization">⚡ Performance Optimization</h2>
<h3 id="design-to-website-conversion-goals">Design-to-Website Conversion Goals</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌─────────────────────────────────────┐
│ Optimization Goals │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Fast │ │ Accurate │ │
│ │ Conversion │ │ Design │ │
│ │ Process │ │ Transfer │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ Quality Focus │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ High │ │ Client │ │
│ │ Fidelity │ │ Friendly │ │
│ │ Design │ │ Interface │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────┘
</code></pre></div></div>
<h2 id="-optimized-conversion-pipeline">🚀 Optimized Conversion Pipeline</h2>
<h3 id="1-parallel-processing">1. <strong>Parallel Processing</strong></h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="kd">class</span> <span class="nc">ParallelConverter</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">convertDesign</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">):</span> <span class="kt">Theme</span>
<span class="p">{</span>
<span class="nv">$startTime</span> <span class="o">=</span> <span class="nb">microtime</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="c1">// Parallel processing for efficiency</span>
<span class="nv">$promises</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'designData'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">extractDesignDataAsync</span><span class="p">(</span><span class="nv">$fileId</span><span class="p">),</span>
<span class="s1">'assets'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">extractAssetsAsync</span><span class="p">(</span><span class="nv">$fileId</span><span class="p">),</span>
<span class="s1">'components'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">extractComponentsAsync</span><span class="p">(</span><span class="nv">$fileId</span><span class="p">),</span>
<span class="s1">'styles'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">extractStylesAsync</span><span class="p">(</span><span class="nv">$fileId</span><span class="p">),</span>
<span class="p">];</span>
<span class="c1">// Wait for all parallel operations</span>
<span class="nv">$results</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">waitForAll</span><span class="p">(</span><span class="nv">$promises</span><span class="p">);</span>
<span class="c1">// Generate theme from results</span>
<span class="nv">$theme</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">generateThemeFromResults</span><span class="p">(</span><span class="nv">$results</span><span class="p">);</span>
<span class="nv">$endTime</span> <span class="o">=</span> <span class="nb">microtime</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="nv">$duration</span> <span class="o">=</span> <span class="nv">$endTime</span> <span class="o">-</span> <span class="nv">$startTime</span><span class="p">;</span>
<span class="nv">$this</span><span class="o">-></span><span class="nf">logPerformance</span><span class="p">(</span><span class="nv">$duration</span><span class="p">);</span>
<span class="k">return</span> <span class="nv">$theme</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">extractDesignDataAsync</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">):</span> <span class="kt">Promise</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nc">Promise</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$resolve</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$fileId</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Async Figma API call</span>
<span class="nv">$figmaApi</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FigmaAPI</span><span class="p">();</span>
<span class="nv">$figmaApi</span><span class="o">-></span><span class="nf">getFileAsync</span><span class="p">(</span><span class="nv">$fileId</span><span class="p">)</span><span class="o">-></span><span class="nf">then</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$data</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$resolve</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$resolve</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="nf">parseDesignData</span><span class="p">(</span><span class="nv">$data</span><span class="p">));</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">extractAssetsAsync</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">):</span> <span class="kt">Promise</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nc">Promise</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$resolve</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$fileId</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Async asset extraction</span>
<span class="nv">$assetExtractor</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">AssetExtractor</span><span class="p">();</span>
<span class="nv">$assetExtractor</span><span class="o">-></span><span class="nf">extractAsync</span><span class="p">(</span><span class="nv">$fileId</span><span class="p">)</span><span class="o">-></span><span class="nf">then</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$assets</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$resolve</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$resolve</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="nf">optimizeAssets</span><span class="p">(</span><span class="nv">$assets</span><span class="p">));</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">extractComponentsAsync</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">):</span> <span class="kt">Promise</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nc">Promise</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$resolve</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$fileId</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Async component extraction</span>
<span class="nv">$componentExtractor</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ComponentExtractor</span><span class="p">();</span>
<span class="nv">$componentExtractor</span><span class="o">-></span><span class="nf">extractAsync</span><span class="p">(</span><span class="nv">$fileId</span><span class="p">)</span><span class="o">-></span><span class="nf">then</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$components</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$resolve</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$resolve</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="nf">generateComponents</span><span class="p">(</span><span class="nv">$components</span><span class="p">));</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">extractStylesAsync</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">):</span> <span class="kt">Promise</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nc">Promise</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$resolve</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$fileId</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Async style extraction</span>
<span class="nv">$styleExtractor</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StyleExtractor</span><span class="p">();</span>
<span class="nv">$styleExtractor</span><span class="o">-></span><span class="nf">extractAsync</span><span class="p">(</span><span class="nv">$fileId</span><span class="p">)</span><span class="o">-></span><span class="nf">then</span><span class="p">(</span><span class="k">function</span><span class="p">(</span><span class="nv">$styles</span><span class="p">)</span> <span class="k">use</span> <span class="p">(</span><span class="nv">$resolve</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$resolve</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="nf">generateStyles</span><span class="p">(</span><span class="nv">$styles</span><span class="p">));</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="2-cached-design-data">2. <strong>Cached Design Data</strong></h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="kd">class</span> <span class="nc">DesignCache</span>
<span class="p">{</span>
<span class="k">private</span> <span class="nv">$cache</span><span class="p">;</span>
<span class="k">private</span> <span class="nv">$cacheTime</span> <span class="o">=</span> <span class="mi">300</span><span class="p">;</span> <span class="c1">// 5 minutes</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">getCachedDesign</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">):</span> <span class="kt">?DesignData</span>
<span class="p">{</span>
<span class="nv">$cacheKey</span> <span class="o">=</span> <span class="s2">"design_</span><span class="si">{</span><span class="nv">$fileId</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
<span class="nv">$cached</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="n">cache</span><span class="o">-></span><span class="nf">get</span><span class="p">(</span><span class="nv">$cacheKey</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$cached</span> <span class="o">&&</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">isCacheValid</span><span class="p">(</span><span class="nv">$cached</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nv">$cached</span><span class="p">[</span><span class="s1">'data'</span><span class="p">];</span>
<span class="p">}</span>
<span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">cacheDesign</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">,</span> <span class="kt">DesignData</span> <span class="nv">$data</span><span class="p">):</span> <span class="kt">void</span>
<span class="p">{</span>
<span class="nv">$cacheKey</span> <span class="o">=</span> <span class="s2">"design_</span><span class="si">{</span><span class="nv">$fileId</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
<span class="nv">$this</span><span class="o">-></span><span class="n">cache</span><span class="o">-></span><span class="nf">set</span><span class="p">(</span><span class="nv">$cacheKey</span><span class="p">,</span> <span class="p">[</span>
<span class="s1">'data'</span> <span class="o">=></span> <span class="nv">$data</span><span class="p">,</span>
<span class="s1">'timestamp'</span> <span class="o">=></span> <span class="nb">time</span><span class="p">(),</span>
<span class="s1">'version'</span> <span class="o">=></span> <span class="nv">$data</span><span class="o">-></span><span class="nf">getVersion</span><span class="p">()</span>
<span class="p">],</span> <span class="nv">$this</span><span class="o">-></span><span class="n">cacheTime</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">invalidateCache</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">):</span> <span class="kt">void</span>
<span class="p">{</span>
<span class="nv">$cacheKey</span> <span class="o">=</span> <span class="s2">"design_</span><span class="si">{</span><span class="nv">$fileId</span><span class="si">}</span><span class="s2">"</span><span class="p">;</span>
<span class="nv">$this</span><span class="o">-></span><span class="n">cache</span><span class="o">-></span><span class="nb">delete</span><span class="p">(</span><span class="nv">$cacheKey</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">isCacheValid</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$cached</span><span class="p">):</span> <span class="kt">bool</span>
<span class="p">{</span>
<span class="k">return</span> <span class="p">(</span><span class="nb">time</span><span class="p">()</span> <span class="o">-</span> <span class="nv">$cached</span><span class="p">[</span><span class="s1">'timestamp'</span><span class="p">])</span> <span class="o"><</span> <span class="nv">$this</span><span class="o">-></span><span class="n">cacheTime</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="3-incremental-updates">3. <strong>Incremental Updates</strong></h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="kd">class</span> <span class="nc">IncrementalUpdater</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">updateDesign</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$fileId</span><span class="p">,</span> <span class="kt">array</span> <span class="nv">$changes</span><span class="p">):</span> <span class="kt">Theme</span>
<span class="p">{</span>
<span class="c1">// Only update changed components</span>
<span class="nv">$updatedComponents</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">foreach</span> <span class="p">(</span><span class="nv">$changes</span> <span class="k">as</span> <span class="nv">$change</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$change</span><span class="p">[</span><span class="s1">'type'</span><span class="p">]</span> <span class="o">===</span> <span class="s1">'component'</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$updatedComponents</span><span class="p">[]</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">updateComponent</span><span class="p">(</span><span class="nv">$change</span><span class="p">);</span>
<span class="p">}</span> <span class="k">elseif</span> <span class="p">(</span><span class="nv">$change</span><span class="p">[</span><span class="s1">'type'</span><span class="p">]</span> <span class="o">===</span> <span class="s1">'style'</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-></span><span class="nf">updateStyle</span><span class="p">(</span><span class="nv">$change</span><span class="p">);</span>
<span class="p">}</span> <span class="k">elseif</span> <span class="p">(</span><span class="nv">$change</span><span class="p">[</span><span class="s1">'type'</span><span class="p">]</span> <span class="o">===</span> <span class="s1">'layout'</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$this</span><span class="o">-></span><span class="nf">updateLayout</span><span class="p">(</span><span class="nv">$change</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Apply incremental updates</span>
<span class="k">return</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">applyUpdates</span><span class="p">(</span><span class="nv">$updatedComponents</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">updateComponent</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$change</span><span class="p">):</span> <span class="kt">Component</span>
<span class="p">{</span>
<span class="nv">$componentId</span> <span class="o">=</span> <span class="nv">$change</span><span class="p">[</span><span class="s1">'id'</span><span class="p">];</span>
<span class="nv">$newData</span> <span class="o">=</span> <span class="nv">$change</span><span class="p">[</span><span class="s1">'data'</span><span class="p">];</span>
<span class="c1">// Update only changed properties</span>
<span class="nv">$component</span> <span class="o">=</span> <span class="nv">$this</span><span class="o">-></span><span class="nf">getComponent</span><span class="p">(</span><span class="nv">$componentId</span><span class="p">);</span>
<span class="nv">$component</span><span class="o">-></span><span class="nf">update</span><span class="p">(</span><span class="nv">$newData</span><span class="p">);</span>
<span class="k">return</span> <span class="nv">$component</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">updateStyle</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$change</span><span class="p">):</span> <span class="kt">void</span>
<span class="p">{</span>
<span class="nv">$styleId</span> <span class="o">=</span> <span class="nv">$change</span><span class="p">[</span><span class="s1">'id'</span><span class="p">];</span>
<span class="nv">$newStyle</span> <span class="o">=</span> <span class="nv">$change</span><span class="p">[</span><span class="s1">'data'</span><span class="p">];</span>
<span class="c1">// Update CSS variable</span>
<span class="nv">$this</span><span class="o">-></span><span class="nf">updateCSSVariable</span><span class="p">(</span><span class="nv">$styleId</span><span class="p">,</span> <span class="nv">$newStyle</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">updateLayout</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$change</span><span class="p">):</span> <span class="kt">void</span>
<span class="p">{</span>
<span class="nv">$layoutId</span> <span class="o">=</span> <span class="nv">$change</span><span class="p">[</span><span class="s1">'id'</span><span class="p">];</span>
<span class="nv">$newLayout</span> <span class="o">=</span> <span class="nv">$change</span><span class="p">[</span><span class="s1">'data'</span><span class="p">];</span>
<span class="c1">// Update layout structure</span>
<span class="nv">$this</span><span class="o">-></span><span class="nf">updateLayoutStructure</span><span class="p">(</span><span class="nv">$layoutId</span><span class="p">,</span> <span class="nv">$newLayout</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="-high-fidelity-design-transfer">🎯 High Fidelity Design Transfer</h2>
<h3 id="1-accurate-color-matching">1. <strong>Accurate Color Matching</strong></h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="kd">class</span> <span class="nc">ColorMatcher</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">matchColor</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$figmaColor</span><span class="p">):</span> <span class="kt">string</span>
<span class="p">{</span>
<span class="c1">// Convert Figma color to CSS color</span>
<span class="nv">$r</span> <span class="o">=</span> <span class="nb">round</span><span class="p">(</span><span class="nv">$figmaColor</span><span class="p">[</span><span class="s1">'r'</span><span class="p">]</span> <span class="o">*</span> <span class="mi">255</span><span class="p">);</span>
<span class="nv">$g</span> <span class="o">=</span> <span class="nb">round</span><span class="p">(</span><span class="nv">$figmaColor</span><span class="p">[</span><span class="s1">'g'</span><span class="p">]</span> <span class="o">*</span> <span class="mi">255</span><span class="p">);</span>
<span class="nv">$b</span> <span class="o">=</span> <span class="nb">round</span><span class="p">(</span><span class="nv">$figmaColor</span><span class="p">[</span><span class="s1">'b'</span><span class="p">]</span> <span class="o">*</span> <span class="mi">255</span><span class="p">);</span>
<span class="nv">$a</span> <span class="o">=</span> <span class="nv">$figmaColor</span><span class="p">[</span><span class="s1">'a'</span><span class="p">]</span> <span class="o">??</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">// Handle different color formats</span>
<span class="k">if</span> <span class="p">(</span><span class="nv">$a</span> <span class="o">===</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="s2">"#"</span> <span class="mf">.</span> <span class="nb">sprintf</span><span class="p">(</span><span class="s2">"%02x%02x%02x"</span><span class="p">,</span> <span class="nv">$r</span><span class="p">,</span> <span class="nv">$g</span><span class="p">,</span> <span class="nv">$b</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="s2">"rgba(</span><span class="si">{</span><span class="nv">$r</span><span class="si">}</span><span class="s2">, </span><span class="si">{</span><span class="nv">$g</span><span class="si">}</span><span class="s2">, </span><span class="si">{</span><span class="nv">$b</span><span class="si">}</span><span class="s2">, </span><span class="si">{</span><span class="nv">$a</span><span class="si">}</span><span class="s2">)"</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">generateColorPalette</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$figmaColors</span><span class="p">):</span> <span class="kt">array</span>
<span class="p">{</span>
<span class="nv">$palette</span> <span class="o">=</span> <span class="p">[];</span>
<span class="k">foreach</span> <span class="p">(</span><span class="nv">$figmaColors</span> <span class="k">as</span> <span class="nv">$name</span> <span class="o">=></span> <span class="nv">$color</span><span class="p">)</span> <span class="p">{</span>
<span class="nv">$palette</span><span class="p">[</span><span class="nv">$name</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'hex'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">matchColor</span><span class="p">(</span><span class="nv">$color</span><span class="p">),</span>
<span class="s1">'rgb'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">toRGB</span><span class="p">(</span><span class="nv">$color</span><span class="p">),</span>
<span class="s1">'hsl'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">toHSL</span><span class="p">(</span><span class="nv">$color</span><span class="p">),</span>
<span class="s1">'figma_id'</span> <span class="o">=></span> <span class="nv">$color</span><span class="p">[</span><span class="s1">'id'</span><span class="p">]</span> <span class="o">??</span> <span class="kc">null</span>
<span class="p">];</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nv">$palette</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="2-typography-precision">2. <strong>Typography Precision</strong></h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="kd">class</span> <span class="nc">TypographyPrecision</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">matchTypography</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$figmaText</span><span class="p">):</span> <span class="kt">array</span>
<span class="p">{</span>
<span class="k">return</span> <span class="p">[</span>
<span class="s1">'font-family'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">mapFontFamily</span><span class="p">(</span><span class="nv">$figmaText</span><span class="p">[</span><span class="s1">'fontName'</span><span class="p">][</span><span class="s1">'family'</span><span class="p">]),</span>
<span class="s1">'font-size'</span> <span class="o">=></span> <span class="nv">$figmaText</span><span class="p">[</span><span class="s1">'fontSize'</span><span class="p">]</span> <span class="mf">.</span> <span class="s1">'px'</span><span class="p">,</span>
<span class="s1">'font-weight'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">mapFontWeight</span><span class="p">(</span><span class="nv">$figmaText</span><span class="p">[</span><span class="s1">'fontName'</span><span class="p">][</span><span class="s1">'style'</span><span class="p">]),</span>
<span class="s1">'line-height'</span> <span class="o">=></span> <span class="nv">$figmaText</span><span class="p">[</span><span class="s1">'lineHeightPx'</span><span class="p">]</span> <span class="mf">.</span> <span class="s1">'px'</span><span class="p">,</span>
<span class="s1">'letter-spacing'</span> <span class="o">=></span> <span class="nv">$figmaText</span><span class="p">[</span><span class="s1">'letterSpacing'</span><span class="p">]</span> <span class="mf">.</span> <span class="s1">'px'</span><span class="p">,</span>
<span class="s1">'text-align'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">mapTextAlign</span><span class="p">(</span><span class="nv">$figmaText</span><span class="p">[</span><span class="s1">'textAlignHorizontal'</span><span class="p">]),</span>
<span class="s1">'text-decoration'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">mapTextDecoration</span><span class="p">(</span><span class="nv">$figmaText</span><span class="p">[</span><span class="s1">'textDecoration'</span><span class="p">]),</span>
<span class="p">];</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">mapFontFamily</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$figmaFamily</span><span class="p">):</span> <span class="kt">string</span>
<span class="p">{</span>
<span class="nv">$fontMap</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'Inter'</span> <span class="o">=></span> <span class="s2">"'Inter', -apple-system, BlinkMacSystemFont, sans-serif"</span><span class="p">,</span>
<span class="s1">'Roboto'</span> <span class="o">=></span> <span class="s2">"'Roboto', sans-serif"</span><span class="p">,</span>
<span class="s1">'Open Sans'</span> <span class="o">=></span> <span class="s2">"'Open Sans', sans-serif"</span><span class="p">,</span>
<span class="s1">'Lato'</span> <span class="o">=></span> <span class="s2">"'Lato', sans-serif"</span><span class="p">,</span>
<span class="p">];</span>
<span class="k">return</span> <span class="nv">$fontMap</span><span class="p">[</span><span class="nv">$figmaFamily</span><span class="p">]</span> <span class="o">??</span> <span class="s2">"'</span><span class="si">{</span><span class="nv">$figmaFamily</span><span class="si">}</span><span class="s2">', sans-serif"</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">mapFontWeight</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$figmaStyle</span><span class="p">):</span> <span class="kt">int</span>
<span class="p">{</span>
<span class="nv">$weightMap</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'Thin'</span> <span class="o">=></span> <span class="mi">100</span><span class="p">,</span>
<span class="s1">'Light'</span> <span class="o">=></span> <span class="mi">300</span><span class="p">,</span>
<span class="s1">'Regular'</span> <span class="o">=></span> <span class="mi">400</span><span class="p">,</span>
<span class="s1">'Medium'</span> <span class="o">=></span> <span class="mi">500</span><span class="p">,</span>
<span class="s1">'SemiBold'</span> <span class="o">=></span> <span class="mi">600</span><span class="p">,</span>
<span class="s1">'Bold'</span> <span class="o">=></span> <span class="mi">700</span><span class="p">,</span>
<span class="s1">'ExtraBold'</span> <span class="o">=></span> <span class="mi">800</span><span class="p">,</span>
<span class="s1">'Black'</span> <span class="o">=></span> <span class="mi">900</span><span class="p">,</span>
<span class="p">];</span>
<span class="k">return</span> <span class="nv">$weightMap</span><span class="p">[</span><span class="nv">$figmaStyle</span><span class="p">]</span> <span class="o">??</span> <span class="mi">400</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="-client-friendly-interface">🎨 Client-Friendly Interface</h2>
<h3 id="1-visual-page-builder">1. <strong>Visual Page Builder</strong></h3>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// assets/js/visual-builder.js</span>
<span class="kd">class</span> <span class="nx">VisualPageBuilder</span> <span class="p">{</span>
<span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">builder-canvas</span><span class="dl">'</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">toolbar</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">builder-toolbar</span><span class="dl">'</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">sidebar</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">builder-sidebar</span><span class="dl">'</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">initBuilder</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">initDragAndDrop</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">initRealTimePreview</span><span class="p">();</span>
<span class="p">}</span>
<span class="nx">initBuilder</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Initialize builder interface</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">position</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">relative</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">minHeight</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">100vh</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">background</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">linear-gradient(45deg, #f0f0f0 25%, transparent 25%), linear-gradient(-45deg, #f0f0f0 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #f0f0f0 75%), linear-gradient(-45deg, transparent 75%, #f0f0f0 75%)</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">backgroundSize</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">20px 20px</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">backgroundPosition</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">0 0, 0 10px, 10px -10px, -10px 0px</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">initDragAndDrop</span><span class="p">()</span> <span class="p">{</span>
<span class="c1">// Make canvas droppable</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">dragover</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">dataTransfer</span><span class="p">.</span><span class="nx">dropEffect</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">copy</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">showDropZone</span><span class="p">(</span><span class="nx">e</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">drop</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">e</span><span class="p">.</span><span class="nx">preventDefault</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">hideDropZone</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">componentData</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">dataTransfer</span><span class="p">.</span><span class="nx">getData</span><span class="p">(</span><span class="dl">'</span><span class="s1">text</span><span class="dl">'</span><span class="p">));</span>
<span class="k">this</span><span class="p">.</span><span class="nx">addComponent</span><span class="p">(</span><span class="nx">componentData</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">clientX</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">clientY</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// Make components draggable and resizable</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">mousedown</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">contains</span><span class="p">(</span><span class="dl">'</span><span class="s1">jankx-component</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">startDragging</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">,</span> <span class="nx">e</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">addComponent</span><span class="p">(</span><span class="nx">componentData</span><span class="p">,</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">component</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">createComponent</span><span class="p">(</span><span class="nx">componentData</span><span class="p">);</span>
<span class="c1">// Position component at drop location</span>
<span class="kd">const</span> <span class="nx">rect</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">getBoundingClientRect</span><span class="p">();</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">position</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">absolute</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">left</span> <span class="o">=</span> <span class="p">(</span><span class="nx">x</span> <span class="o">-</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">left</span><span class="p">)</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">top</span> <span class="o">=</span> <span class="p">(</span><span class="nx">y</span> <span class="o">-</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">top</span><span class="p">)</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">canvas</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">selectComponent</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">saveState</span><span class="p">();</span>
<span class="p">}</span>
<span class="nx">createComponent</span><span class="p">(</span><span class="nx">componentData</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">component</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">jankx-component</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">dataset</span><span class="p">.</span><span class="nx">componentId</span> <span class="o">=</span> <span class="nx">componentData</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">dataset</span><span class="p">.</span><span class="nx">componentType</span> <span class="o">=</span> <span class="nx">componentData</span><span class="p">.</span><span class="nx">type</span><span class="p">;</span>
<span class="c1">// Apply styling from design</span>
<span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">(</span><span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">,</span> <span class="nx">componentData</span><span class="p">.</span><span class="nx">styles</span><span class="p">);</span>
<span class="c1">// Add content</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">componentData</span><span class="p">.</span><span class="nx">html</span><span class="p">;</span>
<span class="c1">// Make editable</span>
<span class="k">this</span><span class="p">.</span><span class="nx">makeEditable</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span>
<span class="k">return</span> <span class="nx">component</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">makeEditable</span><span class="p">(</span><span class="nx">component</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Double-click to edit text</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">dblclick</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">tagName</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">P</span><span class="dl">'</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">tagName</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">H1</span><span class="dl">'</span> <span class="o">||</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">tagName</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">H2</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">makeTextEditable</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="c1">// Add resize handles</span>
<span class="k">this</span><span class="p">.</span><span class="nx">addResizeHandles</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span>
<span class="c1">// Add move handle</span>
<span class="k">this</span><span class="p">.</span><span class="nx">addMoveHandle</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">selectComponent</span><span class="p">(</span><span class="nx">component</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Remove previous selection</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">.jankx-component--selected</span><span class="dl">'</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">el</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">el</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">remove</span><span class="p">(</span><span class="dl">'</span><span class="s1">jankx-component--selected</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="c1">// Select new component</span>
<span class="nx">component</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">jankx-component--selected</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">// Show properties panel</span>
<span class="k">this</span><span class="p">.</span><span class="nx">showProperties</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">showProperties</span><span class="p">(</span><span class="nx">component</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">properties</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">getComponentProperties</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">renderPropertiesPanel</span><span class="p">(</span><span class="nx">properties</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">getComponentProperties</span><span class="p">(</span><span class="nx">component</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">rect</span> <span class="o">=</span> <span class="nx">component</span><span class="p">.</span><span class="nx">getBoundingClientRect</span><span class="p">();</span>
<span class="kd">const</span> <span class="nx">styles</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">getComputedStyle</span><span class="p">(</span><span class="nx">component</span><span class="p">);</span>
<span class="k">return</span> <span class="p">{</span>
<span class="na">position</span><span class="p">:</span> <span class="p">{</span>
<span class="na">x</span><span class="p">:</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">left</span><span class="p">),</span>
<span class="na">y</span><span class="p">:</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">top</span><span class="p">)</span>
<span class="p">},</span>
<span class="na">size</span><span class="p">:</span> <span class="p">{</span>
<span class="na">width</span><span class="p">:</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span>
<span class="na">height</span><span class="p">:</span> <span class="nx">rect</span><span class="p">.</span><span class="nx">height</span>
<span class="p">},</span>
<span class="na">style</span><span class="p">:</span> <span class="p">{</span>
<span class="na">backgroundColor</span><span class="p">:</span> <span class="nx">styles</span><span class="p">.</span><span class="nx">backgroundColor</span><span class="p">,</span>
<span class="na">color</span><span class="p">:</span> <span class="nx">styles</span><span class="p">.</span><span class="nx">color</span><span class="p">,</span>
<span class="na">fontSize</span><span class="p">:</span> <span class="nx">styles</span><span class="p">.</span><span class="nx">fontSize</span><span class="p">,</span>
<span class="na">fontFamily</span><span class="p">:</span> <span class="nx">styles</span><span class="p">.</span><span class="nx">fontFamily</span><span class="p">,</span>
<span class="na">borderRadius</span><span class="p">:</span> <span class="nx">styles</span><span class="p">.</span><span class="nx">borderRadius</span><span class="p">,</span>
<span class="na">padding</span><span class="p">:</span> <span class="nx">styles</span><span class="p">.</span><span class="nx">padding</span><span class="p">,</span>
<span class="na">margin</span><span class="p">:</span> <span class="nx">styles</span><span class="p">.</span><span class="nx">margin</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="nx">renderPropertiesPanel</span><span class="p">(</span><span class="nx">properties</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">panel</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">properties-panel</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">panel</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s2">`
<div class="jankx-properties">
<h3>Properties</h3>
<div class="jankx-property-group">
<label>Position</label>
<div class="jankx-property-inputs">
<input type="number" value="</span><span class="p">${</span><span class="nx">properties</span><span class="p">.</span><span class="nx">position</span><span class="p">.</span><span class="nx">x</span><span class="p">}</span><span class="s2">" placeholder="X" data-property="left" />
<input type="number" value="</span><span class="p">${</span><span class="nx">properties</span><span class="p">.</span><span class="nx">position</span><span class="p">.</span><span class="nx">y</span><span class="p">}</span><span class="s2">" placeholder="Y" data-property="top" />
</div>
</div>
<div class="jankx-property-group">
<label>Size</label>
<div class="jankx-property-inputs">
<input type="number" value="</span><span class="p">${</span><span class="nx">properties</span><span class="p">.</span><span class="nx">size</span><span class="p">.</span><span class="nx">width</span><span class="p">}</span><span class="s2">" placeholder="Width" data-property="width" />
<input type="number" value="</span><span class="p">${</span><span class="nx">properties</span><span class="p">.</span><span class="nx">size</span><span class="p">.</span><span class="nx">height</span><span class="p">}</span><span class="s2">" placeholder="Height" data-property="height" />
</div>
</div>
<div class="jankx-property-group">
<label>Background Color</label>
<input type="color" value="</span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">rgbToHex</span><span class="p">(</span><span class="nx">properties</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">backgroundColor</span><span class="p">)}</span><span class="s2">" data-property="backgroundColor" />
</div>
<div class="jankx-property-group">
<label>Text Color</label>
<input type="color" value="</span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">rgbToHex</span><span class="p">(</span><span class="nx">properties</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">color</span><span class="p">)}</span><span class="s2">" data-property="color" />
</div>
<div class="jankx-property-group">
<label>Font Size</label>
<input type="number" value="</span><span class="p">${</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">properties</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">fontSize</span><span class="p">)}</span><span class="s2">" data-property="fontSize" />
</div>
<div class="jankx-property-group">
<label>Border Radius</label>
<input type="number" value="</span><span class="p">${</span><span class="nb">parseInt</span><span class="p">(</span><span class="nx">properties</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">borderRadius</span><span class="p">)}</span><span class="s2">" data-property="borderRadius" />
</div>
</div>
`</span><span class="p">;</span>
<span class="c1">// Add event listeners</span>
<span class="nx">panel</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">input</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">input</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">change</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">updateComponentProperty</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">dataset</span><span class="p">.</span><span class="nx">property</span><span class="p">,</span> <span class="nx">e</span><span class="p">.</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">updateComponentProperty</span><span class="p">(</span><span class="nx">property</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">selectedComponent</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="dl">'</span><span class="s1">.jankx-component--selected</span><span class="dl">'</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">selectedComponent</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">property</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">left</span><span class="dl">'</span> <span class="o">||</span> <span class="nx">property</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">top</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">selectedComponent</span><span class="p">.</span><span class="nx">style</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">property</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">width</span><span class="dl">'</span> <span class="o">||</span> <span class="nx">property</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">height</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">selectedComponent</span><span class="p">.</span><span class="nx">style</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">property</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">backgroundColor</span><span class="dl">'</span> <span class="o">||</span> <span class="nx">property</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">color</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">selectedComponent</span><span class="p">.</span><span class="nx">style</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">property</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">fontSize</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">selectedComponent</span><span class="p">.</span><span class="nx">style</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">property</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">borderRadius</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">selectedComponent</span><span class="p">.</span><span class="nx">style</span><span class="p">[</span><span class="nx">property</span><span class="p">]</span> <span class="o">=</span> <span class="nx">value</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">px</span><span class="dl">'</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">this</span><span class="p">.</span><span class="nx">saveState</span><span class="p">();</span>
<span class="p">}</span>
<span class="nx">saveState</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">state</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">getBuilderState</span><span class="p">();</span>
<span class="nx">localStorage</span><span class="p">.</span><span class="nx">setItem</span><span class="p">(</span><span class="dl">'</span><span class="s1">jankx-builder-state</span><span class="dl">'</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">state</span><span class="p">));</span>
<span class="c1">// Auto-save to server</span>
<span class="k">this</span><span class="p">.</span><span class="nx">autoSave</span><span class="p">(</span><span class="nx">state</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">getBuilderState</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">components</span> <span class="o">=</span> <span class="p">[];</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="dl">'</span><span class="s1">.jankx-component</span><span class="dl">'</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">component</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">components</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
<span class="na">id</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">dataset</span><span class="p">.</span><span class="nx">componentId</span><span class="p">,</span>
<span class="na">type</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">dataset</span><span class="p">.</span><span class="nx">componentType</span><span class="p">,</span>
<span class="na">position</span><span class="p">:</span> <span class="p">{</span>
<span class="na">x</span><span class="p">:</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">left</span><span class="p">),</span>
<span class="na">y</span><span class="p">:</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">top</span><span class="p">)</span>
<span class="p">},</span>
<span class="na">size</span><span class="p">:</span> <span class="p">{</span>
<span class="na">width</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">offsetWidth</span><span class="p">,</span>
<span class="na">height</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">offsetHeight</span>
<span class="p">},</span>
<span class="na">styles</span><span class="p">:</span> <span class="p">{</span>
<span class="na">backgroundColor</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">backgroundColor</span><span class="p">,</span>
<span class="na">color</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">color</span><span class="p">,</span>
<span class="na">fontSize</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">fontSize</span><span class="p">,</span>
<span class="na">borderRadius</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">borderRadius</span>
<span class="p">},</span>
<span class="na">content</span><span class="p">:</span> <span class="nx">component</span><span class="p">.</span><span class="nx">innerHTML</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="k">return</span> <span class="p">{</span>
<span class="nx">components</span><span class="p">,</span>
<span class="na">timestamp</span><span class="p">:</span> <span class="nb">Date</span><span class="p">.</span><span class="nx">now</span><span class="p">()</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="nx">autoSave</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">fetch</span><span class="p">(</span><span class="dl">'</span><span class="s1">/api/builder/save</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span>
<span class="na">method</span><span class="p">:</span> <span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span>
<span class="na">headers</span><span class="p">:</span> <span class="p">{</span>
<span class="dl">'</span><span class="s1">Content-Type</span><span class="dl">'</span><span class="p">:</span> <span class="dl">'</span><span class="s1">application/json</span><span class="dl">'</span>
<span class="p">},</span>
<span class="na">body</span><span class="p">:</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span>
<span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="nx">error</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="dl">'</span><span class="s1">Auto-save failed:</span><span class="dl">'</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="nx">rgbToHex</span><span class="p">(</span><span class="nx">rgb</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Convert rgb(r, g, b) to hex</span>
<span class="kd">const</span> <span class="nx">match</span> <span class="o">=</span> <span class="nx">rgb</span><span class="p">.</span><span class="nx">match</span><span class="p">(</span><span class="sr">/^rgb</span><span class="se">\((\d</span><span class="sr">+</span><span class="se">)</span><span class="sr">,</span><span class="se">\s</span><span class="sr">*</span><span class="se">(\d</span><span class="sr">+</span><span class="se">)</span><span class="sr">,</span><span class="se">\s</span><span class="sr">*</span><span class="se">(\d</span><span class="sr">+</span><span class="se">)\)</span><span class="sr">$/</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">match</span><span class="p">)</span> <span class="k">return</span> <span class="dl">'</span><span class="s1">#000000</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">r</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">match</span><span class="p">[</span><span class="mi">1</span><span class="p">]);</span>
<span class="kd">const</span> <span class="nx">g</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">match</span><span class="p">[</span><span class="mi">2</span><span class="p">]);</span>
<span class="kd">const</span> <span class="nx">b</span> <span class="o">=</span> <span class="nb">parseInt</span><span class="p">(</span><span class="nx">match</span><span class="p">[</span><span class="mi">3</span><span class="p">]);</span>
<span class="k">return</span> <span class="dl">'</span><span class="s1">#</span><span class="dl">'</span> <span class="o">+</span> <span class="p">((</span><span class="mi">1</span> <span class="o"><<</span> <span class="mi">24</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nx">r</span> <span class="o"><<</span> <span class="mi">16</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="nx">g</span> <span class="o"><<</span> <span class="mi">8</span><span class="p">)</span> <span class="o">+</span> <span class="nx">b</span><span class="p">).</span><span class="nx">toString</span><span class="p">(</span><span class="mi">16</span><span class="p">).</span><span class="nx">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Initialize visual builder</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">DOMContentLoaded</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">new</span> <span class="nx">VisualPageBuilder</span><span class="p">();</span>
<span class="p">});</span>
</code></pre></div></div>
<h2 id="-performance-monitoring">📊 Performance Monitoring</h2>
<h3 id="conversion-tracking">Conversion Tracking</h3>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?php</span>
<span class="kd">class</span> <span class="nc">ConversionTracker</span>
<span class="p">{</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">trackConversion</span><span class="p">():</span> <span class="kt">array</span>
<span class="p">{</span>
<span class="nv">$startTime</span> <span class="o">=</span> <span class="nb">microtime</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="c1">// Track conversion process</span>
<span class="nv">$metrics</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">'design_extraction'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">trackStep</span><span class="p">(</span><span class="s1">'extractDesign'</span><span class="p">),</span>
<span class="s1">'css_generation'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">trackStep</span><span class="p">(</span><span class="s1">'generateCSS'</span><span class="p">),</span>
<span class="s1">'block_creation'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">trackStep</span><span class="p">(</span><span class="s1">'createBlocks'</span><span class="p">),</span>
<span class="s1">'theme_generation'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">trackStep</span><span class="p">(</span><span class="s1">'generateTheme'</span><span class="p">),</span>
<span class="s1">'deployment'</span> <span class="o">=></span> <span class="nv">$this</span><span class="o">-></span><span class="nf">trackStep</span><span class="p">(</span><span class="s1">'deployTheme'</span><span class="p">),</span>
<span class="p">];</span>
<span class="nv">$endTime</span> <span class="o">=</span> <span class="nb">microtime</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="nv">$metrics</span><span class="p">[</span><span class="s1">'total_time'</span><span class="p">]</span> <span class="o">=</span> <span class="nv">$endTime</span> <span class="o">-</span> <span class="nv">$startTime</span><span class="p">;</span>
<span class="k">return</span> <span class="nv">$metrics</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">function</span> <span class="n">trackStep</span><span class="p">(</span><span class="kt">string</span> <span class="nv">$stepName</span><span class="p">):</span> <span class="kt">float</span>
<span class="p">{</span>
<span class="nv">$startTime</span> <span class="o">=</span> <span class="nb">microtime</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="c1">// Execute step</span>
<span class="nv">$this</span><span class="o">-></span><span class="nf">executeStep</span><span class="p">(</span><span class="nv">$stepName</span><span class="p">);</span>
<span class="nv">$endTime</span> <span class="o">=</span> <span class="nb">microtime</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
<span class="k">return</span> <span class="nv">$endTime</span> <span class="o">-</span> <span class="nv">$startTime</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">function</span> <span class="n">getMetrics</span><span class="p">(</span><span class="kt">array</span> <span class="nv">$metrics</span><span class="p">):</span> <span class="kt">array</span>
<span class="p">{</span>
<span class="k">return</span> <span class="p">[</span>
<span class="s1">'design_extraction'</span> <span class="o">=></span> <span class="nv">$metrics</span><span class="p">[</span><span class="s1">'design_extraction'</span><span class="p">],</span>
<span class="s1">'css_generation'</span> <span class="o">=></span> <span class="nv">$metrics</span><span class="p">[</span><span class="s1">'css_generation'</span><span class="p">],</span>
<span class="s1">'block_creation'</span> <span class="o">=></span> <span class="nv">$metrics</span><span class="p">[</span><span class="s1">'block_creation'</span><span class="p">],</span>
<span class="s1">'theme_generation'</span> <span class="o">=></span> <span class="nv">$metrics</span><span class="p">[</span><span class="s1">'theme_generation'</span><span class="p">],</span>
<span class="s1">'deployment'</span> <span class="o">=></span> <span class="nv">$metrics</span><span class="p">[</span><span class="s1">'deployment'</span><span class="p">],</span>
<span class="s1">'total_time'</span> <span class="o">=></span> <span class="nv">$metrics</span><span class="p">[</span><span class="s1">'total_time'</span><span class="p">]</span>
<span class="p">];</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<hr />
<p><strong>Jankx 2.0 cung cấp workflow tối ưu để chuyển đổi design thành website với độ chính xác cao và interface thân thiện cho clients!</strong> 🚀</p>
\n";
$html .= " </div>\n";
}
$html .= "</div>";
return $html;
}
private function generateTemplates(): void
{
$templatesDir = $this->outputPath . '/templates';
// Generate index template
$indexTemplate = $this->generateIndexTemplate();
file_put_contents($templatesDir . '/index.html', $indexTemplate);
// Generate single template
$singleTemplate = $this->generateSingleTemplate();
file_put_contents($templatesDir . '/single.html', $singleTemplate);
// Generate page template
$pageTemplate = $this->generatePageTemplate();
file_put_contents($templatesDir . '/page.html', $pageTemplate);
}
private function generateIndexTemplate(): string
{
$html = "<!DOCTYPE html>\n";
$html .= "<html lang=\"\">\n";
$html .= "<head>\n";
$html .= " <meta charset=\"\">\n";
$html .= " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n";
$html .= " <title></title>\n";
$html .= " \n";
$html .= "</head>\n";
$html .= "<body class=\"jankx-layout\">\n";
$html .= " <div class=\"jankx-layout-wrapper\">\n";
$html .= " <header class=\"jankx-layout-header\">\n";
$html .= " \n";
$html .= " </header>\n";
$html .= " <main class=\"jankx-layout-main\">\n";
$html .= " <div class=\"jankx-layout-container\">\n";
$html .= " \n";
$html .= " </div>\n";
$html .= " </main>\n";
$html .= " <footer class=\"jankx-layout-footer\">\n";
$html .= " \n";
$html .= " </footer>\n";
$html .= " </div>\n";
$html .= " \n";
$html .= "</body>\n";
$html .= "</html>";
return $html;
}
private function generateAssets(): void
{
// Copy assets from design data
$assets = $this->designData->getAssets();
if (isset($assets['images'])) {
foreach ($assets['images'] as $image) {
$this->copyAsset($image, 'images');
}
}
if (isset($assets['css'])) {
foreach ($assets['css'] as $css) {
$this->copyAsset($css, 'css');
}
}
if (isset($assets['js'])) {
foreach ($assets['js'] as $js) {
$this->copyAsset($js, 'js');
}
}
}
private function copyAsset(string $source, string $type): void
{
$destDir = $this->outputPath . "/assets/{$type}";
if (!is_dir($destDir)) {
mkdir($destDir, 0755, true);
}
$filename = basename($source);
$destPath = $destDir . '/' . $filename;
if (file_exists($source)) {
copy($source, $destPath);
}
}
private function generateConfiguration(): void
{
$config = [
'theme' => [
'name' => 'Generated Theme',
'version' => '1.0.0',
'description' => 'Theme generated from design data',
],
'design' => [
'colors' => $this->designData->getColors(),
'typography' => $this->designData->getTypography(),
'spacing' => $this->designData->getSpacing(),
],
'components' => array_keys($this->designData->getComponents()),
'layouts' => array_keys($this->designData->getLayouts()),
];
$configJson = json_encode($config, JSON_PRETTY_PRINT);
file_put_contents($this->outputPath . '/config/theme.json', $configJson);
}
}
🎨 Designer Tools
Figma Plugin
// figma-plugin.js
figma.showUI(__html__);
figma.ui.onmessage = async (msg) => {
if (msg.type === 'export-design') {
const designData = await extractDesignData();
figma.ui.postMessage({
type: 'design-data',
data: designData
});
}
};
async function extractDesignData() {
const colors = await extractColors();
const typography = await extractTypography();
const components = await extractComponents();
const layouts = await extractLayouts();
return {
colors,
typography,
components,
layouts,
spacing: extractSpacing()
};
}
async function extractColors() {
const styles = figma.getLocalPaintStyles();
return styles
.filter(style => style.paints.length > 0)
.map(style => ({
name: style.name,
value: style.paints[0].color,
type: 'color'
}));
}
async function extractTypography() {
const styles = figma.getLocalTextStyles();
return styles.map(style => ({
name: style.name,
font_family: style.fontName.family,
font_size: style.fontSize,
font_weight: style.fontName.style
}));
}
async function extractComponents() {
const components = figma.currentPage.findAll(node => node.type === 'COMPONENT');
return components.map(component => ({
name: component.name,
id: component.id,
description: component.description || '',
properties: extractComponentProperties(component)
}));
}
async function extractLayouts() {
const frames = figma.currentPage.findAll(node => node.type === 'FRAME');
return frames.map(frame => ({
name: frame.name,
id: frame.id,
components: extractFrameComponents(frame)
}));
}
HTML Template Converter
<?php
namespace Jankx\Designer\Converter;
class HTMLTemplateConverter
{
private $htmlContent;
private $convertedContent;
public function convertHTMLToJankx(string $htmlContent): string
{
$this->htmlContent = $htmlContent;
$this->convertedContent = $htmlContent;
// Convert HTML to Jankx template syntax
$this->convertHTMLTags();
$this->convertCSSClasses();
$this->convertJavaScript();
$this->convertAssets();
return $this->convertedContent;
}
private function convertHTMLTags(): void
{
// Convert common HTML patterns to Jankx components
$patterns = [
'/<header[^>]*>/i' => '<header class="jankx-layout-header">',
'/<nav[^>]*>/i' => '<nav class="jankx-navigation">',
'/<main[^>]*>/i' => '<main class="jankx-layout-main">',
'/<footer[^>]*>/i' => '<footer class="jankx-layout-footer">',
'/<section[^>]*>/i' => '<section class="jankx-section">',
'/<article[^>]*>/i' => '<article class="jankx-article">',
];
foreach ($patterns as $pattern => $replacement) {
$this->convertedContent = preg_replace($pattern, $replacement, $this->convertedContent);
}
}
private function convertCSSClasses(): void
{
// Convert CSS classes to Jankx naming convention
$patterns = [
'/\bcontainer\b/' => 'jankx-container',
'/\brow\b/' => 'jankx-row',
'/\bcol\b/' => 'jankx-col',
'/\bcard\b/' => 'jankx-card',
'/\bbutton\b/' => 'jankx-button',
'/\bform\b/' => 'jankx-form',
];
foreach ($patterns as $pattern => $replacement) {
$this->convertedContent = preg_replace($pattern, $replacement, $this->convertedContent);
}
}
private function convertJavaScript(): void
{
// Convert JavaScript to Jankx patterns
$patterns = [
'/document\.querySelector\(/g' => 'jankx.querySelector(',
'/addEventListener\(/g' => 'jankx.addEventListener(',
'/classList\.add\(/g' => 'jankx.classList.add(',
];
foreach ($patterns as $pattern => $replacement) {
$this->convertedContent = str_replace($pattern, $replacement, $this->convertedContent);
}
}
private function convertAssets(): void
{
// Convert asset paths to Jankx structure
$patterns = [
'/src="([^"]*\.(jpg|jpeg|png|gif|svg))"/i' => 'src=""',
'/href="([^"]*\.css)"/i' => 'href=""',
'/src="([^"]*\.js)"/i' => 'src=""',
];
foreach ($patterns as $pattern => $replacement) {
$this->convertedContent = preg_replace($pattern, $replacement, $this->convertedContent);
}
}
}
🚀 Quick Start for Designers
Command Line Tool
<?php
// jankx-designer.php
#!/usr/bin/env php
require_once __DIR__ . '/vendor/autoload.php';
use Jankx\Designer\CLI\DesignerCLI;
$cli = new DesignerCLI();
$cli->run();
<?php
namespace Jankx\Designer\CLI;
class DesignerCLI
{
public function run(): void
{
$command = $argv[1] ?? 'help';
switch ($command) {
case 'convert-figma':
$this->convertFigma($argv[2] ?? '', $argv[3] ?? '');
break;
case 'convert-html':
$this->convertHTML($argv[2] ?? '', $argv[3] ?? '');
break;
case 'generate-theme':
$this->generateTheme($argv[2] ?? '');
break;
default:
$this->showHelp();
}
}
private function convertFigma(string $fileId, string $outputPath): void
{
echo "Converting Figma design...\n";
$parser = new FigmaDesignParser($_ENV['FIGMA_TOKEN']);
$designData = $parser->parseFigmaFile($fileId);
$generator = new WordPressThemeGenerator($designData, $outputPath);
$generator->generateTheme();
echo "Theme generated successfully!\n";
}
private function convertHTML(string $htmlFile, string $outputPath): void
{
echo "Converting HTML template...\n";
$parser = new HTMLTemplateParser();
$designData = $parser->parseHTMLFile($htmlFile);
$converter = new HTMLTemplateConverter();
$convertedHTML = $converter->convertHTMLToJankx(file_get_contents($htmlFile));
file_put_contents($outputPath . '/templates/index.html', $convertedHTML);
echo "HTML converted successfully!\n";
}
private function generateTheme(string $designPath): void
{
echo "Generating WordPress theme...\n";
$designData = DesignData::loadFromPath($designPath);
$generator = new WordPressThemeGenerator($designData, 'generated-theme');
$generator->generateTheme();
echo "Theme generated successfully!\n";
}
private function showHelp(): void
{
echo "Jankx Designer CLI\n\n";
echo "Usage:\n";
echo " php jankx-designer.php convert-figma <file-id> <output-path>\n";
echo " php jankx-designer.php convert-html <html-file> <output-path>\n";
echo " php jankx-designer.php generate-theme <design-path>\n\n";
}
}
Usage Examples
Convert Figma Design
# Convert Figma design to WordPress theme
php jankx-designer.php convert-figma abc123def456 /path/to/output
# Convert HTML template to Jankx theme
php jankx-designer.php convert-html template.html /path/to/output
# Generate theme from design data
php jankx-designer.php generate-theme /path/to/design-data
Figma Plugin Usage
- Install Jankx Figma plugin
- Select design elements
- Click “Export to Jankx”
- Download generated theme files
HTML Template Conversion
- Prepare HTML template with CSS
- Run conversion command
- Get WordPress-ready theme files
- Customize as needed
Next: Design System | Component Library |