Skip to content

Commit 17bd977

Browse files
Dynamic column calculation
1 parent eed8ad4 commit 17bd977

File tree

3 files changed

+84
-20
lines changed

3 files changed

+84
-20
lines changed

src/editor.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import * as monaco from 'monaco-editor';
33
import { EXAMPLES } from './examples.js';
44
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
5+
import { calculateColumns } from './utils.js';
56

67
// Helper to get ?code param from URL
78
function getCodeFromQuery(): string | null {
@@ -84,13 +85,14 @@ export function initMonaco(runCode: () => void, clearConsole: () => void): void
8485
return 14;
8586
};
8687

87-
// Responsive word wrap column based on screen width
88-
const getWordWrapColumn = (): number => {
89-
const width = window.innerWidth;
90-
if (width <= 375) return 40;
91-
if (width <= 480) return 50;
92-
if (width <= 768) return 55;
93-
return 80;
88+
// Calculate word wrap column based on actual editor container width
89+
const calculateWordWrapColumn = (): number => {
90+
const fontSize = getFontSize();
91+
return calculateColumns(
92+
editorContainer,
93+
"'JetBrains Mono', 'Fira Code', Consolas, monospace",
94+
`${fontSize}px`
95+
);
9496
};
9597

9698
editor = monaco.editor.create(editorContainer, {
@@ -106,7 +108,7 @@ export function initMonaco(runCode: () => void, clearConsole: () => void): void
106108
tabSize: 4,
107109
insertSpaces: true,
108110
wordWrap: 'wordWrapColumn',
109-
wordWrapColumn: getWordWrapColumn(),
111+
wordWrapColumn: calculateWordWrapColumn(),
110112
lineNumbers: 'on',
111113
renderLineHighlight: 'line',
112114
cursorBlinking: 'smooth',
@@ -115,13 +117,24 @@ export function initMonaco(runCode: () => void, clearConsole: () => void): void
115117
padding: { top: 12, bottom: 12 },
116118
});
117119

118-
// Update font size on window resize
119-
const updateFontSize = () => {
120+
// Update font size and word wrap column on window resize
121+
const updateEditorOptions = () => {
120122
if (editor) {
121-
editor.updateOptions({ fontSize: getFontSize() });
123+
editor.updateOptions({
124+
fontSize: getFontSize(),
125+
wordWrapColumn: calculateWordWrapColumn()
126+
});
122127
}
123128
};
124-
window.addEventListener('resize', updateFontSize);
129+
130+
// Use ResizeObserver for more accurate container size tracking
131+
const resizeObserver = new ResizeObserver(() => {
132+
updateEditorOptions();
133+
});
134+
resizeObserver.observe(editorContainer);
135+
136+
// Also listen to window resize as fallback
137+
window.addEventListener('resize', updateEditorOptions);
125138

126139
// Update ?code param on every change
127140
editor.onDidChangeModelContent(() => {

src/main.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { initMonaco, getEditor } from './editor.js';
55
import { initCompiler, runCode } from './env.js';
66
import { Terminal } from '@xterm/xterm';
77
import '@xterm/xterm/css/xterm.css';
8+
import { calculateColumns } from './utils.js';
89

910
// ============================================
1011
// DOM ELEMENTS
@@ -49,13 +50,9 @@ const elements: Elements = {
4950
sendInputBtn: $('send-input-btn') as HTMLButtonElement
5051
};
5152

52-
// Responsive cols based on screen width
53-
const getCols = (): number => {
54-
const width = window.innerWidth;
55-
if (width <= 375) return 40;
56-
if (width <= 480) return 48;
57-
if (width <= 768) return 60;
58-
return 80;
53+
// Calculate columns based on actual container width
54+
const calculateCols = (): number => {
55+
return calculateColumns(elements.consoleOutput);
5956
};
6057

6158
// Initialize xterm.js terminal
@@ -66,12 +63,28 @@ const term = new Terminal({
6663
cursor: '#58a6ff',
6764
},
6865
rows: 48,
69-
cols: getCols(),
66+
cols: calculateCols(),
7067
convertEol: true,
7168
scrollback: 10000,
7269
});
7370
term.open(elements.consoleOutput);
7471

72+
// Resize terminal when window or container size changes
73+
const resizeTerminal = (): void => {
74+
const cols = calculateCols();
75+
const rows = term.rows || 48;
76+
term.resize(cols, rows);
77+
};
78+
79+
// Use ResizeObserver for more accurate container size tracking
80+
const resizeObserver = new ResizeObserver(() => {
81+
resizeTerminal();
82+
});
83+
resizeObserver.observe(elements.consoleOutput);
84+
85+
// Also listen to window resize as fallback
86+
window.addEventListener('resize', resizeTerminal);
87+
7588
let isRunning = false;
7689

7790
// ============================================

src/utils.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Calculate the number of columns that fit in a container based on its width
3+
* and the actual character width of the font being used.
4+
*
5+
* @param container - The HTML element to measure
6+
* @param fontFamily - Optional font family override (defaults to container's computed style)
7+
* @param fontSize - Optional font size override (defaults to container's computed style)
8+
* @returns The number of columns that fit in the container
9+
*/
10+
export function calculateColumns(
11+
container: HTMLElement,
12+
fontFamily?: string,
13+
fontSize?: string
14+
): number {
15+
const computedStyle = window.getComputedStyle(container);
16+
const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0;
17+
const paddingRight = parseFloat(computedStyle.paddingRight) || 0;
18+
const availableWidth = container.clientWidth - paddingLeft - paddingRight;
19+
20+
// Measure actual character width by creating a temporary element
21+
const measureEl = document.createElement('span');
22+
measureEl.style.position = 'absolute';
23+
measureEl.style.visibility = 'hidden';
24+
measureEl.style.whiteSpace = 'pre';
25+
measureEl.style.fontFamily = fontFamily || computedStyle.fontFamily;
26+
measureEl.style.fontSize = fontSize || computedStyle.fontSize;
27+
measureEl.style.fontWeight = computedStyle.fontWeight;
28+
measureEl.textContent = 'M'; // 'M' is typically the widest character
29+
document.body.appendChild(measureEl);
30+
31+
const charWidth = measureEl.offsetWidth;
32+
document.body.removeChild(measureEl);
33+
34+
// Calculate columns, ensuring at least 1 column
35+
const cols = Math.floor(availableWidth / charWidth);
36+
return Math.max(1, cols);
37+
}
38+

0 commit comments

Comments
 (0)