Compare commits

..

5 Commits

16 changed files with 499 additions and 211 deletions

View File

@ -1122,9 +1122,9 @@ func (b *browserService) SetKeyValue(param types.SetKeyParam) (resp types.JSResp
return
}
resp.Success = true
resp.Data = map[string]any{
"value": param.Value,
}
//resp.Data = map[string]any{
// "value": param.Value,
//}
return
}

View File

@ -12,9 +12,10 @@
"dayjs": "^1.11.10",
"highlight.js": "^11.9.0",
"lodash": "^4.17.21",
"monaco-editor": "^0.44.0",
"pinia": "^2.1.7",
"sass": "^1.69.5",
"vue": "^3.3.8",
"vue": "^3.3.9",
"vue-i18n": "^9.7.1",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"
@ -46,9 +47,9 @@
"dev": true
},
"node_modules/@babel/parser": {
"version": "7.23.0",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"version": "7.23.4",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.4.tgz",
"integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==",
"bin": {
"parser": "bin/babel-parser.js"
},
@ -761,36 +762,36 @@
}
},
"node_modules/@vue/compiler-core": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
"integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.9.tgz",
"integrity": "sha512-+/Lf68Vr/nFBA6ol4xOtJrW+BQWv3QWKfRwGSm70jtXwfhZNF4R/eRgyVJYoxFRhdCTk/F6g99BP0ffPgZihfQ==",
"dependencies": {
"@babel/parser": "^7.23.0",
"@vue/shared": "3.3.8",
"@babel/parser": "^7.23.3",
"@vue/shared": "3.3.9",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
}
},
"node_modules/@vue/compiler-dom": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
"integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.9.tgz",
"integrity": "sha512-nfWubTtLXuT4iBeDSZ5J3m218MjOy42Vp2pmKVuBKo2/BLcrFUX8nCSr/bKRFiJ32R8qbdnnnBgRn9AdU5v0Sg==",
"dependencies": {
"@vue/compiler-core": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/compiler-core": "3.3.9",
"@vue/shared": "3.3.9"
}
},
"node_modules/@vue/compiler-sfc": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
"integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.9.tgz",
"integrity": "sha512-wy0CNc8z4ihoDzjASCOCsQuzW0A/HP27+0MDSSICMjVIFzk/rFViezkR3dzH+miS2NDEz8ywMdbjO5ylhOLI2A==",
"dependencies": {
"@babel/parser": "^7.23.0",
"@vue/compiler-core": "3.3.8",
"@vue/compiler-dom": "3.3.8",
"@vue/compiler-ssr": "3.3.8",
"@vue/reactivity-transform": "3.3.8",
"@vue/shared": "3.3.8",
"@babel/parser": "^7.23.3",
"@vue/compiler-core": "3.3.9",
"@vue/compiler-dom": "3.3.9",
"@vue/compiler-ssr": "3.3.9",
"@vue/reactivity-transform": "3.3.9",
"@vue/shared": "3.3.9",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"postcss": "^8.4.31",
@ -798,12 +799,12 @@
}
},
"node_modules/@vue/compiler-ssr": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
"integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.9.tgz",
"integrity": "sha512-NO5oobAw78R0G4SODY5A502MGnDNiDjf6qvhn7zD7TJGc8XDeIEw4fg6JU705jZ/YhuokBKz0A5a/FL/XZU73g==",
"dependencies": {
"@vue/compiler-dom": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/compiler-dom": "3.3.9",
"@vue/shared": "3.3.9"
}
},
"node_modules/@vue/devtools-api": {
@ -812,41 +813,41 @@
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
},
"node_modules/@vue/reactivity": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.8.tgz",
"integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.9.tgz",
"integrity": "sha512-VmpIqlNp+aYDg2X0xQhJqHx9YguOmz2UxuUJDckBdQCNkipJvfk9yA75woLWElCa0Jtyec3lAAt49GO0izsphw==",
"dependencies": {
"@vue/shared": "3.3.8"
"@vue/shared": "3.3.9"
}
},
"node_modules/@vue/reactivity-transform": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
"integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.9.tgz",
"integrity": "sha512-HnUFm7Ry6dFa4Lp63DAxTixUp8opMtQr6RxQCpDI1vlh12rkGIeYqMvJtK+IKyEfEOa2I9oCkD1mmsPdaGpdVg==",
"dependencies": {
"@babel/parser": "^7.23.0",
"@vue/compiler-core": "3.3.8",
"@vue/shared": "3.3.8",
"@babel/parser": "^7.23.3",
"@vue/compiler-core": "3.3.9",
"@vue/shared": "3.3.9",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5"
}
},
"node_modules/@vue/runtime-core": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
"integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.9.tgz",
"integrity": "sha512-xxaG9KvPm3GTRuM4ZyU8Tc+pMVzcu6eeoSRQJ9IE7NmCcClW6z4B3Ij6L4EDl80sxe/arTtQ6YmgiO4UZqRc+w==",
"dependencies": {
"@vue/reactivity": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/reactivity": "3.3.9",
"@vue/shared": "3.3.9"
}
},
"node_modules/@vue/runtime-dom": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
"integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.9.tgz",
"integrity": "sha512-e7LIfcxYSWbV6BK1wQv9qJyxprC75EvSqF/kQKe6bdZEDNValzeRXEVgiX7AHI6hZ59HA4h7WT5CGvm69vzJTQ==",
"dependencies": {
"@vue/runtime-core": "3.3.8",
"@vue/shared": "3.3.8",
"@vue/runtime-core": "3.3.9",
"@vue/shared": "3.3.9",
"csstype": "^3.1.2"
}
},
@ -856,21 +857,21 @@
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ=="
},
"node_modules/@vue/server-renderer": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
"integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.9.tgz",
"integrity": "sha512-w0zT/s5l3Oa3ZjtLW88eO4uV6AQFqU8X5GOgzq7SkQQu6vVr+8tfm+OI2kDBplS/W/XgCBuFXiPw6T5EdwXP0A==",
"dependencies": {
"@vue/compiler-ssr": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/compiler-ssr": "3.3.9",
"@vue/shared": "3.3.9"
},
"peerDependencies": {
"vue": "3.3.8"
"vue": "3.3.9"
}
},
"node_modules/@vue/shared": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.8.tgz",
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.9.tgz",
"integrity": "sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA=="
},
"node_modules/acorn": {
"version": "8.10.0",
@ -1414,6 +1415,11 @@
"ufo": "^1.3.0"
}
},
"node_modules/monaco-editor": {
"version": "0.44.0",
"resolved": "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.44.0.tgz",
"integrity": "sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q=="
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
@ -2074,15 +2080,15 @@
}
},
"node_modules/vue": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz",
"integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.9.tgz",
"integrity": "sha512-sy5sLCTR8m6tvUk1/ijri3Yqzgpdsmxgj6n6yl7GXXCXqVbmW2RCXe9atE4cEI6Iv7L89v5f35fZRRr5dChP9w==",
"dependencies": {
"@vue/compiler-dom": "3.3.8",
"@vue/compiler-sfc": "3.3.8",
"@vue/runtime-dom": "3.3.8",
"@vue/server-renderer": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/compiler-dom": "3.3.9",
"@vue/compiler-sfc": "3.3.9",
"@vue/runtime-dom": "3.3.9",
"@vue/server-renderer": "3.3.9",
"@vue/shared": "3.3.9"
},
"peerDependencies": {
"typescript": "*"
@ -2198,9 +2204,9 @@
"dev": true
},
"@babel/parser": {
"version": "7.23.0",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw=="
"version": "7.23.4",
"resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.23.4.tgz",
"integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ=="
},
"@babel/runtime": {
"version": "7.23.1",
@ -2595,36 +2601,36 @@
"requires": {}
},
"@vue/compiler-core": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.8.tgz",
"integrity": "sha512-hN/NNBUECw8SusQvDSqqcVv6gWq8L6iAktUR0UF3vGu2OhzRqcOiAno0FmBJWwxhYEXRlQJT5XnoKsVq1WZx4g==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.3.9.tgz",
"integrity": "sha512-+/Lf68Vr/nFBA6ol4xOtJrW+BQWv3QWKfRwGSm70jtXwfhZNF4R/eRgyVJYoxFRhdCTk/F6g99BP0ffPgZihfQ==",
"requires": {
"@babel/parser": "^7.23.0",
"@vue/shared": "3.3.8",
"@babel/parser": "^7.23.3",
"@vue/shared": "3.3.9",
"estree-walker": "^2.0.2",
"source-map-js": "^1.0.2"
}
},
"@vue/compiler-dom": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.8.tgz",
"integrity": "sha512-+PPtv+p/nWDd0AvJu3w8HS0RIm/C6VGBIRe24b9hSyNWOAPEUosFZ5diwawwP8ip5sJ8n0Pe87TNNNHnvjs0FQ==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.3.9.tgz",
"integrity": "sha512-nfWubTtLXuT4iBeDSZ5J3m218MjOy42Vp2pmKVuBKo2/BLcrFUX8nCSr/bKRFiJ32R8qbdnnnBgRn9AdU5v0Sg==",
"requires": {
"@vue/compiler-core": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/compiler-core": "3.3.9",
"@vue/shared": "3.3.9"
}
},
"@vue/compiler-sfc": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.8.tgz",
"integrity": "sha512-WMzbUrlTjfYF8joyT84HfwwXo+8WPALuPxhy+BZ6R4Aafls+jDBnSz8PDz60uFhuqFbl3HxRfxvDzrUf3THwpA==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.3.9.tgz",
"integrity": "sha512-wy0CNc8z4ihoDzjASCOCsQuzW0A/HP27+0MDSSICMjVIFzk/rFViezkR3dzH+miS2NDEz8ywMdbjO5ylhOLI2A==",
"requires": {
"@babel/parser": "^7.23.0",
"@vue/compiler-core": "3.3.8",
"@vue/compiler-dom": "3.3.8",
"@vue/compiler-ssr": "3.3.8",
"@vue/reactivity-transform": "3.3.8",
"@vue/shared": "3.3.8",
"@babel/parser": "^7.23.3",
"@vue/compiler-core": "3.3.9",
"@vue/compiler-dom": "3.3.9",
"@vue/compiler-ssr": "3.3.9",
"@vue/reactivity-transform": "3.3.9",
"@vue/shared": "3.3.9",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5",
"postcss": "^8.4.31",
@ -2632,12 +2638,12 @@
}
},
"@vue/compiler-ssr": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.8.tgz",
"integrity": "sha512-hXCqQL/15kMVDBuoBYpUnSYT8doDNwsjvm3jTefnXr+ytn294ySnT8NlsFHmTgKNjwpuFy7XVV8yTeLtNl/P6w==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.3.9.tgz",
"integrity": "sha512-NO5oobAw78R0G4SODY5A502MGnDNiDjf6qvhn7zD7TJGc8XDeIEw4fg6JU705jZ/YhuokBKz0A5a/FL/XZU73g==",
"requires": {
"@vue/compiler-dom": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/compiler-dom": "3.3.9",
"@vue/shared": "3.3.9"
}
},
"@vue/devtools-api": {
@ -2646,41 +2652,41 @@
"integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
},
"@vue/reactivity": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.8.tgz",
"integrity": "sha512-ctLWitmFBu6mtddPyOKpHg8+5ahouoTCRtmAHZAXmolDtuZXfjL2T3OJ6DL6ezBPQB1SmMnpzjiWjCiMYmpIuw==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.3.9.tgz",
"integrity": "sha512-VmpIqlNp+aYDg2X0xQhJqHx9YguOmz2UxuUJDckBdQCNkipJvfk9yA75woLWElCa0Jtyec3lAAt49GO0izsphw==",
"requires": {
"@vue/shared": "3.3.8"
"@vue/shared": "3.3.9"
}
},
"@vue/reactivity-transform": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.8.tgz",
"integrity": "sha512-49CvBzmZNtcHua0XJ7GdGifM8GOXoUMOX4dD40Y5DxI3R8OUhMlvf2nvgUAcPxaXiV5MQQ1Nwy09ADpnLQUqRw==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.3.9.tgz",
"integrity": "sha512-HnUFm7Ry6dFa4Lp63DAxTixUp8opMtQr6RxQCpDI1vlh12rkGIeYqMvJtK+IKyEfEOa2I9oCkD1mmsPdaGpdVg==",
"requires": {
"@babel/parser": "^7.23.0",
"@vue/compiler-core": "3.3.8",
"@vue/shared": "3.3.8",
"@babel/parser": "^7.23.3",
"@vue/compiler-core": "3.3.9",
"@vue/shared": "3.3.9",
"estree-walker": "^2.0.2",
"magic-string": "^0.30.5"
}
},
"@vue/runtime-core": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.8.tgz",
"integrity": "sha512-qurzOlb6q26KWQ/8IShHkMDOuJkQnQcTIp1sdP4I9MbCf9FJeGVRXJFr2mF+6bXh/3Zjr9TDgURXrsCr9bfjUw==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.3.9.tgz",
"integrity": "sha512-xxaG9KvPm3GTRuM4ZyU8Tc+pMVzcu6eeoSRQJ9IE7NmCcClW6z4B3Ij6L4EDl80sxe/arTtQ6YmgiO4UZqRc+w==",
"requires": {
"@vue/reactivity": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/reactivity": "3.3.9",
"@vue/shared": "3.3.9"
}
},
"@vue/runtime-dom": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.8.tgz",
"integrity": "sha512-Noy5yM5UIf9UeFoowBVgghyGGPIDPy1Qlqt0yVsUdAVbqI8eeMSsTqBtauaEoT2UFXUk5S64aWVNJN4MJ2vRdA==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.3.9.tgz",
"integrity": "sha512-e7LIfcxYSWbV6BK1wQv9qJyxprC75EvSqF/kQKe6bdZEDNValzeRXEVgiX7AHI6hZ59HA4h7WT5CGvm69vzJTQ==",
"requires": {
"@vue/runtime-core": "3.3.8",
"@vue/shared": "3.3.8",
"@vue/runtime-core": "3.3.9",
"@vue/shared": "3.3.9",
"csstype": "^3.1.2"
},
"dependencies": {
@ -2692,18 +2698,18 @@
}
},
"@vue/server-renderer": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.8.tgz",
"integrity": "sha512-zVCUw7RFskvPuNlPn/8xISbrf0zTWsTSdYTsUTN1ERGGZGVnRxM2QZ3x1OR32+vwkkCm0IW6HmJ49IsPm7ilLg==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.3.9.tgz",
"integrity": "sha512-w0zT/s5l3Oa3ZjtLW88eO4uV6AQFqU8X5GOgzq7SkQQu6vVr+8tfm+OI2kDBplS/W/XgCBuFXiPw6T5EdwXP0A==",
"requires": {
"@vue/compiler-ssr": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/compiler-ssr": "3.3.9",
"@vue/shared": "3.3.9"
}
},
"@vue/shared": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.8.tgz",
"integrity": "sha512-8PGwybFwM4x8pcfgqEQFy70NaQxASvOC5DJwLQfpArw1UDfUXrJkdxD3BhVTMS+0Lef/TU7YO0Jvr0jJY8T+mw=="
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.3.9.tgz",
"integrity": "sha512-ZE0VTIR0LmYgeyhurPTpy4KzKsuDyQbMSdM49eKkMnT5X4VfFBLysMzjIZhLEFQYjjOVVfbvUDHckwjDFiO2eA=="
},
"acorn": {
"version": "8.10.0",
@ -3128,6 +3134,11 @@
"ufo": "^1.3.0"
}
},
"monaco-editor": {
"version": "0.44.0",
"resolved": "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.44.0.tgz",
"integrity": "sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
@ -3575,15 +3586,15 @@
}
},
"vue": {
"version": "3.3.8",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.8.tgz",
"integrity": "sha512-5VSX/3DabBikOXMsxzlW8JyfeLKlG9mzqnWgLQLty88vdZL7ZJgrdgBOmrArwxiLtmS+lNNpPcBYqrhE6TQW5w==",
"version": "3.3.9",
"resolved": "https://registry.npmmirror.com/vue/-/vue-3.3.9.tgz",
"integrity": "sha512-sy5sLCTR8m6tvUk1/ijri3Yqzgpdsmxgj6n6yl7GXXCXqVbmW2RCXe9atE4cEI6Iv7L89v5f35fZRRr5dChP9w==",
"requires": {
"@vue/compiler-dom": "3.3.8",
"@vue/compiler-sfc": "3.3.8",
"@vue/runtime-dom": "3.3.8",
"@vue/server-renderer": "3.3.8",
"@vue/shared": "3.3.8"
"@vue/compiler-dom": "3.3.9",
"@vue/compiler-sfc": "3.3.9",
"@vue/runtime-dom": "3.3.9",
"@vue/server-renderer": "3.3.9",
"@vue/shared": "3.3.9"
}
},
"vue-i18n": {

View File

@ -13,9 +13,10 @@
"dayjs": "^1.11.10",
"highlight.js": "^11.9.0",
"lodash": "^4.17.21",
"monaco-editor": "^0.44.0",
"pinia": "^2.1.7",
"sass": "^1.69.5",
"vue": "^3.3.8",
"vue": "^3.3.9",
"vue-i18n": "^9.7.1",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"

View File

@ -1 +1 @@
13b88be029994e83241098ae7c3ee1c4
3dc6322e376d91c1cb4a6bd5312d176f

View File

@ -0,0 +1,159 @@
<script setup>
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import * as monaco from 'monaco-editor'
import usePreferencesStore from 'stores/preferences.js'
import { useThemeVars } from 'naive-ui'
const props = defineProps({
content: {
type: String,
},
language: {
type: String,
default: 'json',
},
readonly: {
type: String,
},
loading: {
type: Boolean,
},
showLineNum: {
type: Boolean,
default: true,
},
border: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['reset', 'input'])
const themeVars = useThemeVars()
/** @type {HTMLElement|null} */
const editorRef = ref(null)
/** @type monaco.editor.IStandaloneCodeEditor */
let editorNode = null
const destroyEditor = () => {
if (editorNode != null && editorNode.dispose != null) {
const model = editorNode.getModel()
if (model != null) {
model.dispose()
}
editorNode.dispose()
editorNode = null
}
}
const readonlyValue = computed(() => {
return props.readonly || props.loading
})
const pref = usePreferencesStore()
onMounted(async () => {
if (editorRef.value != null) {
const { fontSize, fontFamily = undefined } = pref.generalFont
editorNode = monaco.editor.create(editorRef.value, {
// value: props.content,
theme: pref.isDark ? 'rdm-dark' : 'rdm-light',
language: props.language,
lineNumbers: props.showLineNum ? 'on' : 'off',
readOnly: readonlyValue.value,
colorDecorators: true,
accessibilitySupport: 'off',
wordWrap: 'on',
tabSize: 2,
fontFamily,
fontSize,
scrollBeyondLastLine: false,
automaticLayout: true,
scrollbar: {
useShadows: false,
verticalScrollbarSize: '10px',
},
// formatOnType: true,
contextmenu: false,
lineNumbersMinChars: 2,
lineDecorationsWidth: 0,
minimap: {
enabled: false,
},
selectionHighlight: false,
renderLineHighlight: 'gutter',
})
// editorNode.onDidChangeModelLanguageConfiguration(() => {
// editorNode?.getAction('editor.action.formatDocument')?.run()
// })
if (editorNode.onDidChangeModelContent) {
editorNode.onDidChangeModelContent(() => {
emit('input', editorNode.getValue())
})
}
}
})
watch(
() => props.content,
async (content) => {
if (editorNode != null) {
editorNode.setValue(content)
await nextTick(() => emit('reset', content))
}
},
)
watch(
() => readonlyValue.value,
(readOnly) => {
if (editorNode != null) {
editorNode.updateOptions({
readOnly,
})
}
},
)
watch(
() => props.language,
(language) => {
if (editorNode != null) {
const model = editorNode.getModel()
if (model != null) {
monaco.editor.setModelLanguage(model, language)
}
}
},
)
watch(
() => pref.isDark,
(dark) => {
if (editorNode != null) {
editorNode.updateOptions({
theme: dark ? 'rdm-dark' : 'rdm-light',
})
}
},
)
onUnmounted(() => {
destroyEditor()
})
</script>
<template>
<div ref="editorRef" :class="{ 'editor-border': props.border === true }" />
</template>
<style lang="scss" scoped>
.editor-border {
border: 1px solid v-bind('themeVars.borderColor');
border-radius: v-bind('themeVars.borderRadius');
padding: 3px;
box-sizing: border-box;
}
</style>

View File

@ -11,6 +11,7 @@ import FullScreen from '@/components/icons/FullScreen.vue'
import WindowClose from '@/components/icons/WindowClose.vue'
import Pin from '@/components/icons/Pin.vue'
import OffScreen from '@/components/icons/OffScreen.vue'
import ContentEditor from '@/components/content_value/ContentEditor.vue'
const props = defineProps({
field: {
@ -79,6 +80,19 @@ const displayValue = computed(() => {
}
return viewAs.value
})
const editingContent = ref('')
const enableSave = computed(() => {
return editingContent.value !== viewAs.value
})
const viewLanguage = computed(() => {
switch (viewAs.format) {
case formatTypes.JSON:
return 'json'
default:
return 'plaintext'
}
})
const btnStyle = computed(() => ({
padding: '3px',
@ -112,7 +126,7 @@ const onFormatChanged = async (decode = '', format = '') => {
format,
})
viewAs.field = props.field + ''
viewAs.value = value
editingContent.value = viewAs.value = value
viewAs.decode = decode || retDecode
viewAs.format = format || retFormat
} finally {
@ -125,6 +139,10 @@ const onUpdateValue = (value) => {
viewAs.value = value
}
const onInput = (content) => {
editingContent.value = content
}
const onToggleFullscreen = () => {
emit('update:fullscreen', !!!props.fullscreen)
}
@ -135,7 +153,7 @@ const onClose = () => {
}
const onSave = () => {
emit('save', viewAs.field, viewAs.value, viewAs.decode, viewAs.format)
emit('save', viewAs.field, editingContent.value, viewAs.decode, viewAs.format)
if (!isPin.value) {
nextTick().then(onClose)
}
@ -160,14 +178,14 @@ const onSave = () => {
<!-- value -->
<div class="editor-content-item flex-box-v flex-item-expand">
<div class="editor-content-item-label">{{ props.valueLabel }}</div>
<n-input
:placeholder="props.value"
:resizable="false"
:value="displayValue"
autofocus
<content-editor
:border="true"
:content="displayValue"
:language="viewLanguage"
class="flex-item-expand"
type="textarea"
@update:value="onUpdateValue" />
style="height: 100%"
@input="onInput"
@reset="onInput" />
<format-selector
:decode="viewAs.decode"
:format="viewAs.format"
@ -202,7 +220,7 @@ const onSave = () => {
</template>
<template #action>
<n-space :wrap="false" :wrap-item="false" justify="end">
<n-button ghost @click="onSave">
<n-button :disabled="!enableSave" :secondary="enableSave" type="primary" @click="onSave">
<template #icon>
<n-icon :component="Save" />
</template>

View File

@ -162,6 +162,7 @@ const saveEdit = async (field, value, decode, format) => {
index: [currentEditRow.no - 1],
})
if (success) {
currentEditRow.value = value
$message.success(i18n.t('dialogue.save_value_succ'))
} else {
$message.error(msg)

View File

@ -6,15 +6,14 @@ import Copy from '@/components/icons/Copy.vue'
import Save from '@/components/icons/Save.vue'
import { useThemeVars } from 'naive-ui'
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
import Close from '@/components/icons/Close.vue'
import { types as redisTypes } from '@/consts/support_redis_type.js'
import { ClipboardSetText } from 'wailsjs/runtime/runtime.js'
import { isEmpty, toLower } from 'lodash'
import EditFile from '@/components/icons/EditFile.vue'
import bytes from 'bytes'
import useBrowserStore from 'stores/browser.js'
import { decodeRedisKey } from '@/utils/key_convert.js'
import FormatSelector from '@/components/content_value/FormatSelector.vue'
import ContentEditor from '@/components/content_value/ContentEditor.vue'
const i18n = useI18n()
const themeVars = useThemeVars()
@ -63,6 +62,11 @@ const viewAs = reactive({
decode: decodeTypes.NONE,
})
const editingContent = ref('')
const enableSave = computed(() => {
return editingContent.value !== viewAs.value && !props.loading
})
const displayValue = computed(() => {
if (props.loading) {
return ''
@ -89,7 +93,7 @@ const onFormatChanged = async (decode = '', format = '') => {
decode,
format,
})
viewAs.value = value
editingContent.value = viewAs.value = value
viewAs.decode = decode || retDecode
viewAs.format = format || retFormat
}
@ -109,23 +113,17 @@ const onCopyValue = () => {
})
}
const editValue = ref('')
const inEdit = ref(false)
const onEditValue = () => {
editValue.value = displayValue.value
inEdit.value = true
}
const onCancelEdit = () => {
inEdit.value = false
}
/**
* Save value
*/
const browserStore = useBrowserStore()
const saving = ref(false)
const onSaveValue = async () => {
const onInput = (content) => {
editingContent.value = content
}
const onSave = async () => {
saving.value = true
try {
const { success, msg } = await browserStore.setKey({
@ -133,13 +131,13 @@ const onSaveValue = async () => {
db: props.db,
key: keyName.value,
keyType: toLower(keyType),
value: editValue.value,
value: editingContent.value,
ttl: -1,
format: viewAs.format,
decode: viewAs.decode,
})
if (success) {
await browserStore.loadKeyDetail({ server: props.name, db: props.db, key: keyName.value })
// await browserStore.loadKeyDetail({ server: props.name, db: props.db, key: keyName.value })
$message.success(i18n.t('dialogue.save_value_succ'))
} else {
$message.error(msg)
@ -147,7 +145,6 @@ const onSaveValue = async () => {
} catch (e) {
$message.error(e.message)
} finally {
inEdit.value = false
saving.value = false
}
}
@ -155,7 +152,7 @@ const onSaveValue = async () => {
defineExpose({
reset: () => {
viewAs.value = ''
inEdit.value = false
editingContent.value = ''
},
beforeShow: () => onFormatChanged(),
})
@ -177,55 +174,46 @@ defineExpose({
@rename="emit('rename')" />
<div class="tb2 value-item-part flex-box-h">
<div class="flex-item-expand"></div>
<n-button-group v-if="!inEdit">
<n-button :focusable="false" @click="onCopyValue">
<n-button-group>
<n-button :disabled="saving" :focusable="false" @click="onCopyValue">
<template #icon>
<n-icon :component="Copy" size="18" />
</template>
{{ $t('interface.copy_value') }}
</n-button>
<n-button :focusable="false" plain @click="onEditValue">
<template #icon>
<n-icon :component="EditFile" size="18" />
</template>
{{ $t('interface.edit_value') }}
</n-button>
</n-button-group>
<n-button-group v-else>
<n-button :focusable="false" :loading="saving" plain @click="onSaveValue">
<n-button
:disabled="!enableSave"
:loading="saving"
:secondary="enableSave"
:type="enableSave ? 'primary' : ''"
@click="onSave">
<template #icon>
<n-icon :component="Save" size="18" />
</template>
{{ $t('interface.save_update') }}
</n-button>
<n-button :focusable="false" :loading="saving" plain @click="onCancelEdit">
<template #icon>
<n-icon :component="Close" size="18" />
</template>
{{ $t('common.cancel') }}
{{ $t('common.save') }}
</n-button>
</n-button-group>
</div>
<div class="value-wrapper value-item-part flex-item-expand flex-box-v">
<n-scrollbar v-if="!inEdit" class="flex-item-expand">
<n-code :code="displayValue" :language="viewLanguage" style="cursor: text" word-wrap />
</n-scrollbar>
<n-input
v-else
v-model:value="editValue"
:disabled="saving"
:resizable="false"
<n-spin :show="props.loading" />
<content-editor
v-show="!props.loading"
:content="displayValue"
:language="viewLanguage"
:loading="props.loading"
class="flex-item-expand"
type="textarea" />
style="height: 100%"
@input="onInput"
@reset="onInput" />
</div>
<div class="value-footer flex-box-h">
<n-text v-if="!isNaN(props.length)">{{ $t('interface.length') }}: {{ props.length }}</n-text>
<n-divider v-if="!isNaN(props.length)" vertical />
<n-text v-if="!isNaN(props.size)">{{ $t('interface.memory_usage') }}: {{ bytes(props.size) }}</n-text>
<div class="flex-item-expand"></div>
<div class="flex-item-expand" />
<format-selector
:decode="viewAs.decode"
:disabled="inEdit"
:disabled="enableSave"
:format="viewAs.format"
@format-changed="onFormatChanged" />
</div>
@ -234,7 +222,7 @@ defineExpose({
<style lang="scss" scoped>
.value-wrapper {
overflow: hidden;
//overflow: hidden;
border-top: v-bind('themeVars.borderColor') 1px solid;
padding: 5px;
}

View File

@ -12,6 +12,7 @@ import { computed, onMounted, ref, watch } from 'vue'
import { isEmpty } from 'lodash'
import useDialogStore from 'stores/dialog.js'
import { decodeTypes, formatTypes } from '@/consts/value_view_type.js'
import { useI18n } from 'vue-i18n'
const themeVars = useThemeVars()
const browserStore = useBrowserStore()
@ -24,6 +25,7 @@ const props = defineProps({
default: {},
},
})
const i18n = useI18n()
/**
*
@ -123,11 +125,11 @@ const onRename = () => {
}
const onDelete = () => {
$dialog.warning(i18n.t('dialogue.remove_tip', { name: props.keyPath }), () => {
$dialog.warning(i18n.t('dialogue.remove_tip', { name: data.value.keyPath }), () => {
const { name, db } = data.value
browserStore.deleteKey(name, db, keyName.value).then((success) => {
if (success) {
$message.success(i18n.t('dialogue.delete_key_succ', { key: props.keyPath }))
$message.success(i18n.t('dialogue.delete_key_succ', { key: data.value.keyPath }))
}
})
})

View File

@ -3,7 +3,7 @@ import { computed, reactive, ref, watch } from 'vue'
import useDialog from 'stores/dialog'
import { useI18n } from 'vue-i18n'
import useConnectionStore from 'stores/connections.js'
import { isEmpty } from 'lodash'
import { every, get, includes, isEmpty } from 'lodash'
/**
* Dialog for create or rename group
@ -13,6 +13,24 @@ const editGroup = ref('')
const groupForm = reactive({
name: '',
})
const groupFormRef = ref(null)
const formRules = computed(() => {
const requiredMsg = i18n.t('dialogue.field_required')
const illegalChars = ['/', '\\']
return {
name: [
{ required: true, message: requiredMsg, trigger: 'input' },
{
validator: (rule, value) => {
return every(illegalChars, (c) => !includes(value, c))
},
message: i18n.t('dialogue.illegal_characters'),
trigger: 'input',
},
],
}
})
const isRenameMode = computed(() => !isEmpty(editGroup.value))
@ -30,6 +48,13 @@ watch(
const i18n = useI18n()
const onConfirm = async () => {
try {
await groupFormRef.value?.validate((errs) => {
const err = get(errs, '0.0.message')
if (err != null) {
$message.error(err)
}
})
const { name } = groupForm
if (isRenameMode.value) {
const { success, msg } = await connectionStore.renameGroup(editGroup.value, name)
@ -47,9 +72,11 @@ const onConfirm = async () => {
}
}
} catch (e) {
$message.error(e.message)
const msg = get(e, 'message')
if (!isEmpty(msg)) {
$message.error(msg)
}
}
onClose()
}
const onClose = () => {
@ -77,8 +104,14 @@ const onClose = () => {
transform-origin="center"
@positive-click="onConfirm"
@negative-click="onClose">
<n-form :model="groupForm" :show-label="false" :show-require-mark="false" label-placement="top">
<n-form-item :label="$t('dialogue.group.name')" required>
<n-form
ref="groupFormRef"
:model="groupForm"
:rules="formRules"
:show-label="false"
:show-require-mark="false"
label-placement="top">
<n-form-item :label="$t('dialogue.group.name')" path="name" required>
<n-input v-model:value="groupForm.name" placeholder="" />
</n-form-item>
</n-form>

View File

@ -2,7 +2,7 @@
import { computed, h, reactive, ref, watch } from 'vue'
import { types, typesColor } from '@/consts/support_redis_type.js'
import useDialog from 'stores/dialog'
import { isEmpty, keys, map } from 'lodash'
import { get, isEmpty, keys, map } from 'lodash'
import NewStringValue from '@/components/new_value/NewStringValue.vue'
import NewHashValue from '@/components/new_value/NewHashValue.vue'
import NewListValue from '@/components/new_value/NewListValue.vue'
@ -103,13 +103,20 @@ const renderTypeLabel = (option) => {
const browserStore = useBrowserStore()
const tabStore = useTabStore()
const onAdd = async () => {
await newFormRef.value?.validate().catch((err) => {
$message.error(err.message)
await newFormRef.value?.validate((errs) => {
const err = get(errs, '0.0.message')
if (err != null) {
$message.error(err)
}
})
await subFormRef.value?.validate((errs) => {
const err = get(errs, '0.0.message')
if (err != null) {
$message.error(err)
} else {
$message.error(i18n.t('dialogue.spec_field_required', { key: i18n.t('dialogue.field.element') }))
}
})
if (subFormRef.value?.validate && !subFormRef.value?.validate()) {
$message.error(i18n.t('dialogue.spec_field_required', { key: i18n.t('dialogue.field.element') }))
return false
}
try {
const { server, db, key, type, ttl } = newForm
let { value } = newForm
@ -132,7 +139,10 @@ const onAdd = async () => {
$message.error(msg)
}
dialogStore.closeNewKeyDialog()
} catch (e) {}
} catch (e) {
return false
}
return true
}
const onClose = () => {

View File

@ -9,6 +9,7 @@ import { i18n } from '@/utils/i18n.js'
import { setupDiscreteApi } from '@/utils/discrete.js'
import usePreferencesStore from 'stores/preferences.js'
import { loadEnvironment } from '@/utils/platform.js'
import { setupMonaco } from '@/utils/monaco.js'
dayjs.extend(duration)
dayjs.extend(relativeTime)
@ -19,6 +20,7 @@ async function setupApp() {
app.use(createPinia())
await loadEnvironment()
setupMonaco()
const prefStore = usePreferencesStore()
await prefStore.loadPreferences()
await setupDiscreteApi()

View File

@ -979,13 +979,15 @@ const useBrowserStore = defineStore('browser', {
decode,
})
if (success) {
const { value } = data
// const { value } = data
// update tree view data
const { newKey = 0 } = this._addKeyNodes(server, db, [key], true)
if (newKey > 0) {
this._tidyNode(server, db, key)
this._updateDBMaxKeys(server, db, newKey)
}
const tab = useTabStore()
tab.updateValue({ server, db, key, value })
return {
success,
nodeKey: `${server}/db${db}#${ConnectionType.RedisValue}/${key}`,

View File

@ -113,7 +113,7 @@ const usePreferencesStore = defineStore('preferences', {
/**
* current font selection
* @returns {{fontSize: string}}
* @returns {{fontSize: string, fontFamily?: string}}
*/
generalFont() {
const fontStyle = {
@ -128,6 +128,24 @@ const usePreferencesStore = defineStore('preferences', {
return fontStyle
},
/**
* current editor font
* @return {{fontSize: string, fontFamily?: string}}
*/
editorFont() {
const fontStyle = {
fontSize: (this.editor.fontSize || 14) + 'px',
}
if (!isEmpty(this.editor.font) && this.editor.font !== 'none') {
const font = find(this.fontList, { name: this.editor.font })
if (font != null) {
fontStyle['fontFamily'] = `${font.name}`
}
}
fontStyle['fontFamily'] = fontStyle['fontFamily'] || 'monaco'
return fontStyle
},
/**
* get current language setting
* @return {string}

View File

@ -212,7 +212,7 @@ const useTabStore = defineStore('tab', {
* @param {string} [format]
* @param {string] [decode]
* @param {string} [matchPattern]
* @param {boolean} reset
* @param {boolean} [reset]
* @param {boolean} [end] keep end status if not set
*/
updateValue({ server, db, key, value, format, decode, matchPattern, reset, end }) {
@ -227,7 +227,7 @@ const useTabStore = defineStore('tab', {
if (typeof end === 'boolean') {
tabData.end = end
}
if (!reset && typeof value === 'object') {
if (!!!reset && typeof value === 'object') {
if (value instanceof Array) {
tabData.value = tabData.value || []
tabData.value.push(...value)

View File

@ -0,0 +1,43 @@
import * as monaco from 'monaco-editor'
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
export const setupMonaco = () => {
window.MonacoEnvironment = {
getWorker: (_, label) => {
switch (label) {
case 'json':
return new jsonWorker()
case 'css':
case 'scss':
case 'less':
return new cssWorker()
case 'html':
return new htmlWorker()
default:
return new editorWorker()
}
},
}
// setup light theme
monaco.editor.defineTheme('rdm-light', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'editorLineNumber.foreground': '#BABBBD',
'editorLineNumber.activeForeground': '#777D83',
},
})
// setup dark theme
monaco.editor.defineTheme('rdm-dark', {
base: 'vs-dark',
inherit: true,
rules: [],
colors: {},
})
}