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')}