Add editor experience features: auto-run, layouts, resizable panels, Emmet, vim/emacs

- Auto-run toggle with localStorage persistence (toolbar checkbox)
- Layout selector: default, top/bottom, editor-only, preview-only
- Resizable panels via drag dividers with double-click reset
- Emmet abbreviation expansion for HTML, CSS, and JSX
- Vim and Emacs keybinding modes with lazy-loaded CDN libraries
- Shared preferences module for localStorage management
- Editor hooks for tab switch and mode change callbacks
This commit is contained in:
root
2026-02-26 11:19:14 -06:00
parent 463b563423
commit 7f51af17a3
8 changed files with 407 additions and 7 deletions

View File

@@ -47,11 +47,11 @@ button:hover { background: var(--accent-hover); }
}
.btn-small:hover { color: var(--text); background: var(--border); }
/* Grid layout — 2 columns: editor | preview+console */
/* Grid layout — 2 columns with dividers: editor | divider | preview+console */
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
grid-template-columns: 1fr 4px 1fr;
grid-template-rows: 1fr 4px 1fr;
height: calc(100vh - var(--toolbar-h));
}
.panel { position: relative; border: 1px solid var(--border); overflow: hidden; display: flex; flex-direction: column; }
@@ -65,9 +65,39 @@ button:hover { background: var(--accent-hover); }
}
/* Editor panel — full left column */
.panel-editor { grid-column: 1; grid-row: 1 / 3; display: flex; flex-direction: column; }
.panel-preview { grid-column: 2; grid-row: 1; }
.panel-console { grid-column: 2; grid-row: 2; }
.panel-editor { grid-column: 1; grid-row: 1 / 4; display: flex; flex-direction: column; }
.divider-col { grid-column: 2; grid-row: 1 / 4; cursor: col-resize; background: var(--border); }
.divider-col:hover { background: var(--accent); }
.panel-preview { grid-column: 3; grid-row: 1; }
.divider-row { grid-column: 3; grid-row: 2; cursor: row-resize; background: var(--border); }
.divider-row:hover { background: var(--accent); }
.panel-console { grid-column: 3; grid-row: 3; }
/* Disable iframe pointer-events while dragging */
body.resizing iframe { pointer-events: none; }
/* Layout variants */
.layout-top-bottom .panel-editor { grid-column: 1 / 4; grid-row: 1; }
.layout-top-bottom .divider-col { display: none; }
.layout-top-bottom .panel-preview { grid-column: 1; grid-row: 3; }
.layout-top-bottom .divider-row { grid-column: 1 / 4; grid-row: 2; cursor: row-resize; }
.layout-top-bottom .panel-console { grid-column: 2 / 4; grid-row: 3; }
.layout-top-bottom {
grid-template-columns: 1fr 4px 1fr;
grid-template-rows: 1fr 4px 1fr;
}
.layout-editor-only .panel-editor { grid-column: 1 / 4; grid-row: 1 / 4; }
.layout-editor-only .divider-col,
.layout-editor-only .divider-row,
.layout-editor-only .panel-preview,
.layout-editor-only .panel-console { display: none; }
.layout-preview-only .panel-preview { grid-column: 1 / 4; grid-row: 1 / 4; }
.layout-preview-only .divider-col,
.layout-preview-only .divider-row,
.layout-preview-only .panel-editor,
.layout-preview-only .panel-console { display: none; }
/* Tab bar */
.tab-bar {
@@ -139,6 +169,41 @@ button:hover { background: var(--accent-hover); }
.console-info { color: #3dc9b0; }
.console-debug { color: #888; }
/* Vim status bar */
.vim-status-bar {
display: none;
height: 24px;
line-height: 24px;
padding: 0 10px;
font-family: 'Cascadia Code', 'Fira Code', monospace;
font-size: 12px;
background: var(--surface);
color: var(--text-dim);
border-top: 1px solid var(--border);
flex-shrink: 0;
}
/* Auto-run toggle */
.auto-run-toggle {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: var(--text-dim);
cursor: pointer;
user-select: none;
}
.auto-run-toggle input { cursor: pointer; }
/* Dividers */
.divider { background: var(--border); transition: background 0.15s; z-index: 2; }
/* Layout/keybinding selects — match framework select */
#layout-mode, #keybinding-mode {
background: var(--bg); color: var(--text); border: 1px solid var(--border);
padding: 4px 6px; border-radius: 4px; font-size: 12px; cursor: pointer;
}
/* Toast */
.toast {
position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);