Files
OSIT-AE-App-Svelte/src/lib/app_components/e_app_codemirror_v5.svelte
Scott Idem 79917edffc feat(launcher): implement device heartbeat and background sync engine
- Hardened 'find_object_id' in Dexie to support 'event_' prefixed IDs.
- Implemented singleton 'LauncherBackgroundSync' in root launcher layout.
- Added V3-compliant heartbeat and room refresh cycles.
- Fixed redundant component imports and Skeleton UI prop errors.
- Added 'inc_file_li' support to event API loader.
2026-01-26 11:23:12 -05:00

213 lines
6.9 KiB
Svelte

<script lang="ts">
import { run } from 'svelte/legacy';
// This will be the wrapper for the CodeMirror editor. It should hide most of the configuration requirements.
// WARNING: This has not been fully updated to Svelte version 5. It is a work in progress.
// *** Import Svelte version 5 specific
import { browser } from '$app/environment';
import { onMount, onDestroy } from 'svelte';
import { ensureCodeMirrorModules } from '../elements/codemirror_modules';
interface Props {
// Props
content?: string;
new_content?: string;
editorView?: any; // Added to interface
// export let language: Extension = markdown(); // javascript()
theme_mode?: string; // 'dark' | 'light'
extensions?: any[]; // Changed to any[] because Extension type is not directly available here
editable?: boolean;
readonly?: boolean;
placeholder?: string;
show_line_numbers?: boolean;
wrap_lines?: boolean;
use_tab?: boolean;
tab_size?: number;
class?: string;
}
let {
content = 'test test test test',
new_content = $bindable(),
editorView = $bindable(), // Exposed for external control
theme_mode = $bindable('light'),
extensions = [],
editable = true,
readonly = false,
placeholder = 'Start typing...',
show_line_numbers = false,
wrap_lines = true,
use_tab = true,
tab_size = 4,
class: classes = ''
}: Props = $props();
let editor_element: HTMLDivElement | undefined = $state();
// let editorView: any = $state(); // Removed redundant declaration
let cm_modules: any = $state(); // To hold the dynamically loaded CodeMirror modules
let editor_extensions: any[] = $state([]);
async function initializeCodeMirror() {
if (!browser) return;
cm_modules = await ensureCodeMirrorModules();
if (!cm_modules) return;
// Reactive declaration for extensions
editor_extensions = [
// Core extensions
cm_modules.highlightSpecialChars(),
cm_modules.history(),
cm_modules.foldGutter(),
cm_modules.drawSelection(),
cm_modules.dropCursor(),
cm_modules.EditorState_allowMultipleSelections.of(true),
cm_modules.indentOnInput(),
cm_modules.bracketMatching(),
cm_modules.closeBrackets(),
cm_modules.autocompletion(),
cm_modules.rectangularSelection(),
cm_modules.crosshairCursor(),
cm_modules.highlightActiveLine(),
cm_modules.highlightActiveLineGutter(),
cm_modules.keymap.of([
...cm_modules.defaultKeymap,
...cm_modules.searchKeymap,
...cm_modules.historyKeymap,
...cm_modules.foldKeymap,
...cm_modules.completionKeymap,
...cm_modules.lintKeymap
]),
cm_modules.markdown({
base: cm_modules.markdownLanguage,
codeLanguages: cm_modules.languages
}),
theme_mode == 'dark' ? cm_modules.oneDark : cm_modules.EditorView.baseTheme(),
cm_modules.EditorView.contentAttributes.of({ spellcheck: 'true' }), // Enable spell check
// Sync document changes back to Svelte
cm_modules.EditorView.updateListener.of((update: any) => {
if (update.docChanged) {
new_content = update.state.doc.toString();
}
}),
// Conditional extensions based on props
editable ? cm_modules.EditorView.editable.of(true) : null,
readonly ? cm_modules.EditorState.readOnly.of(true) : null,
placeholder ? cm_modules.placeholderExt(placeholder) : null,
show_line_numbers ? cm_modules.lineNumbers() : null,
wrap_lines ? cm_modules.EditorView_lineWrapping : null,
use_tab ? cm_modules.keymap.of([cm_modules.indentWithTab]) : null,
tab_size ? cm_modules.indentUnit.of(' '.repeat(tab_size)) : null,
...extensions // Add any custom extensions passed in props
].filter(Boolean);
if (!editor_element) {
console.error('Editor element not found.');
return;
}
editorView = new cm_modules.EditorView({
state: cm_modules.EditorState.create({
doc: content,
extensions: editor_extensions
}),
parent: editor_element as HTMLElement
});
}
// Initialize CodeMirror on mount
onMount(async () => {
await initializeCodeMirror();
});
// Clean up on destroy
onDestroy(() => {
editorView?.destroy();
});
// Update editor content when `content` prop changes
$effect(() => {
if (editorView && content !== editorView.state.doc.toString()) {
editorView.dispatch({
changes: { from: 0, to: editorView.state.doc.length, insert: content || '' }
});
}
});
</script>
{#if browser}
<!-- flex flex-col flex-wrap items-center justify-center -->
<div
bind:this={editor_element}
class="codemirror-wrapper h-100 max-h-full w-100 max-w-6xl {classes}"
></div>
{:else}
<div class="scm-waiting {classes}">
<div class="scm-waiting__loading scm-loading">
<p class="scm-loading__text">Loading editor...</p>
</div>
<pre class="scm-pre cm-editor">{content}</pre>
</div>
{/if}
<style>
/* .codemirror-wrapper :global(.cm-focused) {
outline: none;
} */
.codemirror-wrapper {
flex-grow: 1;
/* flex-shrink: 1; */
/* flex-basis: 100%; */
/* font-size: 1rem; */
width: 100%;
max-width: 100%;
height: 100%;
max-height: 100%;
/* overflow: auto; */
/* background-color: var(--cm-background); */
/* text-wrap: normal; */
/* text-wrap: balance; */
/* text-wrap: wrap; */
/* text-wrap: break-word; */
}
.codemirror-wrapper :global(.cm-editor) {
/* font-size: .8rem; */
/* text-wrap: normal; */
/* text-wrap: balance; */
/* text-wrap: wrap; */
/* text-wrap: break-word; */
/* max-width: 100%; */
/* max-height: 100%; */
/* overflow: auto; */
/* width: 100%; */
/* height: 100%; */
/* background-color: var(--cm-background); */
/* color: var(--cm-text); */
}
/* .codemirror-wrapper :global(.cm-gutters) {
background-color: var(--cm-gutter-background);
color: var(--cm-gutter-text);
}
.codemirror-wrapper :global(.cm-gutter) {
background-color: var(--cm-gutter-background);
color: var(--cm-gutter-text);
}
.codemirror-wrapper :global(.cm-gutterElement) {
background-color: var(--cm-gutter-background);
color: var(--cm-gutter-text);
} */
</style>