diff --git a/package.json b/package.json index 98cf20a..5ed4eb1 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,12 @@ }, "dependencies": { "@fontsource-variable/inter": "^5.2.5", + "@fontsource/dm-mono": "^5.2.5", + "@microlink/react-json-view": "^1.26.2", + "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tabs": "^1.1.12", "@radix-ui/react-tooltip": "^1.2.7", "@tailwindcss/typography": "^0.5.16", "class-variance-authority": "^0.7.1", @@ -23,6 +27,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "react-resizable-panels": "^3.0.2", + "react-router": "^7.6.1", "react-simple-code-editor": "^0.14.1", "tailwind-merge": "^3.3.0", "tailwindcss-animate": "^1.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb8c01f..d4957fa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,12 +11,24 @@ importers: '@fontsource-variable/inter': specifier: ^5.2.5 version: 5.2.5 + '@fontsource/dm-mono': + specifier: ^5.2.5 + version: 5.2.5 + '@microlink/react-json-view': + specifier: ^1.26.2 + version: 1.26.2(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dialog': + specifier: ^1.1.14 + version: 1.1.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dropdown-menu': specifier: ^2.1.15 version: 2.1.15(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.3(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-tabs': + specifier: ^1.1.12 + version: 1.1.12(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-tooltip': specifier: ^1.2.7 version: 1.2.7(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -47,6 +59,9 @@ importers: react-resizable-panels: specifier: ^3.0.2 version: 3.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react-router: + specifier: ^7.6.1 + version: 7.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react-simple-code-editor: specifier: ^0.14.1 version: 0.14.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -192,6 +207,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.27.4': + resolution: {integrity: sha512-t3yaEOuGu9NlIZ+hIeGbBjFtZT7j2cb2tg0fuaJKeGotchRjjLfrBA9Kwf8quhpP1EUuxModQg04q/mBwyg8uA==} + engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} @@ -410,6 +429,9 @@ packages: '@fontsource-variable/inter@5.2.5': resolution: {integrity: sha512-TrWffUAFOnT8zroE9YmGybagoOgM/HjRqMQ8k9R0vVgXlnUh/vnpbGPAS/Caz1KIlOPnPGh6fvJbb7DHbFCncA==} + '@fontsource/dm-mono@5.2.5': + resolution: {integrity: sha512-+dTNMXWxmvNtEWE/45MxFO7s60sy9famJhbtNghcvuEleMFcf32anQe3y4FqoCEL/rdQFvRiCoMCDguHw9krog==} + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -452,6 +474,13 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@microlink/react-json-view@1.26.2': + resolution: {integrity: sha512-NamaHDT21njvbg2RZQq+rnu+owlPyj5lnUdVH5ZtChfTX+75QD2EGnccB1gs0De42jdPj77UQHYLr7d4J46IYA==} + engines: {node: '>=17'} + peerDependencies: + react: '>= 15' + react-dom: '>= 15' + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -515,6 +544,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-dialog@1.1.14': + resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-direction@1.1.1': resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} peerDependencies: @@ -668,6 +710,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-tabs@1.1.12': + resolution: {integrity: sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-tooltip@1.2.7': resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} peerDependencies: @@ -877,12 +932,18 @@ packages: '@types/babel__traverse@7.20.7': resolution: {integrity: sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==} + '@types/base16@1.0.5': + resolution: {integrity: sha512-OzOWrTluG9cwqidEzC/Q6FAmIPcnZfm8BFRlIx0+UIUqnuAmi5OS88O0RpT3Yz6qdmqObvUhasrbNsCofE4W9A==} + '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/lodash@4.17.17': + resolution: {integrity: sha512-RRVJ+J3J+WmyOTqnz3PiBLA501eKwXl2noseKOrNo/6+XEHjTAxO4xHvxQB6QuNm+s4WRbn6rSiap8+EA+ykFQ==} + '@types/node@22.15.21': resolution: {integrity: sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==} @@ -1012,6 +1073,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base16@1.0.0: + resolution: {integrity: sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -1057,13 +1121,25 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@3.2.1: + resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -1074,6 +1150,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1316,6 +1396,9 @@ packages: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1397,6 +1480,9 @@ packages: lodash.castarray@4.4.0: resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==} + lodash.curry@4.1.1: + resolution: {integrity: sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==} + lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} @@ -1599,11 +1685,17 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + react-base16-styling@0.9.1: + resolution: {integrity: sha512-1s0CY1zRBOQ5M3T61wetEpvQmsYSNtWEcdYzyZNxKa8t7oDvaOn9d21xrGezGAHFWLM7SHcktPuPTrvoqxSfKw==} + react-dom@19.1.0: resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} peerDependencies: react: ^19.1.0 + react-lifecycles-compat@3.0.4: + resolution: {integrity: sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==} + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -1634,6 +1726,16 @@ packages: react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-router@7.6.1: + resolution: {integrity: sha512-hPJXXxHJZEsPFNVbtATH7+MMX43UDeOauz+EAU4cgqTn7ojdI9qQORqS8Z0qmDlL1TclO/6jLRYUEtbWidtdHQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + react-simple-code-editor@0.14.1: resolution: {integrity: sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==} peerDependencies: @@ -1650,6 +1752,12 @@ packages: '@types/react': optional: true + react-textarea-autosize@8.5.9: + resolution: {integrity: sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==} + engines: {node: '>=10'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react@19.1.0: resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} @@ -1694,6 +1802,9 @@ packages: engines: {node: '>=10'} hasBin: true + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1706,6 +1817,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1821,6 +1935,33 @@ packages: '@types/react': optional: true + use-composed-ref@1.4.0: + resolution: {integrity: sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-isomorphic-layout-effect@1.2.1: + resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-latest@1.3.0: + resolution: {integrity: sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} @@ -2025,6 +2166,8 @@ snapshots: '@babel/core': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 + '@babel/runtime@7.27.4': {} + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 @@ -2186,6 +2329,8 @@ snapshots: '@fontsource-variable/inter@5.2.5': {} + '@fontsource/dm-mono@5.2.5': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.6': @@ -2225,6 +2370,16 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@microlink/react-json-view@1.26.2(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + react: 19.1.0 + react-base16-styling: 0.9.1 + react-dom: 19.1.0(react@19.1.0) + react-lifecycles-compat: 3.0.4 + react-textarea-autosize: 8.5.9(@types/react@19.1.4)(react@19.1.0) + transitivePeerDependencies: + - '@types/react' + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -2275,6 +2430,28 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-dialog@1.1.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.0(@types/react@19.1.4)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-direction@1.1.1(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 @@ -2430,6 +2607,22 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-tabs@1.1.12(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@radix-ui/primitive': 1.1.2 @@ -2598,10 +2791,14 @@ snapshots: dependencies: '@babel/types': 7.27.1 + '@types/base16@1.0.5': {} + '@types/estree@1.0.7': {} '@types/json-schema@7.0.15': {} + '@types/lodash@4.17.17': {} + '@types/node@22.15.21': dependencies: undici-types: 6.21.0 @@ -2758,6 +2955,8 @@ snapshots: balanced-match@1.0.2: {} + base16@1.0.0: {} + binary-extensions@2.3.0: {} brace-expansion@1.1.11: @@ -2809,18 +3008,36 @@ snapshots: clsx@2.1.1: {} + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + color-convert@2.0.1: dependencies: color-name: 1.1.4 + color-name@1.1.3: {} + color-name@1.1.4: {} + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@3.2.1: + dependencies: + color-convert: 1.9.3 + color-string: 1.9.1 + commander@4.1.1: {} concat-map@0.0.1: {} convert-source-map@2.0.0: {} + cookie@1.0.2: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -3069,6 +3286,8 @@ snapshots: imurmurhash@0.1.4: {} + is-arrayish@0.3.2: {} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -3132,6 +3351,8 @@ snapshots: lodash.castarray@4.4.0: {} + lodash.curry@4.1.1: {} + lodash.isplainobject@4.0.6: {} lodash.merge@4.6.2: {} @@ -3293,11 +3514,23 @@ snapshots: queue-microtask@1.2.3: {} + react-base16-styling@0.9.1: + dependencies: + '@babel/runtime': 7.27.4 + '@types/base16': 1.0.5 + '@types/lodash': 4.17.17 + base16: 1.0.0 + color: 3.2.1 + csstype: 3.1.3 + lodash.curry: 4.1.1 + react-dom@19.1.0(react@19.1.0): dependencies: react: 19.1.0 scheduler: 0.26.0 + react-lifecycles-compat@3.0.4: {} + react-refresh@0.17.0: {} react-remove-scroll-bar@2.3.8(@types/react@19.1.4)(react@19.1.0): @@ -3324,6 +3557,14 @@ snapshots: react: 19.1.0 react-dom: 19.1.0(react@19.1.0) + react-router@7.6.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + cookie: 1.0.2 + react: 19.1.0 + set-cookie-parser: 2.7.1 + optionalDependencies: + react-dom: 19.1.0(react@19.1.0) + react-simple-code-editor@0.14.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 @@ -3337,6 +3578,15 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + react-textarea-autosize@8.5.9(@types/react@19.1.4)(react@19.1.0): + dependencies: + '@babel/runtime': 7.27.4 + react: 19.1.0 + use-composed-ref: 1.4.0(@types/react@19.1.4)(react@19.1.0) + use-latest: 1.3.0(@types/react@19.1.4)(react@19.1.0) + transitivePeerDependencies: + - '@types/react' + react@19.1.0: {} read-cache@1.0.0: @@ -3393,6 +3643,8 @@ snapshots: semver@7.7.2: {} + set-cookie-parser@2.7.1: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3401,6 +3653,10 @@ snapshots: signal-exit@4.1.0: {} + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + source-map-js@1.2.1: {} string-width@4.2.3: @@ -3534,6 +3790,25 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + use-composed-ref@1.4.0(@types/react@19.1.4)(react@19.1.0): + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + use-isomorphic-layout-effect@1.2.1(@types/react@19.1.4)(react@19.1.0): + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.4 + + use-latest@1.3.0(@types/react@19.1.4)(react@19.1.0): + dependencies: + react: 19.1.0 + use-isomorphic-layout-effect: 1.2.1(@types/react@19.1.4)(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + use-sidecar@1.1.3(@types/react@19.1.4)(react@19.1.0): dependencies: detect-node-es: 1.1.0 diff --git a/src/App.css b/src/App.css deleted file mode 100644 index b9d355d..0000000 --- a/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/Editor.tsx b/src/Editor.tsx index e80193f..49038b2 100644 --- a/src/Editor.tsx +++ b/src/Editor.tsx @@ -7,6 +7,7 @@ import { DropdownMenuTrigger, } from "@/components/DropdownMenu"; import { ResizablePanel } from "@/components/Resizable"; +import * as Tabs from "@/components/Tabs"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/Tooltip"; import { useStore } from "@/store"; import { @@ -15,11 +16,11 @@ import { ChevronDownIcon, CopyIcon, FileJsonIcon, - SettingsIcon, - ToggleLeftIcon, RadioIcon, + SettingsIcon, SquareMousePointerIcon, TextCursorInputIcon, + ToggleLeftIcon, } from "lucide-react"; import { type FC, useEffect, useRef, useState } from "react"; import CodeEditor from "react-simple-code-editor"; @@ -28,8 +29,8 @@ import CodeEditor from "react-simple-code-editor"; // @ts-expect-error TODO: create types for this import { highlight, languages } from "prismjs/components/prism-core"; import "prismjs/components/prism-hcl"; -// @ts-expect-error TODO: create types for this import "prismjs/themes/prism.css"; +import { cn } from "@/utils/cn"; // Adds line numbers to the highlight. const hightlightWithLineNumbers = (input: string, language: unknown) => @@ -50,6 +51,8 @@ export const Editor: FC = () => { undefined, ); + const [tab, setTab] = useState(() => "code"); + const onCopy = () => { navigator.clipboard.writeText($code); setCodeCopied(() => true); @@ -70,81 +73,88 @@ export const Editor: FC = () => { }, [codeCopied]); return ( - - {/* EDITOR TOP BAR */} -
-
- - - - - - - Coming soon - -
- - - -
- -

Examples

+ setTab(() => tab)} + > + + {/* EDITOR TOP BAR */} + +
+
+ + + + + + Coming soon +
- - - - - - - Text input - - - - Multi-select - - - - Radio - - - Switches - - - - -
+ + +
+ +

Examples

+
+ +
- {/* CODE EDITOR */} -
-
+
+ + {/* CODE EDITOR */} +
- {codeCopied ? : } Copy - -
+ +
-
- $setCode(code)} - highlight={(code) => hightlightWithLineNumbers(code, languages.hcl)} - textareaId="codeArea" - className="editor pt-3" - /> -
- + +
+ $setCode(code)} + highlight={(code) => + hightlightWithLineNumbers(code, languages.hcl) + } + textareaId="codeArea" + className="editor pt-3" + /> +
+
+ + ); }; diff --git a/src/Preview.tsx b/src/Preview.tsx index 4d5cbd5..6a59326 100644 --- a/src/Preview.tsx +++ b/src/Preview.tsx @@ -1,17 +1,34 @@ import { Button } from "@/components/Button"; -import { ResizablePanel } from "@/components/Resizable"; +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from "@/components/Resizable"; +import * as Tabs from "@/components/Tabs"; import { type Diagnostic, type InternalDiagnostic, outputToDiagnostics, } from "@/diagnostics"; -import type { PreviewOutput } from "@/gen/types"; +import type { ParserLog, PreviewOutput } from "@/gen/types"; import { useDebouncedValue } from "@/hooks/debounce"; import { useStore } from "@/store"; import { cn } from "@/utils/cn"; -import { ActivityIcon, ExternalLinkIcon, LoaderIcon } from "lucide-react"; +import * as Dialog from "@radix-ui/react-dialog"; +import { + ActivityIcon, + BugIcon, + DownloadIcon, + ExternalLinkIcon, + LoaderIcon, + PlayIcon, + ScrollTextIcon, + XIcon, +} from "lucide-react"; import { AnimatePresence, motion } from "motion/react"; -import { type FC, useEffect, useMemo, useState } from "react"; +import { type FC, useCallback, useEffect, useMemo, useState } from "react"; +import { useSearchParams } from "react-router"; +import ReactJsonView from "@microlink/react-json-view"; export const Preview: FC = () => { const $wasmState = useStore((state) => state.wasmState); @@ -20,9 +37,35 @@ export const Preview: FC = () => { const $setError = useStore((state) => state.setError); const [debouncedCode, isDebouncing] = useDebouncedValue($code, 1000); - const [output, setOutput] = useState(() => null); + const [params] = useSearchParams(); + const isDebug = useMemo(() => params.has("debug"), [params]); + + const [tab, setTab] = useState(() => "preview"); + + const onDownloadOutput = useCallback(() => { + const blob = new Blob([JSON.stringify(output, null, 2)], { + type: "application/json", + }); + + const url = URL.createObjectURL(blob); + + const link = document.createElement("a"); + link.href = url; + link.download = "output.json"; + document.appendChild(link); + link.click(); + document.removeChild(link); + + // Give the click event enough time to fire and then revoke the URL. + // This method of doing it doesn't seem great but I'm not sure if there is a + // better way. + setTimeout(() => { + URL.revokeObjectURL(url); + }, 100); + }, [output]); + useEffect(() => { if (!window.go_preview) { return; @@ -68,96 +111,105 @@ export const Preview: FC = () => { }, [debouncedCode, $setError]); return ( - - {$wasmState !== "loaded" ? ( -
- {$wasmState === "loading" ? : } -
- ) : null} - -
0) - } - className={cn( - "flex h-full w-full flex-col items-start gap-6 p-8", - ($wasmState !== "loaded" || - ($errors.show && $errors.diagnostics.length > 0)) && - "pointer-events-none", - )} - > -
-
-

