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;
|
||||
--font-size: 12px;
|
||||
--terminal-height: 400px;
|
||||
--wrap-mode: pre-wrap;
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
@ -243,12 +242,28 @@
|
|||
font-family: "SF Mono", Monaco, "Cascadia Code", "Courier New", monospace;
|
||||
font-size: var(--font-size);
|
||||
line-height: 1.3;
|
||||
white-space: var(--wrap-mode);
|
||||
word-wrap: normal;
|
||||
overflow-wrap: normal;
|
||||
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 {
|
||||
background: #2a2a2a;
|
||||
border: 2px solid #ff9800;
|
||||
|
|
@ -1020,7 +1035,7 @@
|
|||
columns: 1, // 1, 2, 3, or 'fit'
|
||||
fontSize: 12, // 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">
|
||||
↓
|
||||
</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>
|
||||
`).join('');
|
||||
|
||||
// Initialize scroll handlers and button visibility
|
||||
// Initialize scroll handlers, button visibility, and mark rule lines
|
||||
sessionsToRender.forEach(s => {
|
||||
initSessionOutputUI(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
|
||||
if (session.expanded) {
|
||||
$output.innerHTML = session.output;
|
||||
// Mark box-drawing lines to prevent wrapping
|
||||
markRuleLines($output);
|
||||
}
|
||||
session.outputRenderedLength = session.output.length;
|
||||
|
||||
|
|
@ -1979,8 +2031,16 @@
|
|||
// Apply terminal height
|
||||
root.style.setProperty('--terminal-height', `${state.settings.terminalHeight}px`);
|
||||
|
||||
// Apply wrap mode
|
||||
root.style.setProperty('--wrap-mode', state.settings.wrapText ? 'pre-wrap' : 'pre');
|
||||
// Apply wrap mode to all terminal elements
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -458,8 +458,9 @@ const server = Bun.serve<SessionData>({
|
|||
// Initialize in-memory session state
|
||||
sessionStates.set(session.id, createDefaultSessionState());
|
||||
|
||||
// Create terminal emulator with default size (will be resized later)
|
||||
const termSession = createTerminal(80, 24);
|
||||
// Create terminal emulator wide enough to avoid premature wrapping
|
||||
// Browser CSS handles responsive display; we just need ANSI processing
|
||||
const termSession = createTerminal(300, 50);
|
||||
sessionTerminals.set(session.id, termSession);
|
||||
|
||||
console.debug(
|
||||
|
|
|
|||
Loading…
Reference in a new issue