`; const blob = new Blob([html], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'timeline.html'; a.click(); URL.revokeObjectURL(url); } // Flowchart functions function parseFlowchart(text) { const nodes = []; const parts = text.split('->').map(p => p.trim()); parts.forEach((part, index) => { if (part.includes('(')) { // Decision node const match = part.match(/(.+?)\s*\((.+)\)/); if (match) { nodes.push({ type: 'decision', text: match[1].trim(), branches: match[2] }); } } else { nodes.push({ type: 'step', text: part }); } }); return nodes; } function updateFlowchart() { state.flowchartText = document.getElementById('flowchart-input').value; renderToolView(); } function loadFlowchartSample() { state.flowchartText = 'Start -> Review Data -> Is Data Valid? (yes: Process Data, no: Request Corrections) -> Generate Report -> End'; document.getElementById('flowchart-input').value = state.flowchartText; renderToolView(); } function exportFlowchart() { const nodes = parseFlowchart(state.flowchartText); const html = ` Quiet Draw · QuietTools XYZ

Flowchart

${nodes.map((node, i) => { const nodeHtml = node.type === 'decision' ? `
${node.text}
${node.branches}
` : `
${node.text}
`; const arrow = i < nodes.length - 1 ? '' : ''; return nodeHtml + arrow; }).join('')}
`; const blob = new Blob([html], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'flowchart.html'; a.click(); URL.revokeObjectURL(url); } // Org chart functions function parseOrgChart(text) { const lines = text.split('\n').filter(l => l.trim()); const hierarchy = []; lines.forEach(line => { const parts = line.split(':'); if (parts.length === 2) { const manager = parts[0].trim(); const employees = parts[1].split(',').map(e => e.trim()).filter(e => e); hierarchy.push({ manager, employees }); } }); return hierarchy; } function updateOrgChart() { state.orgChartText = document.getElementById('orgchart-input').value; renderToolView(); } function loadOrgChartSample() { state.orgChartText = `CEO: VP Sales, VP Engineering, VP Marketing VP Engineering: Lead Developer, Senior Developer, QA Lead VP Sales: Sales Manager, Account Executive VP Marketing: Content Lead, Social Media Manager`; document.getElementById('orgchart-input').value = state.orgChartText; renderToolView(); } function exportOrgChart() { const hierarchy = parseOrgChart(state.orgChartText); const html = ` Quiet Draw · QuietTools XYZ

Organization Chart

${hierarchy.map(level => `
${level.manager}
${level.employees.map(emp => `
${emp}
`).join('')}
`).join('')} `; const blob = new Blob([html], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'orgchart.html'; a.click(); URL.revokeObjectURL(url); } // SWOT functions function addSWOTItem(category) { const input = document.getElementById(`swot-${category}-input`); if (input.value.trim()) { state.swotData[category].push({ text: input.value.trim(), id: Date.now() }); input.value = ''; renderToolView(); } } function removeSWOTItem(category, id) { state.swotData[category] = state.swotData[category].filter(item => item.id !== id); renderToolView(); } function loadSWOTSample() { state.swotData = { strengths: [ { text: 'Strong brand recognition', id: 1 }, { text: 'Experienced team', id: 2 }, { text: 'Quality products', id: 3 } ], weaknesses: [ { text: 'Limited marketing budget', id: 4 }, { text: 'Small customer base', id: 5 } ], opportunities: [ { text: 'Growing market demand', id: 6 }, { text: 'New partnerships', id: 7 }, { text: 'Technology advancement', id: 8 } ], threats: [ { text: 'Increased competition', id: 9 }, { text: 'Economic downturn', id: 10 } ] }; renderToolView(); } function exportSWOT() { const html = ` Quiet Draw · QuietTools XYZ

SWOT Analysis

Strengths

${state.swotData.strengths.map(item => `
${item.text}
`).join('')}

Weaknesses

${state.swotData.weaknesses.map(item => `
${item.text}
`).join('')}

Opportunities

${state.swotData.opportunities.map(item => `
${item.text}
`).join('')}

