diff --git a/.github/workflows/auto-format.yml b/.github/workflows/auto-format.yml new file mode 100644 index 0000000..bbe997e --- /dev/null +++ b/.github/workflows/auto-format.yml @@ -0,0 +1,47 @@ +name: Auto Format + +on: + pull_request: + types: [opened, synchronize] + +permissions: + contents: write + +jobs: + format: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install Biome + run: npm install --save-dev @biomejs/biome + + - name: Run Biome format + run: npx @biomejs/biome check --write --no-errors-on-unmatched . + + - name: Check for changes + id: changes + run: | + if git diff --quiet; then + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Commit changes + if: steps.changes.outputs.has_changes == 'true' + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add . + git commit -m "style: auto-format with Biome" + git push diff --git a/app/manifest.ts b/app/manifest.ts index cb93576..4716200 100644 --- a/app/manifest.ts +++ b/app/manifest.ts @@ -1,27 +1,28 @@ -import type { MetadataRoute } from "next"; +import type { MetadataRoute } from "next" export default function manifest(): MetadataRoute.Manifest { - return { - name: 'Next AI Draw.io', - short_name: 'AIDraw.io', - description: 'Create AWS architecture diagrams, flowcharts, and technical diagrams using AI. Free online tool integrating draw.io with AI assistance for professional diagram creation.', - start_url: '/', - display: 'standalone', - background_color: '#f9fafb', - theme_color: '#171d26', - icons: [ - { - src: '/favicon-192x192.png', - sizes: '192x192', - type: 'image/png', - purpose: 'any', - }, - { - src: '/favicon-512x512.png', - sizes: '512x512', - type: 'image/png', - purpose: 'any', - }, - ], - } + return { + name: "Next AI Draw.io", + short_name: "AIDraw.io", + description: + "Create AWS architecture diagrams, flowcharts, and technical diagrams using AI. Free online tool integrating draw.io with AI assistance for professional diagram creation.", + start_url: "/", + display: "standalone", + background_color: "#f9fafb", + theme_color: "#171d26", + icons: [ + { + src: "/favicon-192x192.png", + sizes: "192x192", + type: "image/png", + purpose: "any", + }, + { + src: "/favicon-512x512.png", + sizes: "512x512", + type: "image/png", + purpose: "any", + }, + ], + } } diff --git a/scripts/test-diagram-operations.mjs b/scripts/test-diagram-operations.mjs index 252660d..f5e9113 100644 --- a/scripts/test-diagram-operations.mjs +++ b/scripts/test-diagram-operations.mjs @@ -20,7 +20,13 @@ function applyDiagramOperations(xmlContent, operations) { if (parseError) { return { result: xmlContent, - errors: [{ type: "update", cellId: "", message: `XML parse error: ${parseError.textContent}` }], + errors: [ + { + type: "update", + cellId: "", + message: `XML parse error: ${parseError.textContent}`, + }, + ], } } @@ -28,7 +34,13 @@ function applyDiagramOperations(xmlContent, operations) { if (!root) { return { result: xmlContent, - errors: [{ type: "update", cellId: "", message: "Could not find element in XML" }], + errors: [ + { + type: "update", + cellId: "", + message: "Could not find element in XML", + }, + ], } } @@ -42,22 +54,41 @@ function applyDiagramOperations(xmlContent, operations) { if (op.type === "update") { const existingCell = cellMap.get(op.cell_id) if (!existingCell) { - errors.push({ type: "update", cellId: op.cell_id, message: `Cell with id="${op.cell_id}" not found` }) + errors.push({ + type: "update", + cellId: op.cell_id, + message: `Cell with id="${op.cell_id}" not found`, + }) continue } if (!op.new_xml) { - errors.push({ type: "update", cellId: op.cell_id, message: "new_xml is required for update operation" }) + errors.push({ + type: "update", + cellId: op.cell_id, + message: "new_xml is required for update operation", + }) continue } - const newDoc = parser.parseFromString(`${op.new_xml}`, "text/xml") + const newDoc = parser.parseFromString( + `${op.new_xml}`, + "text/xml", + ) const newCell = newDoc.querySelector("mxCell") if (!newCell) { - errors.push({ type: "update", cellId: op.cell_id, message: "new_xml must contain an mxCell element" }) + errors.push({ + type: "update", + cellId: op.cell_id, + message: "new_xml must contain an mxCell element", + }) continue } const newCellId = newCell.getAttribute("id") if (newCellId !== op.cell_id) { - errors.push({ type: "update", cellId: op.cell_id, message: `ID mismatch: cell_id is "${op.cell_id}" but new_xml has id="${newCellId}"` }) + errors.push({ + type: "update", + cellId: op.cell_id, + message: `ID mismatch: cell_id is "${op.cell_id}" but new_xml has id="${newCellId}"`, + }) continue } const importedNode = doc.importNode(newCell, true) @@ -65,22 +96,41 @@ function applyDiagramOperations(xmlContent, operations) { cellMap.set(op.cell_id, importedNode) } else if (op.type === "add") { if (cellMap.has(op.cell_id)) { - errors.push({ type: "add", cellId: op.cell_id, message: `Cell with id="${op.cell_id}" already exists` }) + errors.push({ + type: "add", + cellId: op.cell_id, + message: `Cell with id="${op.cell_id}" already exists`, + }) continue } if (!op.new_xml) { - errors.push({ type: "add", cellId: op.cell_id, message: "new_xml is required for add operation" }) + errors.push({ + type: "add", + cellId: op.cell_id, + message: "new_xml is required for add operation", + }) continue } - const newDoc = parser.parseFromString(`${op.new_xml}`, "text/xml") + const newDoc = parser.parseFromString( + `${op.new_xml}`, + "text/xml", + ) const newCell = newDoc.querySelector("mxCell") if (!newCell) { - errors.push({ type: "add", cellId: op.cell_id, message: "new_xml must contain an mxCell element" }) + errors.push({ + type: "add", + cellId: op.cell_id, + message: "new_xml must contain an mxCell element", + }) continue } const newCellId = newCell.getAttribute("id") if (newCellId !== op.cell_id) { - errors.push({ type: "add", cellId: op.cell_id, message: `ID mismatch: cell_id is "${op.cell_id}" but new_xml has id="${newCellId}"` }) + errors.push({ + type: "add", + cellId: op.cell_id, + message: `ID mismatch: cell_id is "${op.cell_id}" but new_xml has id="${newCellId}"`, + }) continue } const importedNode = doc.importNode(newCell, true) @@ -89,7 +139,11 @@ function applyDiagramOperations(xmlContent, operations) { } else if (op.type === "delete") { const existingCell = cellMap.get(op.cell_id) if (!existingCell) { - errors.push({ type: "delete", cellId: op.cell_id, message: `Cell with id="${op.cell_id}" not found` }) + errors.push({ + type: "delete", + cellId: op.cell_id, + message: `Cell with id="${op.cell_id}" not found`, + }) continue } existingCell.parentNode?.removeChild(existingCell) @@ -149,28 +203,52 @@ test("Update operation changes cell value", () => { { type: "update", cell_id: "2", - new_xml: '', + new_xml: + '', }, ]) - assert(errors.length === 0, `Expected no errors, got: ${JSON.stringify(errors)}`) - assert(result.includes('value="Updated Box A"'), "Updated value should be in result") - assert(!result.includes('value="Box A"'), "Old value should not be in result") + assert( + errors.length === 0, + `Expected no errors, got: ${JSON.stringify(errors)}`, + ) + assert( + result.includes('value="Updated Box A"'), + "Updated value should be in result", + ) + assert( + !result.includes('value="Box A"'), + "Old value should not be in result", + ) }) test("Update operation fails for non-existent cell", () => { const { errors } = applyDiagramOperations(sampleXml, [ - { type: "update", cell_id: "999", new_xml: '' }, + { + type: "update", + cell_id: "999", + new_xml: '', + }, ]) assert(errors.length === 1, "Should have one error") - assert(errors[0].message.includes("not found"), "Error should mention not found") + assert( + errors[0].message.includes("not found"), + "Error should mention not found", + ) }) test("Update operation fails on ID mismatch", () => { const { errors } = applyDiagramOperations(sampleXml, [ - { type: "update", cell_id: "2", new_xml: '' }, + { + type: "update", + cell_id: "2", + new_xml: '', + }, ]) assert(errors.length === 1, "Should have one error") - assert(errors[0].message.includes("ID mismatch"), "Error should mention ID mismatch") + assert( + errors[0].message.includes("ID mismatch"), + "Error should mention ID mismatch", + ) }) test("Add operation creates new cell", () => { @@ -178,41 +256,72 @@ test("Add operation creates new cell", () => { { type: "add", cell_id: "new1", - new_xml: '', + new_xml: + '', }, ]) - assert(errors.length === 0, `Expected no errors, got: ${JSON.stringify(errors)}`) + assert( + errors.length === 0, + `Expected no errors, got: ${JSON.stringify(errors)}`, + ) assert(result.includes('id="new1"'), "New cell should be in result") - assert(result.includes('value="New Box"'), "New cell value should be in result") + assert( + result.includes('value="New Box"'), + "New cell value should be in result", + ) }) test("Add operation fails for duplicate ID", () => { const { errors } = applyDiagramOperations(sampleXml, [ - { type: "add", cell_id: "2", new_xml: '' }, + { + type: "add", + cell_id: "2", + new_xml: '', + }, ]) assert(errors.length === 1, "Should have one error") - assert(errors[0].message.includes("already exists"), "Error should mention already exists") + assert( + errors[0].message.includes("already exists"), + "Error should mention already exists", + ) }) test("Add operation fails on ID mismatch", () => { const { errors } = applyDiagramOperations(sampleXml, [ - { type: "add", cell_id: "new1", new_xml: '' }, + { + type: "add", + cell_id: "new1", + new_xml: '', + }, ]) assert(errors.length === 1, "Should have one error") - assert(errors[0].message.includes("ID mismatch"), "Error should mention ID mismatch") + assert( + errors[0].message.includes("ID mismatch"), + "Error should mention ID mismatch", + ) }) test("Delete operation removes cell", () => { - const { result, errors } = applyDiagramOperations(sampleXml, [{ type: "delete", cell_id: "3" }]) - assert(errors.length === 0, `Expected no errors, got: ${JSON.stringify(errors)}`) + const { result, errors } = applyDiagramOperations(sampleXml, [ + { type: "delete", cell_id: "3" }, + ]) + assert( + errors.length === 0, + `Expected no errors, got: ${JSON.stringify(errors)}`, + ) assert(!result.includes('id="3"'), "Deleted cell should not be in result") assert(result.includes('id="2"'), "Other cells should remain") }) test("Delete operation fails for non-existent cell", () => { - const { errors } = applyDiagramOperations(sampleXml, [{ type: "delete", cell_id: "999" }]) + const { errors } = applyDiagramOperations(sampleXml, [ + { type: "delete", cell_id: "999" }, + ]) assert(errors.length === 1, "Should have one error") - assert(errors[0].message.includes("not found"), "Error should mention not found") + assert( + errors[0].message.includes("not found"), + "Error should mention not found", + ) }) test("Multiple operations in sequence", () => { @@ -220,30 +329,45 @@ test("Multiple operations in sequence", () => { { type: "update", cell_id: "2", - new_xml: '', + new_xml: + '', }, { type: "add", cell_id: "new1", - new_xml: '', + new_xml: + '', }, { type: "delete", cell_id: "3" }, ]) - assert(errors.length === 0, `Expected no errors, got: ${JSON.stringify(errors)}`) - assert(result.includes('value="Updated"'), "Updated value should be present") + assert( + errors.length === 0, + `Expected no errors, got: ${JSON.stringify(errors)}`, + ) + assert( + result.includes('value="Updated"'), + "Updated value should be present", + ) assert(result.includes('id="new1"'), "Added cell should be present") assert(!result.includes('id="3"'), "Deleted cell should not be present") }) test("Invalid XML returns parse error", () => { - const { errors } = applyDiagramOperations(" { - const { errors } = applyDiagramOperations("", [{ type: "delete", cell_id: "1" }]) + const { errors } = applyDiagramOperations("", [ + { type: "delete", cell_id: "1" }, + ]) assert(errors.length === 1, "Should have one error") - assert(errors[0].message.includes("root"), "Error should mention root element") + assert( + errors[0].message.includes("root"), + "Error should mention root element", + ) }) // Summary