mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-09 16:23:27 +08:00
🎨 style(theme): 对齐表概览主题表面并优化工具栏与时间展示
- 主内容区与侧栏统一 panel-2,消除表概览空白区色差 - 查询工具栏保存按钮改为 default 图标样式,提升暗色可读性 - 表概览时间格式化为 YYYY-MM-DD HH:mm:ss,保留小数秒 - 补充多语言文案与相关测试
This commit is contained in:
@@ -1 +1 @@
|
||||
83b4b7d166ed9172beba2d6236382c0b
|
||||
339d5c974e6ab5cba2d5def171f5ce0a
|
||||
@@ -557,7 +557,9 @@ const QueryEditorToolbar: React.FC<QueryEditorToolbarProps> = ({
|
||||
<Button
|
||||
aria-label={t("query_editor.action.save")}
|
||||
className={isV2Ui ? "gn-v2-query-toolbar-icon-action gn-v2-query-toolbar-save-action" : undefined}
|
||||
type="primary"
|
||||
/* v2 与终端等工具按钮同族(default),避免 primary 的 on-accent 前景在暗色下发脏绿;
|
||||
可读性靠 CSS 把图标提到 fg-1。legacy 仍用 primary 实心按钮。 */
|
||||
type={isV2Ui ? "default" : "primary"}
|
||||
icon={<SaveOutlined />}
|
||||
onClick={onQuickSave}
|
||||
>
|
||||
|
||||
@@ -588,6 +588,7 @@ describe('TableOverview metadata compatibility', () => {
|
||||
const renderedText = collectText(renderer!.toJSON());
|
||||
expect(renderedText).toContain('orders');
|
||||
expect(renderedText).toContain('订单表');
|
||||
// 展示统一为 YYYY-MM-DD HH:mm:ss(有小数秒时保留,与侧栏 formatSidebarTableTimestamp 一致)
|
||||
expect(renderedText).toContain('2026-06-02 10:30:00');
|
||||
expect(renderedText).toContain('2026-05-01 09:00:00');
|
||||
});
|
||||
|
||||
@@ -32,6 +32,7 @@ import { extractTableNameFromMetadataRow } from '../utils/tableMetadataRows';
|
||||
import { V2TableContextMenuView, type V2TableContextMenuActionKey } from './V2TableContextMenu';
|
||||
import { confirmCopyTable } from './tableCopyAction';
|
||||
import { APP_POPUP_Z_INDEX } from '../utils/overlayZIndex';
|
||||
import { formatSidebarTableTimestamp } from './sidebar/sidebarHelpers';
|
||||
|
||||
interface TableOverviewProps {
|
||||
tab: TabData;
|
||||
@@ -816,14 +817,15 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
}, [addAIContext, buildConfig, connection?.id, setAIPanelVisible, tab.dbName]);
|
||||
|
||||
// --- Theme ---
|
||||
const cardBg = darkMode ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.02)';
|
||||
// v2 背景/边框/强调色交给 CSS token(跟自定义主题);legacy 仍用 darkMode 近似色。
|
||||
const cardBg = isV2Ui ? undefined : (darkMode ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.02)');
|
||||
const cardHoverBg = darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)';
|
||||
const cardBorder = darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)';
|
||||
const textPrimary = darkMode ? 'rgba(255,255,255,0.88)' : 'rgba(0,0,0,0.88)';
|
||||
const textSecondary = darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)';
|
||||
const textMuted = darkMode ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.35)';
|
||||
const accentColor = '#1677ff';
|
||||
const containerBg = darkMode ? 'rgba(0,0,0,0.15)' : 'rgba(0,0,0,0.01)';
|
||||
const cardBorder = isV2Ui ? undefined : (darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)');
|
||||
const textPrimary = isV2Ui ? 'var(--gn-fg-1)' : (darkMode ? 'rgba(255,255,255,0.88)' : 'rgba(0,0,0,0.88)');
|
||||
const textSecondary = isV2Ui ? 'var(--gn-fg-3)' : (darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.55)');
|
||||
const textMuted = isV2Ui ? 'var(--gn-fg-4)' : (darkMode ? 'rgba(255,255,255,0.35)' : 'rgba(0,0,0,0.35)');
|
||||
const accentColor = isV2Ui ? 'var(--gn-accent)' : '#1677ff';
|
||||
const containerBg = isV2Ui ? undefined : (darkMode ? 'rgba(0,0,0,0.15)' : 'rgba(0,0,0,0.01)');
|
||||
|
||||
const toggleSort = (field: SortField) => {
|
||||
if (sortField === field) {
|
||||
@@ -1065,17 +1067,24 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const formatOverviewTimestamp = useCallback((value?: string): string => {
|
||||
if (!value) return '';
|
||||
return formatSidebarTableTimestamp(value) || value;
|
||||
}, []);
|
||||
|
||||
const renderTableOverviewMetaBadges = useCallback((table: TableStatRow, compact = false) => {
|
||||
const items = [
|
||||
...(table.updateTime ? [{
|
||||
key: 'updated',
|
||||
label: t('table_overview.metric.updated_at'),
|
||||
value: table.updateTime,
|
||||
raw: table.updateTime,
|
||||
value: formatOverviewTimestamp(table.updateTime),
|
||||
}] : []),
|
||||
...(table.createTime ? [{
|
||||
key: 'created',
|
||||
label: t('table_overview.metric.created_at'),
|
||||
value: table.createTime,
|
||||
raw: table.createTime,
|
||||
value: formatOverviewTimestamp(table.createTime),
|
||||
}] : []),
|
||||
];
|
||||
if (items.length === 0) return null;
|
||||
@@ -1090,7 +1099,7 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
}}
|
||||
>
|
||||
{items.map((item) => (
|
||||
<Tooltip key={item.key} title={`${item.label}: ${item.value}`} mouseEnterDelay={0.4}>
|
||||
<Tooltip key={item.key} title={`${item.label}: ${item.raw}`} mouseEnterDelay={0.4}>
|
||||
<span
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
@@ -1099,7 +1108,9 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
maxWidth: '100%',
|
||||
padding: compact ? '1px 7px' : '2px 8px',
|
||||
borderRadius: 999,
|
||||
background: darkMode ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)',
|
||||
background: isV2Ui
|
||||
? 'var(--gn-bg-active)'
|
||||
: (darkMode ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)'),
|
||||
color: textSecondary,
|
||||
fontSize: compact ? 10 : 11,
|
||||
lineHeight: compact ? '16px' : '18px',
|
||||
@@ -1115,7 +1126,7 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}, [darkMode, t, textPrimary, textSecondary]);
|
||||
}, [darkMode, formatOverviewTimestamp, isV2Ui, t, textPrimary, textSecondary]);
|
||||
|
||||
const renderCardTableContent = (table: TableStatRow) => (
|
||||
<div
|
||||
@@ -1123,8 +1134,10 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
onDoubleClick={() => openTableByDefaultAction(table.name)}
|
||||
onContextMenu={isV2Ui ? (event) => openV2OverviewContextMenu(event, table) : undefined}
|
||||
style={{
|
||||
background: cardBg,
|
||||
border: `1px solid ${cardBorder}`,
|
||||
...(isV2Ui ? {} : {
|
||||
background: cardBg,
|
||||
border: `1px solid ${cardBorder}`,
|
||||
}),
|
||||
borderRadius: 10,
|
||||
padding: '14px 16px',
|
||||
cursor: 'pointer',
|
||||
@@ -1132,7 +1145,7 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
userSelect: 'none',
|
||||
}}
|
||||
onMouseEnter={isV2Ui ? undefined : e => { (e.currentTarget as HTMLDivElement).style.background = cardHoverBg; (e.currentTarget as HTMLDivElement).style.borderColor = accentColor; }}
|
||||
onMouseLeave={isV2Ui ? undefined : e => { (e.currentTarget as HTMLDivElement).style.background = cardBg; (e.currentTarget as HTMLDivElement).style.borderColor = cardBorder; }}
|
||||
onMouseLeave={isV2Ui ? undefined : e => { (e.currentTarget as HTMLDivElement).style.background = cardBg || ''; (e.currentTarget as HTMLDivElement).style.borderColor = cardBorder || ''; }}
|
||||
>
|
||||
<div className={isV2Ui ? 'gn-v2-table-card-name' : undefined} style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
|
||||
<TableOutlined style={{ fontSize: 14, color: accentColor }} />
|
||||
@@ -1182,7 +1195,9 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
const combinedSize = getCombinedTableSize(table);
|
||||
const sizeRatio = maxCombinedSize > 0 && hasKnownTableSize(table) ? combinedSize / maxCombinedSize : 0;
|
||||
const fillWidth = maxCombinedSize > 0 && hasKnownTableSize(table) ? `${Math.max(10, Math.round(sizeRatio * 100))}%` : '0%';
|
||||
const fillColor = darkMode ? 'rgba(22,119,255,0.18)' : 'rgba(22,119,255,0.12)';
|
||||
const fillColor = isV2Ui
|
||||
? 'var(--gn-accent-soft, rgba(34, 197, 94, 0.16))'
|
||||
: (darkMode ? 'rgba(22,119,255,0.18)' : 'rgba(22,119,255,0.12)');
|
||||
const rowSecondary = table.comment || (table.engine
|
||||
? t('table_overview.row.engine_table', { engine: table.engine })
|
||||
: t('table_overview.row.open_hint'));
|
||||
@@ -1195,15 +1210,17 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
style={{
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
borderRadius: 10,
|
||||
border: `1px solid ${cardBorder}`,
|
||||
background: cardBg,
|
||||
borderRadius: isV2Ui ? undefined : 10,
|
||||
...(isV2Ui ? {} : {
|
||||
border: `1px solid ${cardBorder}`,
|
||||
background: cardBg,
|
||||
}),
|
||||
cursor: 'pointer',
|
||||
transition: isV2Ui ? undefined : 'all 0.15s ease',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
onMouseEnter={isV2Ui ? undefined : e => { (e.currentTarget as HTMLDivElement).style.background = cardHoverBg; (e.currentTarget as HTMLDivElement).style.borderColor = accentColor; }}
|
||||
onMouseLeave={isV2Ui ? undefined : e => { (e.currentTarget as HTMLDivElement).style.background = cardBg; (e.currentTarget as HTMLDivElement).style.borderColor = cardBorder; }}
|
||||
onMouseLeave={isV2Ui ? undefined : e => { (e.currentTarget as HTMLDivElement).style.background = cardBg || ''; (e.currentTarget as HTMLDivElement).style.borderColor = cardBorder || ''; }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
@@ -1244,7 +1261,9 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
borderRadius: 999,
|
||||
fontSize: 11,
|
||||
color: textMuted,
|
||||
background: darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)',
|
||||
background: isV2Ui
|
||||
? 'var(--gn-bg-active)'
|
||||
: (darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)'),
|
||||
}}
|
||||
>
|
||||
{table.engine}
|
||||
@@ -1379,11 +1398,16 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
alignItems: 'center',
|
||||
minWidth: 1120,
|
||||
minHeight: 32,
|
||||
borderBottom: `1px solid ${cardBorder}`,
|
||||
background: cardBg,
|
||||
color: textPrimary,
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none',
|
||||
...(isV2Ui ? {
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none',
|
||||
} : {
|
||||
borderBottom: `1px solid ${cardBorder}`,
|
||||
background: cardBg,
|
||||
color: textPrimary,
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none',
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<div className="gn-table-overview-compact-name" role="cell" title={table.name}>
|
||||
@@ -1395,8 +1419,20 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
<div className="gn-table-overview-compact-cell gn-table-overview-compact-number" role="cell" title={table.dataSize >= 0 ? String(table.dataSize) : undefined}>{formatSize(table.dataSize)}</div>
|
||||
<div className="gn-table-overview-compact-cell gn-table-overview-compact-number" role="cell" title={table.indexSize >= 0 ? String(table.indexSize) : undefined}>{formatSize(table.indexSize)}</div>
|
||||
<div className="gn-table-overview-compact-cell" role="cell" title={table.engine || undefined}>{table.engine || '—'}</div>
|
||||
<div className="gn-table-overview-compact-cell" role="cell" title={table.updateTime || undefined}>{table.updateTime || '—'}</div>
|
||||
<div className="gn-table-overview-compact-cell" role="cell" title={table.createTime || undefined}>{table.createTime || '—'}</div>
|
||||
<div
|
||||
className="gn-table-overview-compact-cell"
|
||||
role="cell"
|
||||
title={table.updateTime || undefined}
|
||||
>
|
||||
{table.updateTime ? formatOverviewTimestamp(table.updateTime) : '—'}
|
||||
</div>
|
||||
<div
|
||||
className="gn-table-overview-compact-cell"
|
||||
role="cell"
|
||||
title={table.createTime || undefined}
|
||||
>
|
||||
{table.createTime ? formatOverviewTimestamp(table.createTime) : '—'}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1422,6 +1458,12 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
);
|
||||
}
|
||||
|
||||
const viewSwitchBtnClass = (mode: ViewMode) => (
|
||||
isV2Ui
|
||||
? `gn-v2-table-overview-view-switch-btn${viewMode === mode ? ' is-active' : ''}`
|
||||
: undefined
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={isV2Ui ? 'gn-table-overview gn-v2-table-overview' : 'gn-table-overview'} style={{ display: 'flex', flexDirection: 'column', height: '100%', background: containerBg, overflow: 'hidden' }}>
|
||||
{/* Toolbar */}
|
||||
@@ -1447,15 +1489,19 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
<Dropdown menu={{ items: sortMenuItems }} trigger={['click']}>
|
||||
<Tooltip title={t('table_overview.tooltip.sort')}><SortAscendingOutlined style={{ fontSize: 16, color: textSecondary, cursor: 'pointer' }} /></Tooltip>
|
||||
</Dropdown>
|
||||
<div style={{ display: 'flex', gap: 2, padding: 2, borderRadius: 6, background: darkMode ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)' }}>
|
||||
<div
|
||||
className={isV2Ui ? 'gn-v2-table-overview-view-switch' : undefined}
|
||||
style={isV2Ui ? undefined : { display: 'flex', gap: 2, padding: 2, borderRadius: 6, background: darkMode ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)' }}
|
||||
>
|
||||
<Tooltip title={t('table_overview.tooltip.card_view')}>
|
||||
<button
|
||||
type="button"
|
||||
className={viewSwitchBtnClass('card')}
|
||||
data-table-overview-view-mode="card"
|
||||
aria-label={t('table_overview.tooltip.card_view')}
|
||||
aria-pressed={viewMode === 'card'}
|
||||
onClick={() => setViewMode('card')}
|
||||
style={{
|
||||
style={isV2Ui ? undefined : {
|
||||
width: 28, height: 24, padding: 0, border: 0, borderRadius: 5, cursor: 'pointer', transition: 'all 0.15s',
|
||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center', font: 'inherit',
|
||||
background: viewMode === 'card' ? (darkMode ? 'rgba(255,255,255,0.12)' : '#fff') : 'transparent',
|
||||
@@ -1469,11 +1515,12 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
<Tooltip title={t('table_overview.tooltip.list_view')}>
|
||||
<button
|
||||
type="button"
|
||||
className={viewSwitchBtnClass('list')}
|
||||
data-table-overview-view-mode="list"
|
||||
aria-label={t('table_overview.tooltip.list_view')}
|
||||
aria-pressed={viewMode === 'list'}
|
||||
onClick={() => setViewMode('list')}
|
||||
style={{
|
||||
style={isV2Ui ? undefined : {
|
||||
width: 28, height: 24, padding: 0, border: 0, borderRadius: 5, cursor: 'pointer', transition: 'all 0.15s',
|
||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center', font: 'inherit',
|
||||
background: viewMode === 'list' ? (darkMode ? 'rgba(255,255,255,0.12)' : '#fff') : 'transparent',
|
||||
@@ -1487,11 +1534,12 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
<Tooltip title={t('table_overview.tooltip.table_view')}>
|
||||
<button
|
||||
type="button"
|
||||
className={viewSwitchBtnClass('table')}
|
||||
data-table-overview-view-mode="table"
|
||||
aria-label={t('table_overview.tooltip.table_view')}
|
||||
aria-pressed={viewMode === 'table'}
|
||||
onClick={() => setViewMode('table')}
|
||||
style={{
|
||||
style={isV2Ui ? undefined : {
|
||||
width: 28, height: 24, padding: 0, border: 0, borderRadius: 5, cursor: 'pointer', transition: 'all 0.15s',
|
||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center', font: 'inherit',
|
||||
background: viewMode === 'table' ? (darkMode ? 'rgba(255,255,255,0.12)' : '#fff') : 'transparent',
|
||||
@@ -1509,7 +1557,11 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
{/* Content Area */}
|
||||
<div
|
||||
className={isV2Ui ? 'gn-v2-table-overview-content' : undefined}
|
||||
style={{
|
||||
data-view-mode={isV2Ui ? viewMode : undefined}
|
||||
style={isV2Ui ? {
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
} : {
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
overflow: viewMode === 'table' ? 'hidden' : 'auto',
|
||||
@@ -1520,7 +1572,15 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
>
|
||||
{sortedFiltered.length > 0 && (isSearchPending || visibleOverview.hiddenCount > 0 || deferredSearchText.trim()) && (
|
||||
<div
|
||||
style={{
|
||||
className={isV2Ui ? 'gn-v2-table-overview-filter-bar' : undefined}
|
||||
style={isV2Ui ? {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 12,
|
||||
fontSize: 12,
|
||||
flexShrink: 0,
|
||||
} : {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
@@ -1581,7 +1641,10 @@ const TableOverview: React.FC<TableOverviewProps> = ({ tab }) => {
|
||||
{section.rows.map(renderCardTable)}
|
||||
</div>
|
||||
) : (
|
||||
<div className={isV2Ui ? 'gn-v2-table-row-list' : undefined} style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||||
<div
|
||||
className={isV2Ui ? 'gn-v2-table-row-list' : undefined}
|
||||
style={{ display: 'flex', flexDirection: 'column', gap: isV2Ui ? 0 : 10 }}
|
||||
>
|
||||
{section.rows.map(renderListTable)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -54,18 +54,36 @@ export const formatSidebarTableSize = (bytes: number): string => {
|
||||
const padSidebarTimestampPart = (value: number): string => String(value).padStart(2, '0');
|
||||
|
||||
/**
|
||||
* formatSidebarTableTimestamp 把数据库返回的时间值统一压缩成 `YYYY-MM-DD HH:mm`。
|
||||
* 若值无法被 Date 可靠解析,则尽量保留原始文本的可读部分。
|
||||
* formatSidebarTableTimestamp 把数据库返回的时间值格式化为
|
||||
* `YYYY-MM-DD HH:mm:ss`;若源值带小数秒(毫秒/微秒),则原样保留小数部分。
|
||||
* 例:
|
||||
* 2026-04-26T18:06:08+08:00 → 2026-04-26 18:06:08
|
||||
* 2026-05-10T09:12:33.456+08:00 → 2026-05-10 09:12:33.456
|
||||
* 若无法被 Date 可靠解析,则尽量保留原始文本的可读部分。
|
||||
*/
|
||||
export const formatSidebarTableTimestamp = (value: unknown): string => {
|
||||
const text = String(value ?? '').trim();
|
||||
if (!text) return '';
|
||||
const normalizedText = text.replace('T', ' ').replace(/\.\d+$/, '');
|
||||
|
||||
// 优先用正则剥离时区,保留秒与小数秒(避免 Date 丢弃微秒精度)
|
||||
const match = text.match(
|
||||
/^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2}:\d{2})(\.\d+)?(?:\s*(?:Z|[+-]\d{2}:?\d{2})(?:\s+[A-Za-z_\/+-]+)?)?$/i,
|
||||
);
|
||||
if (match) {
|
||||
return `${match[1]} ${match[2]}${match[3] || ''}`;
|
||||
}
|
||||
|
||||
const parsed = new Date(text);
|
||||
if (Number.isNaN(parsed.getTime())) {
|
||||
return normalizedText.length > 16 ? normalizedText.slice(0, 16) : normalizedText;
|
||||
// 无法解析时去掉 T/时区噪音,尽量给出可读截断
|
||||
const fallback = text
|
||||
.replace('T', ' ')
|
||||
.replace(/([+-]\d{2}:?\d{2}|Z)\s*$/i, '')
|
||||
.trim();
|
||||
return fallback.length > 23 ? fallback.slice(0, 23) : fallback;
|
||||
}
|
||||
return [
|
||||
|
||||
const base = [
|
||||
parsed.getFullYear(),
|
||||
'-',
|
||||
padSidebarTimestampPart(parsed.getMonth() + 1),
|
||||
@@ -75,7 +93,15 @@ export const formatSidebarTableTimestamp = (value: unknown): string => {
|
||||
padSidebarTimestampPart(parsed.getHours()),
|
||||
':',
|
||||
padSidebarTimestampPart(parsed.getMinutes()),
|
||||
':',
|
||||
padSidebarTimestampPart(parsed.getSeconds()),
|
||||
].join('');
|
||||
|
||||
const ms = parsed.getMilliseconds();
|
||||
if (ms > 0) {
|
||||
return `${base}.${String(ms).padStart(3, '0')}`;
|
||||
}
|
||||
return base;
|
||||
};
|
||||
|
||||
export interface SidebarTableMetadataDisplayItem {
|
||||
|
||||
@@ -34,8 +34,10 @@ export const useAppUtilityStyles = ({
|
||||
return `rgba(${r}, ${g}, ${b}, ${effectiveOpacity})`;
|
||||
};
|
||||
|
||||
const bgMain = getBg('#141414');
|
||||
const bgContent = getBg('#1d1d1d');
|
||||
// v2 / 自定义主题:走 CSS token,主内容与 sider 同为 panel-2,避免并排色差。
|
||||
// legacy 仍用 getBg 做透明度混合。
|
||||
const bgMain = isV2Ui ? 'var(--gn-bg-panel-2)' : getBg('#141414');
|
||||
const bgContent = isV2Ui ? 'var(--gn-bg-panel-2)' : getBg('#1d1d1d');
|
||||
const floatingLogButtonBorderColor = darkMode ? 'rgba(255,255,255,0.20)' : 'rgba(0,0,0,0.16)';
|
||||
const floatingLogButtonTextColor = darkMode ? 'rgba(255,255,255,0.92)' : 'rgba(0,0,0,0.82)';
|
||||
const floatingLogButtonBgColor = darkMode
|
||||
|
||||
@@ -336,6 +336,38 @@ body[data-ui-version="v2"] .gn-v2-query-toolbar-run-action.ant-btn {
|
||||
font-weight: 650 !important;
|
||||
}
|
||||
|
||||
/* 保存:与终端等 default 图标按钮同族,只把图标提到 fg-1,保证暗色下可读。
|
||||
不再用软绿底/描边——会在实心「执行」旁显得脏、不协调。 */
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled) {
|
||||
background: var(--gn-bg-panel) !important;
|
||||
border-color: var(--gn-br-2) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn .anticon,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled) .anticon {
|
||||
color: var(--gn-fg-1) !important;
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:hover,
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:focus-visible,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):hover,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):focus-visible {
|
||||
background: var(--gn-bg-hover) !important;
|
||||
border-color: var(--gn-br-3) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:hover .anticon,
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:focus-visible .anticon,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):hover .anticon,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):focus-visible .anticon {
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar-ai-action.ant-btn {
|
||||
font-weight: 650 !important;
|
||||
}
|
||||
@@ -1904,16 +1936,55 @@ body[data-ui-version="v2"] .gn-nacos-service-list-footer {
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview {
|
||||
background: var(--gn-bg-app) !important;
|
||||
/* 与左侧 sider / 主内容区同用 panel-2,消除并排色差 */
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
color: var(--gn-fg-1);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-header {
|
||||
min-height: 54px;
|
||||
padding: 12px 16px !important;
|
||||
background: var(--gn-bg-panel) !important;
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
border-bottom: 0.5px solid var(--gn-br-1);
|
||||
}
|
||||
|
||||
/* 视图切换器:底/选中态跟主题,不再写死白/灰 */
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-view-switch {
|
||||
display: inline-flex;
|
||||
gap: 2px;
|
||||
padding: 2px;
|
||||
border-radius: 6px;
|
||||
background: var(--gn-bg-active) !important;
|
||||
border: 0.5px solid var(--gn-br-1);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-view-switch-btn {
|
||||
width: 28px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
font: inherit;
|
||||
color: var(--gn-fg-4);
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
transition: background 0.12s ease, color 0.12s ease;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-view-switch-btn.is-active {
|
||||
background: color-mix(in srgb, var(--gn-fg-1) 6%, var(--gn-bg-panel-2)) !important;
|
||||
color: var(--gn-accent) !important;
|
||||
box-shadow: var(--gn-shadow-sm);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-view-switch-btn:hover {
|
||||
color: var(--gn-fg-1);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
@@ -1961,15 +2032,48 @@ body[data-ui-version="v2"] .gn-v2-table-overview-summary strong {
|
||||
}
|
||||
}
|
||||
|
||||
/* 卡片保留内边距;列表/表格铺满内容区,去掉红框里那圈空白 */
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content {
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content[data-view-mode="card"] {
|
||||
padding: 14px 16px 20px !important;
|
||||
background: var(--gn-bg-app);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content[data-view-mode="list"],
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content[data-view-mode="table"] {
|
||||
padding: 0 !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content[data-view-mode="list"] {
|
||||
overflow: auto !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-sections {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
background: var(--gn-bg-panel-2);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-section {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content[data-view-mode="list"] .gn-v2-table-overview-section-title {
|
||||
margin: 0 !important;
|
||||
padding: 8px 14px 6px !important;
|
||||
background: var(--gn-bg-panel-2);
|
||||
border-bottom: 0.5px solid var(--gn-br-1);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-section-title {
|
||||
@@ -2002,7 +2106,9 @@ body[data-ui-version="v2"] .gn-v2-table-card,
|
||||
body[data-ui-version="v2"] .gn-v2-table-row {
|
||||
border: 0.5px solid var(--gn-br-2) !important;
|
||||
border-radius: 8px !important;
|
||||
background: var(--gn-bg-panel) !important;
|
||||
/* 底已是 panel-2:卡片用轻微抬升混合色,避免与底/左侧同色糊成一片 */
|
||||
background: color-mix(in srgb, var(--gn-fg-1) 3.5%, var(--gn-bg-panel-2)) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
box-shadow: var(--gn-shadow-card);
|
||||
transition: background 0.08s, border-color 0.08s, box-shadow 0.08s;
|
||||
}
|
||||
@@ -2014,6 +2120,29 @@ body[data-ui-version="v2"] .gn-v2-table-row:hover {
|
||||
box-shadow: var(--gn-shadow-md);
|
||||
}
|
||||
|
||||
/* 列表铺满:去掉卡片间距与圆角容器,整列跟面板色 */
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content[data-view-mode="list"] .gn-v2-table-row-list {
|
||||
gap: 0 !important;
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content[data-view-mode="list"] .gn-v2-table-row {
|
||||
border-radius: 0 !important;
|
||||
border-width: 0 0 0.5px !important;
|
||||
border-color: var(--gn-br-1) !important;
|
||||
box-shadow: none !important;
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-content[data-view-mode="list"] .gn-v2-table-row:hover {
|
||||
background: var(--gn-bg-hover) !important;
|
||||
border-color: var(--gn-br-1) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-table-card-name span {
|
||||
font-family: var(--gn-font-mono);
|
||||
}
|
||||
@@ -2059,6 +2188,7 @@ body[data-ui-version="v2"] .gn-v2-table-row:last-child {
|
||||
.gn-table-overview-compact-scroll {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
border: 1px solid rgba(15, 23, 42, 0.12);
|
||||
border-radius: 6px;
|
||||
@@ -2068,6 +2198,8 @@ body[data-ui-version="v2"] .gn-v2-table-row:last-child {
|
||||
|
||||
.gn-table-overview-compact-header,
|
||||
.gn-table-overview-compact-row {
|
||||
width: max(100%, 1120px);
|
||||
box-sizing: border-box;
|
||||
grid-template-columns:
|
||||
minmax(190px, 1.7fr)
|
||||
minmax(170px, 1.4fr)
|
||||
@@ -2176,18 +2308,24 @@ body[data-ui-version="legacy"][data-theme="dark"] .gn-table-overview-compact-row
|
||||
background: rgba(22, 119, 255, 0.16) !important;
|
||||
}
|
||||
|
||||
/* 表格视图:贴边铺满内容区,背景全程跟主题 token */
|
||||
body[data-ui-version="v2"] .gn-table-overview-compact-scroll {
|
||||
border-width: 0.5px;
|
||||
border-color: var(--gn-br-2);
|
||||
background: var(--gn-bg-panel);
|
||||
flex: 1 1 auto !important;
|
||||
min-height: 0 !important;
|
||||
width: 100% !important;
|
||||
border: none !important;
|
||||
border-radius: 0 !important;
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-table-overview-compact-header {
|
||||
min-height: 30px;
|
||||
min-width: 1120px;
|
||||
width: max(100%, 1120px);
|
||||
border-bottom-width: 0.5px;
|
||||
border-bottom-color: var(--gn-br-2);
|
||||
background: var(--gn-bg-panel-2);
|
||||
color: var(--gn-fg-3);
|
||||
background: color-mix(in srgb, var(--gn-fg-1) 2.5%, var(--gn-bg-panel-2)) !important;
|
||||
color: var(--gn-fg-3) !important;
|
||||
font-family: var(--gn-font-mono);
|
||||
}
|
||||
|
||||
@@ -2196,21 +2334,35 @@ body[data-ui-version="v2"] .gn-table-overview-compact-header > [role="columnhead
|
||||
border-right-color: var(--gn-br-1);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-table-overview-compact-sort-button {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-table-overview-compact-sort-button:hover {
|
||||
background: var(--gn-bg-hover) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-table-overview-compact-sort-button[aria-pressed="true"] {
|
||||
color: var(--gn-accent) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-table-overview-compact-section {
|
||||
min-width: 1120px;
|
||||
width: max(100%, 1120px);
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
border-bottom-width: 0.5px;
|
||||
border-bottom-color: var(--gn-br-1);
|
||||
background: var(--gn-bg-panel-2);
|
||||
background: color-mix(in srgb, var(--gn-fg-1) 2.5%, var(--gn-bg-panel-2)) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-table-overview-compact-row {
|
||||
min-height: 30px !important;
|
||||
min-width: 1120px !important;
|
||||
width: max(100%, 1120px) !important;
|
||||
border-bottom-width: 0.5px !important;
|
||||
border-bottom-color: var(--gn-br-1) !important;
|
||||
background: var(--gn-bg-panel) !important;
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
font-family: var(--gn-font-mono);
|
||||
}
|
||||
@@ -2225,10 +2377,21 @@ body[data-ui-version="v2"] .gn-table-overview-compact-cell {
|
||||
padding-bottom: 4px;
|
||||
font-size: 11.5px;
|
||||
line-height: 21px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-table-overview-compact-name .anticon {
|
||||
color: var(--gn-accent);
|
||||
color: var(--gn-accent) !important;
|
||||
}
|
||||
|
||||
/* 表概览筛选条也跟主题 */
|
||||
body[data-ui-version="v2"] .gn-v2-table-overview-filter-bar {
|
||||
margin: 0 !important;
|
||||
padding: 8px 14px !important;
|
||||
border-radius: 0 !important;
|
||||
border-bottom: 0.5px solid var(--gn-br-1);
|
||||
background: color-mix(in srgb, var(--gn-fg-1) 2.5%, var(--gn-bg-panel-2)) !important;
|
||||
color: var(--gn-fg-4) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-data-grid-column-quick-find {
|
||||
|
||||
@@ -72,6 +72,11 @@ describe('built-in custom theme presets', () => {
|
||||
expect(comfortDark.css).toContain('--gn-fg-5: #878e98');
|
||||
expect(comfortDark.css).toContain('--gn-on-accent: #142019');
|
||||
expect(comfortDark.css).toContain('.gn-v2-query-toolbar-save-action');
|
||||
// 保存:default 同族 + fg-1 图标,禁止软绿底/透明底踩 primary 前景
|
||||
expect(comfortDark.css).toContain('color: var(--gn-fg-1) !important');
|
||||
expect(comfortDark.css).not.toMatch(
|
||||
/\.gn-v2-query-toolbar-save-action[\s\S]{0,200}background:\s*transparent\s*!important/,
|
||||
);
|
||||
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;');
|
||||
|
||||
@@ -396,27 +396,38 @@ body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-transaction-commit-bu
|
||||
color: var(--gn-accent-text, var(--gn-accent)) !important;
|
||||
}
|
||||
|
||||
/* 「保存」降为次级样式:与 v2-theme.css 保持一致。
|
||||
原先填 --gn-info,而 info 与 accent 在内置主题下的区分度仅 1.05–1.25,
|
||||
两个相邻实心按钮一眼分不清;且 Midnight Navy / Nord Slate 的 accent 本身即蓝青系,
|
||||
靠色相无法拉开,故改用层级区分,只让「执行」保持实心主按钮。 */
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar-save-action.ant-btn-primary:not(:disabled) {
|
||||
background: transparent !important;
|
||||
border-color: var(--gn-br-3) !important;
|
||||
color: var(--gn-fg-2) !important;
|
||||
/* 「保存」与工具栏 default 图标按钮同族(v2 已不用 type=primary)。
|
||||
仅保证图标用 fg-1,避免与终端等按钮混成一摊灰时单独发闷。 */
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled) {
|
||||
background: var(--gn-bg-panel) !important;
|
||||
border-color: var(--gn-br-2) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar-save-action.ant-btn-primary:not(:disabled):hover,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar-save-action.ant-btn-primary:not(:disabled):focus-visible {
|
||||
background: var(--gn-bg-hover) !important;
|
||||
border-color: var(--gn-accent) !important;
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled) .anticon {
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar-save-action.ant-btn-primary:not(:disabled):active {
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):hover,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):focus-visible {
|
||||
background: var(--gn-bg-hover) !important;
|
||||
border-color: var(--gn-br-3) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):hover .anticon,
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):focus-visible .anticon {
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):active {
|
||||
background: var(--gn-bg-active) !important;
|
||||
border-color: var(--gn-accent) !important;
|
||||
border-color: var(--gn-br-3) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
|
||||
body[data-custom-theme][data-ui-version="v2"] .gn-v2-query-toolbar .gn-v2-query-toolbar-save-action.ant-btn:not(:disabled):active .anticon {
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -156,11 +156,16 @@ body[data-ui-version="v2"] [class*="monospace"] {
|
||||
ANT DESIGN OVERRIDES — surfaces & chrome
|
||||
────────────────────────────────────────────────────────── */
|
||||
|
||||
body[data-ui-version="v2"] .ant-layout,
|
||||
body[data-ui-version="v2"] .ant-layout-content {
|
||||
body[data-ui-version="v2"] .ant-layout {
|
||||
background: var(--gn-bg-app) !important;
|
||||
}
|
||||
|
||||
/* 主内容区与左侧 sider 同用 panel-2:原先 app 近黑、panel 更深一档,
|
||||
与 sider 的 panel-2 并排时会出现明显色差(表概览空白区尤其扎眼)。 */
|
||||
body[data-ui-version="v2"] .ant-layout-content {
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-layout-sider {
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
border-right: 0.5px solid var(--gn-br-1) !important;
|
||||
@@ -319,31 +324,7 @@ body[data-ui-version="v2"] .ant-btn-primary:active {
|
||||
color: var(--gn-ant-on-primary, var(--gn-on-accent, #fff)) !important;
|
||||
}
|
||||
|
||||
/* 查询工具栏「保存」降为次级样式:描边 + 中性底。
|
||||
原先它与「执行」一样是实心饱满按钮,而两者在内置主题下的填充色(accent 与 info)
|
||||
区分度仅 1.05–1.25 —— 相邻两个同色实心方块,一眼分不出哪个是执行、哪个是保存。
|
||||
由于 Midnight Navy / Nord Slate 的 accent 本身就是蓝青系,靠拉开色相无法解决,
|
||||
改用层级区分:工具栏里只有「执行」是实心主按钮,其余降级,主操作因此真正突出。 */
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar-save-action.ant-btn-primary {
|
||||
background: transparent !important;
|
||||
border-color: var(--gn-br-3) !important;
|
||||
color: var(--gn-fg-2) !important;
|
||||
font-weight: 600 !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar-save-action.ant-btn-primary:hover,
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar-save-action.ant-btn-primary:focus-visible {
|
||||
background: var(--gn-bg-hover) !important;
|
||||
border-color: var(--gn-accent) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-query-toolbar-save-action.ant-btn-primary:active {
|
||||
background: var(--gn-bg-active) !important;
|
||||
border-color: var(--gn-accent) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
}
|
||||
/* 查询工具栏「保存」在 v2 已改为 default 图标按钮;样式见 v2-theme-workbench.css。 */
|
||||
|
||||
body[data-ui-version="v2"] .ant-btn-primary:disabled {
|
||||
background: var(--gn-bg-active) !important;
|
||||
@@ -454,17 +435,47 @@ body[data-ui-version="v2"] .ant-picker-dropdown .ant-picker-panel-container {
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-title,
|
||||
body[data-ui-version="v2"] .ant-select-item {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: 0;
|
||||
border-radius: 6px !important;
|
||||
padding: 6px 10px !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
min-height: 28px !important;
|
||||
min-height: 32px !important;
|
||||
line-height: 1.25 !important;
|
||||
font-size: 12.5px !important;
|
||||
/* Flat item surface — no outline/box that makes submenu rows look different */
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
/* Icon + label share one baseline/vertical center (leaf and submenu rows) */
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-title-content {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
min-width: 0;
|
||||
line-height: 1.25 !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item:hover,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item-active,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-title:hover,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-active > .ant-dropdown-menu-submenu-title,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-open > .ant-dropdown-menu-submenu-title,
|
||||
body[data-ui-version="v2"] .ant-select-item-option-active:not(.ant-select-item-option-disabled) {
|
||||
background: var(--gn-bg-active) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-selected > .ant-dropdown-menu-submenu-title {
|
||||
background: var(--gn-bg-active) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-select-item-option-selected:not(.ant-select-item-option-disabled) {
|
||||
@@ -479,9 +490,43 @@ body[data-ui-version="v2"] .ant-dropdown-menu-item-divider {
|
||||
}
|
||||
|
||||
/* Right-click menu items get a tighter, sectioned look */
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item-icon {
|
||||
color: var(--gn-fg-4);
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item-icon,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-title .ant-dropdown-menu-item-icon,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-expand-icon {
|
||||
color: var(--gn-fg-4) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item-icon,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-title .ant-dropdown-menu-item-icon {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
flex: 0 0 16px !important;
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
margin-inline-end: 8px !important;
|
||||
margin-right: 8px !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item-icon > .anticon,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-title .ant-dropdown-menu-item-icon > .anticon {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-expand-icon {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
top: 50% !important;
|
||||
transform: translateY(-50%) !important;
|
||||
margin-inline-end: 2px !important;
|
||||
height: 16px !important;
|
||||
line-height: 1 !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item-danger {
|
||||
@@ -490,10 +535,62 @@ body[data-ui-version="v2"] .ant-dropdown-menu-item-danger {
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-item-danger:hover {
|
||||
background: rgba(220, 38, 38, 0.08) !important;
|
||||
}
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-title {
|
||||
border-radius: 6px !important;
|
||||
padding: 6px 10px !important;
|
||||
font-size: 12.5px !important;
|
||||
|
||||
/* Nested dropdown menus: collapse double border, no glow/halo */
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-popup,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-submenu,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu-submenu-popup {
|
||||
margin-inline-start: -2px !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-popup > .ant-dropdown-menu,
|
||||
body[data-ui-version="v2"] .ant-dropdown-menu-submenu-popup.ant-dropdown-menu,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-submenu .ant-dropdown-menu,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-submenu.ant-dropdown-menu {
|
||||
/* Match primary menu border only — strip outer glow that reads as white rim in dark theme */
|
||||
border: 0.5px solid var(--gn-br-2) !important;
|
||||
border-inline-start: none !important;
|
||||
border-radius: 10px !important;
|
||||
border-start-start-radius: 8px !important;
|
||||
border-end-start-radius: 8px !important;
|
||||
box-shadow: none !important;
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
/* Title-bar cascade: keep both levels flat (no shadow/halo on either panel) */
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-menu {
|
||||
box-shadow: none !important;
|
||||
filter: none !important;
|
||||
}
|
||||
|
||||
/* Title-bar more menu: leaf rows and submenu rows share the same hover fill + alignment */
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu-item,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu-submenu-title,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-menu .ant-dropdown-menu-item,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-menu .ant-dropdown-menu-submenu-title {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
background: transparent !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
min-height: 32px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu-item:hover,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu-item-active,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu-submenu-title:hover,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu-submenu-active > .ant-dropdown-menu-submenu-title,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-dropdown .ant-dropdown-menu-submenu-open > .ant-dropdown-menu-submenu-title,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-menu .ant-dropdown-menu-item:hover,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-menu .ant-dropdown-menu-item-active,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-menu .ant-dropdown-menu-submenu-title:hover,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-menu .ant-dropdown-menu-submenu-active > .ant-dropdown-menu-submenu-title,
|
||||
body[data-ui-version="v2"] .gn-v2-titlebar-quick-menu .ant-dropdown-menu-submenu-open > .ant-dropdown-menu-submenu-title {
|
||||
background: var(--gn-bg-active) !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* ─── Tables (ant-table, used by some legacy panes) ────── */
|
||||
@@ -1494,12 +1591,15 @@ body[data-ui-version="v2"] .ant-divider {
|
||||
body[data-ui-version="v2"] .gn-v2-object-explorer .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -6px;
|
||||
top: 4px;
|
||||
bottom: 4px;
|
||||
width: 2px;
|
||||
left: 2px;
|
||||
top: 5px;
|
||||
bottom: 5px;
|
||||
width: 3px;
|
||||
background: var(--gn-accent);
|
||||
border-radius: 2px;
|
||||
display: block;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .ant-tree .ant-tree-node-content-wrapper {
|
||||
@@ -1950,9 +2050,10 @@ body[data-ui-version="v2"] .gn-v2-connection-rail {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: calc(4px * var(--gn-v2-rail-scale));
|
||||
padding: calc(8px * var(--gn-v2-rail-scale)) 0;
|
||||
gap: 0;
|
||||
padding: calc(6px * var(--gn-v2-rail-scale)) 0 0;
|
||||
border-right: 0.5px solid var(--gn-br-1);
|
||||
/* Match explorer panel — no tint strip (tinted empty rail looked worse) */
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@@ -2087,17 +2188,21 @@ body[data-ui-version="v2"] .gn-v2-rail-secondary-actions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: calc(6px * var(--gn-v2-rail-scale));
|
||||
gap: calc(4px * var(--gn-v2-rail-scale));
|
||||
}
|
||||
|
||||
/* Top tool cluster — compact, not floating in empty space */
|
||||
body[data-ui-version="v2"] .gn-v2-rail-primary-actions {
|
||||
padding-top: calc(4px * var(--gn-v2-rail-scale));
|
||||
padding: calc(2px * var(--gn-v2-rail-scale)) 0 calc(4px * var(--gn-v2-rail-scale));
|
||||
}
|
||||
|
||||
/* Bottom system actions — pin to foot with a hairline only, no fill */
|
||||
body[data-ui-version="v2"] .gn-v2-rail-secondary-actions {
|
||||
margin-top: auto;
|
||||
flex: 0 0 auto;
|
||||
padding-bottom: calc(4px * var(--gn-v2-rail-scale));
|
||||
padding: calc(6px * var(--gn-v2-rail-scale)) 0 calc(8px * var(--gn-v2-rail-scale));
|
||||
border-top: 0.5px solid var(--gn-br-1);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-rail-action-group,
|
||||
@@ -2148,19 +2253,39 @@ body[data-ui-version="v2"] .gn-v2-rail-tool {
|
||||
justify-content: center;
|
||||
gap: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
transition: background-color 140ms ease, color 140ms ease, box-shadow 140ms ease;
|
||||
}
|
||||
|
||||
/* Compact square hit target for rail tools (locate / AI / settings) */
|
||||
body[data-ui-version="v2"] .gn-v2-rail-tool {
|
||||
width: calc(24px * var(--gn-v2-rail-scale));
|
||||
height: calc(32px * var(--gn-v2-rail-scale));
|
||||
border-radius: calc(8px * var(--gn-v2-rail-scale));
|
||||
width: calc(28px * var(--gn-v2-rail-scale));
|
||||
height: calc(28px * var(--gn-v2-rail-scale));
|
||||
border-radius: calc(7px * var(--gn-v2-rail-scale));
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
color: var(--gn-fg-3);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-rail-tool .anticon {
|
||||
font-size: calc(15px * var(--gn-v2-rail-scale));
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-rail-item:hover,
|
||||
body[data-ui-version="v2"] .gn-v2-rail-tool:hover {
|
||||
body[data-ui-version="v2"] .gn-v2-rail-tool:hover:not(:disabled) {
|
||||
background: var(--gn-bg-hover);
|
||||
color: var(--gn-fg-1);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-rail-tool:hover:not(:disabled) {
|
||||
background: color-mix(in srgb, var(--gn-accent) 12%, var(--gn-bg-hover));
|
||||
color: var(--gn-accent);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-rail-tool:focus-visible {
|
||||
outline: 2px solid var(--gn-accent);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-rail-item.is-active {
|
||||
@@ -2326,7 +2451,7 @@ body[data-ui-version="v2"] .gn-v2-active-connection-copy span {
|
||||
body[data-ui-version="v2"] .gn-v2-active-connection-copy strong {
|
||||
color: var(--gn-fg-1);
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
@@ -2338,6 +2463,11 @@ body[data-ui-version="v2"] .gn-v2-active-connection-copy span {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* Empty database selection: guide tone, not error tone */
|
||||
body[data-ui-version="v2"] .gn-v2-active-connection-copy span.is-placeholder {
|
||||
color: var(--gn-fg-5);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-active-connection-tooltip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -2366,6 +2496,13 @@ body[data-ui-version="v2"] .gn-v2-active-connection-actions {
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex: 0 0 auto;
|
||||
opacity: 0.72;
|
||||
transition: opacity 140ms ease;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-active-connection-header:hover .gn-v2-active-connection-actions,
|
||||
body[data-ui-version="v2"] .gn-v2-active-connection-header:focus-within .gn-v2-active-connection-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@container gn-v2-object-explorer (max-width: 440px) {
|
||||
@@ -2815,19 +2952,26 @@ body[data-ui-version="v2"] .gn-v2-explorer-filter-tabs::-webkit-scrollbar {
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-filter-tabs button {
|
||||
flex: 0 0 auto;
|
||||
height: 22px;
|
||||
padding: 0 6px;
|
||||
padding: 0 7px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: var(--gn-fg-4);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
transition: background-color 140ms ease, color 140ms ease;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-filter-tabs button:hover {
|
||||
background: var(--gn-bg-hover);
|
||||
color: var(--gn-fg-2);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-filter-tabs button.is-active {
|
||||
background: var(--gn-bg-active);
|
||||
color: var(--gn-fg-1);
|
||||
background: color-mix(in srgb, var(--gn-accent) 14%, var(--gn-bg-panel-2));
|
||||
color: var(--gn-accent);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-toolbar {
|
||||
@@ -2910,6 +3054,11 @@ body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-treenode {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Connection rows sit one step taller than object rows for hierarchy */
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-treenode:has(.gn-v2-tree-title.is-connection) {
|
||||
min-height: 30px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-switcher {
|
||||
width: 16px !important;
|
||||
min-width: 16px !important;
|
||||
@@ -2919,6 +3068,10 @@ body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-switcher {
|
||||
color: var(--gn-fg-4);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-treenode:has(.gn-v2-tree-title.is-connection) .ant-tree-switcher {
|
||||
line-height: 30px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-indent-unit {
|
||||
width: 12px !important;
|
||||
}
|
||||
@@ -2927,21 +3080,26 @@ body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-node-content-wra
|
||||
min-width: 100%;
|
||||
width: max-content !important;
|
||||
min-height: 26px !important;
|
||||
padding: 0 6px !important;
|
||||
padding: 0 6px 0 8px !important;
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
border-radius: 6px !important;
|
||||
color: var(--gn-fg-3) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-treenode:has(.gn-v2-tree-title.is-connection) .ant-tree-node-content-wrapper {
|
||||
min-height: 30px !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-node-content-wrapper:hover {
|
||||
background: var(--gn-bg-hover) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-node-content-wrapper.ant-tree-node-selected {
|
||||
background: var(--gn-bg-selected) !important;
|
||||
background: color-mix(in srgb, var(--gn-accent) 10%, var(--gn-bg-selected)) !important;
|
||||
color: var(--gn-fg-1) !important;
|
||||
font-weight: 400 !important;
|
||||
box-shadow: inset 0 0 0 0.5px color-mix(in srgb, var(--gn-accent) 16%, transparent);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-iconEle {
|
||||
@@ -3041,6 +3199,8 @@ body[data-ui-version="v2"] .gn-v2-tree-title.is-group {
|
||||
body[data-ui-version="v2"] .gn-v2-tree-title.is-connection {
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-weight: 500 !important;
|
||||
color: var(--gn-fg-1);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-tree-status {
|
||||
@@ -3243,9 +3403,7 @@ body[data-ui-version="v2"] .gn-v2-table-pin-indicator {
|
||||
color: var(--gn-warn);
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-object-explorer .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected::before {
|
||||
display: none;
|
||||
}
|
||||
/* Selected accent bar kept on; see rule near Small additions block */
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-tree-title[data-node-type="database"] {
|
||||
font-weight: 400 !important;
|
||||
@@ -3747,7 +3905,7 @@ body[data-ui-version="v2"] .gn-v2-workspace-body {
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-workspace-main {
|
||||
background: var(--gn-bg-panel) !important;
|
||||
background: var(--gn-bg-panel-2) !important;
|
||||
}
|
||||
|
||||
body[data-ui-version="v2"] .gn-v2-ai-panel-dock {
|
||||
|
||||
Reference in New Issue
Block a user