Skip to content

Commit

Permalink
Merge pull request #8 from not-three/feat/lang-detection
Browse files Browse the repository at this point in the history
feat: new language detection
feat: support for many new languages / file types
feat: new share overview dialog after saving
feat: download file
ci: optimized build pipeline to be much faster
style: multiple smaller fixes
  • Loading branch information
scolastico authored Nov 11, 2024
2 parents 80f1490 + 0f8776b commit 993ca0c
Show file tree
Hide file tree
Showing 48 changed files with 1,914 additions and 140 deletions.
1 change: 1 addition & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ components: components/**
server: server/**
pages: pages/**
public: public/**
lib: lib/**
11 changes: 11 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ jobs:
username: ${{github.actor}}
password: ${{secrets.GITHUB_TOKEN}}

- name: Install node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Prebuild static files
run: pnpm generate && mv .output .output-prebuild

- uses: mr-smithers-excellent/docker-build-push@v6
id: docker_build
with:
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ jobs:
username: ${{github.actor}}
password: ${{secrets.GITHUB_TOKEN}}

- name: Install node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install pnpm
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Prebuild static files
run: pnpm generate && mv .output .output-prebuild

- uses: mr-smithers-excellent/docker-build-push@v6
id: docker_build
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ logs
!.env.example

db.sqlite
.output-prebuild
10 changes: 2 additions & 8 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,10 @@ ENV NUXT_APP_BASE_URL=$NUXT_APP_BASE_URL
RUN pnpm build
RUN cp -rL .output /app/SERVER_OUTPUT

RUN pnpm generate
RUN test -d .output-prebuilt || pnpm generate
RUN test -d .output-prebuilt && rm .output && mv .output-prebuilt .output || true
RUN cp -rL .output/public /app/CLIENT_OUTPUT

RUN mkdir /app/SERVER_OUTPUT/public/node_modules && \
mkdir /app/CLIENT_OUTPUT/node_modules && \
cp -rL node_modules/monaco-editor-workers /app/SERVER_OUTPUT/public/node_modules/monaco-editor-workers && \
cp -rL node_modules/monaco-editor /app/SERVER_OUTPUT/public/node_modules/monaco-editor && \
cp -rL node_modules/monaco-editor-workers /app/CLIENT_OUTPUT/node_modules/monaco-editor-workers && \
cp -rL node_modules/monaco-editor /app/CLIENT_OUTPUT/node_modules/monaco-editor

### Production Image
FROM node:20-alpine
LABEL maintainer="Joschua Becker EDV <support@scolasti.co>"
Expand Down
28 changes: 28 additions & 0 deletions assets/css/scrollbar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@

* {
--sb-track-color: #000;
--sb-thumb-color: #fff;
--sb-size: 8px;
}

*::-webkit-scrollbar {
width: var(--sb-size)
}

*::-webkit-scrollbar-track {
background: var(--sb-track-color);
border-radius: 0;
}

*::-webkit-scrollbar-thumb {
background: var(--sb-thumb-color);
border-radius: 0;

}

@supports not selector(::-webkit-scrollbar) {
body {
scrollbar-color: var(--sb-thumb-color)
var(--sb-track-color);
}
}
127 changes: 98 additions & 29 deletions components/editor.vue
Original file line number Diff line number Diff line change
@@ -1,60 +1,129 @@
<template>
<div ref="container" class="w-full h-full" />
<div ref="container" class="w-full flex-grow" />
</template>

<script lang="ts" setup>
const container = ref(null) as any as Ref<HTMLDivElement>;
import { ref, watch, onMounted, onBeforeUnmount, type Ref } from 'vue';
import * as monaco from 'monaco-editor';
import { buildWorkerDefinition } from 'monaco-editor-workers';
import type { EditorEvents, LanguageInfo } from '~/lib/monaco/types';
import { detectLanguageFromContent, debounce } from '~/lib/monaco/utils';
import { setupMonaco } from '~/lib/monaco/setup';
import { languageDefinitions } from '~/lib/monaco/languages';
const props = defineProps<{
const props = withDefaults(defineProps<{
modelValue: string;
readonly?: boolean;
}>();
theme?: string;
forcedLanguage?: string|null;
}>(), {
readonly: false,
theme: 'vs-dark'
});
const emit = defineEmits<EditorEvents>();
const container = ref(null) as any as Ref<HTMLDivElement>;
let editor: monaco.editor.IStandaloneCodeEditor | null = null;
const emit = defineEmits(['update:modelValue', 'loaded', 'save', 'duplicate', 'new']);
const currentLanguage = ref<string>('plaintext');
let editor: any = null;
const setEditorLanguage = (languageId: string) => {
if (currentLanguage.value === languageId) return;
if (editor && editor.getModel()) {
console.log('Setting language:', languageId);
currentLanguage.value = languageId;
monaco.editor.setModelLanguage(editor.getModel()!, languageId);
}
};
const updateLanguage = debounce((content: string) => {
if (editor) {
const detectedLanguage = detectLanguageFromContent(content);
emit('language-detected', detectedLanguage);
if (!props.forcedLanguage) setEditorLanguage(detectedLanguage);
}
}, 500);
onMounted(async () => {
const [monaco, worker] = await Promise.all([
import('monaco-editor'),
import('monaco-editor-workers'),
]);
worker.buildWorkerDefinition(
buildWorkerDefinition(
'../node_modules/monaco-editor-workers/dist/workers',
import.meta.url,
false
);
const ed = monaco.editor.create(container.value, {
value: props.modelValue + '',
language: 'javascript',
theme: 'vs-dark',
readOnly: !!props.readonly,
await setupMonaco();
const availableLanguages = languageDefinitions.map(lang => ({
id: lang.id,
extensions: lang.extensions,
aliases: lang.aliases || [],
mimeTypes: lang.mimeTypes || [],
} as LanguageInfo));
emit('loaded-languages', availableLanguages);
const initialDetectedLanguage = detectLanguageFromContent(props.modelValue);
emit('language-detected', initialDetectedLanguage);
const initialLanguage = props.forcedLanguage || initialDetectedLanguage;
currentLanguage.value = initialLanguage;
const model = monaco.editor.createModel(props.modelValue, initialLanguage);
editor = monaco.editor.create(container.value, {
model,
theme: props.theme,
readOnly: props.readonly,
automaticLayout: true,
minimap: { enabled: true },
scrollBeyondLastLine: false,
fontSize: 14,
tabSize: 2,
wordWrap: 'on',
lineNumbers: 'on',
});
editor = ed;
ed.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, async () => {
// Register keyboard shortcuts
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, () => {
emit('save');
});
ed.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyD, async () => {
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyD, () => {
emit('duplicate');
});
ed.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyN, async () => {
editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyN, () => {
emit('new');
});
ed.onDidChangeModelContent(async () => {
setTimeout(() => {
emit('update:modelValue', ed.getValue());
}, 1);
// emit('update:modelValue', ed.getValue());
// Handle content changes
editor.onDidChangeModelContent(() => {
const value = editor?.getValue() || '';
emit('update:modelValue', value);
updateLanguage(value);
});
emit('loaded');
});
onBeforeUnmount(() => {
editor?.dispose();
});
// Watch for prop changes
watch(() => props.readonly, (value) => {
editor.updateOptions({ readOnly: !!value });
editor?.updateOptions({ readOnly: !!value });
});
watch(() => props.modelValue, (newValue, oldValue) => {
if (newValue === oldValue || newValue === editor?.getValue()) return;
editor?.setValue(newValue);
console.log('Model value updated:', newValue);
updateLanguage(newValue);
});
watch(() => props.theme, (newTheme) => {
editor?.updateOptions({ theme: newTheme });
});
watch(() => props.modelValue, (n, o) => {
if (n === o || n === editor.getValue()) return;
editor.setValue(n);
watch(() => props.forcedLanguage, (newLanguage) => {
if (!editor) return;
if (newLanguage) setEditorLanguage(newLanguage);
else setEditorLanguage(detectLanguageFromContent(editor.getValue()));
});
</script>
4 changes: 2 additions & 2 deletions components/gh-badge.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div class="fixed top-0 right-0 translate-x-1/2 -translate-y-1/2 z-50">
<div class="fixed top-0 right-0 translate-x-1/2 -translate-y-1/2 z-50 pointer-events-none">
<div
class="bg-white shadow-md p-2 select-none hover:opacity-100 opacity-40 transition-opacity duration-300"
class="bg-white shadow-md p-2 select-none hover:opacity-100 opacity-40 transition-opacity duration-300 pointer-events-auto"
:class="{
'cursor-pointer': props.to !== undefined,
}"
Expand Down
Loading

0 comments on commit 993ca0c

Please sign in to comment.