This is an automated email from the ASF dual-hosted git repository.
chengpan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kyuubi.git
The following commit(s) were added to refs/heads/master by this push:
new 2b20f7b62 [KYUUBI #5517] [UI] Initial implement the SQL Lab page
2b20f7b62 is described below
commit 2b20f7b6268e30b4d4e719a1135cd521098cb7a0
Author: labbomb <[email protected]>
AuthorDate: Wed Oct 25 21:17:44 2023 +0800
[KYUUBI #5517] [UI] Initial implement the SQL Lab page
### _Why are the changes needed?_
1. New SQL Lab Page
2. New core editor functionality
<img width="1674" alt="image"
src="https://github.com/apache/kyuubi/assets/15062456/00cc9194-f43b-422f-a3dd-7b4456d9eb5b">
### _How was this patch tested?_
- [ ] Add some test cases that check the changes thoroughly including
negative and positive cases if possible
- [x] Add screenshots for manual tests if appropriate
- [ ] [Run
test](https://kyuubi.readthedocs.io/en/master/contributing/code/testing.html#running-tests)
locally before make a pull request
### _Was this patch authored or co-authored using generative AI tooling?_
No.
Closes #5517 from labbomb/dev.
Closes #5517
7b9f6dcea [labbomb] fix: adjust keyword case
edd71d232 [labbomb] fix: delete some language configs
45d999b88 [labbomb] fix: update license-binary
70edf9f47 [labbomb] fix: fix some errors
becb4e3a6 [labbomb] fix: fix some errors
d47e7f5c7 [labbomb] feat: add editor component
Authored-by: labbomb <[email protected]>
Signed-off-by: Cheng Pan <[email protected]>
---
LICENSE-binary | 22 +++
kyuubi-server/web-ui/package.json | 2 +
kyuubi-server/web-ui/pnpm-lock.yaml | 72 +++++++++-
.../web-ui/src/components/monaco-editor/index.vue | 149 +++++++++++++++++++++
.../web-ui/src/components/monaco-editor/type.ts | 81 +++++++++++
.../web-ui/src/layout/components/aside/types.ts | 5 +
kyuubi-server/web-ui/src/pinia/editor/index.ts | 37 +++++
kyuubi-server/web-ui/src/pinia/editor/type.ts | 18 +++
kyuubi-server/web-ui/src/router/index.ts | 4 +-
kyuubi-server/web-ui/src/router/lab/index.ts | 26 ++++
kyuubi-server/web-ui/src/views/lab/index.vue | 64 +++++++++
11 files changed, 474 insertions(+), 6 deletions(-)
diff --git a/LICENSE-binary b/LICENSE-binary
index b449a8344..748842a61 100644
--- a/LICENSE-binary
+++ b/LICENSE-binary
@@ -374,12 +374,16 @@ is auto-generated by `pnpm licenses list --prod`.
├────────────────────────────────────┼──────────────┤
│ typescript │ Apache-2.0 │
├────────────────────────────────────┼──────────────┤
+│ moo │ BSD-3-Clause │
+├────────────────────────────────────┼──────────────┤
│ normalize-wheel-es │ BSD-3-Clause │
├────────────────────────────────────┼──────────────┤
│ source-map │ BSD-3-Clause │
├────────────────────────────────────┼──────────────┤
│ source-map-js │ BSD-3-Clause │
├────────────────────────────────────┼──────────────┤
+│ railroad-diagrams │ CC0-1.0 │
+├────────────────────────────────────┼──────────────┤
│ picocolors │ ISC │
├────────────────────────────────────┼──────────────┤
│ @babel/helper-string-parser │ MIT │
@@ -452,6 +456,8 @@ is auto-generated by `pnpm licenses list --prod`.
├────────────────────────────────────┼──────────────┤
│ combined-stream │ MIT │
├────────────────────────────────────┼──────────────┤
+│ commander │ MIT │
+├────────────────────────────────────┼──────────────┤
│ csstype │ MIT │
├────────────────────────────────────┼──────────────┤
│ date-fns │ MIT │
@@ -460,6 +466,8 @@ is auto-generated by `pnpm licenses list --prod`.
├────────────────────────────────────┼──────────────┤
│ delayed-stream │ MIT │
├────────────────────────────────────┼──────────────┤
+│ discontinuous-range │ MIT │
+├────────────────────────────────────┼──────────────┤
│ element-plus │ MIT │
├────────────────────────────────────┼──────────────┤
│ escape-html │ MIT │
@@ -470,6 +478,8 @@ is auto-generated by `pnpm licenses list --prod`.
├────────────────────────────────────┼──────────────┤
│ form-data │ MIT │
├────────────────────────────────────┼──────────────┤
+│ get-stdin │ MIT │
+├────────────────────────────────────┼──────────────┤
│ lodash │ MIT │
├────────────────────────────────────┼──────────────┤
│ lodash-es │ MIT │
@@ -484,16 +494,26 @@ is auto-generated by `pnpm licenses list --prod`.
├────────────────────────────────────┼──────────────┤
│ mime-types │ MIT │
├────────────────────────────────────┼──────────────┤
+│ monaco-editor │ MIT │
+├────────────────────────────────────┼──────────────┤
│ nanoid │ MIT │
├────────────────────────────────────┼──────────────┤
+│ nearley │ MIT │
+├────────────────────────────────────┼──────────────┤
│ pinia │ MIT │
├────────────────────────────────────┼──────────────┤
│ pinia-plugin-persistedstate │ MIT │
├────────────────────────────────────┼──────────────┤
│ postcss │ MIT │
├────────────────────────────────────┼──────────────┤
+│ randexp │ MIT │
+├────────────────────────────────────┼──────────────┤
+│ ret │ MIT │
+├────────────────────────────────────┼──────────────┤
│ sourcemap-codec │ MIT │
├────────────────────────────────────┼──────────────┤
+│ sql-formatter │ MIT │
+├────────────────────────────────────┼──────────────┤
│ to-fast-properties │ MIT │
├────────────────────────────────────┼──────────────┤
│ vue │ MIT │
@@ -503,4 +523,6 @@ is auto-generated by `pnpm licenses list --prod`.
│ vue-i18n │ MIT │
├────────────────────────────────────┼──────────────┤
│ vue-router │ MIT │
+├────────────────────────────────────┼──────────────┤
+│ argparse │ Python-2.0 │
└────────────────────────────────────┴──────────────┘
diff --git a/kyuubi-server/web-ui/package.json
b/kyuubi-server/web-ui/package.json
index f31b836dc..239c62706 100644
--- a/kyuubi-server/web-ui/package.json
+++ b/kyuubi-server/web-ui/package.json
@@ -19,8 +19,10 @@
"axios": "^0.27.2",
"date-fns": "^2.29.3",
"element-plus": "^2.2.12",
+ "monaco-editor": "^0.44.0",
"pinia": "^2.0.18",
"pinia-plugin-persistedstate": "^2.1.1",
+ "sql-formatter": "^13.0.1",
"swagger-ui-dist": "^4.9.1",
"vue": "^3.2.37",
"vue-i18n": "^9.2.2",
diff --git a/kyuubi-server/web-ui/pnpm-lock.yaml
b/kyuubi-server/web-ui/pnpm-lock.yaml
index ffed6c6bd..a83d162ab 100644
--- a/kyuubi-server/web-ui/pnpm-lock.yaml
+++ b/kyuubi-server/web-ui/pnpm-lock.yaml
@@ -1,9 +1,5 @@
lockfileVersion: '6.0'
-settings:
- autoInstallPeers: true
- excludeLinksFromLockfile: false
-
dependencies:
'@element-plus/icons-vue':
specifier: ^2.0.9
@@ -17,12 +13,18 @@ dependencies:
element-plus:
specifier: ^2.2.12
version: 2.2.13([email protected])
+ monaco-editor:
+ specifier: ^0.44.0
+ version: 0.44.0
pinia:
specifier: ^2.0.18
version: 2.0.18([email protected])([email protected])
pinia-plugin-persistedstate:
specifier: ^2.1.1
version: 2.1.1([email protected])
+ sql-formatter:
+ specifier: ^13.0.1
+ version: 13.0.1
swagger-ui-dist:
specifier: ^4.9.1
version: 4.19.1
@@ -1019,7 +1021,6 @@ packages:
/[email protected]:
resolution: {integrity:
sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
- dev: true
/[email protected]:
resolution: {integrity:
sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
@@ -1148,6 +1149,10 @@ packages:
dependencies:
delayed-stream: 1.0.0
+ /[email protected]:
+ resolution: {integrity:
sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
@@ -1268,6 +1273,10 @@ packages:
path-type: 4.0.0
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
@@ -1648,6 +1657,11 @@ packages:
resolution: {integrity:
sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==}
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==}
+ engines: {node: '>=10'}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
@@ -2055,6 +2069,14 @@ packages:
ufo: 1.1.2
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-5SmjNStN6bSuSE5WPT2ZV+iYn1/yI9sd4Igtk23ChvqB7kDk9lZbB9F5frsuvpB+2njdIeGGFf2G4gbE6rCC9Q==}
+ dev: false
+
+ /[email protected]:
+ resolution: {integrity:
sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
@@ -2068,6 +2090,16 @@ packages:
resolution: {integrity:
sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==}
+ hasBin: true
+ dependencies:
+ commander: 2.20.3
+ moo: 0.5.2
+ railroad-diagrams: 1.0.0
+ randexp: 0.4.6
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
@@ -2293,6 +2325,18 @@ packages:
resolution: {integrity:
sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==}
+ dev: false
+
+ /[email protected]:
+ resolution: {integrity:
sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==}
+ engines: {node: '>=0.12'}
+ dependencies:
+ discontinuous-range: 1.0.0
+ ret: 0.1.15
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
dev: true
@@ -2327,6 +2371,11 @@ packages:
supports-preserve-symlinks-flag: 1.0.0
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==}
+ engines: {node: '>=0.12'}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@@ -2423,6 +2472,15 @@ packages:
/[email protected]:
resolution: {integrity:
sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
+ /[email protected]:
+ resolution: {integrity:
sha512-/tM0H1O2kto6DKv1YuaZa75PMr1J9vTHj5aMu3GF0coKskWUNNcHCvtE1YBHpoFEuKnGUqxAwEpeAH+BAcZcEQ==}
+ hasBin: true
+ dependencies:
+ argparse: 2.0.1
+ get-stdin: 8.0.0
+ nearley: 2.20.1
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
dev: true
@@ -2911,3 +2969,7 @@ packages:
resolution: {integrity:
sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
engines: {node: '>=12.20'}
dev: true
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
diff --git a/kyuubi-server/web-ui/src/components/monaco-editor/index.vue
b/kyuubi-server/web-ui/src/components/monaco-editor/index.vue
new file mode 100644
index 000000000..4c387172a
--- /dev/null
+++ b/kyuubi-server/web-ui/src/components/monaco-editor/index.vue
@@ -0,0 +1,149 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.
+-->
+
+<template>
+ <div ref="codeEditBox" style="height: 100%" />
+</template>
+
+<script lang="ts" setup>
+ import * as monaco from 'monaco-editor'
+ import { format } from 'sql-formatter'
+ import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
+ import { editorProps } from './type'
+ import { useEditorStore } from '@/pinia/editor'
+ import { ref, toRaw, watch, onBeforeUnmount, onMounted } from 'vue'
+
+ // @ts-ignore: worker
+ self.MonacoEnvironment = {
+ getWorker() {
+ return new EditorWorker()
+ }
+ }
+
+ const props = defineProps(editorProps)
+ const emit = defineEmits([
+ 'update:modelValue',
+ 'change',
+ 'editorMounted',
+ 'editorSave'
+ ])
+
+ const editorStore = useEditorStore()
+ const monacoEditorThemeRef = ref(
+ editorStore.getCurrentTheme === 'dark' ? 'vs-dark' : 'vs'
+ )
+ let editor: monaco.editor.IStandaloneCodeEditor
+ const codeEditBox = ref()
+ const init = () => {
+ monaco.languages.registerCompletionItemProvider('sql', {
+ provideCompletionItems: function (model: any, position: any) {
+ const word = model.getWordUntilPosition(position)
+ const range = {
+ startLineNumber: position.lineNumber,
+ endLineNumber: position.lineNumber,
+ startColumn: word.startColumn,
+ endColumn: word.endColumn
+ }
+ const suggestions = []
+ const keywords = [
+ 'SELECT',
+ 'FROM',
+ 'WHERE',
+ 'AND',
+ 'OR',
+ 'LIMIT',
+ 'ORDER BY',
+ 'GROUP BY'
+ ]
+ for (const i in keywords) {
+ suggestions.push({
+ label: keywords[i],
+ kind: monaco.languages.CompletionItemKind['Function'],
+ insertText: keywords[i],
+ detail: '',
+ range: range
+ })
+ }
+ return {
+ suggestions: suggestions
+ }
+ }
+ })
+
+ editor = monaco.editor.create(codeEditBox.value, {
+ value: props.modelValue,
+ language: props.language,
+ theme: monacoEditorThemeRef.value,
+ ...props.options
+ })
+
+ editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, function ()
{
+ emit('editorSave')
+ })
+
+ editor.setValue(format(toRaw(editor).getValue()))
+
+ editor.onDidChangeModelContent(() => {
+ const value = editor.getValue()
+ emit('update:modelValue', value)
+ emit('change', value)
+ })
+ emit('editorMounted', editor)
+ }
+ watch(
+ () => props.modelValue,
+ (newValue) => {
+ if (editor) {
+ const value = editor.getValue()
+ if (newValue !== value) {
+ editor.setValue(newValue)
+ editor.setValue(format(toRaw(editor).getValue()))
+ }
+ }
+ }
+ )
+ watch(
+ () => props.options,
+ (newValue) => {
+ editor.updateOptions(newValue)
+ },
+ { deep: true }
+ )
+ watch(
+ () => props.language,
+ (newValue) => {
+ monaco.editor.setModelLanguage(editor.getModel()!, newValue)
+ }
+ )
+ watch(
+ () => editorStore.getCurrentTheme,
+ () => {
+ editor?.dispose()
+ monacoEditorThemeRef.value =
+ editorStore.getCurrentTheme === 'dark' ? 'vs-dark' : 'vs'
+ init()
+ }
+ )
+
+ onBeforeUnmount(() => {
+ editor.dispose()
+ })
+ onMounted(() => {
+ init()
+ })
+</script>
diff --git a/kyuubi-server/web-ui/src/components/monaco-editor/type.ts
b/kyuubi-server/web-ui/src/components/monaco-editor/type.ts
new file mode 100644
index 000000000..80400565e
--- /dev/null
+++ b/kyuubi-server/web-ui/src/components/monaco-editor/type.ts
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { PropType } from 'vue'
+
+export type Theme = 'vs' | 'vs-dark'
+export type FoldingStrategy = 'auto' | 'indentation'
+export type RenderLineHighlight = 'all' | 'line' | 'none' | 'gutter'
+export interface Options {
+ automaticLayout?: boolean
+ foldingStrategy?: FoldingStrategy
+ renderLineHighlight?: RenderLineHighlight
+ selectOnLineNumbers?: boolean
+ minimap?: {
+ enabled: boolean
+ }
+ readOnly: boolean
+ contextmenu: boolean
+ fontSize?: number
+ scrollBeyondLastLine?: boolean
+ overviewRulerBorder?: boolean
+}
+
+export const editorProps = {
+ modelValue: {
+ type: String as PropType<string>,
+ default: null
+ },
+ width: {
+ type: [String, Number] as PropType<string | number>,
+ default: '100%'
+ },
+ height: {
+ type: [String, Number] as PropType<string | number>,
+ default: '100%'
+ },
+ language: {
+ type: String as PropType<string>,
+ default: 'sql'
+ },
+ theme: {
+ type: String as PropType<Theme>,
+ validator(value: string): boolean {
+ return ['vs', 'vs-dark'].includes(value)
+ },
+ default: 'vs'
+ },
+ options: {
+ type: Object as PropType<Options>,
+ default() {
+ return {
+ automaticLayout: true,
+ foldingStrategy: 'indentation',
+ renderLineHighlight: 'line',
+ selectOnLineNumbers: true,
+ minimap: {
+ enabled: false
+ },
+ readOnly: false,
+ contextmenu: true,
+ fontSize: 16,
+ scrollBeyondLastLine: true,
+ overviewRulerBorder: false
+ }
+ }
+ }
+}
diff --git a/kyuubi-server/web-ui/src/layout/components/aside/types.ts
b/kyuubi-server/web-ui/src/layout/components/aside/types.ts
index 728ee326d..a7e495e18 100644
--- a/kyuubi-server/web-ui/src/layout/components/aside/types.ts
+++ b/kyuubi-server/web-ui/src/layout/components/aside/types.ts
@@ -94,5 +94,10 @@ export const MENUS = [
label: 'Contact Us',
icon: 'PhoneFilled',
router: '/contact'
+ },
+ {
+ label: 'SQL Lab',
+ icon: 'Cpu',
+ router: '/lab'
}
]
diff --git a/kyuubi-server/web-ui/src/pinia/editor/index.ts
b/kyuubi-server/web-ui/src/pinia/editor/index.ts
new file mode 100644
index 000000000..8902d172c
--- /dev/null
+++ b/kyuubi-server/web-ui/src/pinia/editor/index.ts
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 { defineStore } from 'pinia'
+import { Theme } from './type'
+
+export const useEditorStore = defineStore({
+ id: 'editor',
+ state: (): { theme: Theme } => ({
+ theme: 'light'
+ }),
+ persist: true,
+ getters: {
+ getCurrentTheme(): Theme {
+ return this.theme
+ }
+ },
+ actions: {
+ setCurrentTheme(theme: Theme): void {
+ this.theme = theme
+ }
+ }
+})
diff --git a/kyuubi-server/web-ui/src/pinia/editor/type.ts
b/kyuubi-server/web-ui/src/pinia/editor/type.ts
new file mode 100644
index 000000000..4155d60e0
--- /dev/null
+++ b/kyuubi-server/web-ui/src/pinia/editor/type.ts
@@ -0,0 +1,18 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+export type Theme = 'dark' | 'light'
diff --git a/kyuubi-server/web-ui/src/router/index.ts
b/kyuubi-server/web-ui/src/router/index.ts
index 423715661..c59c5f28c 100644
--- a/kyuubi-server/web-ui/src/router/index.ts
+++ b/kyuubi-server/web-ui/src/router/index.ts
@@ -23,6 +23,7 @@ import contactRoutes from './contact'
import managementRoutes from './management'
import detailRoutes from './detail'
import swaggerRoutes from './swagger'
+import labRoutes from './lab'
const routes = [
{
@@ -44,7 +45,8 @@ const routes = [
...managementRoutes,
...detailRoutes,
...swaggerRoutes,
- ...contactRoutes
+ ...contactRoutes,
+ ...labRoutes
]
}
]
diff --git a/kyuubi-server/web-ui/src/router/lab/index.ts
b/kyuubi-server/web-ui/src/router/lab/index.ts
new file mode 100644
index 000000000..d78838079
--- /dev/null
+++ b/kyuubi-server/web-ui/src/router/lab/index.ts
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+const routes = [
+ {
+ path: '/lab',
+ name: 'lab',
+ component: () => import('@/views/lab/index.vue')
+ }
+]
+
+export default routes
diff --git a/kyuubi-server/web-ui/src/views/lab/index.vue
b/kyuubi-server/web-ui/src/views/lab/index.vue
new file mode 100644
index 000000000..26ecfac0d
--- /dev/null
+++ b/kyuubi-server/web-ui/src/views/lab/index.vue
@@ -0,0 +1,64 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you 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.
+-->
+
+<template>
+ <div class="container">
+ <MonacoEditor
+ v-model="editorVariables.content"
+ :language="editorVariables.language"
+ @editor-mounted="editorMounted"
+ @change="handleContentChange"
+ @editor-save="editorSave" />
+ </div>
+</template>
+
+<script lang="ts" setup>
+ import MonacoEditor from '@/components/monaco-editor/index.vue'
+ import { reactive, toRaw } from 'vue'
+ import * as monaco from 'monaco-editor'
+ import { format } from 'sql-formatter'
+
+ const editorVariables = reactive({
+ editor: {} as any,
+ language: 'sql',
+ content: ''
+ })
+
+ const editorMounted = (editor: monaco.editor.IStandaloneCodeEditor) => {
+ editorVariables.editor = editor
+ }
+ const handleFormat = () => {
+ toRaw(editorVariables.editor).setValue(
+ format(toRaw(editorVariables.editor).getValue())
+ )
+ }
+
+ const editorSave = () => {
+ handleFormat()
+ }
+
+ const handleContentChange = (value: string) => {
+ editorVariables.content = value
+ }
+</script>
+
+<style lang="scss" scoped>
+ .container {
+ height: 70%;
+ }
+</style>