Use CodeMirror editor on mobile

This commit is contained in:
Sam Wright 2024-06-20 01:05:07 +10:00
parent 7b57a66d45
commit d70aeaeb04
6 changed files with 886 additions and 81 deletions

653
web/package-lock.json generated
View File

@ -8,6 +8,9 @@
"name": "web-new",
"version": "0.0.0",
"dependencies": {
"@codemirror/lang-yaml": "^6.1.1",
"@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.28.1",
"@cycjimmy/jsmpeg-player": "^6.0.5",
"@hookform/resolvers": "^3.4.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
@ -29,10 +32,12 @@
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@uiw/codemirror-themes-all": "^4.22.2",
"apexcharts": "^3.49.1",
"axios": "^1.7.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"codemirror": "^6.0.1",
"copy-to-clipboard": "^3.3.3",
"date-fns": "^3.6.0",
"hls.js": "^1.5.8",
@ -226,6 +231,103 @@
"statuses": "^2.0.1"
}
},
"node_modules/@codemirror/autocomplete": {
"version": "6.16.3",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.3.tgz",
"integrity": "sha512-Vl/tIeRVVUCRDuOG48lttBasNQu8usGgXQawBXI7WJAiUDSFOfzflmEsZFZo48mAvAaa4FZ/4/yLLxFtdJaKYA==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.17.0",
"@lezer/common": "^1.0.0"
},
"peerDependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"@lezer/common": "^1.0.0"
}
},
"node_modules/@codemirror/commands": {
"version": "6.6.0",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.6.0.tgz",
"integrity": "sha512-qnY+b7j1UNcTS31Eenuc/5YJB6gQOzkUoNmJQc0rznwqSRpeaWWpjkWy2C/MPTcePpsKJEM26hXrOXl1+nceXg==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.4.0",
"@codemirror/view": "^6.27.0",
"@lezer/common": "^1.1.0"
}
},
"node_modules/@codemirror/lang-yaml": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/@codemirror/lang-yaml/-/lang-yaml-6.1.1.tgz",
"integrity": "sha512-HV2NzbK9bbVnjWxwObuZh5FuPCowx51mEfoFT9y3y+M37fA3+pbxx4I7uePuygFzDsAmCTwQSc/kXh/flab4uw==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.2.0",
"@lezer/yaml": "^1.0.0"
}
},
"node_modules/@codemirror/language": {
"version": "6.10.2",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.2.tgz",
"integrity": "sha512-kgbTYTo0Au6dCSc/TFy7fK3fpJmgHDv1sG1KNQKJXVi+xBTEeBPY/M30YXiU6mMXeH+YIDLsbrT4ZwNRdtF+SA==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.23.0",
"@lezer/common": "^1.1.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0",
"style-mod": "^4.0.0"
}
},
"node_modules/@codemirror/lint": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.1.tgz",
"integrity": "sha512-IZ0Y7S4/bpaunwggW2jYqwLuHj0QtESf5xcROewY6+lDNwZ/NzvR4t+vpYgg9m7V8UXLPYqG+lu3DF470E5Oxg==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/search": {
"version": "6.5.6",
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.6.tgz",
"integrity": "sha512-rpMgcsh7o0GuCDUXKPvww+muLA1pDJaFrpq/CCHtpQJYz8xopu4D1hPcKRoDD0YlF8gZaqTNIRa4VRBWyhyy7Q==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0",
"crelt": "^1.0.5"
}
},
"node_modules/@codemirror/state": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz",
"integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==",
"license": "MIT"
},
"node_modules/@codemirror/view": {
"version": "6.28.1",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.28.1.tgz",
"integrity": "sha512-BUWr+zCJpMkA/u69HlJmR+YkV4yPpM81HeMkOMZuwFa8iM5uJdEPKAs1icIRZKkKmy0Ub1x9/G3PQLTXdpBxrQ==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.4.0",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@cycjimmy/jsmpeg-player": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/@cycjimmy/jsmpeg-player/-/jsmpeg-player-6.0.5.tgz",
@ -846,6 +948,41 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@lezer/common": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz",
"integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==",
"license": "MIT"
},
"node_modules/@lezer/highlight": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz",
"integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lezer/lr": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.1.tgz",
"integrity": "sha512-CHsKq8DMKBf9b3yXPDIU4DbH+ZJd/sJdYOW2llbW/HudP5u0VS6Bfq1hLYfgU7uAYGFIyGGQIsSOXGPEErZiJw==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.0.0"
}
},
"node_modules/@lezer/yaml": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@lezer/yaml/-/yaml-1.0.3.tgz",
"integrity": "sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.4.0"
}
},
"node_modules/@mswjs/cookies": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@mswjs/cookies/-/cookies-1.1.0.tgz",
@ -2856,6 +2993,489 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@uiw/codemirror-theme-abcdef": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-abcdef/-/codemirror-theme-abcdef-4.22.2.tgz",
"integrity": "sha512-ouordQh/zUT1Y1htGZkWMtGTKgA5Zrb0Wxq6quS2cddx/4/ADAUAKzZCrOL0O6AB7XOH6WCnJWCqEbU2s1K6gg==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-abyss": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-abyss/-/codemirror-theme-abyss-4.22.2.tgz",
"integrity": "sha512-jTLq7xUwVOlv0+t516eNgEaJ4xbePXs8KryEsmFQ5sTh4hzKP0wxW/P3X0gxtz+ENXxaGYz8wiC4yeqJntuzjA==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-androidstudio": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-androidstudio/-/codemirror-theme-androidstudio-4.22.2.tgz",
"integrity": "sha512-GAk8Ux4FFUiqauXKKdvHsrcYQ26si/+98zheIc0K18kEr9KB1i0WP1mczrFO4yAQsMbpDBedQPJ1LTLhZZNyow==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-andromeda": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-andromeda/-/codemirror-theme-andromeda-4.22.2.tgz",
"integrity": "sha512-CE9IvVjnPpRN+ZRbz7kA26mlQWxqHw3QqWc/3Z26mZwrQ2zNC3+oQJyy47GJSCGLyVrvvsKbvVqg9XPw5yrrvQ==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-atomone": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-atomone/-/codemirror-theme-atomone-4.22.2.tgz",
"integrity": "sha512-3LKzF04rUuli0H6FZhxWicaYkKiucGEDERmkvr7mMPLMW7uwzRjv26GxhLCggtRD5dI1+XbAm9QstyweOWYAeg==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-aura": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-aura/-/codemirror-theme-aura-4.22.2.tgz",
"integrity": "sha512-/womOFwkrDFxKzqe13YZCtUmtljYeaYABpsDEyrJZnhtgW26i71tX4laDoI58DE9yuiyiAfgxWSDZhvmK/1mUA==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-basic": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-basic/-/codemirror-theme-basic-4.22.2.tgz",
"integrity": "sha512-RHWhWMhCzTGscfbJvJtIRW9dccPWYS/BJiE5DtCPk1IYPnJHrXF3hzCpTK2CYz1/d+6V/XRyIzujhyxrMs0+mw==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-bbedit": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-bbedit/-/codemirror-theme-bbedit-4.22.2.tgz",
"integrity": "sha512-adeyK7fbdfDS3zELSUPbce7xMEMNhJbSX0hXxaL6L4jdmt2/OTHCwovNPzlH6AWyMupE+WTQmVY6y67m14dikg==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-bespin": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-bespin/-/codemirror-theme-bespin-4.22.2.tgz",
"integrity": "sha512-NZ+kyWNzZgJq38Ba2FGl+nnZDAPdy97w5y160QiiAW+Vuwii24YnkYvoHVmMxsXDphwd/sLfVktfahbAh4ntpQ==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-console": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-console/-/codemirror-theme-console-4.22.2.tgz",
"integrity": "sha512-K8fir17aBiwkTBwMbyVbnPD+3Sx5irRtFzwG6TSK+EciWofsVkWGbkIZpDQqbUYbf4WBf5dw3j5byl3isNTWnA==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
}
},
"node_modules/@uiw/codemirror-theme-copilot": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-copilot/-/codemirror-theme-copilot-4.22.2.tgz",
"integrity": "sha512-M1mif1uqZdc49rPNCpXcs4gANr30w/I5C+dK+Mxwe6+RAkwcqNB/uHuIUj0UvARiSbwNUTpTMkMNXeuT//f3Pg==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-darcula": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-darcula/-/codemirror-theme-darcula-4.22.2.tgz",
"integrity": "sha512-YF3LTTc1Rus3atNuFvkLsZvhdtxRwGEAOcEGknu7by5jzZxLPHpDLzAF9VFOIKg6j/KBZ2RNmMicVTjrB8LXHg==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-dracula": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-dracula/-/codemirror-theme-dracula-4.22.2.tgz",
"integrity": "sha512-OEB5Tu0EHNDf99m5g39hoSqf0UICzWn+z3HLsOfdjzJz7hhZpQ99x/YEZ+j+utfLQbHcusvk06DrV+C88kRyyg==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-duotone": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-duotone/-/codemirror-theme-duotone-4.22.2.tgz",
"integrity": "sha512-bZ8CvPcnHDIbMLhDrmF4M084Zf5daKiGyrmCFhHeFUeKGbIgIUu+UnkmaNjz6jteOmYEZR05wh8h3NK4Ty2OtA==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-eclipse": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-eclipse/-/codemirror-theme-eclipse-4.22.2.tgz",
"integrity": "sha512-iYdl3wJ9D0e+z6opw5fLWzrJ1ge83/WpbPvtA++hqPZnFdoPfKynzdEq8U6V+uQpoVQnZuo0DRQljnWW070y2g==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-github": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-github/-/codemirror-theme-github-4.22.2.tgz",
"integrity": "sha512-tqGOOgVzbStJWUQmMLRWyymyD3DPF4TUSJx2DcXpeCF3YStWMd957I26uQQaqR1ppAcWFsprjkl2oL3piBxFdA==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-gruvbox-dark": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-gruvbox-dark/-/codemirror-theme-gruvbox-dark-4.22.2.tgz",
"integrity": "sha512-U2YHFguSOX5xmCW/E5h7sAWrnkYRlJB2yB9ucOs/u1TohG7wD9zbGu5lCN40xflAB1BJ+r5y1j5uSQNgQrNheA==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-kimbie": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-kimbie/-/codemirror-theme-kimbie-4.22.2.tgz",
"integrity": "sha512-dzXmOlgM8Ho6M4fzs95Zbttj9+VEFZtbS5nF98aoFqaqNeQU1iEk3EAtuNz9KRu9ogPvAeUsHqc76jnxB2J63g==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-material": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-material/-/codemirror-theme-material-4.22.2.tgz",
"integrity": "sha512-YJPNkNZo1RqtGgF3KNxFkSY7LDY8RqzJ7N5DR6o0d0IHCQSeTNk3TE14Gmc66GqfoPXfEQVLtPk/MiRZ/JQSpQ==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-monokai": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-monokai/-/codemirror-theme-monokai-4.22.2.tgz",
"integrity": "sha512-lkpG9W2eDBylcqqD7w8zDXib9ydZOCnGOKRsZh651F4SN1iK4Za1hGrzx+g9PD+rpBBrGZ3UAomoAb2BZRV0og==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-monokai-dimmed": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-monokai-dimmed/-/codemirror-theme-monokai-dimmed-4.22.2.tgz",
"integrity": "sha512-q/jopOSfttwh/uc04XMLXKKs2EsqRpbL6krBLQhQKahDXRR54G1UcLQNG+KglBLWSuCc5dYlQwcTjzwAFQJWgA==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-noctis-lilac": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-noctis-lilac/-/codemirror-theme-noctis-lilac-4.22.2.tgz",
"integrity": "sha512-nU+XC5NwbwZ+8X29OSMRV0e8eb1YCiwGkzuXN/cEnEnIcW5udNPydHalOwObOaDphC3dO+x8WSIf7LLycjpUsw==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-nord": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-nord/-/codemirror-theme-nord-4.22.2.tgz",
"integrity": "sha512-aqvS4WqFT7NWmWZ42m75SOfOGQE0n1+Hryb4ZxGpBweo9FQjxq7sswUACZMqH0gY8+JTUSCoRu7fFq0QqCykCQ==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-okaidia": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-okaidia/-/codemirror-theme-okaidia-4.22.2.tgz",
"integrity": "sha512-X8QR4FB/GihU9Q/YTtpFGICLGQ7fY+zGJsmt7WAdvomBcfNe4grvj08MU+KoJ967iB1LvluH8cFVDRdu4Nh8sw==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-quietlight": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-quietlight/-/codemirror-theme-quietlight-4.22.2.tgz",
"integrity": "sha512-NtHUttXgnDE8F+lrJA6HfiTNjY8GVJjUwtpvOQywqgNoBDwBpuEfdpvZ/OI0EW0sqZpbVlWfmANT4H5oj3ODaw==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-red": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-red/-/codemirror-theme-red-4.22.2.tgz",
"integrity": "sha512-q+AZv6i9FrAXEFipcGsc754QPhWlLFIW/FEjcKz9fHXtmeSHR1e1ZONG/xzFcjRBor0rQizrZ/Buk3cFaCCNUQ==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-solarized": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-solarized/-/codemirror-theme-solarized-4.22.2.tgz",
"integrity": "sha512-WSjLMzgXXUFxdDUmAyOyX1NmxdTdcLtQXZzZkgkJAm1e/F8OKlbPAkDcrNsTrMV4SGpcM2iwJiauNIxG48evJA==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-sublime": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-sublime/-/codemirror-theme-sublime-4.22.2.tgz",
"integrity": "sha512-9qcRyflyhc4J9veTVAY3JfXXgs3QOPvIIGm3YrZTwcx8s0OTD+iLUSUY/UMwyqEGCIHhyrNr6dr8qisdIL0Zvg==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-tokyo-night": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-tokyo-night/-/codemirror-theme-tokyo-night-4.22.2.tgz",
"integrity": "sha512-e6Z2r+TJsI+HY4vbozSOGkyoRQpcKb2BPY/7EHv+BRMzvUC62b8ZDbgYfctQBrEw50+vBRcs91slfMCWQ19AbQ==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-tokyo-night-day": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-tokyo-night-day/-/codemirror-theme-tokyo-night-day-4.22.2.tgz",
"integrity": "sha512-bP48DXp+tmFhGoRVyBtntkZl2a7tNacHwE2fDgj/Le97pHfzxS/DQ3iHqcUq49I0ZefQnswCZEASfupApqucvQ==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-tokyo-night-storm": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-tokyo-night-storm/-/codemirror-theme-tokyo-night-storm-4.22.2.tgz",
"integrity": "sha512-gnBxYrf0hVvE57yPaFX7esZsyRoDYWD3NBc6+YngBjGAq8CZ/JZphs13UBY3RNZq0ka9jJTkVanQOp+qb8cF1w==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-tomorrow-night-blue": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-tomorrow-night-blue/-/codemirror-theme-tomorrow-night-blue-4.22.2.tgz",
"integrity": "sha512-VFcqGpeXKS2laV4er9DiqnpOthmfMYQ85Jfmf44qe8T9iqFdE0YVyUNnKqH6vXrzaF17/PJgAxZFuohxAz381w==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-vscode": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-vscode/-/codemirror-theme-vscode-4.22.2.tgz",
"integrity": "sha512-wy+rd27Pz1f979QT8wHlBu7+XuwduSGDjqE972JRVf+Wqn2MbhXfTqD5YDx0lQJ+u05Y2IJKKbgPrWS+wt1a6A==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-white": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-white/-/codemirror-theme-white-4.22.2.tgz",
"integrity": "sha512-jFilUJLyNZkvMld+UFc/j27CHcMxxENiYHYrovBIO8lWN0qssvemvPNr/fKMFWuTHXwx6uGGUKP2MunxkrN0Fw==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-theme-xcode": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-xcode/-/codemirror-theme-xcode-4.22.2.tgz",
"integrity": "sha512-pHOeWHMK7lmxGjrwBnvaIsFppMaKjDbZ6fgWCItWUFI+JeO4/Orrn+q0r1tRbsbRAMPaVuxl+SUZUhPGuo8GCw==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@uiw/codemirror-themes": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.22.2.tgz",
"integrity": "sha512-gsLHn6SUuV5iboBvGrM7YimzLFHQmsNlkGIYs3UaVUJTo/A/ZrKoSJNyPziShLRjBXA2UwKdBTIU6VhHyyaChw==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
},
"peerDependencies": {
"@codemirror/language": ">=6.0.0",
"@codemirror/state": ">=6.0.0",
"@codemirror/view": ">=6.0.0"
}
},
"node_modules/@uiw/codemirror-themes-all": {
"version": "4.22.2",
"resolved": "https://registry.npmjs.org/@uiw/codemirror-themes-all/-/codemirror-themes-all-4.22.2.tgz",
"integrity": "sha512-/57s1Pt8BUS+nBrFm8F4R1vpSs8FWhENL2PkfvO8opedTtFUzsIdV88jZHG8PDl7ydJta0ijO+w3qr4Z0IJ5ZQ==",
"license": "MIT",
"dependencies": {
"@uiw/codemirror-theme-abcdef": "4.22.2",
"@uiw/codemirror-theme-abyss": "4.22.2",
"@uiw/codemirror-theme-androidstudio": "4.22.2",
"@uiw/codemirror-theme-andromeda": "4.22.2",
"@uiw/codemirror-theme-atomone": "4.22.2",
"@uiw/codemirror-theme-aura": "4.22.2",
"@uiw/codemirror-theme-basic": "4.22.2",
"@uiw/codemirror-theme-bbedit": "4.22.2",
"@uiw/codemirror-theme-bespin": "4.22.2",
"@uiw/codemirror-theme-console": "4.22.2",
"@uiw/codemirror-theme-copilot": "4.22.2",
"@uiw/codemirror-theme-darcula": "4.22.2",
"@uiw/codemirror-theme-dracula": "4.22.2",
"@uiw/codemirror-theme-duotone": "4.22.2",
"@uiw/codemirror-theme-eclipse": "4.22.2",
"@uiw/codemirror-theme-github": "4.22.2",
"@uiw/codemirror-theme-gruvbox-dark": "4.22.2",
"@uiw/codemirror-theme-kimbie": "4.22.2",
"@uiw/codemirror-theme-material": "4.22.2",
"@uiw/codemirror-theme-monokai": "4.22.2",
"@uiw/codemirror-theme-monokai-dimmed": "4.22.2",
"@uiw/codemirror-theme-noctis-lilac": "4.22.2",
"@uiw/codemirror-theme-nord": "4.22.2",
"@uiw/codemirror-theme-okaidia": "4.22.2",
"@uiw/codemirror-theme-quietlight": "4.22.2",
"@uiw/codemirror-theme-red": "4.22.2",
"@uiw/codemirror-theme-solarized": "4.22.2",
"@uiw/codemirror-theme-sublime": "4.22.2",
"@uiw/codemirror-theme-tokyo-night": "4.22.2",
"@uiw/codemirror-theme-tokyo-night-day": "4.22.2",
"@uiw/codemirror-theme-tokyo-night-storm": "4.22.2",
"@uiw/codemirror-theme-tomorrow-night-blue": "4.22.2",
"@uiw/codemirror-theme-vscode": "4.22.2",
"@uiw/codemirror-theme-white": "4.22.2",
"@uiw/codemirror-theme-xcode": "4.22.2",
"@uiw/codemirror-themes": "4.22.2"
},
"funding": {
"url": "https://jaywcjlove.github.io/#/sponsor"
}
},
"node_modules/@ungap/structured-clone": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
@ -3565,6 +4185,21 @@
"node": ">=6"
}
},
"node_modules/codemirror": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
"license": "MIT",
"dependencies": {
"@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"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -3629,6 +4264,12 @@
"toggle-selection": "^1.0.6"
}
},
"node_modules/crelt": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
"license": "MIT"
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -7200,6 +7841,12 @@
"integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==",
"dev": true
},
"node_modules/style-mod": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz",
"integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==",
"license": "MIT"
},
"node_modules/sucrase": {
"version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
@ -8008,6 +8655,12 @@
"resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz",
"integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw=="
},
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
"license": "MIT"
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",

