+ {isSqlLogActive && tabsClearButton}
+ {tabsHideButton}
+
+ ),
+ }
: undefined;
return (
@@ -736,7 +751,8 @@ const QueryEditorResultsPanel: React.FC = ({
.query-result-panel-header { flex: 0 0 auto; min-height: 38px; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 0 12px; border-bottom: 1px solid rgba(0, 0, 0, 0.06); background: rgba(255, 255, 255, 0.9); }
.query-result-panel-header-title { font-size: 13px; font-weight: 600; color: #666; }
.query-result-panel-hide { display: inline-flex; align-items: center; gap: 4px; }
- .query-result-panel-hide-compact { min-width: 28px; padding: 0 6px; justify-content: center; }
+ .query-result-panel-tab-actions { display: inline-flex; flex-direction: row; align-items: center; gap: 4px; }
+ .query-result-tabs .ant-tabs-extra-content .query-result-panel-tab-action { width: 28px; min-width: 28px; height: 28px !important; min-height: 28px !important; padding: 0 !important; display: inline-flex; align-items: center; justify-content: center; }
`}
{tabItems.length > 0 ? (
diff --git a/frontend/src/components/QueryEditorToolbar.layout.test.tsx b/frontend/src/components/QueryEditorToolbar.layout.test.tsx
index 4001987e..78e25426 100644
--- a/frontend/src/components/QueryEditorToolbar.layout.test.tsx
+++ b/frontend/src/components/QueryEditorToolbar.layout.test.tsx
@@ -32,6 +32,38 @@ describe('QueryEditorToolbar layout', () => {
expect(css).toContain('body[data-ui-version="v2"] .gn-v2-query-toolbar-action-pair {');
});
+ it('shares the active theme surface across the SQL toolbar and Monaco editor', () => {
+ const css = readV2ThemeCss();
+ const defaultMonacoCss = css.slice(
+ css.indexOf('body[data-ui-version="v2"]:not([data-custom-theme]) {'),
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-editor {'),
+ );
+ const editorCss = css.slice(
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-editor {'),
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-editor-pane {'),
+ );
+ const toolbarCss = css.slice(
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-toolbar {'),
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-toolbar-main {'),
+ );
+ const toolbarSelectCss = css.slice(
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-toolbar .ant-select-selector {'),
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-toolbar .ant-select-selection-item,'),
+ );
+ const monacoStageCss = css.slice(
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-monaco-stage {'),
+ css.indexOf('body[data-ui-version="v2"] .gn-v2-query-monaco-stage:has('),
+ );
+
+ expect(defaultMonacoCss).toContain('--gn-monaco-bg: var(--gn-bg-panel-2);');
+ expect(editorCss).toContain('--gn-query-workbench-bg: var(--gn-bg-panel-2);');
+ expect(toolbarCss).toContain('background: var(--gn-query-workbench-bg) !important;');
+ expect(toolbarSelectCss).toContain('background: var(--gn-query-workbench-bg) !important;');
+ expect(monacoStageCss).toContain(
+ 'background: var(--gn-monaco-bg, var(--gn-query-workbench-bg));',
+ );
+ });
+
it('keeps run and stop buttons separated in the v2 toolbar action group', () => {
const toolbarSource = readFileSync(new URL('./QueryEditorToolbar.tsx', import.meta.url), 'utf8');
const css = readV2ThemeCss();
diff --git a/frontend/src/styles/v2-theme-workbench.css b/frontend/src/styles/v2-theme-workbench.css
index 43c426e1..787e634e 100644
--- a/frontend/src/styles/v2-theme-workbench.css
+++ b/frontend/src/styles/v2-theme-workbench.css
@@ -1,5 +1,11 @@
/* ─── V2 Query editor ─ */
+/* Base themes follow the sidebar surface; custom themes keep an explicit Monaco override. */
+body[data-ui-version="v2"]:not([data-custom-theme]) {
+ --gn-monaco-bg: var(--gn-bg-panel-2);
+}
+
body[data-ui-version="v2"] .gn-v2-query-editor {
+ --gn-query-workbench-bg: var(--gn-bg-panel-2);
background: var(--gn-bg-app);
}
@@ -95,7 +101,7 @@ body[data-ui-version="v2"] .gn-v2-query-toolbar {
flex-wrap: nowrap;
overflow-x: auto;
overflow-y: hidden;
- background: var(--gn-bg-panel) !important;
+ background: var(--gn-query-workbench-bg) !important;
border-bottom: 0.5px solid var(--gn-br-1) !important;
}
@@ -198,6 +204,7 @@ body[data-ui-version="v2"] .gn-v2-query-toolbar-select-icon {
body[data-ui-version="v2"] .gn-v2-query-toolbar .ant-select-selector {
height: 32px !important;
padding: 0 10px !important;
+ background: var(--gn-query-workbench-bg) !important;
border-radius: 9px !important;
}
@@ -356,8 +363,7 @@ body[data-ui-version="v2"] .gn-v2-query-monaco-stage {
overflow: hidden;
border-top: 0.5px solid var(--gn-br-1);
border-bottom: 0.5px solid var(--gn-br-1);
- /* Same token as Monaco surface */
- background: var(--gn-monaco-bg, var(--gn-bg-panel));
+ background: var(--gn-monaco-bg, var(--gn-query-workbench-bg));
}
body[data-ui-version="v2"] .gn-v2-query-monaco-stage:has(.monaco-editor .find-widget.visible:not(.hiddenEditor)) {
@@ -367,7 +373,7 @@ body[data-ui-version="v2"] .gn-v2-query-monaco-stage:has(.monaco-editor .find-wi
body[data-ui-version="v2"] .gn-v2-query-monaco-shell {
min-height: 0;
overflow: visible;
- background: var(--gn-monaco-bg, var(--gn-bg-panel));
+ background: var(--gn-monaco-bg, var(--gn-query-workbench-bg));
}
.gn-query-execution-statusbar {
@@ -423,24 +429,25 @@ body[data-ui-version="v2"] .gn-v2-query-results {
}
body[data-ui-version="v2"] .gn-v2-query-results .query-result-tabs > .ant-tabs-nav {
+ box-sizing: border-box;
background: var(--gn-bg-panel-2);
- padding-left: 8px;
- min-height: 38px;
+ padding: 0 8px 0 0;
+ min-height: 46px;
}
body[data-ui-version="v2"] .gn-v2-query-results .query-result-tabs > .ant-tabs-nav .ant-tabs-extra-content {
display: inline-flex;
align-items: center;
- padding-right: 8px;
+ padding-right: 0;
}
body[data-ui-version="v2"] .gn-v2-query-results .query-result-tabs > .ant-tabs-nav .ant-tabs-tab {
width: auto !important;
min-width: 0 !important;
max-width: 148px !important;
- height: 30px !important;
- min-height: 30px;
- margin: 4px 6px 4px 0 !important;
+ height: 46px !important;
+ min-height: 46px;
+ margin: 0 !important;
padding: 0 9px !important;
border: 0.5px solid var(--gn-br-1) !important;
border-radius: 8px !important;
@@ -451,6 +458,11 @@ body[data-ui-version="v2"] .gn-v2-query-results .query-result-tabs > .ant-tabs-n
font-size: 14px;
}
+body[data-ui-version="v2"] .gn-v2-query-results .query-result-tabs > .ant-tabs-nav .ant-tabs-nav-wrap,
+body[data-ui-version="v2"] .gn-v2-query-results .query-result-tabs > .ant-tabs-nav .ant-tabs-nav-list {
+ min-height: 46px;
+}
+
body[data-ui-version="v2"] .gn-v2-query-results .query-result-tabs > .ant-tabs-nav .ant-tabs-nav-list {
align-items: center;
width: auto;
@@ -503,6 +515,10 @@ body[data-ui-version="v2"] .gn-v2-query-result-panel-header {
border-bottom-color: var(--gn-br-1);
}
+body[data-ui-version="v2"] .log-panel-embedded .log-panel-table .ant-table {
+ background: var(--gn-query-workbench-bg, var(--gn-bg-panel-2)) !important;
+}
+
body[data-ui-version="v2"] .gn-v2-query-result-panel-header .query-result-panel-header-title {
color: var(--gn-fg-3);
}
diff --git a/frontend/src/utils/customTheme.test.ts b/frontend/src/utils/customTheme.test.ts
index c7a7ef81..a32e8091 100644
--- a/frontend/src/utils/customTheme.test.ts
+++ b/frontend/src/utils/customTheme.test.ts
@@ -51,11 +51,18 @@ describe('custom theme CSS validation', () => {
);
});
- it('injects --gn-monaco-bg for hand-written themes that only set panel', () => {
+ it('injects --gn-monaco-bg for hand-written themes that only set theme surfaces', () => {
+ const css = 'body[data-custom-theme] {\n --gn-bg-panel: #f7faf8;\n --gn-bg-panel-2: #eef3f0;\n}';
+ const next = ensureCustomThemeMonacoSurfaceVars(css);
+ expect(next).toContain('--gn-monaco-bg: var(--gn-bg-panel-2)');
+ expect(next).toContain('--gn-bg-panel: #f7faf8');
+ });
+
+ it('keeps legacy hand-written themes aligned when they only define panel', () => {
const css = 'body[data-custom-theme] {\n --gn-bg-panel: #f7faf8;\n}';
const next = ensureCustomThemeMonacoSurfaceVars(css);
+
expect(next).toContain('--gn-monaco-bg: var(--gn-bg-panel)');
- expect(next).toContain('--gn-bg-panel: #f7faf8');
});
it('does not override an explicit --gn-monaco-bg in custom theme CSS', () => {
diff --git a/frontend/src/utils/customTheme.ts b/frontend/src/utils/customTheme.ts
index 7521d660..51557af5 100644
--- a/frontend/src/utils/customTheme.ts
+++ b/frontend/src/utils/customTheme.ts
@@ -220,19 +220,20 @@ export const createCustomThemeId = (): string => {
/**
* Ensure Monaco surface token exists for hand-written custom themes.
- * Presets already define it; user CSS that only sets --gn-bg-panel still needs this.
+ * Presets already define it; hand-written CSS still needs the shared theme surface default.
* If the theme already declares --gn-monaco-bg, leave it alone (user override wins).
*/
export const ensureCustomThemeMonacoSurfaceVars = (css: string): string => {
const source = String(css || '');
if (/--gn-monaco-bg\s*:/.test(source)) return source;
+ const surfaceVar = /--gn-bg-panel-2\s*:/.test(source) ? '--gn-bg-panel-2' : '--gn-bg-panel';
const trimmed = source.trimEnd();
const appendix = [
'',
- '/* GoNavi: Monaco surface follows panel unless the theme overrides --gn-monaco-bg */',
+ `/* GoNavi: Monaco surface follows ${surfaceVar} unless the theme overrides --gn-monaco-bg */`,
'body[data-custom-theme],',
'body[data-custom-theme][data-ui-version="v2"] {',
- ' --gn-monaco-bg: var(--gn-bg-panel);',
+ ` --gn-monaco-bg: var(${surfaceVar});`,
'}',
'',
].join('\n');
@@ -396,7 +397,7 @@ body[data-custom-theme][data-ui-version="v2"] {
--gn-bg-chrome: #171a23;
--gn-bg-panel: #1d202b;
--gn-bg-panel-2: #232733;
- --gn-monaco-bg: var(--gn-bg-panel);
+ --gn-monaco-bg: var(--gn-bg-panel-2);
--gn-bg-hover: rgba(255, 255, 255, 0.06);
--gn-bg-active: rgba(255, 255, 255, 0.10);
--gn-bg-selected: rgba(139, 92, 246, 0.18);
diff --git a/frontend/src/utils/customThemePresets.test.ts b/frontend/src/utils/customThemePresets.test.ts
index d07bcda4..ad16f17c 100644
--- a/frontend/src/utils/customThemePresets.test.ts
+++ b/frontend/src/utils/customThemePresets.test.ts
@@ -48,11 +48,15 @@ describe('built-in custom theme presets', () => {
expect(sanitizeCustomThemeDefinition(preset)).toEqual(expect.objectContaining({ id: preset.id }));
expect(preset.css).toContain('--gn-ant-primary:');
expect(preset.css).toContain('--gn-ant-on-primary:');
+ expect(preset.css).toContain('--gn-monaco-bg: var(--gn-bg-panel-2);');
expect(preset.css).toContain('--gn-settings-card-bg:');
expect(preset.css).toContain('--gn-explain-critical:');
expect(preset.css).toContain('--gn-status-connected:');
expect(preset.css).toContain('.gn-v2-tab-label-part-host');
expect(preset.css).toContain('.gn-v2-tab-label-part-database');
+ expect(preset.css).toContain(
+ 'background-color: var(--gn-monaco-bg, var(--gn-bg-panel-2)) !important;',
+ );
}
expect(BUILTIN_CUSTOM_THEME_PRESETS.filter((preset) => preset.baseMode === 'dark')).toHaveLength(4);
expect(BUILTIN_CUSTOM_THEME_PRESETS.filter((preset) => preset.baseMode === 'light')).toHaveLength(2);
@@ -70,6 +74,7 @@ describe('built-in custom theme presets', () => {
expect(comfortDark.css).toContain('.gn-v2-query-toolbar-save-action');
expect(comfortDark.css).toContain('.gn-v2-ai-panel .ai-logo');
expect(comfortDark.css).toContain('.monaco-editor-background');
+ expect(comfortDark.css).not.toContain('background-color: var(--gn-bg-input) !important;');
});
it('keeps preset text and solid-button colors at WCAG AA contrast', () => {
diff --git a/frontend/src/utils/customThemePresets.ts b/frontend/src/utils/customThemePresets.ts
index 9811545b..5298f4b6 100644
--- a/frontend/src/utils/customThemePresets.ts
+++ b/frontend/src/utils/customThemePresets.ts
@@ -254,7 +254,7 @@ body[data-custom-theme][data-ui-version="v2"] {
--gn-bg-chrome: ${palette.chrome};
--gn-bg-panel: ${palette.panel};
--gn-bg-panel-2: ${palette.panel2};
- --gn-monaco-bg: var(--gn-bg-panel);
+ --gn-monaco-bg: var(--gn-bg-panel-2);
--gn-bg-input: ${palette.input};
--gn-bg-subtle: ${palette.panel2};
--gn-bg-hover: ${palette.hover};
@@ -455,7 +455,7 @@ body[data-custom-theme][data-ui-version="v2"] .monaco-editor,
body[data-custom-theme][data-ui-version="v2"] .monaco-editor-background,
body[data-custom-theme][data-ui-version="v2"] .monaco-editor .margin,
body[data-custom-theme][data-ui-version="v2"] .monaco-editor .sticky-widget {
- background-color: var(--gn-bg-input) !important;
+ background-color: var(--gn-monaco-bg, var(--gn-bg-panel-2)) !important;
}`;
const createPreset = (