Threats

${state.swotData.threats.map(item => `
${item.text}
`).join('')}
`; const blob = new Blob([html], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'swot.html'; a.click(); URL.revokeObjectURL(url); } // Kanban functions function addKanbanCard(columnId) { const input = document.getElementById(`kanban-input-${columnId}`); if (input.value.trim()) { state.kanbanColumns[columnId].cards.push({ text: input.value.trim(), id: Date.now() }); input.value = ''; saveKanban(); renderToolView(); } } function removeKanbanCard(columnId, cardId) { state.kanbanColumns[columnId].cards = state.kanbanColumns[columnId].cards.filter(c => c.id !== cardId); saveKanban(); renderToolView(); } function handleDragStart(e, columnId, cardId) { state.draggedCard = cardId; state.draggedFrom = columnId; e.dataTransfer.effectAllowed = 'move'; } function handleDragOver(e) { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; } function handleDrop(e, targetColumnId) { e.preventDefault(); if (state.draggedCard && state.draggedFrom) { const card = state.kanbanColumns[state.draggedFrom].cards.find(c => c.id === state.draggedCard); if (card) { state.kanbanColumns[state.draggedFrom].cards = state.kanbanColumns[state.draggedFrom].cards.filter(c => c.id !== state.draggedCard); state.kanbanColumns[targetColumnId].cards.push(card); saveKanban(); renderToolView(); } } state.draggedCard = null; state.draggedFrom = null; } function saveKanban() { localStorage.setItem('quickdiagrams.last', JSON.stringify(state.kanbanColumns)); } function loadKanbanSample() { state.kanbanColumns = { todo: { title: 'To Do', cards: [ { text: 'Design landing page', id: 1 }, { text: 'Write product copy', id: 2 }, { text: 'Set up analytics', id: 3 } ] }, doing: { title: 'Doing', cards: [ { text: 'Build user dashboard', id: 4 }, { text: 'Implement authentication', id: 5 } ] }, done: { title: 'Done', cards: [ { text: 'Project kickoff meeting', id: 6 }, { text: 'Technical architecture', id: 7 } ] } }; saveKanban(); renderToolView(); } function exportKanban() { const html = ` Quiet Draw · QuietTools XYZ

Kanban Board

${Object.entries(state.kanbanColumns).map(([id, col]) => `
${col.title}
${col.cards.map(card => `
${card.text}
`).join('')}
`).join('')}
`; const blob = new Blob([html], { type: 'text/html' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'kanban.html'; a.click(); URL.revokeObjectURL(url); } // Render functions function render() { const customFont = config.font_family || defaultConfig.font_family; const baseFontStack = 'system-ui, -apple-system, sans-serif'; const baseSize = config.font_size || defaultConfig.font_size; const bgColor = config.background_color || defaultConfig.background_color; const surfaceColor = config.surface_color || defaultConfig.surface_color; const textColor = config.text_color || defaultConfig.text_color; const primaryColor = config.primary_action_color || defaultConfig.primary_action_color; const secondaryColor = config.secondary_action_color || defaultConfig.secondary_action_color; const appTitle = config.app_title || defaultConfig.app_title; const tagline = config.tagline || defaultConfig.tagline; document.body.style.fontFamily = `${customFont}, ${baseFontStack}`; document.body.style.fontSize = `${baseSize}px`; document.body.style.background = state.darkMode ? '#111827' : bgColor; document.body.style.color = state.darkMode ? '#f9fafb' : textColor; const app = document.getElementById('app'); const tools = [ { id: 'timeline', name: 'Timeline', icon: '', desc: 'Create event timelines' }, { id: 'flowchart', name: 'Flowchart', icon: '', desc: 'Simple flowcharts' }, { id: 'orgchart', name: 'Org Chart', icon: '', desc: 'Organization structure' }, { id: 'swot', name: 'SWOT', icon: '', desc: 'SWOT analysis grid' }, { id: 'kanban', name: 'Kanban', icon: '', desc: 'Task board' } ]; let content = ''; if (state.currentRoute === 'home') { content = `