View File

@ -14,6 +14,9 @@
"coverage": "vitest run --coverage"
},
"dependencies": {
"@codemirror/lang-yaml": "^6.1.1",
"@codemirror/state": "^6.4.1",
"@codemirror/view": "^6.28.1",
"@cycjimmy/jsmpeg-player": "^6.0.5",
"@hookform/resolvers": "^3.4.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
@ -35,10 +38,12 @@
"@radix-ui/react-toggle": "^1.0.3",
"@radix-ui/react-toggle-group": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@uiw/codemirror-themes-all": "^4.22.2",
"apexcharts": "^3.49.1",
"axios": "^1.7.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"codemirror": "^6.0.1",
"copy-to-clipboard": "^3.3.3",
"date-fns": "^3.6.0",
"hls.js": "^1.5.8",

View File

@ -0,0 +1,21 @@
import { isDesktop } from "react-device-detect";
import { MonacoEditor } from "./MonacoEditor";
import { CodeMirrorEditor } from "./CodeMirrorEditor";
/**
* Provides an interface for a Code Editor, to enable easy swapping between different implementations
*/
export type CodeEditorProps = {
initialContent: string;
yamlSchemaUrl: string;
onDidChangeContent: (content: string) => void;
};
/** Used to swap between the Monaco editor and the CodeMirror editor depending on device type.
* The Monaco editor doesn't work well on mobile devices, see Github issue: https://github.com/microsoft/monaco-editor/issues/246
*
* A common solution is to switch to CodeMirror on mobile devices as they have invested significantly in touch support.
*
* It would be great to revisit this in in the future if/when Monaco improves mobile support.
*/
export const CodeEditor: (props: CodeEditorProps) => JSX.Element = isDesktop ? MonacoEditor : CodeMirrorEditor;

View File

@ -0,0 +1,109 @@
import { EditorState } from "@codemirror/state";
import { yaml } from "@codemirror/lang-yaml";
import {
EditorView,
crosshairCursor,
drawSelection,
dropCursor,
highlightActiveLine,
highlightActiveLineGutter,
highlightSpecialChars,
keymap,
lineNumbers,
rectangularSelection,
} from "@codemirror/view";
import { useEffect, useRef } from "react";
import type { CodeEditorProps } from "./CodeEditor";
import {
indentOnInput,
bracketMatching,
foldGutter,
foldKeymap,
} from "@codemirror/language";
import {
defaultKeymap,
history,
historyKeymap,
indentWithTab,
} from "@codemirror/commands";
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
import {
autocompletion,
completionKeymap,
closeBrackets,
closeBracketsKeymap,
} from "@codemirror/autocomplete";
import { lintKeymap } from "@codemirror/lint";
import { vscodeLight, githubDark } from "@uiw/codemirror-themes-all";
import { useTheme } from "@/context/theme-provider";
/**
* Uses `CodeMirror` as a code editor implementation.
* Currently doesn't support yaml schema validation or linting.
*/
export function CodeMirrorEditor({
initialContent,
onDidChangeContent,
}: Omit<CodeEditorProps, "schemaUrl">) {
const containerRef = useRef<HTMLDivElement | null>(null);
const { theme, systemTheme } = useTheme();
useEffect(() => {
if (containerRef.current == null) {
return;
}
const onChangeListener = EditorView.updateListener.of((update) => {
if (update.docChanged) {
onDidChangeContent(update.state.doc.toString());
}
});
const state = EditorState.create({
doc: initialContent,
extensions: [
// vscodeDark isn't highlighting colors properly in yaml
(systemTheme || theme) == "dark" ? githubDark : vscodeLight,
lineNumbers(),
highlightActiveLineGutter(),
highlightSpecialChars(),
history(),
foldGutter(),
drawSelection(),
dropCursor(),
EditorState.allowMultipleSelections.of(true),
indentOnInput(),
bracketMatching(),
closeBrackets(),
autocompletion(),
rectangularSelection(),
crosshairCursor(),
highlightActiveLine(),
highlightSelectionMatches(),
keymap.of([
...closeBracketsKeymap,
...defaultKeymap,
...searchKeymap,
...historyKeymap,
...foldKeymap,
...completionKeymap,
...lintKeymap,
indentWithTab,
]),
yaml(),
onChangeListener,
],
});
const view = new EditorView({
state,
parent: containerRef.current,
});
return () => {
view.destroy();
};
}, [initialContent, onDidChangeContent, systemTheme, theme]);
return <div ref={containerRef} className="h-full w-full overflow-y-scroll" />;
}

View File

@ -0,0 +1,80 @@
import { useTheme } from "@/context/theme-provider";
import * as monaco from "monaco-editor";
import { configureMonacoYaml } from "monaco-yaml";
import { useEffect, useRef, useState } from "react";
import type { CodeEditorProps } from "./CodeEditor";
export function MonacoEditor({
initialContent,
yamlSchemaUrl,
onDidChangeContent,
}: CodeEditorProps) {
const [editor, setEditor] =
useState<monaco.editor.IStandaloneCodeEditor | null>(null);
const [model, setModel] = useState<monaco.editor.ITextModel | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null);
const { theme, systemTheme } = useTheme();
useEffect(() => {
const disposeOnDidChangeContent = model?.onDidChangeContent(() => {
onDidChangeContent(model.getValue());
}).dispose;
return () => {
disposeOnDidChangeContent?.();
};
}, [model, onDidChangeContent]);
useEffect(() => {
if (!initialContent) {
return;
}
if (model != null && editor != null) {
// we don't need to recreate the editor if it already exists
editor.layout();
return;
}
const modelUri = monaco.Uri.parse("a://b/api/config/schema.json");
let tempModel: monaco.editor.ITextModel;
if (monaco.editor.getModels().length > 0) {
tempModel = monaco.editor.getModel(modelUri)!;
} else {
tempModel = monaco.editor.createModel(initialContent, "yaml", modelUri);
}
setModel(tempModel);
configureMonacoYaml(monaco, {
enableSchemaRequest: true,
hover: true,
completion: true,
validate: true,
format: true,
schemas: [
{
uri: yamlSchemaUrl,
fileMatch: [String(modelUri)],
},
],
});
if (containerRef.current != null) {
setEditor(
monaco.editor.create(containerRef.current, {
language: "yaml",
model: tempModel,
scrollBeyondLastLine: false,
theme: (systemTheme || theme) == "dark" ? "vs-dark" : "vs-light",
}),
);
}
return () => {
containerRef.current = null;
};
}, [initialContent, model, editor, yamlSchemaUrl, systemTheme, theme]);
return <div ref={containerRef} className="h-full w-full" />;
}

