update
This commit is contained in:
@@ -252,17 +252,17 @@ const isElementVisibleAndFocusable = (element: HTMLElement): boolean => {
|
|||||||
<!-- Left navigation links with Tailwind classes using theme variables -->
|
<!-- Left navigation links with Tailwind classes using theme variables -->
|
||||||
<div class="flex items-center space-x-1">
|
<div class="flex items-center space-x-1">
|
||||||
<!-- <RouterLink to="/">{{ t('nav.dashboard') }}</RouterLink> --> <!-- 隐藏仪表盘链接 -->
|
<!-- <RouterLink to="/">{{ t('nav.dashboard') }}</RouterLink> --> <!-- 隐藏仪表盘链接 -->
|
||||||
<RouterLink to="/workspace" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-link-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-link-active-bg">{{ t('nav.terminal') }}</RouterLink>
|
<RouterLink to="/workspace" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-nav-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-nav-active-bg">{{ t('nav.terminal') }}</RouterLink>
|
||||||
<RouterLink to="/proxies" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-link-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-link-active-bg">{{ t('nav.proxies') }}</RouterLink>
|
<RouterLink to="/proxies" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-nav-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-nav-active-bg">{{ t('nav.proxies') }}</RouterLink>
|
||||||
<RouterLink to="/notifications" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-link-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-link-active-bg">{{ t('nav.notifications') }}</RouterLink>
|
<RouterLink to="/notifications" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-nav-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-nav-active-bg">{{ t('nav.notifications') }}</RouterLink>
|
||||||
<RouterLink to="/audit-logs" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-link-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-link-active-bg">{{ t('nav.auditLogs') }}</RouterLink>
|
<RouterLink to="/audit-logs" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-nav-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-nav-active-bg">{{ t('nav.auditLogs') }}</RouterLink>
|
||||||
<RouterLink to="/settings" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-link-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-link-active-bg">{{ t('nav.settings') }}</RouterLink>
|
<RouterLink to="/settings" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-nav-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap" active-class="text-link-active bg-nav-active-bg">{{ t('nav.settings') }}</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
<!-- Right navigation links with Tailwind classes using theme variables -->
|
<!-- Right navigation links with Tailwind classes using theme variables -->
|
||||||
<div class="flex items-center space-x-1">
|
<div class="flex items-center space-x-1">
|
||||||
<a href="#" @click.prevent="openStyleCustomizer" :title="t('nav.customizeStyle')" class="px-2 py-2 rounded-md text-lg text-icon hover:text-icon-hover hover:bg-link-active-bg hover:no-underline transition duration-150 ease-in-out">🎨</a>
|
<a href="#" @click.prevent="openStyleCustomizer" :title="t('nav.customizeStyle')" class="px-2 py-2 rounded-md text-lg text-icon hover:text-icon-hover hover:bg-nav-active-bg hover:no-underline transition duration-150 ease-in-out">🎨</a>
|
||||||
<RouterLink v-if="!isAuthenticated" to="/login" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-link-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap">{{ t('nav.login') }}</RouterLink>
|
<RouterLink v-if="!isAuthenticated" to="/login" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-nav-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap">{{ t('nav.login') }}</RouterLink>
|
||||||
<a href="#" v-if="isAuthenticated" @click.prevent="handleLogout" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-link-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap">{{ t('nav.logout') }}</a>
|
<a href="#" v-if="isAuthenticated" @click.prevent="handleLogout" class="px-3 py-2 rounded-md text-sm font-medium text-secondary hover:text-link-hover hover:bg-nav-active-bg hover:no-underline transition duration-150 ease-in-out whitespace-nowrap">{{ t('nav.logout') }}</a>
|
||||||
</div>
|
</div>
|
||||||
<!-- Sliding underline element with Tailwind classes using theme variables (JS still controls positioning) -->
|
<!-- Sliding underline element with Tailwind classes using theme variables (JS still controls positioning) -->
|
||||||
<div ref="underlineRef" class="absolute bottom-0 h-0.5 bg-link-active rounded transition-all duration-300 ease-in-out pointer-events-none opacity-0"></div> <!-- Added opacity-0 -->
|
<div ref="underlineRef" class="absolute bottom-0 h-0.5 bg-link-active rounded transition-all duration-300 ease-in-out pointer-events-none opacity-0"></div> <!-- Added opacity-0 -->
|
||||||
|
|||||||
@@ -766,6 +766,14 @@ watch(() => editingTheme.value?.themeData, (newThemeData) => {
|
|||||||
}
|
}
|
||||||
}, { deep: true }); // 需要 deep watch 来监听 themeData 内部的变化
|
}, { deep: true }); // 需要 deep watch 来监听 themeData 内部的变化
|
||||||
|
|
||||||
|
// Helper function to safely select text in an input on focus
|
||||||
|
const handleFocusAndSelect = (event: FocusEvent) => {
|
||||||
|
const target = event.target;
|
||||||
|
if (target instanceof HTMLInputElement) {
|
||||||
|
target.select();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
@@ -798,21 +806,34 @@ watch(() => editingTheme.value?.themeData, (newThemeData) => {
|
|||||||
<!-- 动态生成 UI 样式编辑控件 -->
|
<!-- 动态生成 UI 样式编辑控件 -->
|
||||||
<div v-for="(value, key) in editableUiTheme" :key="key" class="form-group">
|
<div v-for="(value, key) in editableUiTheme" :key="key" class="form-group">
|
||||||
<label :for="`ui-${key}`">{{ formatLabel(key) }}:</label>
|
<label :for="`ui-${key}`">{{ formatLabel(key) }}:</label>
|
||||||
<!-- 简单判断是否为颜色值,显示颜色选择器 -->
|
<!-- Container for color picker and text display -->
|
||||||
<input
|
<div class="color-input-group">
|
||||||
v-if="typeof value === 'string' && (value.startsWith('#') || value.startsWith('rgb') || value.startsWith('hsl'))"
|
<!-- Color Picker -->
|
||||||
type="color"
|
<input
|
||||||
:id="`ui-${key}`"
|
v-if="typeof value === 'string' && (value.startsWith('#') || value.startsWith('rgb') || value.startsWith('hsl'))"
|
||||||
v-model="editableUiTheme[key]"
|
type="color"
|
||||||
/>
|
:id="`ui-${key}`"
|
||||||
<!-- 否则显示文本输入框 -->
|
v-model="editableUiTheme[key]"
|
||||||
<input
|
class="color-picker-input"
|
||||||
v-else
|
/>
|
||||||
type="text"
|
<!-- Readonly text input to display and copy color value -->
|
||||||
:id="`ui-${key}`"
|
<input
|
||||||
v-model="editableUiTheme[key]"
|
v-if="typeof value === 'string' && (value.startsWith('#') || value.startsWith('rgb') || value.startsWith('hsl'))"
|
||||||
class="text-input"
|
type="text"
|
||||||
/>
|
:value="editableUiTheme[key]"
|
||||||
|
readonly
|
||||||
|
class="color-text-input text-input"
|
||||||
|
@focus="handleFocusAndSelect"
|
||||||
|
/>
|
||||||
|
<!-- Fallback for non-color values -->
|
||||||
|
<input
|
||||||
|
v-else
|
||||||
|
type="text"
|
||||||
|
:id="`ui-${key}`"
|
||||||
|
v-model="editableUiTheme[key]"
|
||||||
|
class="text-input full-span-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- UI Theme Textarea -->
|
<!-- UI Theme Textarea -->
|
||||||
<hr style="margin-top: calc(var(--base-padding) * 2); margin-bottom: calc(var(--base-padding) * 2);">
|
<hr style="margin-top: calc(var(--base-padding) * 2); margin-bottom: calc(var(--base-padding) * 2);">
|
||||||
@@ -920,21 +941,34 @@ watch(() => editingTheme.value?.themeData, (newThemeData) => {
|
|||||||
<!-- 动态生成终端样式编辑控件 -->
|
<!-- 动态生成终端样式编辑控件 -->
|
||||||
<div v-for="(value, key) in editingTheme.themeData" :key="key" class="form-group">
|
<div v-for="(value, key) in editingTheme.themeData" :key="key" class="form-group">
|
||||||
<label :for="`xterm-${key}`">{{ formatXtermLabel(key as keyof ITheme) }}:</label>
|
<label :for="`xterm-${key}`">{{ formatXtermLabel(key as keyof ITheme) }}:</label>
|
||||||
<!-- 简单判断是否为颜色值 -->
|
<!-- Container for color picker and text display -->
|
||||||
<input
|
<div class="color-input-group">
|
||||||
v-if="typeof value === 'string' && value.startsWith('#')"
|
<!-- Color Picker -->
|
||||||
type="color"
|
<input
|
||||||
:id="`xterm-${key}`"
|
v-if="typeof value === 'string' && value.startsWith('#')"
|
||||||
v-model="(editingTheme.themeData as any)[key]"
|
type="color"
|
||||||
/>
|
:id="`xterm-${key}`"
|
||||||
<!-- 其他类型(如数字、布尔值)可以添加相应控件,这里简化为文本 -->
|
v-model="(editingTheme.themeData as any)[key]"
|
||||||
<input
|
class="color-picker-input"
|
||||||
v-else
|
/>
|
||||||
type="text"
|
<!-- Readonly text input to display and copy color value -->
|
||||||
:id="`xterm-${key}`"
|
<input
|
||||||
v-model="(editingTheme.themeData as any)[key]"
|
v-if="typeof value === 'string' && value.startsWith('#')"
|
||||||
class="text-input"
|
type="text"
|
||||||
/>
|
:value="(editingTheme.themeData as any)[key]"
|
||||||
|
readonly
|
||||||
|
class="color-text-input text-input"
|
||||||
|
@focus="handleFocusAndSelect"
|
||||||
|
/>
|
||||||
|
<!-- Fallback for non-color values -->
|
||||||
|
<input
|
||||||
|
v-else
|
||||||
|
type="text"
|
||||||
|
:id="`xterm-${key}`"
|
||||||
|
v-model="(editingTheme.themeData as any)[key]"
|
||||||
|
class="text-input full-span-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 显示解析错误(如果颜色选择器下方也需要的话) -->
|
<!-- 显示解析错误(如果颜色选择器下方也需要的话) -->
|
||||||
<!-- <p v-if="terminalThemeParseError" class="error-message full-width-group">{{ terminalThemeParseError }}</p> --> <!-- 错误消息统一显示在 Textarea 下方 -->
|
<!-- <p v-if="terminalThemeParseError" class="error-message full-width-group">{{ terminalThemeParseError }}</p> --> <!-- 错误消息统一显示在 Textarea 下方 -->
|
||||||
@@ -1218,14 +1252,32 @@ section[v-if*="isEditingTheme"] .form-group {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transition: border-color 0.2s ease, box-shadow 0.2s ease; /* 添加过渡效果 */
|
transition: border-color 0.2s ease, box-shadow 0.2s ease; /* 添加过渡效果 */
|
||||||
}
|
}
|
||||||
.form-group input[type="color"] {
|
.color-input-group {
|
||||||
padding: 2px;
|
display: flex;
|
||||||
height: 34px;
|
align-items: center;
|
||||||
min-width: 50px;
|
gap: 0.5rem; /* Space between color picker and text input */
|
||||||
max-width: 70px;
|
grid-column: 2 / 3; /* Ensure the group stays in the second column */
|
||||||
justify-self: start;
|
width: 100%; /* Take full width of the column */
|
||||||
border-radius: 4px; /* 保持圆角一致 */
|
|
||||||
}
|
}
|
||||||
|
.color-picker-input {
|
||||||
|
padding: 2px !important; /* Override general padding */
|
||||||
|
height: 34px !important; /* Match text input height */
|
||||||
|
min-width: 40px !important; /* Slightly smaller */
|
||||||
|
max-width: 50px !important;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
border: 1px solid var(--border-color) !important; /* Add border */
|
||||||
|
flex-shrink: 0; /* Prevent shrinking */
|
||||||
|
}
|
||||||
|
.color-text-input {
|
||||||
|
flex-grow: 1; /* Allow text input to take remaining space */
|
||||||
|
min-width: 80px; /* Minimum width for text */
|
||||||
|
background-color: var(--header-bg-color) !important; /* Indicate readonly */
|
||||||
|
cursor: text; /* Allow text selection cursor */
|
||||||
|
}
|
||||||
|
.full-span-input {
|
||||||
|
grid-column: 1 / -1; /* Make non-color inputs span the whole group width */
|
||||||
|
}
|
||||||
|
|
||||||
.form-group input:focus, .form-group select:focus {
|
.form-group input:focus, .form-group select:focus {
|
||||||
border-color: var(--link-active-color);
|
border-color: var(--link-active-color);
|
||||||
outline: 0;
|
outline: 0;
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ export const defaultUiTheme: Record<string, string> = {
|
|||||||
'--link-color': '#333',
|
'--link-color': '#333',
|
||||||
'--link-hover-color': '#0056b3',
|
'--link-hover-color': '#0056b3',
|
||||||
'--link-active-color': '#007bff',
|
'--link-active-color': '#007bff',
|
||||||
|
'--link-active-bg-color': '#e0e0ff', /* Added */
|
||||||
|
'--nav-item-active-bg-color': 'var(--link-active-bg-color)', /* Added */
|
||||||
'--header-bg-color': '#f0f0f0',
|
'--header-bg-color': '#f0f0f0',
|
||||||
'--footer-bg-color': '#f0f0f0',
|
'--footer-bg-color': '#f0f0f0',
|
||||||
'--button-bg-color': '#007bff',
|
'--button-bg-color': '#007bff',
|
||||||
@@ -47,6 +49,8 @@ export const defaultUiTheme: Record<string, string> = {
|
|||||||
'--split-line-hover-color': 'var(--border-color)', /* 分割线悬停颜色 */
|
'--split-line-hover-color': 'var(--border-color)', /* 分割线悬停颜色 */
|
||||||
'--input-focus-border-color': 'var(--link-active-color)', /* 输入框聚焦边框颜色 */
|
'--input-focus-border-color': 'var(--link-active-color)', /* 输入框聚焦边框颜色 */
|
||||||
'--input-focus-glow': 'var(--link-active-color)', /* 输入框聚焦光晕值 */
|
'--input-focus-glow': 'var(--link-active-color)', /* 输入框聚焦光晕值 */
|
||||||
|
'--ssh-tab-active': 'transparent', /* Added SSH Tab Active */
|
||||||
|
'--ssh-tab-background': 'transparent', /* Added SSH Tab Background */
|
||||||
// End added variables
|
// End added variables
|
||||||
'--font-family-sans-serif': 'sans-serif',
|
'--font-family-sans-serif': 'sans-serif',
|
||||||
'--base-padding': '1rem',
|
'--base-padding': '1rem',
|
||||||
|
|||||||
@@ -3,15 +3,19 @@
|
|||||||
/* Tailwind Theme Variables Mapping */
|
/* Tailwind Theme Variables Mapping */
|
||||||
@theme inline {
|
@theme inline {
|
||||||
/* Base Colors */
|
/* Base Colors */
|
||||||
--color-app: var(--app-bg-color);
|
--color-background: var(--app-bg-color); /* More generic name */
|
||||||
--color-text-default: var(--text-color);
|
--color-foreground: var(--text-color); /* More generic name */
|
||||||
|
--color-app: var(--app-bg-color); /* Keep specific if needed */
|
||||||
|
--color-text-default: var(--text-color); /* Keep specific if needed */
|
||||||
--color-text-secondary: var(--text-color-secondary);
|
--color-text-secondary: var(--text-color-secondary);
|
||||||
--color-border-default: var(--border-color);
|
--color-border: var(--border-color); /* Simplified name */
|
||||||
|
--color-border-default: var(--border-color); /* Keep specific if needed */
|
||||||
--color-link: var(--link-color);
|
--color-link: var(--link-color);
|
||||||
--color-link-hover: var(--link-hover-color);
|
--color-link-hover: var(--link-hover-color);
|
||||||
--color-link-active: var(--link-active-color); /* Also used as primary/theme color */
|
--color-link-active: var(--link-active-color); /* Also used as primary/theme color */
|
||||||
--color-primary: var(--link-active-color); /* Map primary to active link color */
|
--color-primary: var(--link-active-color); /* Map primary to active link color */
|
||||||
--color-link-active-bg: var(--link-active-bg-color); /* Map active link background */
|
--color-link-active-bg: var(--link-active-bg-color); /* Map active link background */
|
||||||
|
--color-nav-active-bg: var(--nav-item-active-bg-color); /* Map specific nav active background */
|
||||||
|
|
||||||
/* Component Colors */
|
/* Component Colors */
|
||||||
--color-header: var(--header-bg-color);
|
--color-header: var(--header-bg-color);
|
||||||
@@ -40,6 +44,7 @@
|
|||||||
--link-hover-color: #0056b3; /* 链接悬停颜色 */
|
--link-hover-color: #0056b3; /* 链接悬停颜色 */
|
||||||
--link-active-color: #007bff; /* 激活链接/主题色 */
|
--link-active-color: #007bff; /* 激活链接/主题色 */
|
||||||
--link-active-bg-color: #e0e0ff; /* 激活链接背景色 (类似 indigo-50) */
|
--link-active-bg-color: #e0e0ff; /* 激活链接背景色 (类似 indigo-50) */
|
||||||
|
--nav-item-active-bg-color: var(--link-active-bg-color); /* 导航选中项背景色, 默认同激活链接背景 */
|
||||||
|
|
||||||
/* 组件颜色 */
|
/* 组件颜色 */
|
||||||
--header-bg-color: #f0f0f0; /* 头部背景色 */
|
--header-bg-color: #f0f0f0; /* 头部背景色 */
|
||||||
@@ -53,8 +58,8 @@
|
|||||||
--split-line-hover-color: var(--border-color); /* 分割线悬停颜色 */
|
--split-line-hover-color: var(--border-color); /* 分割线悬停颜色 */
|
||||||
--input-focus-border-color: var(--link-active-color); /* 输入框聚焦边框颜色 */
|
--input-focus-border-color: var(--link-active-color); /* 输入框聚焦边框颜色 */
|
||||||
--input-focus-glow: var(--link-active-color); /* 输入框聚焦光晕值 */
|
--input-focus-glow: var(--link-active-color); /* 输入框聚焦光晕值 */
|
||||||
--ssh-tab-active: none;/* ssh标签激活状态颜色 */
|
--ssh-tab-active: transparent; /* Corrected value */
|
||||||
--ssh-tab-background: none;/* ssh标签背景颜色 */
|
--ssh-tab-background: transparent; /* Corrected value */
|
||||||
|
|
||||||
/* 字体 */
|
/* 字体 */
|
||||||
--font-family-sans-serif: sans-serif; /* 默认字体 */
|
--font-family-sans-serif: sans-serif; /* 默认字体 */
|
||||||
|
|||||||
Reference in New Issue
Block a user