diff --git a/web/app.js b/web/app.js index 8133eab..529235c 100644 --- a/web/app.js +++ b/web/app.js @@ -12,11 +12,12 @@ const state = { comments: [], overallComment: "", hideUnchanged: false, - wrapLines: true, + wrapLines: false, collapsedDirs: {}, reviewedFiles: {}, scrollPositions: {}, sidebarCollapsed: false, + sidebarWidth: 280, fileFilter: "", selectedCommitSha: reviewData.commits?.[0]?.sha || null, fileContents: {}, @@ -25,6 +26,7 @@ const state = { }; const sidebarEl = document.getElementById("sidebar"); +const sidebarResizerEl = document.getElementById("sidebar-resizer"); const sidebarTitleEl = document.getElementById("sidebar-title"); const sidebarSearchInputEl = document.getElementById("sidebar-search-input"); const toggleSidebarButton = document.getElementById("toggle-sidebar-button"); @@ -480,13 +482,23 @@ function renderSearchResults(files) { }); } +function clamp(value, min, max) { + return Math.min(max, Math.max(min, value)); +} + +function getMaxSidebarWidth() { + const layoutWidth = document.getElementById("content-layout")?.clientWidth || window.innerWidth; + return Math.max(220, Math.min(720, layoutWidth - 360)); +} + function updateSidebarLayout() { const collapsed = state.sidebarCollapsed; - sidebarEl.style.width = collapsed ? "0px" : "280px"; - sidebarEl.style.minWidth = collapsed ? "0px" : "280px"; - sidebarEl.style.flexBasis = collapsed ? "0px" : "280px"; - sidebarEl.style.borderRightWidth = collapsed ? "0px" : "1px"; + const width = `${clamp(state.sidebarWidth, 220, getMaxSidebarWidth())}px`; + sidebarEl.style.width = collapsed ? "0px" : width; + sidebarEl.style.minWidth = collapsed ? "0px" : width; + sidebarEl.style.flexBasis = collapsed ? "0px" : width; sidebarEl.style.pointerEvents = collapsed ? "none" : "auto"; + sidebarResizerEl.style.display = collapsed ? "none" : "block"; toggleSidebarButton.textContent = collapsed ? "Show sidebar" : "Hide sidebar"; } @@ -998,18 +1010,19 @@ function setupMonaco() { diffEditor = monacoApi.editor.createDiffEditor(editorContainerEl, { automaticLayout: true, renderSideBySide: activeFileShowsDiff(), + enableSplitViewResizing: true, readOnly: true, originalEditable: false, minimap: { enabled: true, renderCharacters: false, showSlider: "always", size: "proportional" }, renderOverviewRuler: true, - diffWordWrap: "on", + diffWordWrap: "off", scrollBeyondLastLine: false, lineNumbersMinChars: 4, glyphMargin: true, folding: true, lineDecorationsWidth: 10, overviewRulerBorder: false, - wordWrap: "on", + wordWrap: "off", }); createGlyphHoverActions(diffEditor.getOriginalEditor(), "original"); @@ -1058,6 +1071,39 @@ function switchScope(scope) { if (file) ensureFileLoaded(file.id, state.currentScope); } +function setupSidebarResizer() { + let startX = 0; + let startWidth = 0; + + const finishResize = () => { + document.body.classList.remove("is-resizing-panels"); + sidebarResizerEl.classList.remove("is-dragging"); + window.removeEventListener("pointermove", onPointerMove); + window.removeEventListener("pointerup", finishResize); + window.removeEventListener("pointercancel", finishResize); + requestAnimationFrame(layoutEditor); + }; + + const onPointerMove = (event) => { + const maxWidth = getMaxSidebarWidth(); + state.sidebarWidth = clamp(startWidth + event.clientX - startX, 220, maxWidth); + updateSidebarLayout(); + layoutEditor(); + }; + + sidebarResizerEl.addEventListener("pointerdown", (event) => { + if (state.sidebarCollapsed) return; + event.preventDefault(); + startX = event.clientX; + startWidth = sidebarEl.getBoundingClientRect().width || state.sidebarWidth; + document.body.classList.add("is-resizing-panels"); + sidebarResizerEl.classList.add("is-dragging"); + window.addEventListener("pointermove", onPointerMove); + window.addEventListener("pointerup", finishResize); + window.addEventListener("pointercancel", finishResize); + }); +} + submitButton.addEventListener("click", () => { syncCommentBodiesFromDOM(); const payload = { @@ -1157,6 +1203,7 @@ commitSelectEl.addEventListener("change", () => { }); populateCommitSelect(); +setupSidebarResizer(); ensureActiveFileForScope(); renderTree(); renderFileComments(); diff --git a/web/index.html b/web/index.html index 66160f1..c836e8f 100644 --- a/web/index.html +++ b/web/index.html @@ -69,6 +69,48 @@ height: 100% !important; } + #sidebar-resizer { + width: 6px; + cursor: col-resize; + flex: 0 0 6px; + background: #30363d; + opacity: 0.65; + transition: background 120ms ease, opacity 120ms ease; + } + + #sidebar-resizer:hover, + #sidebar-resizer.is-dragging { + background: #58a6ff; + opacity: 1; + } + + body.is-resizing-panels, + body.is-resizing-panels * { + cursor: col-resize !important; + user-select: none !important; + } + + .monaco-sash.vertical, + .monaco-diff-editor .sash.vertical { + cursor: col-resize !important; + } + + .monaco-diff-editor .monaco-sash.vertical { + background: rgba(48, 54, 61, 0.65) !important; + transition: none !important; + } + + .monaco-diff-editor .monaco-sash.vertical:hover, + .monaco-diff-editor .monaco-sash.vertical.active, + .monaco-diff-editor .monaco-sash.vertical.hover { + background: #58a6ff !important; + } + + .monaco-diff-editor .monaco-sash.vertical::before, + .monaco-diff-editor .monaco-sash.vertical::after { + transition: none !important; + } + .monaco-editor .view-zones { z-index: 10; } @@ -142,7 +184,7 @@