diff --git a/BitFun-Installer/src/styles/global.css b/BitFun-Installer/src/styles/global.css index 7f766960..e67fbccd 100644 --- a/BitFun-Installer/src/styles/global.css +++ b/BitFun-Installer/src/styles/global.css @@ -85,7 +85,7 @@ html, body, #root { padding: 6px 10px; min-height: 28px; background: transparent; - border: none; + border: 1px dashed var(--border-base); border-radius: var(--radius-sm); font-family: var(--font-sans); font-size: 13px; font-weight: 500; @@ -93,7 +93,9 @@ html, body, #root { cursor: pointer; box-shadow: none; transition: background var(--motion-base) var(--easing-standard), - color var(--motion-base) var(--easing-standard); + color var(--motion-base) var(--easing-standard), + border-color var(--motion-base) var(--easing-standard), + border-style var(--motion-base) var(--easing-standard); } .btn svg { color: var(--color-text-muted); @@ -102,6 +104,8 @@ html, body, #root { .btn:hover:not(:disabled) { background: var(--element-bg-subtle); color: var(--color-text-primary); + border-style: solid; + border-color: var(--border-medium); box-shadow: none; } .btn:hover:not(:disabled) svg { color: var(--color-accent-500); } @@ -135,12 +139,12 @@ html, body, #root { .btn-ghost { min-height: 28px; - border: none; padding: 6px 10px; + padding: 6px 10px; box-shadow: none; color: var(--color-text-muted); } .btn-ghost:hover:not(:disabled) { - background: var(--element-bg-subtle); border: none; + background: var(--element-bg-subtle); color: var(--color-text-secondary); } @@ -149,16 +153,18 @@ html, body, #root { padding: 14px 16px; min-height: 68px; width: 100%; - background: rgba(148, 163, 184, 0.08); - border: none; + background: transparent; + border: 1px dashed var(--border-base); border-radius: var(--radius-sm); cursor: pointer; text-align: left; box-shadow: none; - transition: background 0.25s ease, transform 0.2s ease; + transition: background 0.25s ease, transform 0.2s ease, border-color 0.25s ease, border-style 0.25s ease; position: relative; overflow: hidden; } .card-btn:hover { - background: rgba(148, 163, 184, 0.14); + background: rgba(148, 163, 184, 0.08); + border-style: solid; + border-color: var(--border-medium); transform: translateY(-1px); box-shadow: none; } @@ -390,12 +396,3 @@ html, body, #root { right: 24px; bottom: 18px; } - -.uninstall-actions .btn { - border: 1px dashed var(--border-base); -} - -.uninstall-actions .btn:hover:not(:disabled) { - border-style: solid; - border-color: var(--border-medium); -} diff --git a/src/web-ui/src/app/components/panels/content-canvas/tab-bar/Tab.scss b/src/web-ui/src/app/components/panels/content-canvas/tab-bar/Tab.scss index f1add210..f1c570ed 100644 --- a/src/web-ui/src/app/components/panels/content-canvas/tab-bar/Tab.scss +++ b/src/web-ui/src/app/components/panels/content-canvas/tab-bar/Tab.scss @@ -95,6 +95,11 @@ transition: color 0.15s ease, font-weight 0.15s ease; } + // Keep title visually centered when tab is not hovered. + &:not(:hover) &__title { + text-align: center; + } + // Active state title color is brighter &.is-active &__title { color: var(--color-text-primary, rgba(255, 255, 255, 0.95)); diff --git a/src/web-ui/src/app/components/panels/content-canvas/tab-bar/Tab.tsx b/src/web-ui/src/app/components/panels/content-canvas/tab-bar/Tab.tsx index 25a4c3f2..eb2415f3 100644 --- a/src/web-ui/src/app/components/panels/content-canvas/tab-bar/Tab.tsx +++ b/src/web-ui/src/app/components/panels/content-canvas/tab-bar/Tab.tsx @@ -119,8 +119,8 @@ export const Tab: React.FC = ({ getStateClassName(tab.state), ].filter(Boolean).join(' '); - // Show close button (hidden in preview, shown on hover) - const showCloseButton = tab.state !== 'preview' || isHovered; + // Show close button only while hovering to avoid reserving layout space. + const showCloseButton = isHovered; return ( diff --git a/src/web-ui/src/component-library/components/Button/Button.scss b/src/web-ui/src/component-library/components/Button/Button.scss index 7d4a6bea..778e46ff 100644 --- a/src/web-ui/src/component-library/components/Button/Button.scss +++ b/src/web-ui/src/component-library/components/Button/Button.scss @@ -105,6 +105,24 @@ } } + &-dashed { + background: transparent; + color: var(--color-text-secondary, #e5e5e5); + border: 1px dashed var(--border-base, rgba(255, 255, 255, 0.16)); + + &:hover:not(:disabled) { + background: var(--element-bg-subtle, rgba(255, 255, 255, 0.08)); + color: var(--color-text-primary, #ffffff); + border-style: solid; + border-color: var(--border-medium, rgba(255, 255, 255, 0.24)); + } + + &:active:not(:disabled) { + background: var(--element-bg-base, rgba(255, 255, 255, 0.1)); + border-color: var(--border-strong, rgba(255, 255, 255, 0.32)); + } + } + &-icon-only { padding: 0; width: var(--button-height-base, 40px); diff --git a/src/web-ui/src/component-library/components/Button/Button.tsx b/src/web-ui/src/component-library/components/Button/Button.tsx index 3421086c..dd1040fe 100644 --- a/src/web-ui/src/component-library/components/Button/Button.tsx +++ b/src/web-ui/src/component-library/components/Button/Button.tsx @@ -6,7 +6,7 @@ import React, { forwardRef } from 'react'; import './Button.scss'; export interface ButtonProps extends React.ButtonHTMLAttributes { - variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'success' | 'accent' | 'ai'; + variant?: 'primary' | 'secondary' | 'ghost' | 'dashed' | 'danger' | 'success' | 'accent' | 'ai'; size?: 'small' | 'medium' | 'large'; isLoading?: boolean; iconOnly?: boolean; @@ -43,6 +43,8 @@ export const Button = forwardRef(({ return 'btn-action btn-action-success'; case 'ghost': return 'btn-ghost'; + case 'dashed': + return 'btn-dashed'; default: return 'btn-secondary'; } diff --git a/src/web-ui/src/infrastructure/config/components/MCPConfig.tsx b/src/web-ui/src/infrastructure/config/components/MCPConfig.tsx index aae7af82..59df8379 100644 --- a/src/web-ui/src/infrastructure/config/components/MCPConfig.tsx +++ b/src/web-ui/src/infrastructure/config/components/MCPConfig.tsx @@ -676,9 +676,8 @@ export const MCPConfig: React.FC = () => {
-

