diff --git a/GEMINI.md b/GEMINI.md index 1ac69a74..6634ae7e 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -73,3 +73,14 @@ The rich text editor component, previously based on Tiptap (`shad-editor`), has - **Wrapper Component Update:** The existing editor wrapper (`src/lib/elements/element_tiptap_editor.svelte`) was renamed to `src/lib/elements/element_codemirror_wrapper.svelte` and refactored to utilize the new CodeMirror component. Tiptap-specific logic and props were removed. - **Component Usage Updates:** All Svelte components that previously imported and used the Tiptap wrapper were updated to import `element_codemirror_wrapper.svelte`. Unsupported props such as `default_minimal`, `show_button_kv`, `bind:new_html`, and `bind:changed` were removed from their usage. - **Dependency Cleanup:** `npm install` was run to remove unneeded packages, and `npm run format` was executed to ensure consistent code style. + +### CodeMirror Bug Fixes (2025-11-18) + +Following the initial migration to CodeMirror, several issues were identified and resolved: + +- **Initialization Errors:** An "Unrecognized extension value" error was fixed by refactoring `codemirror_modules.ts` to explicitly import individual CodeMirror extensions instead of relying on the `basicSetup` bundle. This ensures proper singleton usage and prevents module duplication. +- **Text Wrapping:** The `EditorView.lineWrapping` extension was added to `element_codemirror_editor.svelte` and `e_app_codemirror_v5.svelte` to enable text wrapping. +- **Content Saving:** Content binding issues were resolved in various IDAA components by correctly binding the `html_text` prop of the CodeMirror wrapper to the corresponding state variables. +- **Save Button Enablement:** The logic for enabling the "Save" button was fixed in `ae_idaa_comp__post_obj_id_edit.svelte` to correctly detect changes in the CodeMirror editor. +- **Editor Buttons:** A `TypeError` in the editor's formatting buttons was fixed by using `EditorSelection.range()` to correctly create selection ranges. +- **Component Renaming:** The `Tiptap_editor` component alias was renamed to `CodeMirror_wrapper` across the project for clarity, and the wrapper file was renamed to `element_codemirror_editor_wrapper.svelte`. diff --git a/TODO.md b/TODO.md index 6f291ae3..b089804c 100644 --- a/TODO.md +++ b/TODO.md @@ -49,7 +49,7 @@ This is a list of tasks to be completed before the next event/show/conference. ### 2. Component Standardization -- [ ] **CodeMirror Migration:** Plan and execute the replacement of the `shad-editor` and potentially other text editors (like Tiptap) with `CodeMirror` to standardize rich text editing. +- [x] **CodeMirror Migration:** Replaced all instances of the old Tiptap editor with a new CodeMirror wrapper. This includes fixing initialization errors, enabling text wrapping, and ensuring content is correctly saved. - [ ] **Component Review:** Audit third-party components to understand their conventions and isolate them from internal standards. --- @@ -83,7 +83,7 @@ These functions are frequently used and critical to the application's data flow. --- -- [ ] Improve the `e_app_codemirror_v5.svelte` component and plan the migration from Tiptap to CodeMirror for rich text editing. +- [x] Improve the `e_app_codemirror_v5.svelte` component and plan the migration from Tiptap to CodeMirror for rich text editing. The migration is complete, and the component has been improved to handle individual extensions and fix various bugs. --- @@ -190,7 +190,7 @@ These functions are frequently used and critical to the application's data flow. - [ ] **UI/UX:** - [ ] Improve the UI for creating and editing posts and comments. - - [ ] Add a rich text editor for a better writing experience. + - [x] Add a rich text editor for a better writing experience. - [ ] **Features:** - [ ] Implement user-specific features, such as editing their own posts and comments. diff --git a/src/lib/app_components/e_app_codemirror_v5.svelte b/src/lib/app_components/e_app_codemirror_v5.svelte index 5e595965..561a0911 100644 --- a/src/lib/app_components/e_app_codemirror_v5.svelte +++ b/src/lib/app_components/e_app_codemirror_v5.svelte @@ -38,7 +38,7 @@ cm_modules = await ensureCodeMirrorModules(); if (!cm_modules) return; - // Reactive declaration for extensions + // Reactive declaration for extensions let editor_extensions = [ // Core extensions cm_modules.highlightSpecialChars(), @@ -63,7 +63,10 @@ ...cm_modules.completionKeymap, ...cm_modules.lintKeymap ]), - cm_modules.markdown({ base: cm_modules.markdownLanguage, codeLanguages: cm_modules.languages }), + 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 diff --git a/src/lib/elements/codemirror_modules.ts b/src/lib/elements/codemirror_modules.ts index d7e5484d..0c697030 100644 --- a/src/lib/elements/codemirror_modules.ts +++ b/src/lib/elements/codemirror_modules.ts @@ -1,170 +1,170 @@ 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; + 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; + 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; + 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; + // 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 [ + 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, + 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, - EditorSelection: stateMod.EditorSelection, - EditorState_allowMultipleSelections: stateMod.EditorState.allowMultipleSelections, - EditorState_readOnly: stateMod.EditorState.readOnly, + EditorState: stateMod.EditorState, + EditorSelection: stateMod.EditorSelection, + EditorState_allowMultipleSelections: stateMod.EditorState.allowMultipleSelections, + EditorState_readOnly: stateMod.EditorState.readOnly, - basicSetup: basicSetupMod?.basicSetup, + basicSetup: basicSetupMod?.basicSetup, - markdown: markdownMod?.markdown, - markdownLanguage: markdownMod?.markdownLanguage, - languages: languageMod?.languages, // From @codemirror/language-data, often re-exported by @codemirror/language + 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, + 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, + 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, + autocompletion: autocompleteMod?.autocompletion, + completionKeymap: autocompleteMod?.completionKeymap, + closeBrackets: autocompleteMod?.closeBrackets, + closeBracketsKeymap: autocompleteMod?.closeBracketsKeymap, - searchKeymap: searchMod?.searchKeymap, - highlightSelectionMatches: searchMod?.highlightSelectionMatches, + searchKeymap: searchMod?.searchKeymap, + highlightSelectionMatches: searchMod?.highlightSelectionMatches, - lintKeymap: lintMod?.lintKeymap, + lintKeymap: lintMod?.lintKeymap, - oneDark: themeOneDarkMod?.oneDark - }; + oneDark: themeOneDarkMod?.oneDark + }; - globalAny[GLOBAL_KEY] = cache; - return cache; -} \ No newline at end of file + globalAny[GLOBAL_KEY] = cache; + return cache; +} diff --git a/src/lib/elements/element_codemirror_editor.svelte b/src/lib/elements/element_codemirror_editor.svelte index fb563ac6..030c0da4 100644 --- a/src/lib/elements/element_codemirror_editor.svelte +++ b/src/lib/elements/element_codemirror_editor.svelte @@ -1,131 +1,151 @@
-
- - - -
-
-
\ No newline at end of file +
+ + + +
+
+ diff --git a/src/lib/elements/element_codemirror_editor_wrapper.svelte b/src/lib/elements/element_codemirror_editor_wrapper.svelte index 2dcfca5f..639ad3b5 100644 --- a/src/lib/elements/element_codemirror_editor_wrapper.svelte +++ b/src/lib/elements/element_codemirror_editor_wrapper.svelte @@ -1,21 +1,21 @@
- {#if browser} - - {:else} - -
Editor (client only)
- {/if} -
\ No newline at end of file + {#if browser} + + {:else} + +
Editor (client only)
+ {/if} + 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 1c844765..f7d4413c 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 @@ -421,11 +421,12 @@ + +