editor: add word wrap toggle to status bar
Adds a "Wrap" button to the editor status bar that toggles word wrap on/off, persisted in localStorage. Default is nowrap with horizontal scroll (standard code editor behaviour). In wrap mode the line-number gutter and current-line highlight are hidden — both rely on fixed line-height calculations that break when lines wrap visually. CSS: textarea/overlay default to white-space:pre; .vv-ed-wrap class (on #vv-editor-wrap) overrides to pre-wrap, hides gutter and cur-line. JS: vvWordWrap state, vvApplyWordWrap(), vvToggleWordWrap(); guards in vvUpdateLineNums() and vvUpdateCurLine(); restored via vvRestoreEditorPrefs() on both script and raw-conf editor entry points.
This commit is contained in:
@@ -323,7 +323,8 @@
|
||||
#vv-editor { flex-direction: column; gap: 0; }
|
||||
.vv-editor-body { font-family: monospace; font-size: 12px; background: #0d0d0d; color: #ccc;
|
||||
border: 1px solid #333; border-radius: 4px; padding: 10px 12px; resize: none;
|
||||
line-height: 1.5; scroll-behavior: auto; tab-size: 2; width: 100%; box-sizing: border-box; }
|
||||
line-height: 1.5; scroll-behavior: auto; tab-size: 2; width: 100%; box-sizing: border-box;
|
||||
white-space: pre; overflow-x: auto; }
|
||||
|
||||
/* Editor layout: gutter + inner area — unified bordered block */
|
||||
#vv-editor-wrap { display: flex; border: 1px solid #2e2e2e; border-radius: 4px 4px 0 0;
|
||||
@@ -347,7 +348,7 @@
|
||||
#vv-hl-overlay { display: none; position: absolute; top: 0; left: 0; right: 0; bottom: 0;
|
||||
margin: 0; padding: 10px 12px; box-sizing: border-box;
|
||||
font-family: monospace; font-size: 12px; line-height: 1.5; tab-size: 2;
|
||||
white-space: pre-wrap; word-break: break-all; overflow: hidden;
|
||||
white-space: pre; overflow: hidden;
|
||||
pointer-events: none; user-select: none;
|
||||
background: transparent; border: none; border-radius: 0; }
|
||||
.vv-editor-hl #vv-hl-overlay { display: block; }
|
||||
@@ -414,6 +415,12 @@
|
||||
.vv-es-btn.active { color: #6495ed; }
|
||||
#vv-es-fontsize { color: #444; font-size: 10px; min-width: 26px; text-align: center; }
|
||||
|
||||
/* Word wrap mode — applied to #vv-editor-wrap when wrap is enabled */
|
||||
#vv-editor-wrap.vv-ed-wrap .vv-editor-body { white-space: pre-wrap !important; overflow-x: hidden !important; }
|
||||
#vv-editor-wrap.vv-ed-wrap #vv-hl-overlay { white-space: pre-wrap !important; word-break: break-word !important; }
|
||||
#vv-editor-wrap.vv-ed-wrap #vv-ln-gutter { display: none; }
|
||||
#vv-editor-wrap.vv-ed-wrap #vv-cur-line { display: none !important; }
|
||||
|
||||
/* Indent guides — 1px lines every 2 chars, starting at indent level 1 */
|
||||
#vv-editor-inner {
|
||||
position: relative; flex: 1; overflow: hidden;
|
||||
|
||||
@@ -944,6 +944,7 @@ Still the same two servers, two households, the same media stack running itself.
|
||||
<button class="vv-es-btn" onclick="vvFontSize(-1)" title="Decrease font size">A−</button>
|
||||
<span id="vv-es-fontsize">12px</span>
|
||||
<button class="vv-es-btn" onclick="vvFontSize(1)" title="Increase font size">A+</button>
|
||||
<button class="vv-es-btn" id="vv-es-wrap-btn" onclick="vvToggleWordWrap()" title="Word wrap OFF — click to enable">Wrap</button>
|
||||
<span class="vv-es-lang" id="vv-es-lang"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1031,6 +1032,7 @@ const VV_PAIRS = {'(':')', '[':']', '{':'}', '"':'"', "'":"'", '`':'`'};
|
||||
const VV_CLOSE = new Set([')', ']', '}', '"', "'", '`']);
|
||||
// Editor display prefs
|
||||
let vvFontSizePx = parseInt(localStorage.getItem('vv-editor-fontsize') || '12') || 12;
|
||||
let vvWordWrap = localStorage.getItem('vv-editor-wordwrap') === '1';
|
||||
// Line height helper — used everywhere instead of hardcoded 18
|
||||
function vvLH() { return vvFontSizePx * 1.5; }
|
||||
|
||||
@@ -2693,6 +2695,7 @@ function vvEditRawConf(file) {
|
||||
|
||||
function vvShowRawConfMode(title) {
|
||||
vvEditorReset();
|
||||
vvRestoreEditorPrefs();
|
||||
document.getElementById('vv-editor-status').classList.add('vv-ed-active');
|
||||
document.getElementById('vv-es-lang').textContent = 'Bash / Config';
|
||||
document.getElementById('vv-undo-btn').style.display = '';
|
||||
@@ -2836,7 +2839,7 @@ function vvSyncHlScroll() {
|
||||
function vvUpdateLineNums() {
|
||||
const gutter = document.getElementById('vv-ln-gutter');
|
||||
const ta = document.getElementById('vv-editor-body');
|
||||
if (!gutter || !ta) return;
|
||||
if (!gutter || !ta || vvWordWrap) return;
|
||||
const lines = ta.value.split('\n').length;
|
||||
const curLn = ta.value.slice(0, ta.selectionStart).split('\n').length;
|
||||
let html = '';
|
||||
@@ -2851,7 +2854,7 @@ function vvUpdateLineNums() {
|
||||
function vvUpdateCurLine() {
|
||||
const ta = document.getElementById('vv-editor-body');
|
||||
const cl = document.getElementById('vv-cur-line');
|
||||
if (!cl || !ta) return;
|
||||
if (!cl || !ta || vvWordWrap) return;
|
||||
const lineH = vvLH();
|
||||
const padTop = 10; // overlay padding-top
|
||||
const lineN = ta.value.slice(0, ta.selectionStart).split('\n').length - 1; // 0-indexed
|
||||
@@ -3058,6 +3061,30 @@ function vvRestoreEditorPrefs() {
|
||||
const fs_el = document.getElementById('vv-es-fontsize');
|
||||
if (fs_el) fs_el.textContent = '12px';
|
||||
}
|
||||
vvWordWrap = localStorage.getItem('vv-editor-wordwrap') === '1';
|
||||
vvApplyWordWrap(false);
|
||||
}
|
||||
|
||||
// ── Word wrap toggle ──────────────────────────────────────────────────────────
|
||||
|
||||
function vvApplyWordWrap(save) {
|
||||
const wrap = document.getElementById('vv-editor-wrap');
|
||||
const btn = document.getElementById('vv-es-wrap-btn');
|
||||
if (!wrap) return;
|
||||
if (vvWordWrap) {
|
||||
wrap.classList.add('vv-ed-wrap');
|
||||
if (btn) { btn.classList.add('active'); btn.title = 'Word wrap ON — click to disable'; }
|
||||
} else {
|
||||
wrap.classList.remove('vv-ed-wrap');
|
||||
if (btn) { btn.classList.remove('active'); btn.title = 'Word wrap OFF — click to enable'; }
|
||||
}
|
||||
if (save) localStorage.setItem('vv-editor-wordwrap', vvWordWrap ? '1' : '0');
|
||||
vvSyncHlOverlay();
|
||||
}
|
||||
|
||||
function vvToggleWordWrap() {
|
||||
vvWordWrap = !vvWordWrap;
|
||||
vvApplyWordWrap(true);
|
||||
}
|
||||
|
||||
// ── Go to line ────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user