ui: Find & Replace, multi-line comments, indent guides, font size

Find & Replace (Ctrl+H):
- Two-row find bar: expand › arrow shows replace row (VS Code pattern)
- Replace button (Enter): replaces current match, advances to next
- Replace All: replaces all occurrences, shows count
- Find input searches live; replace row preserved across open/close

Multi-line comment toggle (Ctrl+/):
- With multi-line selection: checks if ALL lines are commented
  → if all commented, uncomments all; otherwise comments all
- Empty lines in selection are skipped
- After toggle: block stays selected (same as VS Code)
- Single-line behavior unchanged

Indent guides:
- CSS repeating-linear-gradient on #vv-editor-inner: 1px guide every 2ch
- First guide at indent level 1 (col 2); then 4, 6, 8... matching 2-space indents
- Shows through the transparent overlay; scales automatically with font size
- Very subtle (0.045 opacity) — visible but not distracting

Font size A− / A+ in status bar (9–22px):
- Persisted to localStorage, restored on every editor open
- Updates textarea, overlay, gutter, and cur-line height together
- All scroll calculations use vvLH() (fontSizePx × 1.5) instead of
  hardcoded 18 — find nav, goto, select-next-occurrence all correct at
  any font size

Also: fixed duplicate id="vv-find-close" in goto bar (now .vv-find-x class)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gmer4Lfe
2026-05-31 21:20:46 -04:00
co-authored by Claude Sonnet 4.6
parent beac98427d
commit 586c7c36c1
2 changed files with 176 additions and 30 deletions
+40 -9
View File
@@ -368,24 +368,37 @@
#vv-editor-body::-webkit-scrollbar-thumb { background: #2a2a2a; border-radius: 4px; }
#vv-editor-body::-webkit-scrollbar-thumb:hover { background: #444; }
/* Editor find bar */
#vv-editor-find { display: none; align-items: center; gap: 6px; padding: 5px 8px;
/* Editor find bar — two-row (find + optional replace) */
#vv-editor-find { display: none; flex-direction: column; gap: 0; padding: 5px 8px;
margin-bottom: 5px; background: #161616; border: 1px solid #333;
border-radius: 4px; font-size: 12px; }
#vv-editor-find.vv-find-open { display: flex; }
#vv-find-input { flex: 1; min-width: 0; background: #0d0d0d; border: 1px solid #333;
color: #ddd; padding: 4px 8px; border-radius: 3px;
font-family: monospace; font-size: 12px; }
#vv-find-input:focus { border-color: #6495ed; outline: none; box-shadow: 0 0 0 1px rgba(100,149,237,0.25); }
.vv-find-row { display: flex; align-items: center; gap: 6px; }
.vv-replace-row { display: none; align-items: center; gap: 6px;
margin-top: 4px; padding-top: 4px; border-top: 1px solid #222; }
.vv-replace-row.vv-repl-open { display: flex; }
.vv-find-expand { font-size: 10px; color: #555; cursor: pointer; flex-shrink: 0;
width: 14px; text-align: center; user-select: none;
transition: transform 0.15s; line-height: 1; }
.vv-find-expand:hover { color: #aaa; }
.vv-find-expand.open { transform: rotate(90deg); color: #888; }
#vv-find-input, #vv-replace-input {
flex: 1; min-width: 0; background: #0d0d0d; border: 1px solid #333;
color: #ddd; padding: 4px 8px; border-radius: 3px;
font-family: monospace; font-size: 12px; }
#vv-find-input:focus, #vv-replace-input:focus {
border-color: #6495ed; outline: none; box-shadow: 0 0 0 1px rgba(100,149,237,0.25); }
#vv-find-input.vv-find-no-match { border-color: #c62828; box-shadow: 0 0 0 1px rgba(198,40,40,0.3); }
.vv-find-count { font-size: 11px; color: #555; white-space: nowrap; flex-shrink: 0; min-width: 64px; text-align: right; }
.vv-find-nav-btn{ padding: 2px 8px !important; }
#vv-find-close { color: #555; cursor: pointer; font-size: 16px; line-height: 1;
.vv-repl-btn { border-color: #444 !important; color: #777 !important; }
.vv-repl-btn:hover { border-color: #777 !important; color: #ddd !important; }
.vv-find-x { color: #555; cursor: pointer; font-size: 16px; line-height: 1;
padding: 0 2px; flex-shrink: 0; }
#vv-find-close:hover { color: #aaa; }
.vv-find-x:hover { color: #aaa; }
/* Editor status bar */
#vv-editor-status { display: none; align-items: center; gap: 14px; padding: 3px 10px;
#vv-editor-status { display: none; align-items: center; gap: 10px; padding: 3px 10px;
background: #0e0e0e; border: 1px solid #2e2e2e; border-top: none;
border-radius: 0 0 4px 4px; font-family: monospace;
font-size: 10px; color: #444; user-select: none; flex-shrink: 0; }
@@ -393,6 +406,24 @@
.vv-es-pos { color: #666; }
.vv-es-sel { color: #555; }
.vv-es-lang { margin-left: auto; color: #4a6a7a; }
/* Status bar action buttons */
.vv-es-btn { background: none; border: none; cursor: pointer; color: #555;
font-size: 10px; padding: 1px 4px; border-radius: 2px; font-family: monospace; }
.vv-es-btn:hover { color: #aaa; background: rgba(255,255,255,0.05); }
.vv-es-btn.active { color: #6495ed; }
#vv-es-fontsize { color: #444; font-size: 10px; min-width: 26px; text-align: center; }
/* Indent guides — 1px lines every 2 chars, starting at indent level 1 */
#vv-editor-inner {
position: relative; flex: 1; overflow: hidden;
background-color: #0d0d0d;
background-image: repeating-linear-gradient(
90deg,
rgba(255,255,255,0.045) 0 1px,
transparent 1px 2ch
);
background-position: calc(12px + 2ch) 0;
}
/* Go to line bar */
#vv-goto-bar { display: none; align-items: center; gap: 8px; padding: 5px 8px;