Use css for wrapping text
This commit is contained in:
parent
31340fe0a8
commit
ab61445bcc
2 changed files with 72 additions and 11 deletions
|
|
@ -10,7 +10,6 @@
|
||||||
--columns: 1;
|
--columns: 1;
|
||||||
--font-size: 12px;
|
--font-size: 12px;
|
||||||
--terminal-height: 400px;
|
--terminal-height: 400px;
|
||||||
--wrap-mode: pre-wrap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
|
|
@ -243,12 +242,28 @@
|
||||||
font-family: "SF Mono", Monaco, "Cascadia Code", "Courier New", monospace;
|
font-family: "SF Mono", Monaco, "Cascadia Code", "Courier New", monospace;
|
||||||
font-size: var(--font-size);
|
font-size: var(--font-size);
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
white-space: var(--wrap-mode);
|
|
||||||
word-wrap: normal;
|
|
||||||
overflow-wrap: normal;
|
|
||||||
color: #e0e0e0;
|
color: #e0e0e0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Default: wrap mode - text wraps to fit container */
|
||||||
|
.terminal.wrap-mode {
|
||||||
|
white-space: break-spaces;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll mode - horizontal scroll, no wrapping */
|
||||||
|
.terminal.scroll-mode {
|
||||||
|
white-space: pre;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Box-drawing lines (detected via JS) should never wrap */
|
||||||
|
.terminal .rule-line {
|
||||||
|
white-space: pre;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.prompt {
|
.prompt {
|
||||||
background: #2a2a2a;
|
background: #2a2a2a;
|
||||||
border: 2px solid #ff9800;
|
border: 2px solid #ff9800;
|
||||||
|
|
@ -1020,7 +1035,7 @@
|
||||||
columns: 1, // 1, 2, 3, or 'fit'
|
columns: 1, // 1, 2, 3, or 'fit'
|
||||||
fontSize: 12, // px value
|
fontSize: 12, // px value
|
||||||
terminalHeight: 400, // px value
|
terminalHeight: 400, // px value
|
||||||
wrapText: false, // true = pre-wrap, false = pre (horizontal scroll)
|
wrapText: true, // true = wrap (mobile-friendly), false = scroll
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -1458,15 +1473,50 @@
|
||||||
title="Scroll to bottom">
|
title="Scroll to bottom">
|
||||||
↓
|
↓
|
||||||
</button>
|
</button>
|
||||||
<div class="terminal" id="output-${s.id}">${s.output}</div>
|
<div class="terminal ${state.settings.wrapText ? 'wrap-mode' : 'scroll-mode'}" id="output-${s.id}">${s.output}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`).join('');
|
||||||
|
|
||||||
// Initialize scroll handlers and button visibility
|
// Initialize scroll handlers, button visibility, and mark rule lines
|
||||||
sessionsToRender.forEach(s => {
|
sessionsToRender.forEach(s => {
|
||||||
initSessionOutputUI(s.id);
|
initSessionOutputUI(s.id);
|
||||||
updateScrollButton(s.id);
|
updateScrollButton(s.id);
|
||||||
|
// Mark box-drawing lines on initial render
|
||||||
|
if (s.expanded) {
|
||||||
|
const $output = document.getElementById(`output-${s.id}`);
|
||||||
|
if ($output) markRuleLines($output);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Box-drawing character range: U+2500 to U+257F
|
||||||
|
const BOX_DRAWING_REGEX = /[\u2500-\u257F]/g;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a line is primarily box-drawing characters (>= 60%)
|
||||||
|
* Used to prevent wrapping on separator/border lines
|
||||||
|
*/
|
||||||
|
function isRuleLine(text) {
|
||||||
|
if (!text || text.length < 3) return false;
|
||||||
|
const visibleChars = text.replace(/\s/g, '');
|
||||||
|
if (visibleChars.length < 3) return false;
|
||||||
|
const boxChars = (visibleChars.match(BOX_DRAWING_REGEX) || []).length;
|
||||||
|
return boxChars / visibleChars.length >= 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process terminal output to mark box-drawing lines as rule-lines
|
||||||
|
* This prevents wrapping on separator/border lines
|
||||||
|
*/
|
||||||
|
function markRuleLines($terminal) {
|
||||||
|
// xterm serializeAsHTML outputs content in divs (one per row)
|
||||||
|
// Each div may contain spans for styling
|
||||||
|
const rows = $terminal.querySelectorAll(':scope > div');
|
||||||
|
rows.forEach(row => {
|
||||||
|
if (isRuleLine(row.textContent)) {
|
||||||
|
row.classList.add('rule-line');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1483,6 +1533,8 @@
|
||||||
// Only mutate DOM when expanded to reduce work
|
// Only mutate DOM when expanded to reduce work
|
||||||
if (session.expanded) {
|
if (session.expanded) {
|
||||||
$output.innerHTML = session.output;
|
$output.innerHTML = session.output;
|
||||||
|
// Mark box-drawing lines to prevent wrapping
|
||||||
|
markRuleLines($output);
|
||||||
}
|
}
|
||||||
session.outputRenderedLength = session.output.length;
|
session.outputRenderedLength = session.output.length;
|
||||||
|
|
||||||
|
|
@ -1979,8 +2031,16 @@
|
||||||
// Apply terminal height
|
// Apply terminal height
|
||||||
root.style.setProperty('--terminal-height', `${state.settings.terminalHeight}px`);
|
root.style.setProperty('--terminal-height', `${state.settings.terminalHeight}px`);
|
||||||
|
|
||||||
// Apply wrap mode
|
// Apply wrap mode to all terminal elements
|
||||||
root.style.setProperty('--wrap-mode', state.settings.wrapText ? 'pre-wrap' : 'pre');
|
document.querySelectorAll('.terminal').forEach(el => {
|
||||||
|
if (state.settings.wrapText) {
|
||||||
|
el.classList.add('wrap-mode');
|
||||||
|
el.classList.remove('scroll-mode');
|
||||||
|
} else {
|
||||||
|
el.classList.add('scroll-mode');
|
||||||
|
el.classList.remove('wrap-mode');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSettingsUI() {
|
function updateSettingsUI() {
|
||||||
|
|
|
||||||
|
|
@ -458,8 +458,9 @@ const server = Bun.serve<SessionData>({
|
||||||
// Initialize in-memory session state
|
// Initialize in-memory session state
|
||||||
sessionStates.set(session.id, createDefaultSessionState());
|
sessionStates.set(session.id, createDefaultSessionState());
|
||||||
|
|
||||||
// Create terminal emulator with default size (will be resized later)
|
// Create terminal emulator wide enough to avoid premature wrapping
|
||||||
const termSession = createTerminal(80, 24);
|
// Browser CSS handles responsive display; we just need ANSI processing
|
||||||
|
const termSession = createTerminal(300, 50);
|
||||||
sessionTerminals.set(session.id, termSession);
|
sessionTerminals.set(session.id, termSession);
|
||||||
|
|
||||||
console.debug(
|
console.debug(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue