diff --git a/itdoc-doc/package.json b/itdoc-doc/package.json index 9c8aa0a..1de2d50 100644 --- a/itdoc-doc/package.json +++ b/itdoc-doc/package.json @@ -20,18 +20,17 @@ ] }, "dependencies": { - "@codemirror/lang-javascript": "^6.2.4", - "@codemirror/theme-one-dark": "^6.1.3", "@docusaurus/core": "3.7.0", "@docusaurus/plugin-google-gtag": "^3.7.0", "@docusaurus/preset-classic": "3.7.0", "@mdx-js/react": "^3.0.0", - "@uiw/react-codemirror": "^4.25.2", + "@monaco-editor/react": "^4.6.0", "@webcontainer/api": "^1.6.1", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", "clsx": "^2.0.0", "docusaurus": "^1.14.7", + "monaco-editor": "^0.52.0", "prism-react-renderer": "^2.3.0", "react": "^19.0.0", "react-dom": "^19.0.0" diff --git a/itdoc-doc/pnpm-lock.yaml b/itdoc-doc/pnpm-lock.yaml index ee06865..db320d8 100644 --- a/itdoc-doc/pnpm-lock.yaml +++ b/itdoc-doc/pnpm-lock.yaml @@ -8,12 +8,6 @@ importers: .: dependencies: - '@codemirror/lang-javascript': - specifier: ^6.2.4 - version: 6.2.4 - '@codemirror/theme-one-dark': - specifier: ^6.1.3 - version: 6.1.3 '@docusaurus/core': specifier: 3.7.0 version: 3.7.0(@mdx-js/react@3.1.0(@types/react@19.1.0)(react@19.1.0))(acorn@8.14.1)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.6.3) @@ -26,9 +20,9 @@ importers: '@mdx-js/react': specifier: ^3.0.0 version: 3.1.0(@types/react@19.1.0)(react@19.1.0) - '@uiw/react-codemirror': - specifier: ^4.25.2 - version: 4.25.2(@babel/runtime@7.27.0)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.4)(codemirror@6.0.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@monaco-editor/react': + specifier: ^4.6.0 + version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@webcontainer/api': specifier: ^1.6.1 version: 1.6.1 @@ -44,6 +38,9 @@ importers: docusaurus: specifier: ^1.14.7 version: 1.14.7(typescript@5.6.3)(webpack@5.98.0) + monaco-editor: + specifier: ^0.52.0 + version: 0.52.2 prism-react-renderer: specifier: ^2.3.0 version: 2.4.1(react@19.1.0) @@ -744,33 +741,6 @@ packages: resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} engines: {node: '>=6.9.0'} - '@codemirror/autocomplete@6.19.0': - resolution: {integrity: sha512-61Hfv3cF07XvUxNeC3E7jhG8XNi1Yom1G0lRC936oLnlF+jrbrv8rc/J98XlYzcsAoTVupfsf5fLej1aI8kyIg==} - - '@codemirror/commands@6.9.0': - resolution: {integrity: sha512-454TVgjhO6cMufsyyGN70rGIfJxJEjcqjBG2x2Y03Y/+Fm99d3O/Kv1QDYWuG6hvxsgmjXmBuATikIIYvERX+w==} - - '@codemirror/lang-javascript@6.2.4': - resolution: {integrity: sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==} - - '@codemirror/language@6.11.3': - resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==} - - '@codemirror/lint@6.9.0': - resolution: {integrity: sha512-wZxW+9XDytH3SKvS8cQzMyQCaaazH8XL1EMHleHe00wVzsv7NBQKVW2yzEHrRhmM7ZOhVdItPbvlRBvMp9ej7A==} - - '@codemirror/search@6.5.11': - resolution: {integrity: sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==} - - '@codemirror/state@6.5.2': - resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} - - '@codemirror/theme-one-dark@6.1.3': - resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} - - '@codemirror/view@6.38.4': - resolution: {integrity: sha512-hduz0suCcUSC/kM8Fq3A9iLwInJDl8fD1xLpTIk+5xkNm8z/FT7UsIa9sOXrkpChh+XXc18RzswE8QqELsVl+g==} - '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -1255,21 +1225,6 @@ packages: '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@lezer/common@1.2.3': - resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} - - '@lezer/highlight@1.2.1': - resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} - - '@lezer/javascript@1.5.4': - resolution: {integrity: sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==} - - '@lezer/lr@1.4.2': - resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} - - '@marijn/find-cluster-break@1.0.2': - resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} - '@mdx-js/mdx@3.1.0': resolution: {integrity: sha512-/QxEhPAvGwbQmy1Px8F899L5Uc2KZ6JtXwlCgJmjSTBedwOZkByYcBG4GceIGPXRDsmfxhHazuS+hlOShRLeDw==} @@ -1279,6 +1234,16 @@ packages: '@types/react': '>=16' react: '>=16' + '@monaco-editor/loader@1.5.0': + resolution: {integrity: sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==} + + '@monaco-editor/react@4.7.0': + resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==} + peerDependencies: + monaco-editor: '>= 0.25.0 < 1' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + '@mrmlnc/readdir-enhanced@2.2.1': resolution: {integrity: sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==} engines: {node: '>=4'} @@ -1589,28 +1554,6 @@ packages: '@types/yargs@17.0.33': resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} - '@uiw/codemirror-extensions-basic-setup@4.25.2': - resolution: {integrity: sha512-s2fbpdXrSMWEc86moll/d007ZFhu6jzwNu5cWv/2o7egymvLeZO52LWkewgbr+BUCGWGPsoJVWeaejbsb/hLcw==} - peerDependencies: - '@codemirror/autocomplete': '>=6.0.0' - '@codemirror/commands': '>=6.0.0' - '@codemirror/language': '>=6.0.0' - '@codemirror/lint': '>=6.0.0' - '@codemirror/search': '>=6.0.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - - '@uiw/react-codemirror@4.25.2': - resolution: {integrity: sha512-XP3R1xyE0CP6Q0iR0xf3ed+cJzJnfmbLelgJR6osVVtMStGGZP3pGQjjwDRYptmjGHfEELUyyBLdY25h0BQg7w==} - peerDependencies: - '@babel/runtime': '>=7.11.0' - '@codemirror/state': '>=6.0.0' - '@codemirror/theme-one-dark': '>=6.0.0' - '@codemirror/view': '>=6.0.0' - codemirror: '>=6.0.0' - react: '>=17.0.0' - react-dom: '>=17.0.0' - '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -2288,9 +2231,6 @@ packages: resolution: {integrity: sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==} engines: {node: '>= 4.0'} - codemirror@6.0.2: - resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} - coffee-script@1.12.7: resolution: {integrity: sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==} engines: {node: '>=0.8.0'} @@ -2483,9 +2423,6 @@ packages: typescript: optional: true - crelt@1.0.6: - resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} - cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} @@ -4946,6 +4883,9 @@ packages: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true + monaco-editor@0.52.2: + resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} + moo@0.5.2: resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} @@ -6735,6 +6675,9 @@ packages: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + static-extend@0.1.2: resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} engines: {node: '>=0.10.0'} @@ -6852,9 +6795,6 @@ packages: strnum@1.1.2: resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} - style-mod@4.1.2: - resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} - style-to-js@1.1.16: resolution: {integrity: sha512-/Q6ld50hKYPH3d/r6nr117TZkHR0w0kGGIVfpG9N6D8NymRPM9RqCUv4pRpJ62E5DqOYx2AFpbZMyCPnjQCnOw==} @@ -7272,9 +7212,6 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - w3c-keyname@2.2.8: - resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} - watchpack@2.4.2: resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} engines: {node: '>=10.13.0'} @@ -8383,69 +8320,6 @@ snapshots: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@codemirror/autocomplete@6.19.0': - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - '@lezer/common': 1.2.3 - - '@codemirror/commands@6.9.0': - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - '@lezer/common': 1.2.3 - - '@codemirror/lang-javascript@6.2.4': - dependencies: - '@codemirror/autocomplete': 6.19.0 - '@codemirror/language': 6.11.3 - '@codemirror/lint': 6.9.0 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - '@lezer/common': 1.2.3 - '@lezer/javascript': 1.5.4 - - '@codemirror/language@6.11.3': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - '@lezer/common': 1.2.3 - '@lezer/highlight': 1.2.1 - '@lezer/lr': 1.4.2 - style-mod: 4.1.2 - - '@codemirror/lint@6.9.0': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - crelt: 1.0.6 - - '@codemirror/search@6.5.11': - dependencies: - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - crelt: 1.0.6 - - '@codemirror/state@6.5.2': - dependencies: - '@marijn/find-cluster-break': 1.0.2 - - '@codemirror/theme-one-dark@6.1.3': - dependencies: - '@codemirror/language': 6.11.3 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - '@lezer/highlight': 1.2.1 - - '@codemirror/view@6.38.4': - dependencies: - '@codemirror/state': 6.5.2 - crelt: 1.0.6 - style-mod: 4.1.2 - w3c-keyname: 2.2.8 - '@colors/colors@1.5.0': optional: true @@ -9534,24 +9408,6 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@lezer/common@1.2.3': {} - - '@lezer/highlight@1.2.1': - dependencies: - '@lezer/common': 1.2.3 - - '@lezer/javascript@1.5.4': - dependencies: - '@lezer/common': 1.2.3 - '@lezer/highlight': 1.2.1 - '@lezer/lr': 1.4.2 - - '@lezer/lr@1.4.2': - dependencies: - '@lezer/common': 1.2.3 - - '@marijn/find-cluster-break@1.0.2': {} - '@mdx-js/mdx@3.1.0(acorn@8.14.1)': dependencies: '@types/estree': 1.0.7 @@ -9588,6 +9444,17 @@ snapshots: '@types/react': 19.1.0 react: 19.1.0 + '@monaco-editor/loader@1.5.0': + dependencies: + state-local: 1.0.7 + + '@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@monaco-editor/loader': 1.5.0 + monaco-editor: 0.52.2 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + '@mrmlnc/readdir-enhanced@2.2.1': dependencies: call-me-maybe: 1.0.2 @@ -9939,33 +9806,6 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@uiw/codemirror-extensions-basic-setup@4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4)': - dependencies: - '@codemirror/autocomplete': 6.19.0 - '@codemirror/commands': 6.9.0 - '@codemirror/language': 6.11.3 - '@codemirror/lint': 6.9.0 - '@codemirror/search': 6.5.11 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - - '@uiw/react-codemirror@4.25.2(@babel/runtime@7.27.0)(@codemirror/autocomplete@6.19.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.3)(@codemirror/view@6.38.4)(codemirror@6.0.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': - dependencies: - '@babel/runtime': 7.27.0 - '@codemirror/commands': 6.9.0 - '@codemirror/state': 6.5.2 - '@codemirror/theme-one-dark': 6.1.3 - '@codemirror/view': 6.38.4 - '@uiw/codemirror-extensions-basic-setup': 4.25.2(@codemirror/autocomplete@6.19.0)(@codemirror/commands@6.9.0)(@codemirror/language@6.11.3)(@codemirror/lint@6.9.0)(@codemirror/search@6.5.11)(@codemirror/state@6.5.2)(@codemirror/view@6.38.4) - codemirror: 6.0.2 - react: 19.1.0 - react-dom: 19.1.0(react@19.1.0) - transitivePeerDependencies: - - '@codemirror/autocomplete' - - '@codemirror/language' - - '@codemirror/lint' - - '@codemirror/search' - '@ungap/structured-clone@1.3.0': {} '@webassemblyjs/ast@1.14.1': @@ -10784,16 +10624,6 @@ snapshots: chalk: 2.4.2 q: 1.5.1 - codemirror@6.0.2: - dependencies: - '@codemirror/autocomplete': 6.19.0 - '@codemirror/commands': 6.9.0 - '@codemirror/language': 6.11.3 - '@codemirror/lint': 6.9.0 - '@codemirror/search': 6.5.11 - '@codemirror/state': 6.5.2 - '@codemirror/view': 6.38.4 - coffee-script@1.12.7: {} collapse-white-space@2.1.0: {} @@ -10971,8 +10801,6 @@ snapshots: optionalDependencies: typescript: 5.6.3 - crelt@1.0.6: {} - cross-spawn@5.1.0: dependencies: lru-cache: 4.1.5 @@ -14152,6 +13980,8 @@ snapshots: dependencies: minimist: 1.2.8 + monaco-editor@0.52.2: {} + moo@0.5.2: {} mrmime@2.0.1: {} @@ -16267,6 +16097,8 @@ snapshots: stable@0.1.8: {} + state-local@1.0.7: {} + static-extend@0.1.2: dependencies: define-property: 0.2.5 @@ -16384,8 +16216,6 @@ snapshots: strnum@1.1.2: {} - style-mod@4.1.2: {} - style-to-js@1.1.16: dependencies: style-to-object: 1.0.8 @@ -16849,8 +16679,6 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - w3c-keyname@2.2.8: {} - watchpack@2.4.2: dependencies: glob-to-regexp: 0.4.1 diff --git a/itdoc-doc/src/components/Playground/EditorTabs.tsx b/itdoc-doc/src/components/Playground/EditorTabs.tsx new file mode 100644 index 0000000..f620f58 --- /dev/null +++ b/itdoc-doc/src/components/Playground/EditorTabs.tsx @@ -0,0 +1,128 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { useMemo } from "react" +import Editor, { OnMount } from "@monaco-editor/react" + +import styles from "./styles.module.css" +import type { PlaygroundFileId, PlaygroundFileMap } from "./types" + +interface EditorTabsProps { + files: PlaygroundFileMap + openFiles: PlaygroundFileId[] + activeFileId: PlaygroundFileId + onSelectFile: (fileId: PlaygroundFileId) => void + onCloseTab: (fileId: PlaygroundFileId) => void + onEditorChange: (value: string | undefined) => void + onEditorMount: OnMount + activeFileValue: string + oasOutput: string + onOpenPreview: () => void + canCloseTabs: boolean +} + +const EditorTabs: React.FC = ({ + files, + openFiles, + activeFileId, + onSelectFile, + onCloseTab, + onEditorChange, + onEditorMount, + activeFileValue, + oasOutput, + onOpenPreview, + canCloseTabs, +}) => { + const activeFile = useMemo(() => files[activeFileId] ?? null, [files, activeFileId]) + + return ( +
+
+ {openFiles.map((fileId) => { + const file = files[fileId] + const isActive = activeFileId === fileId + const tabId = `editor-tab-${file.id}` + const panelId = `editor-panel-${file.id}` + + return ( +
+ + +
+ ) + })} +
+
+ {activeFile ? ( + + ) : null} +
+
+

+ Tip: Keep the Express app and itdoc tests aligned before you run the suite. +

+
+
+ ) +} + +export default EditorTabs diff --git a/itdoc-doc/src/components/Playground/FileExplorer.tsx b/itdoc-doc/src/components/Playground/FileExplorer.tsx new file mode 100644 index 0000000..70f7763 --- /dev/null +++ b/itdoc-doc/src/components/Playground/FileExplorer.tsx @@ -0,0 +1,107 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react" + +import styles from "./styles.module.css" +import type { ExplorerNode, PlaygroundFileId, PlaygroundFileMap } from "./types" + +interface FileExplorerProps { + nodes: ExplorerNode[] + files: PlaygroundFileMap + activeFileId: PlaygroundFileId + openFiles: PlaygroundFileId[] + onSelectFile: (fileId: PlaygroundFileId) => void +} + +const FileExplorer: React.FC = ({ + nodes, + files, + activeFileId, + openFiles, + onSelectFile, +}) => { + return ( + + ) +} + +export default FileExplorer diff --git a/itdoc-doc/src/components/Playground/InstallOverlay.tsx b/itdoc-doc/src/components/Playground/InstallOverlay.tsx new file mode 100644 index 0000000..aec9329 --- /dev/null +++ b/itdoc-doc/src/components/Playground/InstallOverlay.tsx @@ -0,0 +1,156 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react" + +import styles from "./styles.module.css" +import type { InstallStatus } from "./types" + +interface InstallOverlayProps { + isVisible: boolean + isInstalling: boolean + installStatus: InstallStatus + currentMilestone: { title: string; description: string } | null + milestones: { title: string; description: string }[] + activeMilestoneIndex: number + progressPercent: number + formattedElapsed: string + showFinalizingHint: boolean + waitingTipTitle?: string + waitingTipBody?: string + onNextTip?: () => void + errorMessage: string | null +} + +const InstallOverlay: React.FC = ({ + isVisible, + isInstalling, + installStatus, + currentMilestone, + milestones, + activeMilestoneIndex, + progressPercent, + formattedElapsed, + showFinalizingHint, + waitingTipTitle, + waitingTipBody, + onNextTip, + errorMessage, +}) => { + if (!isVisible) { + return null + } + + const renderTipCard = () => { + if (!waitingTipTitle || !waitingTipBody) { + return null + } + + return ( + + ) + } + + return ( +
+ {isInstalling && currentMilestone ? ( +
+
+
+
+ {progressPercent}% +
+
+
+

{currentMilestone.description}

+
+ +
+
    + {milestones.map((milestone, index) => { + const stateClass = + index < activeMilestoneIndex + ? styles.milestoneItemComplete + : index === activeMilestoneIndex + ? styles.milestoneItemActive + : styles.milestoneItemUpcoming + + return ( +
  • +
  • + ) + })} +
+
Elapsed: {formattedElapsed}
+ {showFinalizingHint ? ( +

+ Almost there—WebContainer is preparing the editors and terminal. This + final step can take up to a minute the first time the sandbox boots. +

+ ) : null} +
+ {renderTipCard()} +
+
+ ) : installStatus === "error" ? ( +
+

Environment failed to start

+

{errorMessage ?? "An unexpected error occurred during setup."}

+

+ Refresh the page to try again or verify your browser supports WebContainer. +

+
+ ) : null} +
+ ) +} + +export default InstallOverlay diff --git a/itdoc-doc/src/components/Playground/RunModal.tsx b/itdoc-doc/src/components/Playground/RunModal.tsx new file mode 100644 index 0000000..19443b2 --- /dev/null +++ b/itdoc-doc/src/components/Playground/RunModal.tsx @@ -0,0 +1,135 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react" +import Editor from "@monaco-editor/react" + +import styles from "./styles.module.css" + +interface RunModalProps { + isOpen: boolean + titleId: string + dockStatusMessage: string + onClose: () => void + terminalHostRef: React.RefObject + oasOutput: string + onOpenPreview: () => void +} + +const RunModal: React.FC = ({ + isOpen, + titleId, + dockStatusMessage, + onClose, + terminalHostRef, + oasOutput, + onOpenPreview, +}) => { + if (!isOpen) { + return null + } + + return ( +
+
event.stopPropagation()}> +
+
+

Run output

+ {dockStatusMessage} +
+ +
+
+
+
+
+

Terminal

+ {dockStatusMessage} +
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+

Generated docs by itdoc

+ +
+
+
+
+
oas.json
+
+ {oasOutput ? ( + + ) : ( +

+ Run the tests to generate the OpenAPI document. +

+ )} +
+
+
+
+
+
+
+
+
+ ) +} + +export default RunModal diff --git a/itdoc-doc/src/components/Playground/SwaggerPreview.tsx b/itdoc-doc/src/components/Playground/SwaggerPreview.tsx new file mode 100644 index 0000000..bcbafb0 --- /dev/null +++ b/itdoc-doc/src/components/Playground/SwaggerPreview.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from "react" + +import styles from "./styles.module.css" + +interface SwaggerPreviewProps { + isOpen: boolean + titleId: string + onClose: () => void + previewHtml: string | null + hasDocument: boolean +} + +const SwaggerPreview: React.FC = ({ + isOpen, + titleId, + onClose, + previewHtml, + hasDocument, +}) => { + if (!isOpen) { + return null + } + + return ( +
+
event.stopPropagation()}> +
+

Swagger Preview

+ +
+
+ {hasDocument ? ( + previewHtml ? ( +