From 95412dd0ad68ccf84ff6801df1d3ee3c6c4c6467 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 18 Nov 2025 13:27:42 -0500 Subject: [PATCH] feat: CodeMirror integration and bug fixes This commit addresses several issues related to the migration from TipTap to CodeMirror: - **CodeMirror Initialization Fixes:** - Resolved 'Unrecognized extension value' errors by refactoring to explicitly import individual CodeMirror extensions instead of relying on . This ensures proper singleton usage and prevents module duplication issues. - Updated and to utilize these individual extensions. - **Text Wrapping Enabled:** - Added to the extensions in and to enable text wrapping in the CodeMirror editors. - **Content Saving Fixes:** - Corrected content binding for CodeMirror editor instances in various IDAA components: - (description, location_text, attend_text) - (content, notes) - (content) - (description, notes) - (description, notes) - Ensured that the prop of is correctly bound to the respective state variables in the parent components, and these state variables are initialized with existing content. - **Save Button Enablement:** - Fixed an issue in where the Save button was not enabling on content changes. The logic now directly compares the and with the original object's content, ensuring reactivity. --- TODO.md | 11 +- package-lock.json | 166 ++++++++++--- package.json | 3 +- src/lib/ae_core/ae_core_functions.ts | 22 +- .../app_components/e_app_codemirror_v5.svelte | 232 +++++------------- src/lib/elements/codemirror_modules.ts | 169 +++++++++++++ .../elements/element_codemirror_editor.svelte | 190 ++++++++------ .../element_codemirror_wrapper.svelte | 26 +- ...a_comp__archive_content_obj_id_edit.svelte | 8 +- .../ae_idaa_comp__archive_obj_id_edit.svelte | 8 +- ...idaa_comp__post_comment_obj_id_edit.svelte | 4 +- .../bb/ae_idaa_comp__post_obj_id_edit.svelte | 22 +- .../ae_idaa_comp__event_obj_id_edit.svelte | 16 +- 13 files changed, 540 insertions(+), 337 deletions(-) create mode 100644 src/lib/elements/codemirror_modules.ts diff --git a/TODO.md b/TODO.md index a3f1c759..6f291ae3 100644 --- a/TODO.md +++ b/TODO.md @@ -35,9 +35,9 @@ This is a list of tasks to be completed before the next event/show/conference. - Svelte Stores: `ae__stores.ts` - Consolidate helper files (e.g., `ae_core_functions.ts`) into the above files. - [ ] **Functions & Variables:** - - Enforce `snake_case` for all function and variable names. - - Deprecate and remove `camelCase`. - - Deprecate and refactor ambiguous `handle_` prefixes. + - [x] Enforce `snake_case` for all function and variable names. + - [x] Deprecate and remove `camelCase`. + - [x] Deprecate and refactor ambiguous `handle_` prefixes. - [ ] **Object & Property Naming:** - Use singular nouns for objects and properties (e.g., `example.id`, not `examples.id`). - [ ] **List Suffixes:** @@ -262,6 +262,7 @@ These functions are frequently used and critical to the application's data flow. --- ## Scott's Quick Notes + These are just quick notes, thoughts, ideas, and reminders for myself. - I need to explain the purpose and thought behind the data_store element or component and how it works. The short version: It is a hierarchy of database results, but only the last result is returned. The results are filtered by data store key/code, then by account_id, and then by for_type and for_id. This allows for shared data among accounts, but also account specific overrides. Finally, it allows for object specific overrides. @@ -277,6 +278,7 @@ These are just quick notes, thoughts, ideas, and reminders for myself. - Reports are manually created and shared among events and clients. We need to be able to enable/disable reports per event. Each report will have its own Svelte page. ### Events modules and main landing pages + - **Presentation Management** - Manage - Session Search @@ -308,6 +310,7 @@ These are just quick notes, thoughts, ideas, and reminders for myself. - **Admin or Manage or Configure Event** ### New Directory Structure? + - src/lib/ - Shared libraries and components - src/routes/core/ - Core Aether management module - sub-directories for: @@ -359,4 +362,4 @@ These are just quick notes, thoughts, ideas, and reminders for myself. - src/lib/components/ - Shared Svelte components - src/lib/stores/ - Shared Svelte stores - src/assets/ - Assets like CSS, Tailwind config, etc.??? -- static/ - Static assets like images, fonts, etc. \ No newline at end of file +- static/ - Static assets like images, fonts, etc. diff --git a/package-lock.json b/package-lock.json index fd4b74ac..2e0c103c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,14 @@ { "name": "osit-aether-app-svelte", - "version": "3.9.6", + "version": "3.11.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "osit-aether-app-svelte", - "version": "3.9.6", + "version": "3.11.18", "dependencies": { + "@codemirror/basic-setup": "^0.20.0", "@codemirror/commands": "^6.8.1", "@codemirror/gutter": "^0.19.9", "@codemirror/lang-css": "^6.3.1", @@ -24,7 +25,6 @@ "@popperjs/core": "^2.11.0", "@tailwindcss/vite": "^4.1.10", "axios": "^1.7.0", - "codemirror": "^6.0.1", "dayjs": "^1.11.10", "dexie": "^4.0.0", "flowbite-svelte": "^1.7.0", @@ -34,7 +34,6 @@ "openai": "^5.20.1", "qrcode": "^1.5.4", "shadcn-svelte": "^1.0.0", - "svelte-codemirror-editor": "^2.1.0", "svelte-persisted-store": "^0.12.0", "typescript-eslint": "^8.47.0" }, @@ -103,6 +102,112 @@ "@lezer/common": "^1.0.0" } }, + "node_modules/@codemirror/basic-setup": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@codemirror/basic-setup/-/basic-setup-0.20.0.tgz", + "integrity": "sha512-W/ERKMLErWkrVLyP5I8Yh8PXl4r+WFNkdYVSzkXYPQv2RMPSkWpr2BgggiSJ8AHF/q3GuApncDD8I4BZz65fyg==", + "deprecated": "In version 6.0, this package has been renamed to just 'codemirror'", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^0.20.0", + "@codemirror/commands": "^0.20.0", + "@codemirror/language": "^0.20.0", + "@codemirror/lint": "^0.20.0", + "@codemirror/search": "^0.20.0", + "@codemirror/state": "^0.20.0", + "@codemirror/view": "^0.20.0" + } + }, + "node_modules/@codemirror/basic-setup/node_modules/@codemirror/autocomplete": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-0.20.3.tgz", + "integrity": "sha512-lYB+NPGP+LEzAudkWhLfMxhTrxtLILGl938w+RcFrGdrIc54A+UgmCoz+McE3IYRFp4xyQcL4uFJwo+93YdgHw==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^0.20.0", + "@codemirror/state": "^0.20.0", + "@codemirror/view": "^0.20.0", + "@lezer/common": "^0.16.0" + } + }, + "node_modules/@codemirror/basic-setup/node_modules/@codemirror/commands": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-0.20.0.tgz", + "integrity": "sha512-v9L5NNVA+A9R6zaFvaTbxs30kc69F6BkOoiEbeFw4m4I0exmDEKBILN6mK+GksJtvTzGBxvhAPlVFTdQW8GB7Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^0.20.0", + "@codemirror/state": "^0.20.0", + "@codemirror/view": "^0.20.0", + "@lezer/common": "^0.16.0" + } + }, + "node_modules/@codemirror/basic-setup/node_modules/@codemirror/language": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-0.20.2.tgz", + "integrity": "sha512-WB3Bnuusw0xhVvhBocieYKwJm04SOk5bPoOEYksVHKHcGHFOaYaw+eZVxR4gIqMMcGzOIUil0FsCmFk8yrhHpw==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^0.20.0", + "@codemirror/view": "^0.20.0", + "@lezer/common": "^0.16.0", + "@lezer/highlight": "^0.16.0", + "@lezer/lr": "^0.16.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/basic-setup/node_modules/@codemirror/lint": { + "version": "0.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-0.20.3.tgz", + "integrity": "sha512-06xUScbbspZ8mKoODQCEx6hz1bjaq9m8W8DxdycWARMiiX1wMtfCh/MoHpaL7ws/KUMwlsFFfp2qhm32oaCvVA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^0.20.0", + "@codemirror/view": "^0.20.2", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/basic-setup/node_modules/@codemirror/state": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz", + "integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==", + "license": "MIT" + }, + "node_modules/@codemirror/basic-setup/node_modules/@codemirror/view": { + "version": "0.20.7", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz", + "integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^0.20.0", + "style-mod": "^4.0.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@codemirror/basic-setup/node_modules/@lezer/common": { + "version": "0.16.1", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.16.1.tgz", + "integrity": "sha512-qPmG7YTZ6lATyTOAWf8vXE+iRrt1NJd4cm2nJHK+v7X9TsOF6+HtuU/ctaZy2RCrluxDb89hI6KWQ5LfQGQWuA==", + "license": "MIT" + }, + "node_modules/@codemirror/basic-setup/node_modules/@lezer/highlight": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-0.16.0.tgz", + "integrity": "sha512-iE5f4flHlJ1g1clOStvXNLbORJoiW4Kytso6ubfYzHnaNo/eo5SKhxs4wv/rtvwZQeZrK3we8S9SyA7OGOoRKQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^0.16.0" + } + }, + "node_modules/@codemirror/basic-setup/node_modules/@lezer/lr": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.16.3.tgz", + "integrity": "sha512-pau7um4eAw94BEuuShUIeQDTf3k4Wt6oIUOYxMmkZgDHdqtIcxWND4LRxi8nI9KuT4I1bXQv67BCapkxt7Ywqw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^0.16.0" + } + }, "node_modules/@codemirror/commands": { "version": "6.10.0", "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.0.tgz", @@ -511,16 +616,33 @@ } }, "node_modules/@codemirror/search": { - "version": "6.5.11", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", - "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-0.20.1.tgz", + "integrity": "sha512-ROe6gRboQU5E4z6GAkNa2kxhXqsGNbeLEisbvzbOeB7nuDYXUZ70vGIgmqPu0tB+1M3F9yWk6W8k2vrFpJaD4Q==", "license": "MIT", "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/state": "^0.20.0", + "@codemirror/view": "^0.20.0", "crelt": "^1.0.5" } }, + "node_modules/@codemirror/search/node_modules/@codemirror/state": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-0.20.1.tgz", + "integrity": "sha512-ms0tlV5A02OK0pFvTtSUGMLkoarzh1F8mr6jy1cD7ucSC2X/VLHtQCxfhdSEGqTYlQF2hoZtmLv+amqhdgbwjQ==", + "license": "MIT" + }, + "node_modules/@codemirror/search/node_modules/@codemirror/view": { + "version": "0.20.7", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-0.20.7.tgz", + "integrity": "sha512-pqEPCb9QFTOtHgAH5XU/oVy9UR/Anj6r+tG5CRmkNVcqSKEPmBU05WtN/jxJCFZBXf6HumzWC9ydE4qstO3TxQ==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^0.20.0", + "style-mod": "^4.0.0", + "w3c-keyname": "^2.2.4" + } + }, "node_modules/@codemirror/state": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", @@ -3966,22 +4088,6 @@ "node": ">=6" } }, - "node_modules/codemirror": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", - "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/commands": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/search": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -7124,16 +7230,6 @@ "typescript": ">=5.0.0" } }, - "node_modules/svelte-codemirror-editor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/svelte-codemirror-editor/-/svelte-codemirror-editor-2.1.0.tgz", - "integrity": "sha512-WGkSsIYNpVcOVxaQPkmdBQhaGyKLmg6pgaS/b+7guRb4eikrbXYtvNFuW2AzKJi8ZbLhSngrf3SRiZOuwuskrQ==", - "license": "MIT", - "peerDependencies": { - "codemirror": "^6.0.0", - "svelte": "^5.0.0" - } - }, "node_modules/svelte-eslint-parser": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-1.4.0.tgz", diff --git a/package.json b/package.json index 95935d27..5117e052 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,7 @@ }, "type": "module", "dependencies": { + "@codemirror/basic-setup": "^0.20.0", "@codemirror/commands": "^6.8.1", "@codemirror/gutter": "^0.19.9", "@codemirror/lang-css": "^6.3.1", @@ -86,7 +87,6 @@ "@popperjs/core": "^2.11.0", "@tailwindcss/vite": "^4.1.10", "axios": "^1.7.0", - "codemirror": "^6.0.1", "dayjs": "^1.11.10", "dexie": "^4.0.0", "flowbite-svelte": "^1.7.0", @@ -96,7 +96,6 @@ "openai": "^5.20.1", "qrcode": "^1.5.4", "shadcn-svelte": "^1.0.0", - "svelte-codemirror-editor": "^2.1.0", "svelte-persisted-store": "^0.12.0", "typescript-eslint": "^8.47.0" } diff --git a/src/lib/ae_core/ae_core_functions.ts b/src/lib/ae_core/ae_core_functions.ts index 73b809ec..1585f0b0 100644 --- a/src/lib/ae_core/ae_core_functions.ts +++ b/src/lib/ae_core/ae_core_functions.ts @@ -58,7 +58,7 @@ import { add_url_params, clean_headers } from '$lib/ae_core/core__api_helpers'; const ae_promises: key_val = {}; // Promise; // Updated 2024-03-29 -async function handle_load_ae_obj_id__site_domain({ +async function load_ae_obj_id__site_domain({ api_cfg, fqdn, try_cache = false, @@ -73,7 +73,7 @@ async function handle_load_ae_obj_id__site_domain({ }) { if (log_lvl) { console.log( - `*** handle_load_ae_obj_id__site_domain() *** api.base_url=${api_cfg.base_url}, fqdn=${fqdn}, timeout=${timeout}` + `*** load_ae_obj_id__site_domain() *** api.base_url=${api_cfg.base_url}, fqdn=${fqdn}, timeout=${timeout}` ); } @@ -121,7 +121,7 @@ async function handle_load_ae_obj_id__site_domain({ } // Updated 2024-03-29 -async function handle_load_ae_obj_code__data_store({ +async function load_ae_obj_code__data_store({ api_cfg, code, data_type = 'text', @@ -143,7 +143,7 @@ async function handle_load_ae_obj_code__data_store({ log_lvl?: number; }) { if (log_lvl) { - console.log(`*** handle_get_data_store_obj_w_code() *** code=${code}`); + console.log(`*** load_ae_obj_code__data_store() *** code=${code}`); } if (!code) { @@ -271,7 +271,7 @@ async function handle_load_ae_obj_code__data_store({ } // Updated 2024-03-27 -async function handle_update_ae_obj_id_crud({ +async function update_ae_obj_id_crud({ api_cfg, object_type, object_id, @@ -561,7 +561,7 @@ async function update_ae_obj_id_crud_v2({ return ae_promises.api_update__ae_obj; } -async function handle_download_export__obj_type({ +async function download_export__obj_type({ api_cfg, get_obj_type, // The type of object to return: event_badge, event_presenter, sponsorship, etc. for_obj_type, // Usually for an account, event, event_exhibit, or sponsorship_cfg @@ -588,7 +588,7 @@ async function handle_download_export__obj_type({ params?: key_val; log_lvl?: number; }) { - console.log('*** ae_core_functions.js: handle_download_export__obj_type() ***'); + console.log('*** ae_core_functions.js: download_export__obj_type() ***'); const task_id = for_obj_id; @@ -651,8 +651,8 @@ const export_obj = { add_url_params: add_url_params, clean_headers: clean_headers, - handle_load_ae_obj_id__site_domain: handle_load_ae_obj_id__site_domain, - handle_load_ae_obj_code__data_store: handle_load_ae_obj_code__data_store, + load_ae_obj_id__site_domain: load_ae_obj_id__site_domain, + load_ae_obj_code__data_store: load_ae_obj_code__data_store, load_ae_obj_id__activity_log: load_ae_obj_id__activity_log, load_ae_obj_li__activity_log: load_ae_obj_li__activity_log, @@ -671,9 +671,9 @@ const export_obj = { qry_ae_obj_li__user_email: qry_ae_obj_li__user_email, auth_ae_obj__user_id_change_password: auth_ae_obj__user_id_change_password, - handle_update_ae_obj_id_crud: handle_update_ae_obj_id_crud, + update_ae_obj_id_crud: update_ae_obj_id_crud, update_ae_obj_id_crud_v2: update_ae_obj_id_crud_v2, - handle_download_export__obj_type: handle_download_export__obj_type, + download_export__obj_type: download_export__obj_type, generate_qr_code: generate_qr_code, js_generate_qr_code: js_generate_qr_code }; diff --git a/src/lib/app_components/e_app_codemirror_v5.svelte b/src/lib/app_components/e_app_codemirror_v5.svelte index 4fce3f7b..5e595965 100644 --- a/src/lib/app_components/e_app_codemirror_v5.svelte +++ b/src/lib/app_components/e_app_codemirror_v5.svelte @@ -5,50 +5,7 @@ import { browser } from '$app/environment'; import { onMount, onDestroy } from 'svelte'; - import { - EditorView, - keymap, - highlightSpecialChars, - drawSelection, - highlightActiveLine, - dropCursor, - rectangularSelection, - crosshairCursor, - gutter, - GutterMarker, - highlightActiveLineGutter, - lineNumbers, - placeholder as placeholderExt - } from '@codemirror/view'; - import { EditorState, RangeSet, StateEffect, type Extension, Text } from '@codemirror/state'; - import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands'; - import { indentUnit } from '@codemirror/language'; - import { languages } from '@codemirror/language-data'; - // import { - // defaultHighlightStyle, syntaxHighlighting, indentOnInput, - // bracketMatching, foldGutter, foldKeymap - // } from "@codemirror/language" - // import { - // defaultHighlightStyle, syntaxHighlighting, indentOnInput, - // bracketMatching, foldGutter, foldKeymap - // } from "@codemirror/language" - // import { - // searchKeymap, highlightSelectionMatches - // } from "@codemirror/search" - // import { - // autocompletion, completionKeymap, closeBrackets, - // closeBracketsKeymap - // } from "@codemirror/autocomplete" - // import {lintKeymap} from "@codemirror/lint" - - // import { } from '@codemirror/gutter'; // Merged into @codemirror/view - import { basicSetup } from 'codemirror'; - import { markdown, markdownLanguage } from '@codemirror/lang-markdown'; - // import { css } from '@codemirror/lang-css'; - // import { javascript } from '@codemirror/lang-javascript'; - // import { json } from '@codemirror/lang-json'; - // import { html } from '@codemirror/lang-html'; - import { oneDark } from '@codemirror/theme-one-dark'; + import { ensureCodeMirrorModules } from '../elements/codemirror_modules'; // Props export let content: string = 'test test test test'; @@ -56,9 +13,7 @@ // export let language: Extension = markdown(); // javascript() export let theme_mode: string = 'light'; // 'dark' | 'light' - export let theme: Extension = EditorView.baseTheme(); // EditorView.baseTheme(); // oneDark - - export let extensions: Extension[] = []; + export let extensions: any[] = []; // Changed to any[] because Extension type is not directly available here export let editable: boolean = true; export let readonly: boolean = false; @@ -73,146 +28,75 @@ export { classes as class }; let editor_element: HTMLDivElement; - let editorView: EditorView; + let editorView: any; // Changed to any - // theme = [ - // // baseTheme, - // theme, - // ]; - // console.log(`Theme:`, theme); + let cm_modules: any; // To hold the dynamically loaded CodeMirror modules - if (theme_mode == 'dark') { - theme = oneDark; - } else { - // theme = EditorView.baseTheme({ - // "&": { - // color: "black", - // backgroundColor: "white" - // }, - // ".cm-cursor, .cm-dropCursor": { borderLeftColor: "#000" }, - // "&.cm-focused .cm-selectionBackground, ::selection": { - // backgroundColor: "#B7D5FF" - // }, - // "&.cm-focused .cm-selectionForeground": { color: "black" } - // }); - } + async function initializeCodeMirror() { + if (!browser) return; - if (editable) { - extensions.push(EditorView.editable.of(true)); - } else { - // extensions.push(EditorState.editable.of(false)); - } - if (readonly) { - extensions.push(EditorState.readOnly.of(true)); - } else { - // extensions.push(EditorState.readOnly.of(false)); - } + cm_modules = await ensureCodeMirrorModules(); + if (!cm_modules) return; - if (placeholder) { - extensions.push(placeholderExt(placeholder)); - } + // Reactive declaration for extensions + let 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 - if (show_line_numbers) { - // extensions.push(lineNumbers({ class: "line-numbers" })); - } else { - // extensions.push(gutter({ class: "hidden-gutter" })); - // extensions.push(lineNumbers(false)); - // extensions.push(gutter(false)); - // extensions.pop(); - // extensions.slice(extensions.indexOf(lineNumbers), 1); - } + // 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, - if (wrap_lines) { - extensions.push(EditorView.lineWrapping); - } + ...extensions // Add any custom extensions passed in props + ].filter(Boolean); - if (use_tab) { - extensions.push(keymap.of([indentWithTab])); - } - if (tab_size) { - extensions.push(indentUnit.of(' '.repeat(tab_size))); - } - - // Enable spell check - extensions.push(EditorView.contentAttributes.of({ spellcheck: 'true' })); - - // let languages = [ - // { name: 'CSS', mode: 'css' }, - // { name: 'HTML', mode: 'html' }, - // { name: 'JavaScript', mode: 'javascript' }, - // { name: 'Markdown', mode: 'markdown' }, - // { name: 'Python', mode: 'python' }, - // ]; - - // Reactive declaration for extensions - $: editor_extensions = [ - basicSetup, - // gutter({class: "hidden-gutter"}), - // A line number gutter - lineNumbers(false), - // A gutter with code folding markers - // foldGutter(false), - // lineWrapping(false), - // EditorView.lineWrapping, // Enable line wrapping - - // EditorView.indentUnit.of(" ".repeat(tab_size)), - // EditorView.tabSize.of(tab_size), - // indentUnit.of(" ".repeat(tab_size)), - - // EditorView.editable.of(editable), - // EditorState.readOnly.of(readonly), - - // EditorState.tabSize.of(tab_size), - // keymap.of([indentWithTab]), - // placeholderExt(placeholder), - // language, - markdown({ base: markdownLanguage, codeLanguages: languages }), - // javascript({typescript: true}), - // json(), - // css(), - // html(), - theme, - - ...extensions - ]; - - // baseTheme = { - // ".cm-o-replacement": { - // display: "inline-block", - // width: ".5em", - // height: ".5em", - // borderRadius: ".25em" - // }, - // "&light .cm-o-replacement": { - // backgroundColor: "#04c" - // }, - // "&dark .cm-o-replacement": { - // backgroundColor: "#5bf" - // } - // }, - // let dimensions = { - // width: '100%', - // height: 'calc(100vh - 48px)' // Adjust for header and other elements - // }; - // editorView.setSize(dimensions.width, dimensions.height); - - // Initialize CodeMirror on mount - onMount(() => { - editorView = new EditorView({ - state: EditorState.create({ + editorView = new cm_modules.EditorView({ + state: cm_modules.EditorState.create({ doc: content, extensions: editor_extensions }), - parent: editor_element, // document.body - dispatch: (transaction) => { + parent: editor_element, + dispatch: (transaction: any) => { editorView.update([transaction]); if (transaction.docChanged) { new_content = editorView.state.doc.toString(); - const newContent = editorView.state.doc.toString(); - // dispatch('change', newContent); } } }); + } + + // Initialize CodeMirror on mount + onMount(async () => { + await initializeCodeMirror(); }); // Clean up on destroy @@ -221,11 +105,11 @@ }); // Update editor content when `content` prop changes - $: if (editorView && editorView.state.doc.toString() !== content) { + $: if (cm_modules && editorView && editorView.state.doc.toString() !== content) { editorView.setState( - EditorState.create({ + cm_modules.EditorState.create({ doc: content, - extensions: editor_extensions + extensions: editor_extensions // Use the reactive extensions }) ); } diff --git a/src/lib/elements/codemirror_modules.ts b/src/lib/elements/codemirror_modules.ts new file mode 100644 index 00000000..eaec35ae --- /dev/null +++ b/src/lib/elements/codemirror_modules.ts @@ -0,0 +1,169 @@ +let _cmCache: { + EditorView?: any; + EditorState?: any; + basicSetup?: any; + markdown?: any; + markdownLanguage?: any; + keymap?: any; + defaultKeymap?: any; + history?: any; + historyKeymap?: any; + lineNumbers?: any; + highlightSpecialChars?: any; + drawSelection?: any; + dropCursor?: any; + rectangularSelection?: any; + crosshairCursor?: any; + highlightActiveLine?: any; + highlightActiveLineGutter?: any; + indentWithTab?: any; + indentUnit?: any; + autocompletion?: any; + completionKeymap?: any; + closeBrackets?: any; + closeBracketsKeymap?: any; + searchKeymap?: any; + highlightSelectionMatches?: any; + lintKeymap?: any; + EditorState_allowMultipleSelections?: any; + EditorView_lineWrapping?: any; + EditorView_editable?: any; + EditorView_contentAttributes?: any; + bracketMatching?: any; + foldGutter?: any; + foldKeymap?: any; + indentOnInput?: any; + languages?: any; + oneDark?: any; + placeholderExt?: any; +} | null = null; + +// ...existing code... +import { browser } from '$app/environment'; + +type CMCache = { + EditorView: any; + EditorState: any; + basicSetup?: any; + markdown?: any; + markdownLanguage?: any; + keymap?: any; + defaultKeymap?: any; + history?: any; + historyKeymap?: any; + lineNumbers?: any; + highlightSpecialChars?: any; + drawSelection?: any; + dropCursor?: any; + rectangularSelection?: any; + crosshairCursor?: any; + highlightActiveLine?: any; + highlightActiveLineGutter?: any; + indentWithTab?: any; + indentUnit?: any; + autocompletion?: any; + completionKeymap?: any; + closeBrackets?: any; + closeBracketsKeymap?: any; + searchKeymap?: any; + highlightSelectionMatches?: any; + lintKeymap?: any; + EditorState_allowMultipleSelections?: any; + EditorView_lineWrapping?: any; + EditorView_editable?: any; + EditorView_contentAttributes?: any; + bracketMatching?: any; + foldGutter?: any; + foldKeymap?: any; + indentOnInput?: any; + languages?: any; + oneDark?: any; + placeholderExt?: any; +} | null; + +const GLOBAL_KEY = '__cm_singleton_modules_v1'; + +export async function ensureCodeMirrorModules(): Promise { + if (!browser) return null; + + // Use a single global object so HMR/multiple module copies reuse same instance + const globalAny = globalThis as any; + if (globalAny[GLOBAL_KEY]) return globalAny[GLOBAL_KEY] as CMCache; + + const [ + viewMod, + stateMod, + basicSetupMod, + markdownMod, + commandsMod, + languageMod, + autocompleteMod, + searchMod, + lintMod, + themeOneDarkMod + ] = await Promise.all([ + import('@codemirror/view'), + import('@codemirror/state'), + import('@codemirror/basic-setup'), + import('@codemirror/lang-markdown'), + import('@codemirror/commands'), + import('@codemirror/language'), + import('@codemirror/autocomplete'), + import('@codemirror/search'), + import('@codemirror/lint'), + import('@codemirror/theme-one-dark') + ]); + + const cache: CMCache = { + EditorView: viewMod.EditorView, + keymap: viewMod.keymap, + lineNumbers: viewMod.lineNumbers, + highlightSpecialChars: viewMod.highlightSpecialChars, + drawSelection: viewMod.drawSelection, + dropCursor: viewMod.dropCursor, + rectangularSelection: viewMod.rectangularSelection, + crosshairCursor: viewMod.crosshairCursor, + highlightActiveLine: viewMod.highlightActiveLine, + highlightActiveLineGutter: viewMod.highlightActiveLineGutter, + EditorView_lineWrapping: viewMod.EditorView.lineWrapping, + EditorView_editable: viewMod.EditorView.editable, + EditorView_contentAttributes: viewMod.EditorView.contentAttributes, + placeholderExt: viewMod.placeholder, + + EditorState: stateMod.EditorState, + EditorState_allowMultipleSelections: stateMod.EditorState.allowMultipleSelections, + EditorState_readOnly: stateMod.EditorState.readOnly, + + basicSetup: basicSetupMod?.basicSetup, + + markdown: markdownMod?.markdown, + markdownLanguage: markdownMod?.markdownLanguage, + languages: languageMod?.languages, // From @codemirror/language-data, often re-exported by @codemirror/language + + defaultKeymap: (commandsMod && commandsMod.defaultKeymap) || [], + history: commandsMod?.history, + historyKeymap: commandsMod?.historyKeymap || [], + indentWithTab: commandsMod?.indentWithTab, + + indentUnit: languageMod?.indentUnit, + indentOnInput: languageMod?.indentOnInput, + bracketMatching: languageMod?.bracketMatching, + foldGutter: languageMod?.foldGutter, + foldKeymap: languageMod?.foldKeymap, + + autocompletion: autocompleteMod?.autocompletion, + completionKeymap: autocompleteMod?.completionKeymap, + closeBrackets: autocompleteMod?.closeBrackets, + closeBracketsKeymap: autocompleteMod?.closeBracketsKeymap, + + searchKeymap: searchMod?.searchKeymap, + highlightSelectionMatches: searchMod?.highlightSelectionMatches, + + lintKeymap: lintMod?.lintKeymap, + + oneDark: themeOneDarkMod?.oneDark + }; + + globalAny[GLOBAL_KEY] = cache; + return cache; +} \ No newline at end of file diff --git a/src/lib/elements/element_codemirror_editor.svelte b/src/lib/elements/element_codemirror_editor.svelte index ded9e535..48f0141d 100644 --- a/src/lib/elements/element_codemirror_editor.svelte +++ b/src/lib/elements/element_codemirror_editor.svelte @@ -1,80 +1,130 @@
-
- - - -
- -
+
+ + + +
+
+ \ No newline at end of file diff --git a/src/lib/elements/element_codemirror_wrapper.svelte b/src/lib/elements/element_codemirror_wrapper.svelte index 3b4a177b..2dcfca5f 100644 --- a/src/lib/elements/element_codemirror_wrapper.svelte +++ b/src/lib/elements/element_codemirror_wrapper.svelte @@ -1,15 +1,21 @@
- -
+ {#if browser} + + {:else} + +
Editor (client only)
+ {/if} + \ No newline at end of file diff --git a/src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_content_obj_id_edit.svelte b/src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_content_obj_id_edit.svelte index 7d1bcd63..5cdfd553 100644 --- a/src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_content_obj_id_edit.svelte +++ b/src/routes/idaa/(idaa)/archives/[archive_id]/ae_idaa_comp__archive_content_obj_id_edit.svelte @@ -36,9 +36,9 @@ let prom_api__archive_content_obj: any = $state(); let prom_api__archive_content_obj__hosted_file: any = $state(); - let description_new_html = $state(''); + let description_new_html = $state($idaa_slct.archive_content_obj?.description ?? ''); let description_changed = $state(false); - let notes_new_html = $state(''); + let notes_new_html = $state($idaa_slct.archive_content_obj?.notes ?? ''); let notes_changed = $state(false); let disable_submit_btn = $state(false); @@ -422,7 +422,7 @@