{searchKeyword ? t('empty.noMatchingServers') : t('empty.noServers')}

{!searchKeyword && ( - )} diff --git a/src/web-ui/src/infrastructure/config/components/PromptTemplateConfig.tsx b/src/web-ui/src/infrastructure/config/components/PromptTemplateConfig.tsx index 88e63995..efa32ae9 100644 --- a/src/web-ui/src/infrastructure/config/components/PromptTemplateConfig.tsx +++ b/src/web-ui/src/infrastructure/config/components/PromptTemplateConfig.tsx @@ -315,9 +315,8 @@ export const PromptTemplateConfig: React.FC = () => {
{sortedTemplates.length === 0 && (
-

{t('empty.noTemplates')}

- ) : isTrailingTodoEditing ? ( - <> - - + {isYamlEditingInPanel ? ( + + + ) : isPanelEditing ? ( + <> + + + + + + + + + ) : ( <> - - + + + + + + )}
- {isEditingYaml ? ( + {isYamlEditingInPanel ? (
= ({
) : (
- {displayedTodos.map((todo, index) => ( + {panelTodos.map((todo, index) => (
{getTodoIcon(todo.status)} - {isTrailingTodoEditing ? ( + {isPanelEditing ? ( <> { const key = todo.id || String(index); - setTrailingTodoDrafts(prev => ({ - ...prev, - [key]: e.target.value, - })); + if (isInline) { + setInlineTodoDrafts(prev => ({ ...prev, [key]: e.target.value })); + } else { + setTrailingTodoDrafts(prev => ({ ...prev, [key]: e.target.value })); + } }} /> - + + + ) : ( {todo.content} @@ -528,20 +642,31 @@ const PlanViewer: React.FC = ({
); }, [ + cancelInlineTodoEdit, cancelTrailingTodoEdit, - displayedTodos, + closeYamlEditor, + displayedInlineTodos, + displayedTrailingTodos, + handleAddInlineTodo, handleAddTrailingTodo, + handleDeleteInlineTodo, handleDeleteTrailingTodo, handleSave, handleYamlChange, + isInlineTodoEditing, isEditingYaml, isTodosExpanded, isTrailingTodoEditing, + openYamlEditor, + saveInlineTodoEdit, saveTrailingTodoEdit, + startInlineTodoEdit, startTrailingTodoEdit, t, + inlineTodoDrafts, trailingTodoDrafts, yamlContent, + yamlEditorPlacement, ]); // Build button click handler @@ -669,7 +794,7 @@ ${JSON.stringify(simpleTodos, null, 2)}
- {hasTodos && (isEditingYaml || isTodosExpanded) && renderSharedTodoPanel('inline')} + {hasTodos && (yamlEditorPlacement === 'inline' || isTodosExpanded) && renderSharedTodoPanel('inline')}
@@ -688,7 +813,7 @@ ${JSON.stringify(simpleTodos, null, 2)} basePath={basePath} />
- {hasTodos && isTodosExpanded && !isEditingYaml && renderSharedTodoPanel('trailing')} + {hasTodos && yamlEditorPlacement !== 'inline' && renderSharedTodoPanel('trailing')}
); diff --git a/src/web-ui/src/tools/lsp/components/LspPluginList/LspPluginList.tsx b/src/web-ui/src/tools/lsp/components/LspPluginList/LspPluginList.tsx index 65b9a405..245f1d0d 100644 --- a/src/web-ui/src/tools/lsp/components/LspPluginList/LspPluginList.tsx +++ b/src/web-ui/src/tools/lsp/components/LspPluginList/LspPluginList.tsx @@ -130,11 +130,9 @@ export const LspPluginList: React.FC = ({ {renderHeader()}
-

{t('pluginList.empty.title')}

-

{t('pluginList.empty.hint')}

{onInstallPlugin && (