View File

@ -1,18 +1,16 @@
import useSWR from "swr";
import * as monaco from "monaco-editor";
import { configureMonacoYaml } from "monaco-yaml";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { useApiHost } from "@/api";
import Heading from "@/components/ui/heading";
import ActivityIndicator from "@/components/indicators/activity-indicator";
import { Button } from "@/components/ui/button";
import axios from "axios";
import copy from "copy-to-clipboard";
import { useTheme } from "@/context/theme-provider";
import { Toaster } from "@/components/ui/sonner";
import { toast } from "sonner";
import { LuCopy, LuSave } from "react-icons/lu";
import { MdOutlineRestartAlt } from "react-icons/md";
import { CodeEditor } from "@/components/code_editor/CodeEditor";
type SaveOptions = "saveonly" | "restart";
@ -25,27 +23,17 @@ function ConfigEditor() {
const { data: config } = useSWR<string>("config/raw");
const { theme, systemTheme } = useTheme();
const [error, setError] = useState<string | undefined>();
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null);
const modelRef = useRef<monaco.editor.ITextModel | null>(null);
const configRef = useRef<HTMLDivElement | null>(null);
/** Contains the config once touched by the user */
const [dirtyConfig, setDirtyConfig] = useState<string | undefined>();
const onHandleSaveConfig = useCallback(
async (save_option: SaveOptions) => {
if (!editorRef.current) {
return;
}
axios
.post(
`config/save?save_option=${save_option}`,
editorRef.current.getValue(),
{
headers: { "Content-Type": "text/plain" },
},
)
.post(`config/save?save_option=${save_option}`, dirtyConfig, {
headers: { "Content-Type": "text/plain" },
})
.then((response) => {
if (response.status === 200) {
setError("");
@ -62,73 +50,16 @@ function ConfigEditor() {
}
});
},
[editorRef],
[dirtyConfig],
);
const handleCopyConfig = useCallback(async () => {
if (!editorRef.current) {
if (!dirtyConfig) {
return;
}
copy(editorRef.current.getValue());
copy(dirtyConfig);
toast.success("Config copied to clipboard.", { position: "top-center" });
}, [editorRef]);
useEffect(() => {
if (!config) {
return;
}
if (modelRef.current != null) {
// we don't need to recreate the editor if it already exists
editorRef.current?.layout();
return;
}
const modelUri = monaco.Uri.parse("a://b/api/config/schema.json");
if (monaco.editor.getModels().length > 0) {
modelRef.current = monaco.editor.getModel(modelUri);
} else {
modelRef.current = monaco.editor.createModel(config, "yaml", modelUri);
}
configureMonacoYaml(monaco, {
enableSchemaRequest: true,
hover: true,
completion: true,
validate: true,
format: true,
schemas: [
{
uri: `${apiHost}api/config/schema.json`,
fileMatch: [String(modelUri)],
},
],
});
const container = configRef.current;
if (container != null) {
editorRef.current = monaco.editor.create(container, {
language: "yaml",
model: modelRef.current,
scrollBeyondLastLine: false,
theme: (systemTheme || theme) == "dark" ? "vs-dark" : "vs-light",
});
}
return () => {
configRef.current = null;
modelRef.current = null;
};
});
useEffect(() => {
if (config && modelRef.current) {
modelRef.current.setValue(config);
}
}, [config]);
}, [dirtyConfig]);
if (!config) {
return <ActivityIndicator />;
@ -178,7 +109,13 @@ function ConfigEditor() {
</div>
)}
<div ref={configRef} className="mt-2 h-[calc(100%-2.75rem)]" />
<div className="mt-2 h-[calc(100%-2.75rem)]">
<CodeEditor
initialContent={config}
onDidChangeContent={setDirtyConfig}
yamlSchemaUrl={`${apiHost}api/config/schema.json`}
/>
</div>
</div>
<Toaster closeButton={true} />
</div>