Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions internal/server/static/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,32 @@ body {
display: flex;
flex-direction: column;
padding: 15px;
align-items: center;
align-items: stretch;
position: relative;
min-width: 200px;
max-width: 50vw;
overflow: visible;
box-sizing: border-box;
}

.resize-handle {
position: absolute;
right: -2px;
top: 0;
bottom: 0;
width: 4px;
cursor: ew-resize;
background-color: transparent;
z-index: 10;
transition: background-color 0.2s ease;
}

.resize-handle:hover {
background-color: var(--toolbox-blue);
}

.resize-handle.active {
background-color: var(--toolbox-blue);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The styles for :hover and .active states of the resize handle are identical. To reduce code duplication and improve maintainability, you can group these selectors.

Suggested change
.resize-handle:hover {
background-color: var(--toolbox-blue);
}
.resize-handle.active {
background-color: var(--toolbox-blue);
}
.resize-handle:hover,
.resize-handle.active {
background-color: var(--toolbox-blue);
}


.nav-logo {
Expand Down Expand Up @@ -626,17 +650,21 @@ body {
.search-container {
display: flex;
width: 100%;
min-width: 0;
margin-bottom: 15px;
box-sizing: border-box;

#toolset-search-input {
flex-grow: 1;
flex: 1;
min-width: 0;
padding: 10px 12px;
border: 1px solid #ccc;
border-radius: 20px 0 0 20px;
border-right: none;
font-family: inherit;
font-size: 0.9em;
color: var(--text-primary-gray);
box-sizing: border-box;

&:focus {
outline: none;
Expand Down
99 changes: 99 additions & 0 deletions internal/server/static/js/resize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const STORAGE_KEY = 'toolbox-second-nav-width';
const DEFAULT_WIDTH = 250;
const MIN_WIDTH = 200;
const MAX_WIDTH_PERCENT = 50;

/**
* Creates and attaches a resize handle to the second navigation panel
*/
export function initializeResize() {
const secondNav = document.querySelector('.second-nav');
if (!secondNav) {
return;
}

// Create resize handle
const resizeHandle = document.createElement('div');
resizeHandle.className = 'resize-handle';
resizeHandle.setAttribute('aria-label', 'Resize panel');
secondNav.appendChild(resizeHandle);

// Load saved width or use default
const savedWidth = localStorage.getItem(STORAGE_KEY);
const initialWidth = savedWidth ? parseInt(savedWidth, 10) : DEFAULT_WIDTH;
setPanelWidth(secondNav, initialWidth);

// Setup resize functionality
let isResizing = false;
let startX = 0;
let startWidth = 0;

resizeHandle.addEventListener('mousedown', (e) => {
isResizing = true;
startX = e.clientX;
startWidth = secondNav.offsetWidth;
resizeHandle.classList.add('active');
document.body.style.cursor = 'ew-resize';
document.body.style.userSelect = 'none';
e.preventDefault();
});

document.addEventListener('mousemove', (e) => {
if (!isResizing) {
return;
}

const deltaX = e.clientX - startX;
const newWidth = startWidth + deltaX;
const maxWidth = (window.innerWidth * MAX_WIDTH_PERCENT) / 100;

const clampedWidth = Math.max(MIN_WIDTH, Math.min(newWidth, maxWidth));
setPanelWidth(secondNav, clampedWidth);
});

document.addEventListener('mouseup', () => {
if (isResizing) {
isResizing = false;
resizeHandle.classList.remove('active');
document.body.style.cursor = '';
document.body.style.userSelect = '';

// Save width to localStorage
localStorage.setItem(STORAGE_KEY, secondNav.offsetWidth.toString());
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation adds mousemove and mouseup event listeners to the global document object and never removes them. This can lead to unnecessary event processing and potential memory leaks over time. A more robust and efficient pattern is to add these listeners only during the drag operation (on mousedown) and remove them immediately after (on mouseup). This refactoring also simplifies the logic by eliminating the need for the isResizing flag.

    let startX = 0;
    let startWidth = 0;

    const onMouseMove = (e) => {
        const deltaX = e.clientX - startX;
        const newWidth = startWidth + deltaX;
        const maxWidth = (window.innerWidth * MAX_WIDTH_PERCENT) / 100;

        const clampedWidth = Math.max(MIN_WIDTH, Math.min(newWidth, maxWidth));
        setPanelWidth(secondNav, clampedWidth);
    };

    const onMouseUp = () => {
        document.removeEventListener('mousemove', onMouseMove);
        document.removeEventListener('mouseup', onMouseUp);

        resizeHandle.classList.remove('active');
        document.body.style.cursor = '';
        document.body.style.userSelect = '';

        // Save width to localStorage
        localStorage.setItem(STORAGE_KEY, secondNav.offsetWidth.toString());
    };

    resizeHandle.addEventListener('mousedown', (e) => {
        startX = e.clientX;
        startWidth = secondNav.offsetWidth;
        resizeHandle.classList.add('active');
        document.body.style.cursor = 'ew-resize';
        document.body.style.userSelect = 'none';
        e.preventDefault();

        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);
    });


// Handle window resize to enforce max width
window.addEventListener('resize', () => {
const currentWidth = secondNav.offsetWidth;
const maxWidth = (window.innerWidth * MAX_WIDTH_PERCENT) / 100;

if (currentWidth > maxWidth) {
setPanelWidth(secondNav, maxWidth);
localStorage.setItem(STORAGE_KEY, maxWidth.toString());
}
});
}

/**
* Sets the width of the panel and updates flex property
*/
function setPanelWidth(panel, width) {
panel.style.flex = `0 0 ${width}px`;
panel.style.width = `${width}px`;
}
Comment on lines 113 to 115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When an element is a flex item, setting flex-basis (via the flex shorthand property) is sufficient to control its size along the main axis. The width property is redundant in this case because flex-basis will take precedence. You can simplify this function by removing the line that sets panel.style.width.

function setPanelWidth(panel, width) {
    panel.style.flex = `0 0 ${width}px`;
}


10 changes: 7 additions & 3 deletions internal/server/static/tools.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ <h4>My Tools</h4>
<script type="module" src="/ui/js/tools.js"></script>
<script src="/ui/js/navbar.js"></script>
<script src="/ui/js/mainContent.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
<script type="module">
document.addEventListener('DOMContentLoaded', async () => {
const navbarContainer = document.getElementById('navbar-container');
const activeNav = navbarContainer.getAttribute('data-active-nav');
renderNavbar('navbar-container', activeNav);
renderMainContent('main-content-container', 'tool-display-area', getToolInstructions())
renderMainContent('main-content-container', 'tool-display-area', getToolInstructions());

// Initialize resize functionality
const { initializeResize } = await import('/ui/js/resize.js');
initializeResize();
});
</script>
</body>
Expand Down
8 changes: 6 additions & 2 deletions internal/server/static/toolsets.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@ <h4>Retrieve Toolset</h4>
<script type="module" src="/ui/js/toolsets.js"></script>
<script src="/ui/js/navbar.js"></script>
<script src="/ui/js/mainContent.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
<script type="module">
document.addEventListener('DOMContentLoaded', async () => {
const navbarContainer = document.getElementById('navbar-container');
const activeNav = navbarContainer.getAttribute('data-active-nav');
renderNavbar('navbar-container', activeNav);
renderMainContent('main-content-container', 'tool-display-area', getToolsetInstructions());

// Initialize resize functionality
const { initializeResize } = await import('/ui/js/resize.js');
initializeResize();
});
</script>
</body>
Expand Down