${appTitle}

${tagline}

${!state.isPro ? `

Upgrade to Pro

Save unlimited boards • Export to multiple formats • Remove ads

` : ''}
${tools.map(tool => `
${tool.icon}

${tool.name}

${tool.desc}

`).join('')}
`; } else if (state.currentRoute.startsWith('/tool/')) { const toolId = state.currentRoute.split('/')[2]; state.currentTool = toolId; content = renderToolView(); } app.innerHTML = `

${appTitle}

${content} `; } function renderToolView() { const customFont = config.font_family || defaultConfig.font_family; const baseFontStack = 'system-ui, -apple-system, sans-serif'; const baseSize = config.font_size || defaultConfig.font_size; const surfaceColor = config.surface_color || defaultConfig.surface_color; const textColor = config.text_color || defaultConfig.text_color; const primaryColor = config.primary_action_color || defaultConfig.primary_action_color; const secondaryColor = config.secondary_action_color || defaultConfig.secondary_action_color; const tool = state.currentTool; let toolContent = ''; if (tool === 'timeline') { const events = getTimelineEvents(); toolContent = `

Timeline Generator

Add Event

${events.length > 0 ? ` ` : `

Add events to create your timeline

`}
`; } else if (tool === 'flowchart') { const nodes = state.flowchartText ? parseFlowchart(state.flowchartText) : []; toolContent = `

Flowchart Lite

Flowchart Syntax

Use "->" to connect steps. Add "(yes: X, no: Y)" for decisions.

${nodes.length > 0 ? ` ` : `

Enter flowchart syntax above to visualize

`}
`; } else if (tool === 'orgchart') { const hierarchy = state.orgChartText ? parseOrgChart(state.orgChartText) : []; toolContent = `

Org Chart Generator

Organization Structure

Format: "Manager: Employee1, Employee2" (one per line)

${hierarchy.length > 0 ? ` ` : `

Enter organization structure above to visualize

`}
`; } else if (tool === 'swot') { toolContent = `

SWOT Builder

`; } else if (tool === 'kanban') { toolContent = `

Kanban Board

${!state.isPro ? `
Free: Auto-saves one board • Pro: Save unlimited boards + export formats
` : ''}
`; } return toolContent; } async function onConfigChange(newConfig) { config = { ...config, ...newConfig }; render(); } // Element SDK initialization if (window.elementSdk) { window.elementSdk.init({ defaultConfig, onConfigChange, mapToCapabilities: (config) => ({ recolorables: [ { get: () => config.background_color || defaultConfig.background_color, set: (value) => { config.background_color = value; window.elementSdk.setConfig({ background_color: value }); } }, { get: () => config.surface_color || defaultConfig.surface_color, set: (value) => { config.surface_color = value; window.elementSdk.setConfig({ surface_color: value }); } }, { get: () => config.text_color || defaultConfig.text_color, set: (value) => { config.text_color = value; window.elementSdk.setConfig({ text_color: value }); } }, { get: () => config.primary_action_color || defaultConfig.primary_action_color, set: (value) => { config.primary_action_color = value; window.elementSdk.setConfig({ primary_action_color: value }); } }, { get: () => config.secondary_action_color || defaultConfig.secondary_action_color, set: (value) => { config.secondary_action_color = value; window.elementSdk.setConfig({ secondary_action_color: value }); } } ], borderables: [], fontEditable: { get: () => config.font_family || defaultConfig.font_family, set: (value) => { config.font_family = value; window.elementSdk.setConfig({ font_family: value }); } }, fontSizeable: { get: () => config.font_size || defaultConfig.font_size, set: (value) => { config.font_size = value; window.elementSdk.setConfig({ font_size: value }); } } }), mapToEditPanelValues: (config) => new Map([ ['app_title', config.app_title || defaultConfig.app_title], ['tagline', config.tagline || defaultConfig.tagline] ]) }); } // Initialize state.currentRoute = getCurrentRoute(); document.documentElement.classList.toggle('dark', state.darkMode); render();