Messy working Monaco editor.

This commit is contained in:
2025-02-22 23:14:21 -07:00
parent 5070b36f6c
commit 9e1bb74656
11 changed files with 152 additions and 31 deletions

View File

@@ -1,7 +1,8 @@
{ {
"$schema": "https://json.schemastore.org/prettierrc", "$schema": "https://json.schemastore.org/prettierrc",
"semi": false, "semi": true,
"singleQuote": true, "singleQuote": true,
"trailingComma": "all",
"printWidth": 100, "printWidth": 100,
"plugins": [ "plugins": [
"prettier-plugin-tailwindcss" "prettier-plugin-tailwindcss"

View File

@@ -1,6 +1,6 @@
import pluginVue from 'eslint-plugin-vue' import pluginVue from 'eslint-plugin-vue';
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript' import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript';
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting' import skipFormatting from '@vue/eslint-config-prettier/skip-formatting';
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines: // To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript' // import { configureVueProject } from '@vue/eslint-config-typescript'
@@ -21,4 +21,4 @@ export default defineConfigWithVueTs(
pluginVue.configs['flat/essential'], pluginVue.configs['flat/essential'],
vueTsConfigs.recommended, vueTsConfigs.recommended,
skipFormatting, skipFormatting,
) );

7
ui/package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.0.6", "@tailwindcss/vite": "^4.0.6",
"monaco-editor": "^0.52.2",
"tailwindcss": "^4.0.6", "tailwindcss": "^4.0.6",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-router": "^4.5.0" "vue-router": "^4.5.0"
@@ -4156,6 +4157,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/monaco-editor": {
"version": "0.52.2",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
"integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
"license": "MIT"
},
"node_modules/mrmime": { "node_modules/mrmime": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz",

View File

@@ -15,6 +15,7 @@
}, },
"dependencies": { "dependencies": {
"@tailwindcss/vite": "^4.0.6", "@tailwindcss/vite": "^4.0.6",
"monaco-editor": "^0.52.2",
"tailwindcss": "^4.0.6", "tailwindcss": "^4.0.6",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-router": "^4.5.0" "vue-router": "^4.5.0"

View File

@@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router' import { RouterLink, RouterView } from 'vue-router';
</script> </script>
<template> <template>
@@ -38,10 +38,10 @@ import { RouterLink, RouterView } from 'vue-router'
<footer class="bg-gray-100"> <footer class="bg-gray-100">
<div class="mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8"> <div class="mx-auto max-w-7xl px-4 py-4 sm:px-6 lg:px-8">
<p class="text-center text-sm text-gray-500">© {{new Date().getFullYear()}} Glancr. All rights reserved.</p> <p class="text-center text-sm text-gray-500">
© {{ new Date().getFullYear() }} Glancr - All rights reserved.
</p>
</div> </div>
</footer> </footer>
</div> </div>
</template> </template>
<style scoped></style>

View File

@@ -1,6 +1,16 @@
<script lang="ts"> <script setup lang="ts">
import { ref, defineAsyncComponent } from 'vue'
const MonacoEditor = defineAsyncComponent(() => import('@/components/MonacoEditor.vue'))
const code = ref('# Hello from markdown\n\n\t> this is a markdown note')
</script> </script>
<template> <template>
<h1 class="font-bold text-red-500">Editor Placeholder</h1> <Suspense>
<MonacoEditor v-model="code" language="markdown" :options="{ readOnly: false }" />
<template #fallback>
<div>Loading editor...</div>
</template>
</Suspense>
</template> </template>

View File

@@ -0,0 +1,96 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import { editor } from 'monaco-editor/esm/vs/editor/editor.api.js'
import { setupMonaco } from '@/workers/monaco.workers'
import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution'
// Load common languages for code fences
const loadLanguage = async (language: string) => {
switch (language) {
case 'javascript':
await import('monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution')
break
case 'typescript':
await import('monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution')
break
case 'python':
await import('monaco-editor/esm/vs/basic-languages/python/python.contribution')
break
// Add more languages as needed
}
}
// Initialize monaco workers
setupMonaco()
const props = defineProps<{
modelValue: string
language?: string
options?: editor.IStandaloneEditorConstructionOptions
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()
const editorContainer = ref<HTMLElement | null>(null)
let editorInstance: editor.IStandaloneCodeEditor | null = null
// Function to detect and load languages from code fences
const loadLanguagesFromContent = async (content: string) => {
const codeBlockRegex = /```(\w+)/g
const matches = [...content.matchAll(codeBlockRegex)]
const languages = [...new Set(matches.map((match) => match[1]))]
await Promise.all(languages.map((lang) => loadLanguage(lang)))
}
onMounted(async () => {
if (!editorContainer.value) return
// Load initial languages from content
await loadLanguagesFromContent(props.modelValue)
// Create editor instance
editorInstance = editor.create(editorContainer.value, {
value: props.modelValue,
language: 'markdown',
theme: 'vs-dark',
automaticLayout: true,
minimap: { enabled: false },
wordWrap: 'on',
quickSuggestions: false,
...props.options,
})
// Handle content changes
editorInstance.onDidChangeModelContent(() => {
const value = editorInstance?.getValue() ?? ''
emit('update:modelValue', value)
loadLanguagesFromContent(value) // Load languages when content changes
})
})
// Watch for external value changes
watch(
() => props.modelValue,
async (newValue) => {
if (editorInstance && newValue !== editorInstance.getValue()) {
await loadLanguagesFromContent(newValue)
editorInstance.setValue(newValue)
}
},
)
// Cleanup
onBeforeUnmount(() => {
if (editorInstance) {
editorInstance.dispose()
}
})
</script>
<template>
<div ref="editorContainer" class="w-full h-full min-h-[300px]"></div>
</template>

View File

@@ -1,6 +1,6 @@
import { createRouter, createWebHistory } from 'vue-router' import { createRouter, createWebHistory } from 'vue-router';
import HomeView from '../views/HomeView.vue' import HomeView from '../views/HomeView.vue';
import NotFoundView from '../views/NotFoundView.vue' import NotFoundView from '../views/NotFoundView.vue';
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@@ -19,11 +19,11 @@ const router = createRouter({
component: () => import('../views/AboutView.vue'), component: () => import('../views/AboutView.vue'),
}, },
{ {
path: "/:catchAll(.*)", path: '/:catchAll(.*)',
name: 'not-found', name: 'not-found',
component: NotFoundView, component: NotFoundView,
}, },
], ],
}) });
export default router export default router;

View File

@@ -3,5 +3,3 @@
<h1>This is an about page</h1> <h1>This is an about page</h1>
</div> </div>
</template> </template>
<style></style>

View File

@@ -0,0 +1,12 @@
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
self.MonacoEnvironment = {
getWorker() {
return new editorWorker();
},
};
export function setupMonaco() {
// This function is intentionally empty - it just needs to be imported
// to set up the workers
}

View File

@@ -1,20 +1,16 @@
import { fileURLToPath, URL } from 'node:url' import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite' import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue';
import vueDevTools from 'vite-plugin-vue-devtools' import vueDevTools from 'vite-plugin-vue-devtools';
import tailwindcss from '@tailwindcss/vite' import tailwindcss from '@tailwindcss/vite';
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [ plugins: [vue(), vueDevTools(), tailwindcss()],
vue(),
vueDevTools(),
tailwindcss(),
],
resolve: { resolve: {
alias: { alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)) '@': fileURLToPath(new URL('./src', import.meta.url)),
}, },
}, },
}) });