{"ast":null,"code":"var _jsxFileName = \"/Users/dwindown/CascadeProjects/developer-tools/src/pages/BeautifierTool.js\",\n _s = $RefreshSig$();\nimport React, { useState } from 'react';\nimport { FileText } from 'lucide-react';\nimport ToolLayout from '../components/ToolLayout';\nimport CopyButton from '../components/CopyButton';\nimport { jsxDEV as _jsxDEV } from \"react/jsx-dev-runtime\";\nconst BeautifierTool = () => {\n _s();\n const [input, setInput] = useState('');\n const [output, setOutput] = useState('');\n const [language, setLanguage] = useState('json');\n const [mode, setMode] = useState('beautify'); // 'beautify' or 'minify'\n\n const beautifyJson = text => {\n try {\n const parsed = JSON.parse(text);\n return JSON.stringify(parsed, null, 2);\n } catch (err) {\n throw new Error(`Invalid JSON: ${err.message}`);\n }\n };\n const minifyJson = text => {\n try {\n const parsed = JSON.parse(text);\n return JSON.stringify(parsed);\n } catch (err) {\n throw new Error(`Invalid JSON: ${err.message}`);\n }\n };\n const beautifyXml = text => {\n try {\n // Clean input text first\n let cleanText = text.trim();\n if (!cleanText) {\n throw new Error('Empty XML input');\n }\n\n // Parse and validate XML\n const parser = new DOMParser();\n const xmlDoc = parser.parseFromString(cleanText, 'text/xml');\n if (xmlDoc.getElementsByTagName('parsererror').length > 0) {\n throw new Error('Invalid XML syntax');\n }\n\n // Format XML properly\n const formatXmlNode = (node, depth = 0) => {\n const indent = ' '.repeat(depth);\n let result = '';\n if (node.nodeType === Node.ELEMENT_NODE) {\n const tagName = node.tagName;\n const attributes = Array.from(node.attributes).map(attr => `${attr.name}=\"${attr.value}\"`).join(' ');\n const openTag = `<${tagName}${attributes ? ' ' + attributes : ''}`;\n if (node.childNodes.length === 0) {\n result += `${indent}${openTag} />\\n`;\n } else {\n const hasTextContent = Array.from(node.childNodes).some(child => child.nodeType === Node.TEXT_NODE && child.textContent.trim());\n if (hasTextContent && node.childNodes.length === 1) {\n // Single text node - keep on same line\n result += `${indent}${openTag}>${node.textContent.trim()}${tagName}>\\n`;\n } else {\n // Multiple children or mixed content\n result += `${indent}${openTag}>\\n`;\n Array.from(node.childNodes).forEach(child => {\n if (child.nodeType === Node.ELEMENT_NODE) {\n result += formatXmlNode(child, depth + 1);\n } else if (child.nodeType === Node.TEXT_NODE) {\n const text = child.textContent.trim();\n if (text) {\n result += `${' '.repeat(depth + 1)}${text}\\n`;\n }\n }\n });\n result += `${indent}${tagName}>\\n`;\n }\n }\n }\n return result;\n };\n let formatted = '';\n if (xmlDoc.documentElement) {\n formatted = formatXmlNode(xmlDoc.documentElement);\n }\n return formatted.trim();\n } catch (err) {\n throw new Error(`XML formatting error: ${err.message}`);\n }\n };\n const minifyXml = text => {\n return text.replace(/>\\s+<').replace(/\\s+/g, ' ').trim();\n };\n const beautifyCss = text => {\n let formatted = text.replace(/\\{/g, ' {\\n ').replace(/\\}/g, '\\n}\\n').replace(/;/g, ';\\n ').replace(/,/g, ',\\n').replace(/\\n\\s*\\n/g, '\\n').trim();\n\n // Clean up extra spaces and indentation\n const lines = formatted.split('\\n');\n let result = '';\n let indent = 0;\n lines.forEach(line => {\n const trimmed = line.trim();\n if (trimmed) {\n if (trimmed === '}') {\n indent--;\n }\n result += ' '.repeat(Math.max(0, indent)) + trimmed + '\\n';\n if (trimmed.endsWith('{')) {\n indent++;\n }\n }\n });\n return result.trim();\n };\n const minifyCss = text => {\n return text.replace(/\\s+/g, ' ').replace(/;\\s*}/g, '}').replace(/\\s*{\\s*/g, '{').replace(/;\\s*/g, ';').replace(/,\\s*/g, ',').trim();\n };\n const beautifyHtml = text => {\n try {\n // Clean input text first\n let cleanText = text.trim();\n if (!cleanText) {\n throw new Error('Empty HTML input');\n }\n\n // Self-closing tags that don't need closing tags\n const selfClosingTags = ['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr'];\n\n // Inline tags that should stay on same line\n const inlineTags = ['a', 'abbr', 'acronym', 'b', 'bdo', 'big', 'br', 'button', 'cite', 'code', 'dfn', 'em', 'i', 'img', 'input', 'kbd', 'label', 'map', 'object', 'q', 'samp', 'script', 'select', 'small', 'span', 'strong', 'sub', 'sup', 'textarea', 'tt', 'var'];\n let formatted = '';\n let indent = 0;\n const tab = ' ';\n\n // Better HTML parsing\n const tokens = cleanText.match(/<\\/?[^>]+>|[^<]+/g) || [];\n for (let i = 0; i < tokens.length; i++) {\n const token = tokens[i].trim();\n if (!token) continue;\n if (token.startsWith('<')) {\n // It's a tag\n if (token.startsWith('')) {\n // Closing tag\n indent = Math.max(0, indent - 1);\n formatted += tab.repeat(indent) + token + '\\n';\n } else if (token.endsWith('/>')) {\n // Self-closing tag\n formatted += tab.repeat(indent) + token + '\\n';\n } else if (token.startsWith(']+)/);\n const tagName = tagMatch ? tagMatch[1].toLowerCase() : '';\n formatted += tab.repeat(indent) + token + '\\n';\n if (!selfClosingTags.includes(tagName)) {\n indent++;\n }\n }\n } else {\n // It's text content\n const textContent = token.trim();\n if (textContent) {\n formatted += tab.repeat(indent) + textContent + '\\n';\n }\n }\n }\n return formatted.trim();\n } catch (err) {\n // Fallback to simple formatting if advanced parsing fails\n let formatted = '';\n let indent = 0;\n const tab = ' ';\n text = text.replace(/>\\n<');\n const lines = text.split('\\n');\n lines.forEach(line => {\n const trimmed = line.trim();\n if (trimmed) {\n if (trimmed.startsWith('')) {\n indent = Math.max(0, indent - 1);\n }\n formatted += tab.repeat(indent) + trimmed + '\\n';\n if (trimmed.startsWith('<') && !trimmed.startsWith('') && !trimmed.endsWith('/>') && !trimmed.includes(' {\n return text.replace(/>\\s+<').replace(/\\s+/g, ' ').trim();\n };\n const beautifySql = text => {\n const keywords = ['SELECT', 'FROM', 'WHERE', 'JOIN', 'INNER JOIN', 'LEFT JOIN', 'RIGHT JOIN', 'ORDER BY', 'GROUP BY', 'HAVING', 'INSERT', 'UPDATE', 'DELETE', 'CREATE', 'ALTER', 'DROP'];\n let formatted = text.toUpperCase();\n keywords.forEach(keyword => {\n const regex = new RegExp(`\\\\b${keyword}\\\\b`, 'gi');\n formatted = formatted.replace(regex, `\\n${keyword}`);\n });\n return formatted.split('\\n').map(line => line.trim()).filter(line => line).join('\\n').trim();\n };\n const minifySql = text => {\n return text.replace(/\\s+/g, ' ').trim();\n };\n const handleProcess = () => {\n try {\n let result = '';\n if (mode === 'beautify') {\n switch (language) {\n case 'json':\n result = beautifyJson(input);\n break;\n case 'xml':\n result = beautifyXml(input);\n break;\n case 'css':\n result = beautifyCss(input);\n break;\n case 'html':\n result = beautifyHtml(input);\n break;\n case 'sql':\n result = beautifySql(input);\n break;\n default:\n result = input;\n }\n } else {\n switch (language) {\n case 'json':\n result = minifyJson(input);\n break;\n case 'xml':\n result = minifyXml(input);\n break;\n case 'css':\n result = minifyCss(input);\n break;\n case 'html':\n result = minifyHtml(input);\n break;\n case 'sql':\n result = minifySql(input);\n break;\n default:\n result = input;\n }\n }\n setOutput(result);\n } catch (err) {\n setOutput(`Error: ${err.message}`);\n }\n };\n const clearAll = () => {\n setInput('');\n setOutput('');\n };\n const loadSample = () => {\n const samples = {\n json: '{\"name\":\"John\",\"age\":30,\"city\":\"New York\",\"hobbies\":[\"reading\",\"coding\"]}',\n xml: '
Content
Content