diff --git a/ui/package.json b/ui/package.json index 7a0a906..b520c0f 100644 --- a/ui/package.json +++ b/ui/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@monaco-editor/react": "^4.7.0", "@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-popover": "^1.1.14", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index b67ed90..2ef40c1 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@monaco-editor/react': + specifier: ^4.7.0 + version: 4.7.0(monaco-editor@0.52.2)(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.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -461,6 +464,16 @@ packages: '@jridgewell/trace-mapping@0.3.29': resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + '@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 + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -1499,6 +1512,9 @@ packages: engines: {node: '>=10'} hasBin: true + monaco-editor@0.52.2: + resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1689,6 +1705,9 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + state-local@1.0.7: + resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -2153,6 +2172,17 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.4 + '@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) + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -3135,6 +3165,8 @@ snapshots: mkdirp@3.0.1: {} + monaco-editor@0.52.2: {} + ms@2.1.3: {} nanoid@3.3.11: {} @@ -3300,6 +3332,8 @@ snapshots: source-map-js@1.2.1: {} + state-local@1.0.7: {} + strip-json-comments@3.1.1: {} supports-color@7.2.0: diff --git a/ui/src/App.tsx b/ui/src/App.tsx index ee3533c..71ce57e 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -5,10 +5,11 @@ import { SettingsDialog } from "@/components/SettingsDialog"; import { Transformers } from "@/components/Transformers"; import { Providers } from "@/components/Providers"; import { Router } from "@/components/Router"; +import { JsonEditor } from "@/components/JsonEditor"; import { Button } from "@/components/ui/button"; import { useConfig } from "@/components/ConfigProvider"; import { api } from "@/lib/api"; -import { Settings, Languages, Save, RefreshCw } from "lucide-react"; +import { Settings, Languages, Save, RefreshCw, FileJson } from "lucide-react"; import { Popover, PopoverContent, @@ -22,6 +23,7 @@ function App() { const navigate = useNavigate(); const { config, error } = useConfig(); const [isSettingsOpen, setIsSettingsOpen] = useState(false); + const [isJsonEditorOpen, setIsJsonEditorOpen] = useState(false); const [isCheckingAuth, setIsCheckingAuth] = useState(true); const [toast, setToast] = useState<{ message: string; type: 'success' | 'error' | 'warning' } | null>(null); @@ -164,6 +166,9 @@ function App() { + + + + + + +
+ setJsonValue(value || '')} + theme="vs" + options={{ + minimap: { enabled: true }, + fontSize: 14, + scrollBeyondLastLine: false, + automaticLayout: true, + wordWrap: 'on', + formatOnPaste: true, + formatOnType: true, + suggest: { + showKeywords: true, + showSnippets: true, + }, + }} + /> +
+ + + ); +} \ No newline at end of file diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json index cc25c27..9f1528a 100644 --- a/ui/src/locales/en.json +++ b/ui/src/locales/en.json @@ -87,5 +87,13 @@ "selectModel": "Select a model...", "searchModel": "Search model...", "noModelFound": "No model found." + }, + "json_editor": { + "title": "JSON Editor", + "save": "Save", + "saving": "Saving...", + "cancel": "Cancel", + "save_failed": "Failed to save config", + "save_and_restart": "Save & Restart" } } diff --git a/ui/src/locales/zh.json b/ui/src/locales/zh.json index 95ecd21..4e1dbe9 100644 --- a/ui/src/locales/zh.json +++ b/ui/src/locales/zh.json @@ -87,5 +87,13 @@ "selectModel": "选择一个模型...", "searchModel": "搜索模型...", "noModelFound": "未找到模型." + }, + "json_editor": { + "title": "JSON 编辑器", + "save": "保存", + "saving": "保存中...", + "cancel": "取消", + "save_failed": "配置保存失败", + "save_and_restart": "保存并重启" } } \ No newline at end of file diff --git a/ui/tsconfig.tsbuildinfo b/ui/tsconfig.tsbuildinfo index 290871d..a64ee0a 100644 --- a/ui/tsconfig.tsbuildinfo +++ b/ui/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/vite-env.d.ts","./src/components/configprovider.tsx","./src/components/login.tsx","./src/components/providerlist.tsx","./src/components/providers.tsx","./src/components/router.tsx","./src/components/settingsdialog.tsx","./src/components/transformerlist.tsx","./src/components/transformers.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/combo-input.tsx","./src/components/ui/combobox.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/multi-combobox.tsx","./src/components/ui/popover.tsx","./src/components/ui/switch.tsx","./src/components/ui/toast.tsx","./src/lib/api.ts","./src/lib/utils.ts"],"version":"5.8.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/vite-env.d.ts","./src/components/configprovider.tsx","./src/components/jsoneditor.tsx","./src/components/login.tsx","./src/components/providerlist.tsx","./src/components/providers.tsx","./src/components/router.tsx","./src/components/settingsdialog.tsx","./src/components/transformerlist.tsx","./src/components/transformers.tsx","./src/components/ui/badge.tsx","./src/components/ui/button.tsx","./src/components/ui/card.tsx","./src/components/ui/combo-input.tsx","./src/components/ui/combobox.tsx","./src/components/ui/command.tsx","./src/components/ui/dialog.tsx","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/multi-combobox.tsx","./src/components/ui/popover.tsx","./src/components/ui/switch.tsx","./src/components/ui/toast.tsx","./src/lib/api.ts","./src/lib/utils.ts"],"version":"5.8.3"} \ No newline at end of file