chore(sfc-playground): update code style and syntax
This commit is contained in:
		
							parent
							
								
									ceace3a8cc
								
							
						
					
					
						commit
						e22d7cdb08
					
				| @ -1,3 +1,10 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import Header from './Header.vue' | ||||||
|  | import SplitPane from './SplitPane.vue' | ||||||
|  | import Editor from './editor/Editor.vue' | ||||||
|  | import Output from './output/Output.vue' | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
| <template> | <template> | ||||||
|   <Header /> |   <Header /> | ||||||
|   <div class="wrapper"> |   <div class="wrapper"> | ||||||
| @ -12,13 +19,6 @@ | |||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script setup lang="ts"> |  | ||||||
| import Header from './Header.vue' |  | ||||||
| import SplitPane from './SplitPane.vue' |  | ||||||
| import Editor from './editor/Editor.vue' |  | ||||||
| import Output from './output/Output.vue' |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <style> | <style> | ||||||
| body { | body { | ||||||
|   font-size: 13px; |   font-size: 13px; | ||||||
|  | |||||||
| @ -1,4 +1,3 @@ | |||||||
| 
 |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { downloadProject } from './download/download' | import { downloadProject } from './download/download' | ||||||
| import { setVersion, resetVersion } from './sfcCompiler' | import { setVersion, resetVersion } from './sfcCompiler' | ||||||
|  | |||||||
| @ -1,23 +1,17 @@ | |||||||
| <template> |  | ||||||
|   <Transition name="fade"> |  | ||||||
|     <pre v-if="!dismissed && (err || warn)" |  | ||||||
|       class="msg" |  | ||||||
|       :class="err ? 'err' : 'warn'" |  | ||||||
|       @click="dismissed = true">{{ formatMessage(err || warn) }}</pre> |  | ||||||
|   </Transition> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref, watch } from 'vue' | import { ref, watch } from 'vue' | ||||||
| import type { CompilerError } from '@vue/compiler-sfc' | import { CompilerError } from '@vue/compiler-sfc' | ||||||
| 
 | 
 | ||||||
| const props = defineProps(['err', 'warn']) | const props = defineProps(['err', 'warn']) | ||||||
| 
 | 
 | ||||||
| const dismissed = ref(false) | const dismissed = ref(false) | ||||||
| 
 | 
 | ||||||
| watch(() => [props.err, props.warn], () => { | watch( | ||||||
|  |   () => [props.err, props.warn], | ||||||
|  |   () => { | ||||||
|     dismissed.value = false |     dismissed.value = false | ||||||
| }) |   } | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| function formatMessage(err: string | Error): string { | function formatMessage(err: string | Error): string { | ||||||
|   if (typeof err === 'string') { |   if (typeof err === 'string') { | ||||||
| @ -33,6 +27,18 @@ function formatMessage(err: string | Error): string { | |||||||
| } | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | <template> | ||||||
|  |   <Transition name="fade"> | ||||||
|  |     <pre | ||||||
|  |       v-if="!dismissed && (err || warn)" | ||||||
|  |       class="msg" | ||||||
|  |       :class="err ? 'err' : 'warn'" | ||||||
|  |       @click="dismissed = true" | ||||||
|  |       >{{ formatMessage(err || warn) }}</pre | ||||||
|  |     > | ||||||
|  |   </Transition> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
| .msg { | .msg { | ||||||
|   position: absolute; |   position: absolute; | ||||||
|  | |||||||
| @ -1,22 +1,3 @@ | |||||||
| <template> |  | ||||||
|   <div |  | ||||||
|     ref="container" |  | ||||||
|     class="split-pane" |  | ||||||
|     :class="{ dragging: state.dragging }" |  | ||||||
|     @mousemove="dragMove" |  | ||||||
|     @mouseup="dragEnd" |  | ||||||
|     @mouseleave="dragEnd" |  | ||||||
|   > |  | ||||||
|     <div class="left" :style="{ width: boundSplit() + '%' }"> |  | ||||||
|       <slot name="left" /> |  | ||||||
|       <div class="dragger" @mousedown.prevent="dragStart" /> |  | ||||||
|     </div> |  | ||||||
|     <div class="right" :style="{ width: (100 - boundSplit()) + '%' }"> |  | ||||||
|       <slot name="right" /> |  | ||||||
|     </div> |  | ||||||
|   </div> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { ref, reactive } from 'vue' | import { ref, reactive } from 'vue' | ||||||
| 
 | 
 | ||||||
| @ -29,11 +10,7 @@ const state = reactive({ | |||||||
| 
 | 
 | ||||||
| function boundSplit() { | function boundSplit() { | ||||||
|   const { split } = state |   const { split } = state | ||||||
|   return split < 20 |   return split < 20 ? 20 : split > 80 ? 80 : split | ||||||
|     ? 20 |  | ||||||
|     : split > 80 |  | ||||||
|       ? 80 |  | ||||||
|       : split |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| let startPosition = 0 | let startPosition = 0 | ||||||
| @ -50,7 +27,7 @@ function dragMove(e: MouseEvent) { | |||||||
|     const position = e.pageX |     const position = e.pageX | ||||||
|     const totalSize = container.value.offsetWidth |     const totalSize = container.value.offsetWidth | ||||||
|     const dp = position - startPosition |     const dp = position - startPosition | ||||||
|     state.split = startSplit + ~~(dp / totalSize * 100) |     state.split = startSplit + ~~((dp / totalSize) * 100) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -59,6 +36,25 @@ function dragEnd() { | |||||||
| } | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | <template> | ||||||
|  |   <div | ||||||
|  |     ref="container" | ||||||
|  |     class="split-pane" | ||||||
|  |     :class="{ dragging: state.dragging }" | ||||||
|  |     @mousemove="dragMove" | ||||||
|  |     @mouseup="dragEnd" | ||||||
|  |     @mouseleave="dragEnd" | ||||||
|  |   > | ||||||
|  |     <div class="left" :style="{ width: boundSplit() + '%' }"> | ||||||
|  |       <slot name="left" /> | ||||||
|  |       <div class="dragger" @mousedown.prevent="dragStart" /> | ||||||
|  |     </div> | ||||||
|  |     <div class="right" :style="{ width: 100 - boundSplit() + '%' }"> | ||||||
|  |       <slot name="right" /> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
| .split-pane { | .split-pane { | ||||||
|   display: flex; |   display: flex; | ||||||
|  | |||||||
| @ -1,11 +1,3 @@ | |||||||
| <template> |  | ||||||
|   <FileSelector/> |  | ||||||
|   <div class="editor-container"> |  | ||||||
|     <CodeMirror @change="onChange" :value="activeCode" :mode="activeMode" /> |  | ||||||
|     <Message :err="store.errors[0]" /> |  | ||||||
|   </div> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import FileSelector from './FileSelector.vue' | import FileSelector from './FileSelector.vue' | ||||||
| import CodeMirror from '../codemirror/CodeMirror.vue' | import CodeMirror from '../codemirror/CodeMirror.vue' | ||||||
| @ -19,8 +11,8 @@ const onChange = debounce((code: string) => { | |||||||
| }, 250) | }, 250) | ||||||
| 
 | 
 | ||||||
| const activeCode = ref(store.activeFile.code) | const activeCode = ref(store.activeFile.code) | ||||||
| const activeMode = computed( | const activeMode = computed(() => | ||||||
|   () => (store.activeFilename.endsWith('.vue') ? 'htmlmixed' : 'javascript') |   store.activeFilename.endsWith('.vue') ? 'htmlmixed' : 'javascript' | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| watch( | watch( | ||||||
| @ -31,6 +23,14 @@ watch( | |||||||
| ) | ) | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | <template> | ||||||
|  |   <FileSelector /> | ||||||
|  |   <div class="editor-container"> | ||||||
|  |     <CodeMirror @change="onChange" :value="activeCode" :mode="activeMode" /> | ||||||
|  |     <Message :err="store.errors[0]" /> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
| .editor-container { | .editor-container { | ||||||
|   height: calc(100% - 35px); |   height: calc(100% - 35px); | ||||||
|  | |||||||
| @ -1,31 +1,6 @@ | |||||||
| <template> |  | ||||||
|   <div class="file-selector"> |  | ||||||
|     <div |  | ||||||
|       v-for="(file, i) in Object.keys(store.files)" |  | ||||||
|       class="file" |  | ||||||
|       :class="{ active: store.activeFilename === file }" |  | ||||||
|       @click="setActive(file)"> |  | ||||||
|       <span class="label">{{ file }}</span> |  | ||||||
|       <span v-if="i > 0" class="remove" @click.stop="deleteFile(file)"> |  | ||||||
|         <svg width="12" height="12" viewBox="0 0 24 24" class="svelte-cghqrp"><line stroke="#999" x1="18" y1="6" x2="6" y2="18"></line><line stroke="#999" x1="6" y1="6" x2="18" y2="18"></line></svg> |  | ||||||
|       </span> |  | ||||||
|     </div> |  | ||||||
|     <div v-if="pending" class="file" > |  | ||||||
|       <input |  | ||||||
|         v-model="pendingFilename" |  | ||||||
|         spellcheck="false" |  | ||||||
|         @keyup.enter="doneAddFile" |  | ||||||
|         @keyup.esc="cancelAddFile" |  | ||||||
|         @vnodeMounted="focus"> |  | ||||||
|     </div> |  | ||||||
|     <button class="add" @click="startAddFile">+</button> |  | ||||||
|   </div> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import { store, addFile, deleteFile, setActive } from '../store' | import { store, addFile, deleteFile, setActive } from '../store' | ||||||
| import { ref } from 'vue' | import { ref, VNode } from 'vue' | ||||||
| import type { VNode } from 'vue' |  | ||||||
| 
 | 
 | ||||||
| const pending = ref(false) | const pending = ref(false) | ||||||
| const pendingFilename = ref('Comp.vue') | const pendingFilename = ref('Comp.vue') | ||||||
| @ -39,7 +14,7 @@ function cancelAddFile() { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function focus({ el }: VNode) { | function focus({ el }: VNode) { | ||||||
|   (el as HTMLInputElement).focus() |   ;(el as HTMLInputElement).focus() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| function doneAddFile() { | function doneAddFile() { | ||||||
| @ -50,7 +25,9 @@ function doneAddFile() { | |||||||
|     !filename.endsWith('.js') && |     !filename.endsWith('.js') && | ||||||
|     filename !== 'import-map.json' |     filename !== 'import-map.json' | ||||||
|   ) { |   ) { | ||||||
|     store.errors = [`Playground only supports *.vue, *.js files or import-map.json.`] |     store.errors = [ | ||||||
|  |       `Playground only supports *.vue, *.js files or import-map.json.` | ||||||
|  |     ] | ||||||
|     return |     return | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -66,6 +43,35 @@ function doneAddFile() { | |||||||
| } | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | <template> | ||||||
|  |   <div class="file-selector"> | ||||||
|  |     <div | ||||||
|  |       v-for="(file, i) in Object.keys(store.files)" | ||||||
|  |       class="file" | ||||||
|  |       :class="{ active: store.activeFilename === file }" | ||||||
|  |       @click="setActive(file)" | ||||||
|  |     > | ||||||
|  |       <span class="label">{{ file }}</span> | ||||||
|  |       <span v-if="i > 0" class="remove" @click.stop="deleteFile(file)"> | ||||||
|  |         <svg width="12" height="12" viewBox="0 0 24 24" class="svelte-cghqrp"> | ||||||
|  |           <line stroke="#999" x1="18" y1="6" x2="6" y2="18"></line> | ||||||
|  |           <line stroke="#999" x1="6" y1="6" x2="18" y2="18"></line> | ||||||
|  |         </svg> | ||||||
|  |       </span> | ||||||
|  |     </div> | ||||||
|  |     <div v-if="pending" class="file"> | ||||||
|  |       <input | ||||||
|  |         v-model="pendingFilename" | ||||||
|  |         spellcheck="false" | ||||||
|  |         @keyup.enter="doneAddFile" | ||||||
|  |         @keyup.esc="cancelAddFile" | ||||||
|  |         @vnodeMounted="focus" | ||||||
|  |       /> | ||||||
|  |     </div> | ||||||
|  |     <button class="add" @click="startAddFile">+</button> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
| <style scoped> | <style scoped> | ||||||
| .file-selector { | .file-selector { | ||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
|  | |||||||
| @ -1,6 +1,24 @@ | |||||||
|  | <script setup lang="ts"> | ||||||
|  | import Preview from './Preview.vue' | ||||||
|  | import CodeMirror from '../codemirror/CodeMirror.vue' | ||||||
|  | import { store } from '../store' | ||||||
|  | import { ref } from 'vue' | ||||||
|  | 
 | ||||||
|  | const modes = ['preview', 'js', 'css', 'ssr'] as const | ||||||
|  | 
 | ||||||
|  | type Modes = typeof modes[number] | ||||||
|  | const mode = ref<Modes>('preview') | ||||||
|  | </script> | ||||||
|  | 
 | ||||||
| <template> | <template> | ||||||
|   <div class="tab-buttons"> |   <div class="tab-buttons"> | ||||||
|     <button v-for="m of modes" :class="{ active: mode === m }" @click="mode = m">{{ m }}</button> |     <button | ||||||
|  |       v-for="m of modes" | ||||||
|  |       :class="{ active: mode === m }" | ||||||
|  |       @click="mode = m" | ||||||
|  |     > | ||||||
|  |       {{ m }} | ||||||
|  |     </button> | ||||||
|   </div> |   </div> | ||||||
| 
 | 
 | ||||||
|   <div class="output-container"> |   <div class="output-container"> | ||||||
| @ -14,18 +32,6 @@ | |||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script setup lang="ts"> |  | ||||||
| import Preview from './Preview.vue' |  | ||||||
| import CodeMirror from '../codemirror/CodeMirror.vue' |  | ||||||
| import { store } from '../store' |  | ||||||
| import { ref } from 'vue' |  | ||||||
| 
 |  | ||||||
| const modes = ['preview', 'js', 'css', 'ssr'] as const |  | ||||||
| 
 |  | ||||||
| type Modes = typeof modes[number] |  | ||||||
| const mode = ref<Modes>('preview') |  | ||||||
| </script> |  | ||||||
| 
 |  | ||||||
| <style scoped> | <style scoped> | ||||||
| .output-container { | .output-container { | ||||||
|   height: calc(100% - 35px); |   height: calc(100% - 35px); | ||||||
|  | |||||||
| @ -1,14 +1,13 @@ | |||||||
| <template> |  | ||||||
|   <div class="preview-container" ref="container"> |  | ||||||
| </div> |  | ||||||
|   <Message :err="runtimeError" /> |  | ||||||
|   <Message v-if="!runtimeError" :warn="runtimeWarning" /> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import Message from '../Message.vue' | import Message from '../Message.vue' | ||||||
| import { ref, onMounted, onUnmounted, watchEffect, watch } from 'vue' | import { | ||||||
| import type { WatchStopHandle } from 'vue' |   ref, | ||||||
|  |   onMounted, | ||||||
|  |   onUnmounted, | ||||||
|  |   watchEffect, | ||||||
|  |   watch, | ||||||
|  |   WatchStopHandle | ||||||
|  | } from 'vue' | ||||||
| import srcdoc from './srcdoc.html?raw' | import srcdoc from './srcdoc.html?raw' | ||||||
| import { PreviewProxy } from './PreviewProxy' | import { PreviewProxy } from './PreviewProxy' | ||||||
| import { MAIN_FILE, vueRuntimeUrl } from '../sfcCompiler' | import { MAIN_FILE, vueRuntimeUrl } from '../sfcCompiler' | ||||||
| @ -27,7 +26,9 @@ let stopUpdateWatcher: WatchStopHandle | |||||||
| onMounted(createSandbox) | onMounted(createSandbox) | ||||||
| 
 | 
 | ||||||
| // reset sandbox when import map changes | // reset sandbox when import map changes | ||||||
| watch(() => store.importMap, (importMap, prev) => { | watch( | ||||||
|  |   () => store.importMap, | ||||||
|  |   (importMap, prev) => { | ||||||
|     if (!importMap) { |     if (!importMap) { | ||||||
|       if (prev) { |       if (prev) { | ||||||
|         // import-map.json deleted |         // import-map.json deleted | ||||||
| @ -38,9 +39,7 @@ watch(() => store.importMap, (importMap, prev) => { | |||||||
|     try { |     try { | ||||||
|       const map = JSON.parse(importMap) |       const map = JSON.parse(importMap) | ||||||
|       if (!map.imports) { |       if (!map.imports) { | ||||||
|       store.errors = [ |         store.errors = [`import-map.json is missing "imports" field.`] | ||||||
|         `import-map.json is missing "imports" field.` |  | ||||||
|       ] |  | ||||||
|         return |         return | ||||||
|       } |       } | ||||||
|       if (map.imports.vue) { |       if (map.imports.vue) { | ||||||
| @ -51,10 +50,11 @@ watch(() => store.importMap, (importMap, prev) => { | |||||||
|       } |       } | ||||||
|       createSandbox() |       createSandbox() | ||||||
|     } catch (e) { |     } catch (e) { | ||||||
|     store.errors = [e] |       store.errors = [e as Error] | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
| }) |   } | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| // reset sandbox when version changes | // reset sandbox when version changes | ||||||
| watch(vueRuntimeUrl, createSandbox) | watch(vueRuntimeUrl, createSandbox) | ||||||
| @ -73,7 +73,9 @@ function createSandbox() { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   sandbox = document.createElement('iframe') |   sandbox = document.createElement('iframe') | ||||||
|   sandbox.setAttribute('sandbox', [ |   sandbox.setAttribute( | ||||||
|  |     'sandbox', | ||||||
|  |     [ | ||||||
|       'allow-forms', |       'allow-forms', | ||||||
|       'allow-modals', |       'allow-modals', | ||||||
|       'allow-pointer-lock', |       'allow-pointer-lock', | ||||||
| @ -81,13 +83,14 @@ function createSandbox() { | |||||||
|       'allow-same-origin', |       'allow-same-origin', | ||||||
|       'allow-scripts', |       'allow-scripts', | ||||||
|       'allow-top-navigation-by-user-activation' |       'allow-top-navigation-by-user-activation' | ||||||
|   ].join(' ')) |     ].join(' ') | ||||||
|  |   ) | ||||||
| 
 | 
 | ||||||
|   let importMap: Record<string, any> |   let importMap: Record<string, any> | ||||||
|   try { |   try { | ||||||
|     importMap = JSON.parse(store.importMap || `{}`) |     importMap = JSON.parse(store.importMap || `{}`) | ||||||
|   } catch (e) { |   } catch (e) { | ||||||
|     store.errors = [`Syntax error in import-map.json: ${e.message}`] |     store.errors = [`Syntax error in import-map.json: ${(e as Error).message}`] | ||||||
|     return |     return | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -95,7 +98,10 @@ function createSandbox() { | |||||||
|     importMap.imports = {} |     importMap.imports = {} | ||||||
|   } |   } | ||||||
|   importMap.imports.vue = vueRuntimeUrl.value |   importMap.imports.vue = vueRuntimeUrl.value | ||||||
|   const sandboxSrc = srcdoc.replace(/<!--IMPORT_MAP-->/, JSON.stringify(importMap)) |   const sandboxSrc = srcdoc.replace( | ||||||
|  |     /<!--IMPORT_MAP-->/, | ||||||
|  |     JSON.stringify(importMap) | ||||||
|  |   ) | ||||||
|   sandbox.srcdoc = sandboxSrc |   sandbox.srcdoc = sandboxSrc | ||||||
|   container.value.appendChild(sandbox) |   container.value.appendChild(sandbox) | ||||||
| 
 | 
 | ||||||
| @ -104,12 +110,14 @@ function createSandbox() { | |||||||
|       // pending_imports = progress; |       // pending_imports = progress; | ||||||
|     }, |     }, | ||||||
|     on_error: (event: any) => { |     on_error: (event: any) => { | ||||||
|       const msg = event.value instanceof Error ? event.value.message : event.value |       const msg = | ||||||
|  |         event.value instanceof Error ? event.value.message : event.value | ||||||
|       if ( |       if ( | ||||||
|         msg.includes('Failed to resolve module specifier') || |         msg.includes('Failed to resolve module specifier') || | ||||||
|         msg.includes('Error resolving module specifier') |         msg.includes('Error resolving module specifier') | ||||||
|       ) { |       ) { | ||||||
|         runtimeError.value = msg.replace(/\. Relative references must.*$/, '') + |         runtimeError.value = | ||||||
|  |           msg.replace(/\. Relative references must.*$/, '') + | ||||||
|           `.\nTip: add an "import-map.json" file to specify import paths for dependencies.` |           `.\nTip: add an "import-map.json" file to specify import paths for dependencies.` | ||||||
|       } else { |       } else { | ||||||
|         runtimeError.value = event.value |         runtimeError.value = event.value | ||||||
| @ -173,24 +181,30 @@ async function updatePreview() { | |||||||
|       `window.__modules__ = {};window.__css__ = ''`, |       `window.__modules__ = {};window.__css__ = ''`, | ||||||
|       ...modules, |       ...modules, | ||||||
|       ` |       ` | ||||||
| import { createApp as _createApp } from "vue" |   import { createApp as _createApp } from "vue" | ||||||
|    |    | ||||||
| if (window.__app__) { |   if (window.__app__) { | ||||||
|     window.__app__.unmount() |     window.__app__.unmount() | ||||||
|     document.getElementById('app').innerHTML = '' |     document.getElementById('app').innerHTML = '' | ||||||
| } |   } | ||||||
|    |    | ||||||
| document.getElementById('__sfc-styles').innerHTML = window.__css__ |   document.getElementById('__sfc-styles').innerHTML = window.__css__ | ||||||
| const app = window.__app__ = _createApp(__modules__["${MAIN_FILE}"].default) |   const app = window.__app__ = _createApp(__modules__["${MAIN_FILE}"].default) | ||||||
| app.config.errorHandler = e => console.error(e) |   app.config.errorHandler = e => console.error(e) | ||||||
| app.mount('#app')`.trim() |   app.mount('#app')`.trim() | ||||||
|     ]) |     ]) | ||||||
|   } catch (e) { |   } catch (e) { | ||||||
|     runtimeError.value = e.message |     runtimeError.value = (e as Error).message | ||||||
|   } |   } | ||||||
| } | } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | <template> | ||||||
|  |   <div class="preview-container" ref="container"></div> | ||||||
|  |   <Message :err="runtimeError" /> | ||||||
|  |   <Message v-if="!runtimeError" :warn="runtimeWarning" /> | ||||||
|  | </template> | ||||||
|  | 
 | ||||||
| <style> | <style> | ||||||
| .preview-container, | .preview-container, | ||||||
| iframe { | iframe { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user