From 4aeb296a9813c142c211fa5ea05b9e40281bb0c9 Mon Sep 17 00:00:00 2001 From: lgq <110796157+GQingL@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:03:33 +0800 Subject: [PATCH 1/7] fix: prevent RBAC member list from shrinking header controls --- web/static/css/style.css | 6 ++++++ web/templates/index.html | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index ba28fa4a..55f6fbb6 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -42301,6 +42301,12 @@ html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="tr max-height: none; } +.rbac-panel-users > .rbac-panel-head, +.rbac-panel-users > .rbac-search { + /* Keep the fixed controls intact when a long member list needs to shrink. */ + flex: 0 0 auto; +} + .rbac-panel-users .rbac-list { flex: 1 1 auto; min-height: 0; diff --git a/web/templates/index.html b/web/templates/index.html index 109d8ace..01ccf701 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -39,7 +39,7 @@ } })(); - + From f0f93283eea8d13d9aabd5eea71f3ae2f9207707 Mon Sep 17 00:00:00 2001 From: lgq <110796157+GQingL@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:15:49 +0800 Subject: [PATCH 2/7] fix: contain RBAC role catalog scrolling within workspace --- web/static/css/style.css | 11 ++++++++++- web/templates/index.html | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 55f6fbb6..21f0d7eb 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -42820,19 +42820,23 @@ html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="tr } .rbac-role-management { + display: flex; + flex-direction: column; min-height: 0; + overflow: hidden; padding: 18px; border-radius: 10px; } .rbac-role-management-head { + flex: 0 0 auto; align-items: flex-end; flex-wrap: wrap; row-gap: 12px; } .rbac-role-management-head > div:first-child { - flex: 1 1 320px; + flex: 1 1 auto; min-width: 0; } @@ -42867,7 +42871,12 @@ html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="tr } .rbac-role-catalog { + flex: 1 1 auto; + min-height: 0; + overflow-y: auto; + overscroll-behavior: contain; grid-template-columns: repeat(auto-fit, minmax(min(100%, 360px), 1fr)); + align-content: start; align-items: stretch; } diff --git a/web/templates/index.html b/web/templates/index.html index 01ccf701..7229f601 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -39,7 +39,7 @@ } })(); - + From 07346ca002a9d6811adf207f953b08e853bb9e40 Mon Sep 17 00:00:00 2001 From: lgq <110796157+GQingL@users.noreply.github.com> Date: Tue, 27 Oct 2026 20:15:57 +0800 Subject: [PATCH 3/7] fix: allow horizontal scrolling in RBAC resource filter bar --- web/static/css/style.css | 4 ++++ web/templates/index.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 21f0d7eb..e4a05df4 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -14008,6 +14008,10 @@ html[data-theme="dark"] .robot-binding-service-hint-icon { display: grid; grid-template-columns: minmax(110px, 0.65fr) minmax(150px, 1fr) minmax(190px, 1.25fr); gap: 8px; + overflow-x: auto; + overflow-y: hidden; + overscroll-behavior-x: contain; + scrollbar-width: thin; } .rbac-resource-filterbar :is(.form-select, .form-input), diff --git a/web/templates/index.html b/web/templates/index.html index 7229f601..e021118c 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -39,7 +39,7 @@ } })(); - + From 9cb6922a88c7fe54546881a514ac36115c73e42f Mon Sep 17 00:00:00 2001 From: lgq <110796157+GQingL@users.noreply.github.com> Date: Tue, 27 Oct 2026 20:23:36 +0800 Subject: [PATCH 4/7] fix: add spacing below RBAC assignment filters --- web/static/css/style.css | 2 +- web/templates/index.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index e4a05df4..3e9210f7 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -42704,7 +42704,7 @@ html[data-theme="dark"] .workflow-toolbar #workflow-connect-btn[aria-pressed="tr gap: 0; min-height: 0; max-height: none; - padding: 0 12px; + padding: 12px; } .rbac-assignments-list .rbac-assignment-row { diff --git a/web/templates/index.html b/web/templates/index.html index e021118c..8433d72c 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -39,7 +39,7 @@ } })(); - + From 816abfb841a8dc6878ddf1fc32ba639f6d3dc9e3 Mon Sep 17 00:00:00 2001 From: lgq <110796157+GQingL@users.noreply.github.com> Date: Tue, 27 Oct 2026 23:09:13 +0800 Subject: [PATCH 5/7] fix: prevent external MCP stats and card controls from overlapping --- web/static/css/style.css | 24 +++++++++++++++++------- web/templates/index.html | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 3e9210f7..7030054b 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -16121,6 +16121,8 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { .external-mcp-actions { display: flex; + flex: 0 0 auto; + min-width: 0; align-items: center; gap: 12px; flex-wrap: wrap; @@ -16128,9 +16130,12 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { .external-mcp-stats { display: flex; + flex-wrap: wrap; align-items: center; - gap: 20px; - margin-left: auto; + gap: 8px 20px; + width: 100%; + min-width: 0; + margin-left: 0; padding: 10px 20px; background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-tertiary) 100%); border: 1px solid var(--border-color); @@ -16232,21 +16237,24 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { .external-mcp-item-header { display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - flex-wrap: wrap; + flex-direction: column; + align-items: stretch; + gap: 12px; } .external-mcp-item-info { display: flex; align-items: center; + flex-wrap: wrap; gap: 12px; - flex: 1; + width: 100%; min-width: 0; } .external-mcp-item-info h4 { + flex: 1 1 180px; + min-width: 0; + overflow-wrap: anywhere; margin: 0; font-size: 1.125rem; font-weight: 600; @@ -16273,6 +16281,7 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { } .external-mcp-status { + flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px; @@ -16351,6 +16360,7 @@ tr.mcp-stats-tool-row[data-tool-name]:focus-visible { .external-mcp-item-actions { display: flex; align-items: center; + justify-content: flex-end; gap: 8px; flex-wrap: wrap; } diff --git a/web/templates/index.html b/web/templates/index.html index 8433d72c..e426bb9c 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -39,7 +39,7 @@ } })(); - + From 0dea628e8980495bfbdc779bae6474dbfeda752c Mon Sep 17 00:00:00 2001 From: lgq <110796157+GQingL@users.noreply.github.com> Date: Tue, 27 Oct 2026 23:23:44 +0800 Subject: [PATCH 6/7] fix: open project pagination dropdown upward --- web/static/css/style.css | 8 ++++++++ web/templates/index.html | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/web/static/css/style.css b/web/static/css/style.css index 7030054b..202e5ff6 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -33748,6 +33748,14 @@ html[data-theme="dark"] .chat-files-toast.chat-toast--error { font-size: 0.75rem; border-radius: 6px; } + +.sidebar-list-pagination .projects-filter-select-dropdown { + top: auto; + bottom: calc(100% + 4px); + left: auto; + min-width: 88px; +} + .projects-fact-filter-field--search input { padding-left: 34px; background: #fff; diff --git a/web/templates/index.html b/web/templates/index.html index e426bb9c..7f6bbd2f 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -39,7 +39,7 @@ } })(); - + From 0a6dc6f872b9525b86e5f89a114d289ebeeb2e97 Mon Sep 17 00:00:00 2001 From: lgq <110796157+GQingL@users.noreply.github.com> Date: Tue, 27 Oct 2026 23:59:47 +0800 Subject: [PATCH 7/7] fix: use floating pagination selects across pages for Chrome compatibility --- web/static/css/style.css | 37 +++++++++++++++ web/static/js/audit.js | 1 + web/static/js/c2.js | 4 ++ web/static/js/chat-files.js | 2 + web/static/js/chat.js | 2 + web/static/js/hitl.js | 1 + web/static/js/monitor.js | 1 + web/static/js/pagination-select.test.cjs | 59 ++++++++++++++++++++++++ web/static/js/projects.js | 1 + web/static/js/roles.js | 1 + web/static/js/settings.js | 44 ++++++++++++++++-- web/static/js/skills.js | 2 + web/static/js/tasks.js | 2 + web/static/js/vulnerability.js | 1 + web/templates/index.html | 26 +++++------ 15 files changed, 166 insertions(+), 18 deletions(-) create mode 100644 web/static/js/pagination-select.test.cjs diff --git a/web/static/css/style.css b/web/static/css/style.css index 202e5ff6..b423436a 100644 --- a/web/static/css/style.css +++ b/web/static/css/style.css @@ -10604,6 +10604,43 @@ html[data-theme="dark"] .robot-binding-service-hint-icon { font-size: 0.875rem; } +.pagination-custom-select { + display: inline-flex; + flex: 0 0 auto; + width: 76px; + vertical-align: middle; +} + +.pagination-custom-select .settings-custom-select-trigger { + min-height: 34px; + padding: 6px 10px; + border-radius: 8px; + font-size: 0.8125rem; +} + +.pagination-custom-select .settings-custom-select-caret { + width: 8px; + height: 8px; + border-right: 1.5px solid currentColor; + border-bottom: 1.5px solid currentColor; + font-size: 0; + transform: translateY(-2px) rotate(45deg); +} + +.settings-custom-select.pagination-custom-select.open .settings-custom-select-caret { + transform: translateY(2px) rotate(225deg); +} + +.sidebar-list-pagination .pagination-custom-select { + width: 64px; +} + +.sidebar-list-pagination .pagination-custom-select .settings-custom-select-trigger { + min-height: 28px; + padding: 4px 8px; + font-size: 0.75rem; +} + .settings-custom-select-value { flex: 1; min-width: 0; diff --git a/web/static/js/audit.js b/web/static/js/audit.js index 4d54d857..7e0023a9 100644 --- a/web/static/js/audit.js +++ b/web/static/js/audit.js @@ -402,6 +402,7 @@ function renderAuditLogsPagination() { html += ''; html += ''; container.innerHTML = html; + if (typeof initPaginationSelects === 'function') initPaginationSelects(container); } function goAuditLogsPage(p) { diff --git a/web/static/js/c2.js b/web/static/js/c2.js index 96f7143f..97060403 100644 --- a/web/static/js/c2.js +++ b/web/static/js/c2.js @@ -3256,6 +3256,7 @@ const totalPages = Math.max(1, Math.ceil(total / pageSize)); if (total === 0) { paginationContainer.innerHTML = ''; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); return; } const start = total === 0 ? 0 : (currentPage - 1) * pageSize + 1; @@ -3284,6 +3285,7 @@ `; html += ''; paginationContainer.innerHTML = html; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); if (typeof applyTranslations === 'function') applyTranslations(paginationContainer); }; @@ -4194,6 +4196,7 @@ if (total === 0) { paginationContainer.innerHTML = ''; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); return; } @@ -4224,6 +4227,7 @@ `; html += ''; paginationContainer.innerHTML = html; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); if (typeof applyTranslations === 'function') applyTranslations(paginationContainer); }; diff --git a/web/static/js/chat-files.js b/web/static/js/chat-files.js index b9d29956..1e010886 100644 --- a/web/static/js/chat-files.js +++ b/web/static/js/chat-files.js @@ -509,6 +509,7 @@ function renderChatFilesPagination() { if (groupMode === 'folder' || chatFilesTotal <= 0) { pager.hidden = true; pager.innerHTML = ''; + if (typeof initPaginationSelects === 'function') initPaginationSelects(pager); return; } const totalPages = chatFilesTotalPages(); @@ -542,6 +543,7 @@ function renderChatFilesPagination() { `; pager.hidden = false; + if (typeof initPaginationSelects === 'function') initPaginationSelects(pager); } function changeChatFilesPage(page) { diff --git a/web/static/js/chat.js b/web/static/js/chat.js index a1072205..5cfb5aee 100644 --- a/web/static/js/chat.js +++ b/web/static/js/chat.js @@ -9923,6 +9923,7 @@ function renderConversationsPagination(visibleCount) { if (!total) { el.innerHTML = ''; el.hidden = true; + if (typeof initPaginationSelects === 'function') initPaginationSelects(el); return; } @@ -9963,6 +9964,7 @@ function renderConversationsPagination(visibleCount) { `; + if (typeof initPaginationSelects === 'function') initPaginationSelects(el); } finally { conversationsPaginationRenderLock = false; } diff --git a/web/static/js/hitl.js b/web/static/js/hitl.js index 3838d477..5e7050f4 100644 --- a/web/static/js/hitl.js +++ b/web/static/js/hitl.js @@ -174,6 +174,7 @@ function renderHitlPagination(containerId, state, goPageFnName, pageSizeChangeFn html += ''; html += ''; container.innerHTML = html; + if (typeof initPaginationSelects === 'function') initPaginationSelects(container); } function hitlEffectiveEnabled(cfg) { diff --git a/web/static/js/monitor.js b/web/static/js/monitor.js index aafad05c..58d4e226 100644 --- a/web/static/js/monitor.js +++ b/web/static/js/monitor.js @@ -9322,6 +9322,7 @@ function renderMonitorPagination() { `; container.appendChild(pagination); + if (typeof initPaginationSelects === 'function') initPaginationSelects(pagination); // 初始化每页显示数量选择器 initializeMonitorPageSize(); diff --git a/web/static/js/pagination-select.test.cjs b/web/static/js/pagination-select.test.cjs new file mode 100644 index 00000000..4b8c81a5 --- /dev/null +++ b/web/static/js/pagination-select.test.cjs @@ -0,0 +1,59 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); +const vm = require('node:vm'); + +function setup() { + const source = fs.readFileSync(path.join(__dirname, 'settings.js'), 'utf8'); + const context = vm.createContext({ window: { innerWidth: 800, innerHeight: 600 } }); + vm.runInContext(source.slice(0, source.indexOf('function getRobotStatus(')), context); + vm.runInContext('globalThis.selects = settingsCustomSelects;', context); + return context; +} + +function menuAt(rect) { + return { + wrapper: { classList: { contains: () => true } }, + trigger: { getBoundingClientRect: () => rect }, + menu: { style: {} }, + }; +} + +test('a pagination menu near the viewport bottom opens above its trigger', () => { + const context = setup(); + const reg = menuAt({ left: 700, top: 550, bottom: 584, width: 76 }); + context.positionSettingsCustomSelectMenu(reg); + assert.equal(reg.menu.style.top, 'auto'); + assert.equal(reg.menu.style.bottom, '56px'); + assert.ok(parseInt(reg.menu.style.left) + parseInt(reg.menu.style.width) <= 788); +}); + +test('a menu near the viewport top opens below instead', () => { + const context = setup(); + const reg = menuAt({ left: 20, top: 20, bottom: 54, width: 76 }); + context.positionSettingsCustomSelectMenu(reg); + assert.equal(reg.menu.style.top, '60px'); + assert.equal(reg.menu.style.bottom, 'auto'); +}); + +test('replacing pagination DOM removes its detached floating menu', () => { + const context = setup(); + let removed = false; + let synced = null; + const stale = { isConnected: false }; + const current = { isConnected: true }; + const reg = { + wrapper: { classList: { remove() {} }, appendChild(menu) { menu.parentNode = this; } }, + trigger: { setAttribute() {} }, + menu: { style: {}, classList: { remove() {} }, remove() { removed = true; } }, + }; + context.selects.set(stale, reg); + context.selects.set(current, {}); + context.syncSettingsCustomSelect = select => { synced = select; }; + context.refreshSettingsCustomSelects(); + assert.equal(removed, true); + assert.equal(context.selects.has(stale), false); + assert.equal(context.selects.has(current), true); + assert.equal(synced, current); +}); diff --git a/web/static/js/projects.js b/web/static/js/projects.js index 9309afbd..7ea48762 100644 --- a/web/static/js/projects.js +++ b/web/static/js/projects.js @@ -1135,6 +1135,7 @@ function renderProjectAssetsPagination() { `; + if (typeof initPaginationSelects === 'function') initPaginationSelects(root); } function changeProjectAssetsPageSize(value) { diff --git a/web/static/js/roles.js b/web/static/js/roles.js index cacd4802..ee94f192 100644 --- a/web/static/js/roles.js +++ b/web/static/js/roles.js @@ -1032,6 +1032,7 @@ function renderRoleToolsPagination() { `; toolsList.appendChild(pagination); + if (typeof initPaginationSelects === 'function') initPaginationSelects(pagination); } function syncRoleToolsFilterButtons() { diff --git a/web/static/js/settings.js b/web/static/js/settings.js index 0eb2eab9..fca5c069 100644 --- a/web/static/js/settings.js +++ b/web/static/js/settings.js @@ -172,7 +172,15 @@ function syncSettingsCustomSelect(select) { } function refreshSettingsCustomSelects() { - settingsCustomSelects.forEach((_reg, select) => syncSettingsCustomSelect(select)); + settingsCustomSelects.forEach((reg, select) => { + if (!select.isConnected) { + closeSettingsCustomSelect(select); + reg.menu.remove(); + settingsCustomSelects.delete(select); + return; + } + syncSettingsCustomSelect(select); + }); } function enhanceSettingsSelect(select) { @@ -277,10 +285,7 @@ function enhanceSettingsSelect(select) { syncSettingsCustomSelect(select); } -function initSettingsCustomSelects(root) { - const scope = root || document.getElementById('page-settings'); - if (!scope) return; - scope.querySelectorAll('select').forEach(enhanceSettingsSelect); +function bindSettingsCustomSelectEvents() { if (!settingsCustomSelectsDocBound) { document.addEventListener('click', closeAllSettingsCustomSelects); document.addEventListener('keydown', (event) => { @@ -288,8 +293,35 @@ function initSettingsCustomSelects(root) { }); document.addEventListener('scroll', repositionOpenSettingsCustomSelects, true); window.addEventListener('resize', repositionOpenSettingsCustomSelects); + window.addEventListener('hashchange', closeAllSettingsCustomSelects); settingsCustomSelectsDocBound = true; } +} + +function initSettingsCustomSelects(root) { + const scope = root || document.getElementById('page-settings'); + if (!scope) return; + scope.querySelectorAll('select').forEach(enhanceSettingsSelect); + bindSettingsCustomSelectEvents(); + refreshSettingsCustomSelects(); +} + +// Pagination menus use the floating select so clipped panels cannot hide options. +function initPaginationSelects(root) { + if (!root) return; + root.querySelectorAll('.pagination-page-size select').forEach(select => { + if (select.dataset.projectsCustomSelect === '1') return; + enhanceSettingsSelect(select); + const reg = settingsCustomSelects.get(select); + if (!reg) return; + reg.wrapper.classList.add('pagination-custom-select'); + const label = select.labels?.[0] || select.closest('.pagination-page-size'); + const labelText = label && Array.from(label.childNodes) + .filter(node => node.nodeType === 3).map(node => node.textContent).join(' ').trim(); + const accessibleName = select.getAttribute('aria-label') || labelText; + if (accessibleName) reg.trigger.setAttribute('aria-label', accessibleName); + }); + bindSettingsCustomSelectEvents(); refreshSettingsCustomSelects(); } @@ -1675,6 +1707,7 @@ function renderToolsPagination() { // 如果只有一页或没有数据,不显示分页 if (toolsPagination.totalPages <= 1) { + initPaginationSelects(toolsList); return; } @@ -1716,6 +1749,7 @@ function renderToolsPagination() { `; toolsList.appendChild(pagination); + initPaginationSelects(pagination); } // 处理工具checkbox状态变化 diff --git a/web/static/js/skills.js b/web/static/js/skills.js index 0b2d5e86..5ed97491 100644 --- a/web/static/js/skills.js +++ b/web/static/js/skills.js @@ -231,6 +231,7 @@ function renderSkillsPagination() { // 即使只有一页也显示分页信息(参考MCP样式) if (total === 0) { paginationContainer.innerHTML = ''; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); return; } @@ -277,6 +278,7 @@ function renderSkillsPagination() { paginationHTML += ''; paginationContainer.innerHTML = paginationHTML; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); // 确保分页组件与列表内容区域对齐(不包括滚动条) function alignPaginationWidth() { diff --git a/web/static/js/tasks.js b/web/static/js/tasks.js index 84a7a60e..0dfe0ea4 100644 --- a/web/static/js/tasks.js +++ b/web/static/js/tasks.js @@ -1687,6 +1687,7 @@ function renderBatchQueuesPagination() { // 即使只有一页也显示分页信息(与 MCP 监控一致) if (total === 0) { paginationContainer.innerHTML = ''; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); return; } @@ -1726,6 +1727,7 @@ function renderBatchQueuesPagination() { paginationHTML += ''; paginationContainer.innerHTML = paginationHTML; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); } // 跳转到指定页面 diff --git a/web/static/js/vulnerability.js b/web/static/js/vulnerability.js index 02d832af..e97e7629 100644 --- a/web/static/js/vulnerability.js +++ b/web/static/js/vulnerability.js @@ -1522,6 +1522,7 @@ function renderVulnerabilityPagination() { paginationHTML += ''; paginationContainer.innerHTML = paginationHTML; + if (typeof initPaginationSelects === 'function') initPaginationSelects(paginationContainer); if (typeof window.applyTranslations === 'function') { window.applyTranslations(paginationContainer); } diff --git a/web/templates/index.html b/web/templates/index.html index 7f6bbd2f..65c782bf 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -39,7 +39,7 @@ } })(); - + @@ -7024,35 +7024,35 @@ - + - + - + - + - + - + - - + + - - + + - + - +