- Parameters -

- - - {isDebouncing && $wasmState === "loaded" ? ( - - - - ) : null} - + setTab(() => tab)} + > + + {$wasmState !== "loaded" ? ( +
+ {$wasmState === "loading" ? : }
- -
+ ) : null} -
- {output ? ( -
-
-

- Parameters -

-

- {JSON.stringify(output.output?.Parameters, null, 2)} -

-
+
+ + ( + + ) + : BugIcon + } + label="Debugger" + />{" "} +
+ + -
-

- Diagnostics -

-

- {JSON.stringify(output.diags, null, 2)} + +

0) + } + className={cn( + "flex h-full w-full flex-col items-start gap-6 p-6", + ($wasmState !== "loaded" || + ($errors.show && $errors.diagnostics.length > 0)) && + "pointer-events-none", + )} + > +
+
+

+ Parameters

-
-
-

- Logs -

-
- {output.parser_logs?.map((log, index) => ( -

+ {isDebouncing && $wasmState === "loaded" ? ( + - {log.time} {log.level}: {log.msg} - {log.err} -

- ))} -
+ + + ) : null} +
+
- ) : ( - - )} -
-
- - +
+ +
+
+ + + + + + + + + ); }; @@ -321,3 +373,170 @@ const WasmError: FC = () => {
); }; + +type DebuggerProps = { + output: PreviewOutput | null; +}; +const Debugger: FC = ({ output }) => { + const parserLogs = output?.parser_logs ?? []; + + return ( + + +
+ +
+
+ + + {parserLogs.length === 0 ? ( + + ) : ( +
+ {parserLogs.map((log, index) => ( + + ))} +
+ )} +
+
+ ); +}; + +const LogsEmptyState = () => { + return ( +
+
+ +
+
+

No logs yet

+

+ Make changes to the template and view the output logs here +

+
+
+ ); +}; + +type LogProps = { log: ParserLog }; +const Log: FC = ({ log }) => { + const [showTable, setShowTable] = useState(() => false); + + return ( + setShowTable(() => show)} + > + +
+

+ {log.msg} +

+
+

+ {JSON.stringify(log)} +

+
+ + + + {showTable ? ( + <> + + + + + +
+

+ Log +

+ + + +
+
+
+
+

field

+
+
+

value

+
+
+ {Object.entries(log).map(([key, value], index) => { + const displayValue = JSON.stringify(value); + + return ( +
+
+

{key}

+
+
+

+ {value === "" + ? "" + : displayValue.substring( + 1, + displayValue.length - 1, + )} +

+
+
+ ); + })} +
+
+
+ + ) : null} +
+
+
+ ); +}; diff --git a/src/components/Tabs.tsx b/src/components/Tabs.tsx new file mode 100644 index 0000000..4370df2 --- /dev/null +++ b/src/components/Tabs.tsx @@ -0,0 +1,52 @@ +import type { FC } from "react"; +import * as Tabs from "@radix-ui/react-tabs"; +import type { LucideProps } from "lucide-react"; +import { cn } from "@/utils/cn"; + +export const Root: FC = ({ children, ...rest }) => { + return ( + + {children} + + ); +}; + +export const List: FC = ({ + className, + children, + ...rest +}) => { + return ( + + {children} + + ); +}; + +type TriggerProps = { + label: string; + icon: FC; +} & Tabs.TabsTriggerProps; +export const Trigger: FC = ({ + label, + icon: Icon = () => null, + className, + ...rest +}) => { + return ( + + +

{label}

+
+ ); +}; + +export const Content = Tabs.Content; diff --git a/src/main.tsx b/src/main.tsx index 79df65c..d8807d4 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,10 +1,11 @@ -// @ts-expect-error TODO: create types for this import "@fontsource-variable/inter"; +import "@fontsource/dm-mono"; import { TooltipProvider } from "@/components/Tooltip"; +import { ThemeProvider } from "@/contexts/theme.tsx"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; +import { BrowserRouter } from "react-router"; import { App } from "./App.tsx"; -import { ThemeProvider } from "@/contexts/theme.tsx"; const root = document.getElementById("root"); @@ -13,11 +14,13 @@ if (!root) { } else { createRoot(root).render( - - - - - + + + + + + + , ); } diff --git a/tailwind.config.js b/tailwind.config.js index 0d44c47..a0054df 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -10,6 +10,7 @@ module.exports = { extend: { fontFamily: { sans: `"Inter Variable", system-ui, sans-serif`, + mono: `"DM Mono", system-mono, monospace`, }, size: { "icon-lg": "1.5rem", diff --git a/tsconfig.app.json b/tsconfig.app.json index 7f3acf6..49dbbe5 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -29,7 +29,7 @@ "noUnusedParameters": true, "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true + "noUncheckedSideEffectImports": false }, "include": [ "src" diff --git a/vite.config.ts b/vite.config.ts index 453d8e8..5fc9e6d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,7 +5,7 @@ import path from "node:path"; // https://vite.dev/config/ export default defineConfig({ - base: "/parameters-playground/", + base: "/", server: { // For dev purposes when using Coder Connect, and ngrok allowedHosts: [".coder", ".ngrok"],