commit 7105b71acb35228513bc1ee1535d81d79f47ef14
parent 997ba7a55f128d8a1aaaa2054683e03262a9dfeb
Author: Hunter
Date: Wed, 6 Aug 2025 16:24:15 -0400
first pass at line wrapping (press F2 to toggle)
Diffstat:
| M | index.html | | | 114 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
1 file changed, 109 insertions(+), 5 deletions(-)
diff --git a/index.html b/index.html
@@ -7,8 +7,8 @@
<link rel="icon" href="resources/rollerskate.png">
<link rel="manifest" href="manifest.json">
<script type="module">
- import {EditorView, keymap, placeholder, lineNumbers} from "https://esm.sh/@codemirror/view@6"
- import {EditorState, Compartment} from "https://esm.sh/@codemirror/state@6"
+ import {EditorView, keymap, placeholder, lineNumbers, Decoration} from "https://esm.sh/@codemirror/view@6"
+ import {EditorState, Compartment, StateField, StateEffect} from "https://esm.sh/@codemirror/state@6"
import {defaultKeymap, indentWithTab, undo, redo, undoDepth, redoDepth, history, historyKeymap} from "https://esm.sh/@codemirror/commands@6"
import {closeBrackets, closeBracketsKeymap} from "https://esm.sh/@codemirror/autocomplete@6"
import {html} from "https://esm.sh/@codemirror/lang-html@6"
@@ -16,7 +16,7 @@
import {indentUnit} from "https://esm.sh/@codemirror/language@6"
import {search, searchKeymap, closeSearchPanel, openSearchPanel} from "https://esm.sh/@codemirror/search@6"
- window.CodeMirror = {EditorView, EditorState, Compartment, keymap, defaultKeymap, indentWithTab, html, githubDark, indentUnit, placeholder, undo, redo, undoDepth, redoDepth, history, historyKeymap, closeBrackets, closeBracketsKeymap, search, searchKeymap, closeSearchPanel, openSearchPanel, lineNumbers};
+ window.CodeMirror = {EditorView, EditorState, Compartment, keymap, defaultKeymap, indentWithTab, html, githubDark, indentUnit, placeholder, undo, redo, undoDepth, redoDepth, history, historyKeymap, closeBrackets, closeBracketsKeymap, search, searchKeymap, closeSearchPanel, openSearchPanel, lineNumbers, Decoration, StateField, StateEffect};
</script>
<style>
:root {
@@ -198,6 +198,8 @@
let isFullscreen = false;
let showLineNumbers = false;
let lineNumbersCompartment;
+ let showLineWrapping = false;
+ let lineWrappingCompartment;
function toggleFullscreen() {
const previewPane = document.querySelector('.preview-pane');
@@ -382,9 +384,11 @@
function loadEditorSettings() {
try {
showLineNumbers = localStorage.getItem('editor-line-numbers') === 'true';
+ showLineWrapping = localStorage.getItem('editor-line-wrapping') === 'true';
} catch (e) {
console.warn('Could not load editor settings from localStorage:', e);
showLineNumbers = false;
+ showLineWrapping = false;
}
}
@@ -406,6 +410,95 @@
});
}
+ function createLineWrappingPlugin() {
+ const {EditorView, Decoration, StateField, StateEffect} = window.CodeMirror;
+
+ // Line wrapping functionality
+ const CharacterWidthEffect = StateEffect.define({});
+ const extra_cycle_character_width = StateField.define({
+ create() {
+ return null;
+ },
+ update(value, tr) {
+ for (let effect of tr.effects) {
+ if (effect.is(CharacterWidthEffect)) return effect.value;
+ }
+ return value;
+ }
+ });
+
+ const character_width_listener = EditorView.updateListener.of((viewupdate) => {
+ const width = viewupdate.view.defaultCharacterWidth;
+ const current_width = viewupdate.view.state.field(extra_cycle_character_width, false);
+
+ if (current_width !== width) {
+ viewupdate.view.dispatch({
+ effects: [CharacterWidthEffect.of(width)]
+ });
+ }
+ });
+
+ const ARBITRARY_INDENT_LINE_WRAP_LIMIT = 48;
+ const line_wrapping_decorations = StateField.define({
+ create() {
+ return Decoration.none;
+ },
+ update(deco, tr) {
+ const tabSize = tr.state.tabSize;
+ const charWidth = tr.state.field(extra_cycle_character_width, false);
+ if (charWidth == null) return Decoration.none;
+
+ if (!tr.docChanged && deco !== Decoration.none) return deco;
+
+ const decorations = [];
+
+ for (let i = 0; i < tr.state.doc.lines; i++) {
+ const line = tr.state.doc.line(i + 1);
+ if (line.length === 0) continue;
+
+ let indented_chars = 0;
+ for (const ch of line.text) {
+ if (ch === '\t') {
+ indented_chars = indented_chars + tabSize;
+ } else if (ch === ' ') {
+ indented_chars = indented_chars + 1;
+ } else {
+ break;
+ }
+ }
+
+ const offset = Math.min(indented_chars, ARBITRARY_INDENT_LINE_WRAP_LIMIT) * charWidth;
+
+ const rules = document.createElement('span').style;
+ rules.setProperty('--idented', `${offset}px`);
+ rules.setProperty('text-indent', 'calc(-1 * var(--idented) - 1px)');
+ rules.setProperty('padding-left', 'calc(var(--idented) + 2px)');
+
+ const linerwapper = Decoration.line({
+ attributes: { style: rules.cssText }
+ });
+
+ decorations.push(linerwapper.range(line.from, line.from));
+ }
+ return Decoration.set(decorations, true);
+ },
+ provide: (f) => EditorView.decorations.from(f)
+ });
+
+ return [extra_cycle_character_width, character_width_listener, line_wrapping_decorations];
+ }
+
+ function toggleLineWrapping() {
+ const {EditorView} = window.CodeMirror;
+ showLineWrapping = !showLineWrapping;
+ saveEditorSetting('editor-line-wrapping', showLineWrapping);
+
+ const wrapExtensions = showLineWrapping ? [EditorView.lineWrapping, ...createLineWrappingPlugin()] : [];
+ editorView.dispatch({
+ effects: lineWrappingCompartment.reconfigure(wrapExtensions)
+ });
+ }
+
// File operations
window.saveFile = function() {
@@ -446,7 +539,7 @@
return;
}
- const {EditorView, EditorState, Compartment, keymap, defaultKeymap, indentWithTab, html, githubDark, indentUnit, placeholder, undo, redo, undoDepth, redoDepth, history, historyKeymap, closeBrackets, closeBracketsKeymap, search, searchKeymap, closeSearchPanel, openSearchPanel, lineNumbers} = window.CodeMirror;
+ const {EditorView, EditorState, Compartment, keymap, defaultKeymap, indentWithTab, html, githubDark, indentUnit, placeholder, undo, redo, undoDepth, redoDepth, history, historyKeymap, closeBrackets, closeBracketsKeymap, search, searchKeymap, closeSearchPanel, openSearchPanel, lineNumbers, Decoration, StateField, StateEffect} = window.CodeMirror;
// Load saved content and editor settings
const savedContent = loadFromStorage();
@@ -454,6 +547,7 @@
// Create compartments for dynamic extensions
lineNumbersCompartment = new Compartment();
+ lineWrappingCompartment = new Compartment();
// Create CodeMirror editor
editorView = new EditorView({
@@ -470,6 +564,7 @@
{key: "Mod-o", run: () => { window.loadFile(); return true; }},
{key: "Mod-s", run: () => { window.saveFile(); return true; }},
{key: "F1", run: () => { toggleLineNumbers(); return true; }},
+ {key: "F2", run: () => { toggleLineWrapping(); return true; }},
indentWithTab,
...searchKeymap.filter(binding => binding.key !== "Mod-f"),
...defaultKeymap
@@ -492,7 +587,8 @@
'autocapitalize': 'off',
'spellcheck': 'false'
}),
- lineNumbersCompartment.of(showLineNumbers ? lineNumbers() : [])
+ lineNumbersCompartment.of(showLineNumbers ? lineNumbers() : []),
+ lineWrappingCompartment.of([])
]
}),
parent: document.getElementById('editor')
@@ -509,6 +605,14 @@
handleViewportChange(); // Exit fullscreen mode if keyboard is open
});
+ // Initialize line wrapping if it was enabled
+ if (showLineWrapping) {
+ const wrapExtensions = [EditorView.lineWrapping, ...createLineWrappingPlugin()];
+ editorView.dispatch({
+ effects: lineWrappingCompartment.reconfigure(wrapExtensions)
+ });
+ }
+
// Initial render
updatePreview();