diff --git a/src/web-ui/src/app/components/NavPanel/NavPanel.scss b/src/web-ui/src/app/components/NavPanel/NavPanel.scss index 5e4867db..ec2a8e8e 100644 --- a/src/web-ui/src/app/components/NavPanel/NavPanel.scss +++ b/src/web-ui/src/app/components/NavPanel/NavPanel.scss @@ -1375,6 +1375,9 @@ $_section-header-height: 24px; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%); + display: flex; + flex-direction: column; + gap: $size-gap-1; min-width: 140px; padding: $size-gap-1; background: var(--color-bg-elevated, #1e1e22); @@ -1393,7 +1396,7 @@ $_section-header-height: 24px; gap: $size-gap-2; width: 100%; padding: 0 $size-gap-2; - height: 30px; + height: 24px; border: none; border-radius: $size-radius-sm; background: transparent; @@ -1449,27 +1452,6 @@ $_section-header-height: 24px; white-space: nowrap; } -.bitfun-nav-panel__footer-multimodal-item-dot { - width: 5px; - height: 5px; - border-radius: 50%; - background: var(--color-primary); - flex-shrink: 0; - margin-left: auto; -} - -// Active dot badge on main Layers button -.bitfun-nav-panel__footer-multimodal-dot { - position: absolute; - bottom: 5px; - right: 5px; - width: 4px; - height: 4px; - border-radius: 50%; - background: var(--color-primary); - pointer-events: none; -} - // Hover-open subtle glow on the main button .bitfun-nav-panel__footer-btn--icon.is-hover-open { color: var(--color-text-primary); diff --git a/src/web-ui/src/app/components/NavPanel/components/PersistentFooterActions.tsx b/src/web-ui/src/app/components/NavPanel/components/PersistentFooterActions.tsx index 4a5e5cdf..d2fc11c6 100644 --- a/src/web-ui/src/app/components/NavPanel/components/PersistentFooterActions.tsx +++ b/src/web-ui/src/app/components/NavPanel/components/PersistentFooterActions.tsx @@ -270,9 +270,6 @@ const PersistentFooterActions: React.FC = () => { aria-haspopup="menu" > - {isAnyActive && ( - - )} @@ -291,7 +288,6 @@ const PersistentFooterActions: React.FC = () => { > {t('scenes.browser')} - {isBrowserActive && } )} diff --git a/src/web-ui/src/flow_chat/components/CodePreview.scss b/src/web-ui/src/flow_chat/components/CodePreview.scss index 92f90305..7f2f366a 100644 --- a/src/web-ui/src/flow_chat/components/CodePreview.scss +++ b/src/web-ui/src/flow_chat/components/CodePreview.scss @@ -6,6 +6,8 @@ @use '../../component-library/styles/_extended-mixins' as mixins; .code-preview { + --markdown-font-mono: "Fira Code", "JetBrains Mono", Consolas, "Courier New", monospace; + position: relative; width: 100%; background: transparent; @@ -29,6 +31,13 @@ white-space: pre-wrap !important; word-break: break-word !important; cursor: text; + font-family: var(--markdown-font-mono, "Fira Code", "JetBrains Mono", Consolas, "Courier New", monospace) !important; + font-weight: 400 !important; + + // Prism themes mark many tokens bold; match markdown body / code blocks (regular weight). + span[style] { + font-weight: 400 !important; + } > span[style] { &:hover { @@ -113,5 +122,9 @@ .linenumber { color: var(--color-text-muted, #94a3b8) !important; } + + pre code > span[style]:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } } } diff --git a/src/web-ui/src/flow_chat/components/CodePreview.tsx b/src/web-ui/src/flow_chat/components/CodePreview.tsx index 5250741d..1a4c3ccd 100644 --- a/src/web-ui/src/flow_chat/components/CodePreview.tsx +++ b/src/web-ui/src/flow_chat/components/CodePreview.tsx @@ -13,8 +13,9 @@ import React, { useMemo, memo, useRef, useEffect, useState, useCallback } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { getPrismLanguage } from '@/infrastructure/language-detection'; +import { useTheme } from '@/infrastructure/theme'; +import { buildCodePreviewPrismStyle, CODE_PREVIEW_FONT_FAMILY } from './codePreviewPrismTheme'; import './CodePreview.scss'; export interface CodePreviewProps { @@ -46,29 +47,6 @@ function detectLanguageFromPath(filePath: string): string { return getPrismLanguage(filePath); } -/** - * Custom theme derived from vscDarkPlus with project-specific tweaks. - */ -const customStyle = { - ...vscDarkPlus, - 'pre[class*="language-"]': { - ...vscDarkPlus['pre[class*="language-"]'], - margin: 0, - padding: 0, - background: 'transparent', - fontSize: '12px', - lineHeight: '1.6', - fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'Monaco', 'Courier New', monospace", - }, - 'code[class*="language-"]': { - ...vscDarkPlus['code[class*="language-"]'], - background: 'transparent', - fontSize: '12px', - lineHeight: '1.6', - fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'Monaco', 'Courier New', monospace", - }, -}; - /** * CodePreview component with streaming-friendly syntax highlighting. */ @@ -83,6 +61,9 @@ export const CodePreview: React.FC = memo(({ maxHeight = 400, onLineClick, }) => { + const { isLight } = useTheme(); + const prismStyle = useMemo(() => buildCodePreviewPrismStyle(isLight), [isLight]); + const containerRef = useRef(null); const prevContentLengthRef = useRef(0); @@ -151,7 +132,7 @@ export const CodePreview: React.FC = memo(({ > = memo(({ }} codeTagProps={{ style: { - fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'Monaco', 'Courier New', monospace", + fontFamily: CODE_PREVIEW_FONT_FAMILY, fontSize: '12px', lineHeight: '1.6', + fontWeight: 400, } }} lineNumberStyle={{ @@ -175,7 +157,7 @@ export const CodePreview: React.FC = memo(({ textAlign: 'right', userSelect: 'none', color: 'var(--color-text-muted, #666)', - opacity: 0.6, + opacity: isLight ? 0.88 : 0.6, }} > {content} diff --git a/src/web-ui/src/flow_chat/components/InlineDiffPreview.scss b/src/web-ui/src/flow_chat/components/InlineDiffPreview.scss index 9c8b56be..fd7d4558 100644 --- a/src/web-ui/src/flow_chat/components/InlineDiffPreview.scss +++ b/src/web-ui/src/flow_chat/components/InlineDiffPreview.scss @@ -4,11 +4,14 @@ */ .inline-diff-preview { + --markdown-font-mono: "Fira Code", "JetBrains Mono", Consolas, "Courier New", monospace; + position: relative; width: 100%; background: transparent; - font-family: 'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'Monaco', 'Courier New', monospace; + font-family: var(--markdown-font-mono, "Fira Code", "JetBrains Mono", Consolas, "Courier New", monospace); font-size: 12px; + font-weight: 400; &__content { overflow-y: auto; @@ -200,6 +203,10 @@ display: inline !important; font-size: inherit !important; } + + code span[style] { + font-weight: 400 !important; + } } } diff --git a/src/web-ui/src/flow_chat/components/InlineDiffPreview.tsx b/src/web-ui/src/flow_chat/components/InlineDiffPreview.tsx index ae9c5f50..c1f57826 100644 --- a/src/web-ui/src/flow_chat/components/InlineDiffPreview.tsx +++ b/src/web-ui/src/flow_chat/components/InlineDiffPreview.tsx @@ -12,10 +12,11 @@ import React, { useMemo, memo, useRef, useCallback, useState } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { diffLines, Change } from 'diff'; import { getPrismLanguage } from '@/infrastructure/language-detection'; +import { useTheme } from '@/infrastructure/theme'; import { createLogger } from '@/shared/utils/logger'; +import { buildCodePreviewPrismStyle, CODE_PREVIEW_FONT_FAMILY } from './codePreviewPrismTheme'; import './InlineDiffPreview.scss'; const log = createLogger('InlineDiffPreview'); @@ -168,29 +169,6 @@ function applyContextCollapsing(diffLines: DiffLine[], contextLines: number): Di return result; } -/** - * Custom theme styles (kept in sync with CodePreview). - */ -const customStyle = { - ...vscDarkPlus, - 'pre[class*="language-"]': { - ...vscDarkPlus['pre[class*="language-"]'], - margin: 0, - padding: 0, - background: 'transparent', - fontSize: '12px', - lineHeight: '1.6', - fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'Monaco', 'Courier New', monospace", - }, - 'code[class*="language-"]': { - ...vscDarkPlus['code[class*="language-"]'], - background: 'transparent', - fontSize: '12px', - lineHeight: '1.6', - fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'Monaco', 'Courier New', monospace", - }, -}; - /** * InlineDiffPreview component. */ @@ -207,6 +185,9 @@ export const InlineDiffPreview: React.FC = memo(({ contextLines = 3, onLineClick, }) => { + const { isLight } = useTheme(); + const prismStyle = useMemo(() => buildCodePreviewPrismStyle(isLight), [isLight]); + const containerRef = useRef(null); const [highlightedLine, setHighlightedLine] = useState(null); @@ -285,7 +266,7 @@ export const InlineDiffPreview: React.FC = memo(({ = memo(({ }} codeTagProps={{ style: { - fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', 'Consolas', 'Monaco', 'Courier New', monospace", + fontFamily: CODE_PREVIEW_FONT_FAMILY, fontSize: '12px', + fontWeight: 400, } }} PreTag="span" @@ -305,7 +287,7 @@ export const InlineDiffPreview: React.FC = memo(({ ); - }, [detectedLanguage, showLineNumbers, lineNumberMode, showPrefix, highlightedLine, handleLineClick]); + }, [detectedLanguage, prismStyle, showLineNumbers, lineNumberMode, showPrefix, highlightedLine, handleLineClick]); if (!originalContent && !modifiedContent) { return ( diff --git a/src/web-ui/src/flow_chat/components/codePreviewPrismTheme.ts b/src/web-ui/src/flow_chat/components/codePreviewPrismTheme.ts new file mode 100644 index 00000000..8e9d05c6 --- /dev/null +++ b/src/web-ui/src/flow_chat/components/codePreviewPrismTheme.ts @@ -0,0 +1,38 @@ +/** + * Prism themes for Flow Chat embedded code previews. + * vscDarkPlus is for dark surfaces; oneLight matches light card backgrounds. + */ +import type { CSSProperties } from 'react'; +import { oneLight, vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; + +/** Match `.markdown-renderer` code blocks (`Markdown.scss` --markdown-font-mono). */ +export const CODE_PREVIEW_FONT_FAMILY = + 'var(--markdown-font-mono, "Fira Code", "JetBrains Mono", Consolas, "Courier New", monospace)'; + +const PRE_KEY = 'pre[class*="language-"]' as const; +const CODE_KEY = 'code[class*="language-"]' as const; + +export function buildCodePreviewPrismStyle(isLight: boolean): Record { + const base = isLight ? oneLight : vscDarkPlus; + return { + ...base, + [PRE_KEY]: { + ...base[PRE_KEY], + margin: 0, + padding: 0, + background: 'transparent', + fontSize: '12px', + lineHeight: '1.6', + fontFamily: CODE_PREVIEW_FONT_FAMILY, + fontWeight: 400, + }, + [CODE_KEY]: { + ...base[CODE_KEY], + background: 'transparent', + fontSize: '12px', + lineHeight: '1.6', + fontFamily: CODE_PREVIEW_FONT_FAMILY, + fontWeight: 400, + }, + }; +} diff --git a/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.scss b/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.scss index f418e39c..3fc59513 100644 --- a/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.scss +++ b/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.scss @@ -3,7 +3,7 @@ * Shows internal model reasoning. * * Single DOM structure for both streaming and completed states. - * Streaming: expanded, muted text with ink-fade shimmer. + * Expanded: max-height with scroll; streaming uses slightly brighter muted text. * Completed: auto-collapses via CSS grid-template-rows animation. */ @@ -88,32 +88,36 @@ line-height: 1.4; font-family: var(--tool-card-font-mono); word-break: break-word; - white-space: pre-wrap; color: var(--tool-card-text-muted); padding: 10px 12px; background: transparent; border: none; border-radius: 6px; margin-top: 0; - max-height: none; - overflow-y: visible; + /* Cap height when expanded so long thinking stays compact; scroll inside */ + max-height: 300px; + overflow-y: auto; + min-height: 0; cursor: text; user-select: text; } -/* During streaming, constrain height and auto-scroll */ .flow-thinking-item.streaming .thinking-content { color: #9ca3af; - max-height: 300px; - overflow-y: auto; } /* Content wrapper with fade gradients */ .thinking-content-wrapper { position: relative; min-height: 0; /* Required for the 0fr grid trick */ + /* + * Match FlowChat list surface (--color-bg-flowchat === scene), not app chrome primary. + * Use a solid fill + mask fade so we never interpolate theme colors with `transparent` in sRGB + * (that pulls toward black and looks wrong on near-black themes). + */ + --thinking-scroll-fade-base: var(--color-bg-flowchat, var(--color-bg-scene, var(--color-bg-primary))); - /* Top fade gradient */ + /* Top fade */ &::before { content: ''; position: absolute; @@ -126,16 +130,16 @@ opacity: 0; transition: opacity 0.2s ease; - background: linear-gradient( - to bottom, - var(--color-bg-primary, #121214) 0%, - color-mix(in srgb, var(--color-bg-primary, #121214) 80%, transparent) 40%, - color-mix(in srgb, var(--color-bg-primary, #121214) 40%, transparent) 70%, - transparent 100% - ); + background: var(--thinking-scroll-fade-base); + -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 22%, transparent 100%); + mask-image: linear-gradient(to bottom, #000 0%, #000 22%, transparent 100%); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-size: 100% 100%; + mask-size: 100% 100%; } - /* Bottom fade gradient */ + /* Bottom fade */ &::after { content: ''; position: absolute; @@ -148,13 +152,13 @@ opacity: 0; transition: opacity 0.2s ease; - background: linear-gradient( - to top, - var(--color-bg-primary, #121214) 0%, - color-mix(in srgb, var(--color-bg-primary, #121214) 80%, transparent) 40%, - color-mix(in srgb, var(--color-bg-primary, #121214) 40%, transparent) 70%, - transparent 100% - ); + background: var(--thinking-scroll-fade-base); + -webkit-mask-image: linear-gradient(to top, #000 0%, #000 22%, transparent 100%); + mask-image: linear-gradient(to top, #000 0%, #000 22%, transparent 100%); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-size: 100% 100%; + mask-size: 100% 100%; } /* Show gradients when content scrolls */ @@ -176,10 +180,150 @@ } } -.thinking-line { - padding: 1px 0; - color: var(--tool-card-text-secondary); - line-height: 1; +/* Markdown body: keep muted monospace look (overrides default .markdown-renderer) */ +.thinking-content .markdown-renderer.thinking-markdown { + --markdown-font-mono: var(--tool-card-font-mono); + + font-family: var(--tool-card-font-mono); + font-size: 12px; + line-height: 1.45; + color: var(--tool-card-text-muted); + animation: none; + + h1, + h2, + h3, + h4, + h5, + h6 { + font-family: var(--tool-card-font-mono); + font-weight: 600; + color: var(--tool-card-text-secondary); + margin-top: 0.65rem; + margin-bottom: 0.35rem; + letter-spacing: normal; + border: none; + padding: 0; + } + + h1 { + font-size: 0.95rem; + } + + h2 { + font-size: 0.9rem; + } + + h3, + h4, + h5, + h6 { + font-size: 0.85rem; + } + + p, + li { + font-size: 12px; + line-height: 1.45; + color: var(--tool-card-text-muted); + } + + p { + margin-bottom: 0.35rem; + } + + strong { + font-weight: 600; + color: var(--tool-card-text-secondary); + } + + em { + color: var(--tool-card-text-muted); + } + + a { + color: var(--text-tertiary, #6b7280); + text-decoration: underline; + text-underline-offset: 2px; + } + + a:hover { + color: var(--text-secondary, #9ca3af); + } + + ul, + ol { + margin: 0.25rem 0 0.35rem; + padding-left: 1.25rem; + } + + hr { + border: none; + border-top: 1px solid color-mix(in srgb, var(--tool-card-text-muted) 25%, transparent); + margin: 0.5rem 0; + } + + blockquote, + .custom-blockquote { + margin: 0.35rem 0; + padding: 0.25rem 0 0.25rem 0.6rem; + border-left: 2px solid color-mix(in srgb, var(--tool-card-text-muted) 35%, transparent); + color: var(--tool-card-text-muted); + background: transparent; + } + + blockquote p { + margin-bottom: 0.25rem; + color: inherit; + } + + .inline-code { + font-family: var(--tool-card-font-mono); + font-size: 0.85em; + padding: 0.1em 0.35em; + border-radius: 3px; + background: color-mix(in srgb, var(--tool-card-text-muted) 12%, transparent); + color: var(--tool-card-text-secondary); + } + + .code-block-wrapper { + margin: 0.35rem 0; + border-radius: 6px; + border: 1px solid color-mix(in srgb, var(--tool-card-text-muted) 18%, transparent); + background: color-mix(in srgb, var(--tool-card-text-muted) 8%, transparent); + } + + .code-block-wrapper pre[class*='language-'], + .code-block-wrapper pre[style] { + font-size: 11px !important; + line-height: 1.4 !important; + border-radius: 6px !important; + } + + .code-block-wrapper .copy-button { + transform: scale(0.85); + transform-origin: top right; + } + + .table-wrapper { + margin: 0.35rem 0; + font-size: 11px; + } + + .table-wrapper th, + .table-wrapper td { + color: var(--tool-card-text-muted); + border-color: color-mix(in srgb, var(--tool-card-text-muted) 22%, transparent); + } + + .table-wrapper th { + color: var(--tool-card-text-secondary); + background: color-mix(in srgb, var(--tool-card-text-muted) 6%, transparent); + } + + .table-wrapper tbody tr:nth-child(2n) { + background: color-mix(in srgb, var(--tool-card-text-muted) 5%, transparent); + } } /* Streaming indicator with ink fade */ diff --git a/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.tsx b/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.tsx index 4892402f..4aae4d2e 100644 --- a/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.tsx +++ b/src/web-ui/src/flow_chat/tool-cards/ModelThinkingDisplay.tsx @@ -13,6 +13,7 @@ import { useTranslation } from 'react-i18next'; import type { FlowThinkingItem } from '../types/flow-chat'; import { useTypewriter } from '../hooks/useTypewriter'; import { useToolCardHeightContract } from './useToolCardHeightContract'; +import { Markdown } from '@/component-library/components/Markdown/Markdown'; import './ModelThinkingDisplay.scss'; interface ModelThinkingDisplayProps { @@ -125,11 +126,11 @@ export const ModelThinkingDisplay: React.FC = ({ thin className={`thinking-content expanded`} onScroll={checkScrollState} > - {renderedContent.split('\n').map((line: string, index: number) => ( -
- {line || '\u00A0'} -
- ))} + diff --git a/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.scss b/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.scss index 6cd6cad0..41b571e4 100644 --- a/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.scss +++ b/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.scss @@ -292,47 +292,94 @@ } .m-editor-inline-ai__panel { - width: min(40rem, calc(100vw - #{$size-gap-8})); - padding: $size-gap-3; - border: 1px solid var(--border-base); - border-radius: $size-radius-lg; - background: var(--color-bg-elevated); - box-shadow: $shadow-base; + width: min(26rem, calc(100vw - #{$size-gap-8})); + padding: 10px 10px 8px; + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 12px; + background: var(--color-bg-tertiary); + backdrop-filter: blur(16px) saturate(1.2); + -webkit-backdrop-filter: blur(16px) saturate(1.2); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.35), + inset 0 1px 0 rgba(255, 255, 255, 0.08), + inset 0 -1px 0 rgba(255, 255, 255, 0.02); + transition: + transform 0.28s cubic-bezier(0.4, 0, 0.2, 1), + box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); + } + + .m-editor-inline-ai:hover .m-editor-inline-ai__panel { + transform: translateY(-4px); + box-shadow: + 0 16px 40px rgba(0, 0, 0, 0.45), + 0 0 28px rgba(96, 165, 250, 0.08), + inset 0 1px 0 rgba(255, 255, 255, 0.12), + inset 0 -1px 0 rgba(255, 255, 255, 0.03); + } + + :root[data-theme='light'] &, + :root[data-theme-type='light'] &, + .light & { + .m-editor-inline-ai__panel { + border-color: rgba(15, 23, 42, 0.12); + box-shadow: + 0 8px 24px rgba(15, 23, 42, 0.08), + inset 0 1px 0 rgba(255, 255, 255, 0.85); + } + + .m-editor-inline-ai:hover .m-editor-inline-ai__panel { + box-shadow: + 0 14px 36px rgba(15, 23, 42, 0.12), + 0 0 24px rgba(59, 130, 246, 0.08), + inset 0 1px 0 rgba(255, 255, 255, 0.95); + } } .m-editor-inline-ai__composer { - margin-bottom: $size-gap-2; + margin-bottom: $size-gap-1; } .m-editor-inline-ai__composer-input { .bitfun-input-container { - height: 3.5rem; - padding-right: $size-gap-2; + height: 36px; + padding: 0 12px 0 14px; border-radius: 999px; - background: $element-bg-subtle; - border-color: $border-base; + background: color-mix(in srgb, var(--color-text-muted) 8%, transparent); + border: 1px solid rgba(255, 255, 255, 0.08); } .bitfun-input { - font-size: $font-size-base; + font-size: $font-size-sm; + line-height: 1.35; + } + } + + :root[data-theme='light'] &, + :root[data-theme-type='light'] &, + .light & { + .m-editor-inline-ai__composer-input .bitfun-input-container { + background: rgba(15, 23, 42, 0.04); + border-color: rgba(15, 23, 42, 0.1); } } .m-editor-inline-ai__composer-actions { display: inline-flex; align-items: center; - gap: $size-gap-2; + gap: 6px; } .m-editor-inline-ai__page-chip { display: inline-flex; align-items: center; - padding: 0 $size-gap-2; - height: 2rem; + padding: 2px 8px; + min-height: 0; border-radius: 999px; - background: $element-bg-subtle; + background: color-mix(in srgb, var(--color-text-muted) 14%, transparent); color: var(--color-text-muted); - font-size: $font-size-xs; + font-size: $font-size-2xs; + font-weight: $font-weight-medium; + letter-spacing: 0.02em; white-space: nowrap; } @@ -340,76 +387,125 @@ display: inline-flex; align-items: center; justify-content: center; - width: 2rem; - height: 2rem; + width: 22px; + height: 22px; padding: 0; - border: none; - border-radius: 999px; - background: var(--color-accent-500); - color: #0b1220; + border-radius: 50%; + border: 1px solid var(--color-accent-300); + background: var(--color-accent-200); + color: var(--color-accent-300); cursor: pointer; - transition: transform $motion-fast $easing-standard, opacity $motion-fast $easing-standard; + flex-shrink: 0; + transition: + transform 0.2s ease, + opacity 0.2s ease, + box-shadow 0.2s ease, + border-color 0.2s ease, + background 0.2s ease; + + &:not(:disabled) { + background: linear-gradient( + 135deg, + rgba(80, 130, 255, 0.85), + rgba(120, 100, 255, 0.9) + ); + border: 1px solid rgba(140, 160, 255, 0.6); + color: #fff; + box-shadow: + 0 2px 8px rgba(100, 140, 255, 0.4), + 0 0 12px rgba(120, 100, 255, 0.25); + } &:hover:not(:disabled) { - transform: translateY(-1px); + background: linear-gradient( + 135deg, + rgba(90, 150, 255, 1), + rgba(140, 120, 255, 1) + ); + border-color: rgba(160, 180, 255, 0.8); + transform: scale(1.06); + box-shadow: + 0 4px 14px rgba(100, 140, 255, 0.5), + 0 0 18px rgba(140, 100, 255, 0.35); + } + + &:active:not(:disabled) { + transform: scale(0.96); } &:disabled { + background: var(--color-accent-100); + border-color: var(--color-accent-200); + color: var(--color-accent-400); opacity: 0.35; cursor: not-allowed; + box-shadow: none; transform: none; } } .m-editor-inline-ai__section-title { - margin: $size-gap-3 0 $size-gap-2; + margin: $size-gap-2 0 6px; color: var(--color-text-muted); - font-size: $font-size-xs; + font-size: $font-size-xxs; font-weight: $font-weight-medium; + letter-spacing: 0.02em; + opacity: 0.72; } .m-editor-inline-ai__quick-actions { display: flex; flex-direction: column; - gap: $size-gap-1; + gap: 2px; } .m-editor-inline-ai__quick-action { display: flex; align-items: center; - gap: $size-gap-2; + gap: 8px; width: 100%; - padding: $size-gap-2 $size-gap-3; - border: 1px solid transparent; - border-radius: $size-radius-base; + padding: 6px 10px; + border: none; + border-radius: 8px; background: transparent; color: var(--color-text-primary); + font-size: $font-size-xs; + line-height: 1.35; cursor: pointer; text-align: left; - transition: background $motion-fast $easing-standard, border-color $motion-fast $easing-standard, color $motion-fast $easing-standard; + transition: background $motion-fast $easing-standard, color $motion-fast $easing-standard; &:hover { - background: $element-bg-subtle; - border-color: $border-base; + background: var(--element-bg-medium, #{$element-bg-medium}); + } + } + + :root[data-theme='light'] &, + :root[data-theme-type='light'] &, + .light & { + .m-editor-inline-ai__quick-action:hover { + background: rgba(15, 23, 42, 0.08); } } .m-editor-inline-ai__quick-action--primary { font-weight: $font-weight-semibold; + font-size: $font-size-sm; } .m-editor-inline-ai__quick-action-icon { display: inline-flex; align-items: center; justify-content: center; - width: 1.5rem; + width: 1.125rem; + flex-shrink: 0; color: var(--color-accent-500); } .m-editor-inline-ai__footer { display: flex; justify-content: flex-end; - margin-top: $size-gap-2; + margin-top: $size-gap-1; } .m-editor-inline-ai__footer-dismiss { @@ -445,18 +541,18 @@ align-items: center; justify-content: space-between; gap: $size-gap-2; - margin-bottom: $size-gap-2; + margin-bottom: 6px; } .m-editor-inline-ai-preview__title { - font-size: $font-size-xs; + font-size: $font-size-2xs; font-weight: $font-weight-medium; color: var(--color-accent-500); letter-spacing: 0.02em; } .m-editor-inline-ai-preview__status { - font-size: $font-size-xs; + font-size: $font-size-2xs; color: var(--color-text-disabled); white-space: nowrap; } @@ -465,15 +561,17 @@ overflow: visible; position: relative; color: inherit; - padding-left: $size-gap-4; + padding-left: $size-gap-3; + font-size: $font-size-xs; + line-height: 1.45; } .m-editor-inline-ai-preview__body::before { content: ''; position: absolute; - left: $size-gap-1; - top: $size-gap-2; - bottom: $size-gap-2; + left: 4px; + top: 6px; + bottom: 6px; width: 2px; border-radius: 999px; background: $color-accent-400; @@ -514,9 +612,9 @@ } .m-editor-inline-ai-rendered__content.markdown-body > * { - margin: 0 0 $size-gap-2; - padding: $size-gap-2; - border-radius: $size-radius-base; + margin: 0 0 6px; + padding: 4px 8px; + border-radius: 8px; transition: background $motion-base $easing-standard; } @@ -536,16 +634,16 @@ } .m-editor-inline-ai-rendered__content.markdown-body h1 { - font-size: $font-size-3xl; - text-align: center; + font-size: $font-size-lg; + text-align: left; } .m-editor-inline-ai-rendered__content.markdown-body h2 { - font-size: $font-size-2xl; + font-size: $font-size-base; } .m-editor-inline-ai-rendered__content.markdown-body h3 { - font-size: $font-size-xl; + font-size: $font-size-sm; } .m-editor-inline-ai-rendered__content.markdown-body p { @@ -553,27 +651,32 @@ } .m-editor-inline-ai-rendered__content.markdown-body blockquote { - margin: 0 0 $size-gap-2; - border-left: 3px solid currentColor; - border-radius: 0 $size-radius-sm $size-radius-sm 0; + margin: 0 0 6px; + padding: 4px 8px; + border-left: 2px solid currentColor; + border-radius: 0 6px 6px 0; opacity: 0.92; + font-size: $font-size-2xs; } .m-editor-inline-ai-rendered__content.markdown-body pre { - margin: 0 0 $size-gap-2; - padding: $size-gap-3; + margin: 0 0 6px; + padding: 8px 10px; + font-size: $font-size-2xs; + line-height: 1.45; background: color-mix(in srgb, currentColor 8%, transparent); border: 1px solid color-mix(in srgb, currentColor 16%, transparent); - border-radius: $size-radius-base; + border-radius: 8px; overflow-x: auto; } .m-editor-inline-ai-rendered__content.markdown-body code { font-family: $font-family-mono; + font-size: 0.95em; } .m-editor-inline-ai-rendered__content.markdown-body hr { - margin: $size-gap-3 0; + margin: $size-gap-2 0; border: none; border-top: 1px solid color-mix(in srgb, currentColor 24%, transparent); } @@ -585,31 +688,34 @@ .m-editor-inline-ai-rendered__content.markdown-body ul:not([data-type='taskList']), .m-editor-inline-ai-rendered__content.markdown-body ol:not([data-type='taskList']) { - margin: 0 0 $size-gap-2; - padding-left: var(--m-editor-list-indent); + margin: 0 0 6px; + padding-left: 1.25rem; } .m-editor-inline-ai-rendered__content.markdown-body li + li { - margin-top: $size-gap-1; + margin-top: 2px; } .m-editor-inline-ai-preview__placeholder { color: inherit; - font-size: $font-size-sm; - line-height: $line-height-relaxed; - padding: $size-gap-2; + font-size: $font-size-xs; + line-height: 1.45; + padding: 4px 6px; } .m-editor-inline-ai-preview__actions { display: flex; flex-wrap: wrap; - gap: $size-gap-2; - margin-top: $size-gap-2; - margin-left: $size-gap-4; + gap: 6px; + margin-top: 6px; + margin-left: $size-gap-3; } .m-editor-inline-ai-preview__actions .btn { - gap: $size-gap-1; + gap: 4px; + min-height: 28px; + padding: 4px 12px; + font-size: $font-size-xs; border-radius: 999px; } diff --git a/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.tsx b/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.tsx index 11db75db..675b56ee 100644 --- a/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.tsx +++ b/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.tsx @@ -1060,9 +1060,9 @@ export const TiptapEditor = React.forwardRef} + prefix={} value={inlineAiState.query} onChange={(event) => { const nextValue = event.target.value; @@ -1114,7 +1114,7 @@ export const TiptapEditor = React.forwardRef - + )} @@ -1134,7 +1134,7 @@ export const TiptapEditor = React.forwardRef - + {t('editor.meditor.inlineAi.continueMode')} @@ -1146,7 +1146,7 @@ export const TiptapEditor = React.forwardRef - + {t('editor.meditor.inlineAi.summaryAction')} @@ -1158,7 +1158,7 @@ export const TiptapEditor = React.forwardRef - + {t('editor.meditor.inlineAi.todoAction')}