diff --git a/src/web-ui/src/app/scenes/profile/views/AssistantConfigPage.tsx b/src/web-ui/src/app/scenes/profile/views/AssistantConfigPage.tsx index 08ab8e63..569bce80 100644 --- a/src/web-ui/src/app/scenes/profile/views/AssistantConfigPage.tsx +++ b/src/web-ui/src/app/scenes/profile/views/AssistantConfigPage.tsx @@ -19,6 +19,7 @@ import { createLogger } from '@/shared/utils/logger'; import { useWorkspaceContext } from '@/infrastructure/contexts/WorkspaceContext'; import { useAgentIdentityDocument } from '@/app/scenes/my-agent/useAgentIdentityDocument'; import { MEditor } from '@/tools/editor/meditor'; +import { useTheme } from '@/infrastructure/theme/hooks/useTheme'; import { PersonaRadar } from './PersonaRadar'; import { useNurseryStore } from '../nurseryStore'; import { useTokenEstimate, formatTokenCount } from './useTokenEstimate'; @@ -62,6 +63,7 @@ function computeRadarDims( const AssistantConfigPage: React.FC = () => { const { t } = useTranslation('scenes/profile'); + const { isLight } = useTheme(); const { openGallery, activeWorkspaceId } = useNurseryStore(); const { assistantWorkspacesList } = useWorkspaceContext(); @@ -337,6 +339,7 @@ const AssistantConfigPage: React.FC = () => { value={identityDocument.body} onChange={handleBodyChange} mode="ir" + theme={isLight ? 'light' : 'dark'} /> diff --git a/src/web-ui/src/app/styles/global.scss b/src/web-ui/src/app/styles/global.scss index a5470063..3692539b 100644 --- a/src/web-ui/src/app/styles/global.scss +++ b/src/web-ui/src/app/styles/global.scss @@ -5,6 +5,8 @@ /* ========== Import design tokens ========== */ @use '../../component-library/styles/tokens.scss' as *; +@use '../../component-library/styles/flowchat-markdown-table-vars.scss'; +@use '../../component-library/styles/flowchat-markdown-code-vars.scss'; /* ========== Global CSS variables ========== */ :root { @@ -137,8 +139,8 @@ blockquote { font-style: italic; } -/* Divider */ -hr { +/* Divider (skip m-editor: ProseMirror uses border-top hairline; global height+background stacks visually) */ +hr:not(:is(.m-editor hr)) { border: none; height: 1px; background: var(--border-base); diff --git a/src/web-ui/src/component-library/components/CubeLoading/CubeLoading.tsx b/src/web-ui/src/component-library/components/CubeLoading/CubeLoading.tsx index 9a745d8c..bfb506d0 100644 --- a/src/web-ui/src/component-library/components/CubeLoading/CubeLoading.tsx +++ b/src/web-ui/src/component-library/components/CubeLoading/CubeLoading.tsx @@ -29,23 +29,16 @@ export const CubeLoading: React.FC = ({ style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px' }} >
-
-
-
-
-
-
-
-
-
-
+
+
+
{text &&
{text}
}
diff --git a/src/web-ui/src/component-library/components/CubeLoading/Spiral.scss b/src/web-ui/src/component-library/components/CubeLoading/Spiral.scss index fc8949d9..51540676 100644 --- a/src/web-ui/src/component-library/components/CubeLoading/Spiral.scss +++ b/src/web-ui/src/component-library/components/CubeLoading/Spiral.scss @@ -1,121 +1,54 @@ -.spiral-container { +// --uib-size : square container side (24px / 40px / 60px) +// --uib-color : currentColor (inherits foreground / theme) +// --uib-speed : base animation duration 0.9s + +.panda-breath-container { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; - height: var(--uib-size); width: var(--uib-size); -} - -.spiral-inner { - --uib-center: calc(var(--uib-size) / 2 - var(--uib-size) / 5 / 2); - position: relative; - display: flex; - align-items: center; - justify-content: flex-start; height: var(--uib-size); - width: var(--uib-size); - animation: spiral-rotate calc(var(--uib-speed) * 3) linear infinite; + position: relative; } -.spiral-dot { +.panda-face { position: absolute; - top: 0; - left: 0; - display: flex; - align-items: center; - justify-content: flex-start; - height: 100%; - width: 100%; - - &::before { - content: ''; - height: 20%; - width: 20%; - border-radius: 50%; - background-color: var(--uib-color); - animation: spiral-oscillate var(--uib-speed) ease-in-out infinite alternate; - transition: background-color 0.3s ease; - } -} - -.spiral-dot:nth-child(1)::before { - transform: translateX(var(--uib-center)); -} - -.spiral-dot:nth-child(2) { - transform: rotate(45deg); - &::before { - transform: translateX(var(--uib-center)); - animation-delay: calc(var(--uib-speed) * -0.125); - } + inset: 0; + border-radius: 50%; + border: calc(var(--uib-size) * 0.07) solid var(--uib-color); + opacity: 0.9; + animation: panda-breathe calc(var(--uib-speed) * 2.2) ease-in-out infinite; } -.spiral-dot:nth-child(3) { - transform: rotate(90deg); - &::before { - transform: translateX(var(--uib-center)); - animation-delay: calc(var(--uib-speed) * -0.25); - } -} - -.spiral-dot:nth-child(4) { - transform: rotate(135deg); - &::before { - transform: translateX(var(--uib-center)); - animation-delay: calc(var(--uib-speed) * -0.375); - } -} - -.spiral-dot:nth-child(5) { - transform: rotate(180deg); - &::before { - transform: translateX(var(--uib-center)); - animation-delay: calc(var(--uib-speed) * -0.5); - } -} - -.spiral-dot:nth-child(6) { - transform: rotate(225deg); - &::before { - transform: translateX(var(--uib-center)); - animation-delay: calc(var(--uib-speed) * -0.625); - } +.panda-eye { + position: absolute; + border-radius: 50%; + background-color: var(--uib-color); + width: calc(var(--uib-size) * 0.16); + height: calc(var(--uib-size) * 0.16); + top: 50%; + transform: translateY(-50%); } -.spiral-dot:nth-child(7) { - transform: rotate(270deg); - &::before { - transform: translateX(var(--uib-center)); - animation-delay: calc(var(--uib-speed) * -0.75); - } +.panda-eye--left { + left: calc(50% - var(--uib-size) * 0.27); + animation: panda-blink calc(var(--uib-speed) * 2.2) ease-in-out infinite; } -.spiral-dot:nth-child(8) { - transform: rotate(315deg); - &::before { - transform: translateX(var(--uib-center)); - animation-delay: calc(var(--uib-speed) * -0.875); - } +.panda-eye--right { + left: calc(50% + var(--uib-size) * 0.11); + animation: panda-blink calc(var(--uib-speed) * 2.2) ease-in-out infinite; + animation-delay: calc(var(--uib-speed) * 0.08); } -@keyframes spiral-oscillate { - 0% { - transform: translateX(var(--uib-center)) scale(0); - opacity: 0.25; - } - - 100% { - transform: translateX(0) scale(1); - opacity: 1; - } +@keyframes panda-breathe { + 0% { transform: scale(0.92); opacity: 0.55; } + 50% { transform: scale(1.06); opacity: 0.95; } + 100% { transform: scale(0.92); opacity: 0.55; } } -@keyframes spiral-rotate { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } +@keyframes panda-blink { + 0%, 55%, 67%, 100% { transform: translateY(-50%) scaleY(1); } + 60% { transform: translateY(-50%) scaleY(0.08); } } diff --git a/src/web-ui/src/component-library/styles/flowchat-markdown-code-vars.scss b/src/web-ui/src/component-library/styles/flowchat-markdown-code-vars.scss new file mode 100644 index 00000000..3ea65659 --- /dev/null +++ b/src/web-ui/src/component-library/styles/flowchat-markdown-code-vars.scss @@ -0,0 +1,56 @@ +/** + * Flow-chat markdown inline code / pre / blockquote palette for m-editor. + * Values mirror component-library Markdown.scss (.markdown-renderer) defaults + light overrides. + * Do not import this into Markdown.scss — flow chat styles stay unchanged. + */ +:root { + --flowchat-md-code-font-mono: 'Fira Code', 'JetBrains Mono', Consolas, 'Courier New', monospace; + + --flowchat-md-inline-code-bg: rgba(255, 255, 255, 0.05); + --flowchat-md-inline-code-border: rgba(255, 255, 255, 0.1); + --flowchat-md-inline-code-fg: #a8b4c8; + --flowchat-md-inline-code-hover-bg: rgba(255, 255, 255, 0.08); + --flowchat-md-inline-code-hover-border: rgba(255, 255, 255, 0.15); + --flowchat-md-inline-code-hover-fg: #c5d0e2; + + --flowchat-md-pre-bg: var(--color-bg-primary); + --flowchat-md-pre-border: rgba(255, 255, 255, 0.1); + --flowchat-md-pre-border-style: dashed; + --flowchat-md-pre-radius: 4px; + --flowchat-md-pre-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + --flowchat-md-pre-padding: 1.25rem; + --flowchat-md-pre-font-size: 0.85rem; + --flowchat-md-pre-hover-border: rgba(255, 255, 255, 0.15); + --flowchat-md-pre-hover-shadow: + 0 8px 24px rgba(0, 0, 0, 0.4), + inset 0 1px 0 rgba(255, 255, 255, 0.1), + inset 0 -1px 0 rgba(255, 255, 255, 0.05); + --flowchat-md-pre-code-fg: var(--color-text-secondary); + + --flowchat-md-bq-bg: rgba(255, 255, 255, 0.02); + --flowchat-md-bq-border: rgba(255, 255, 255, 0.15); + --flowchat-md-bq-radius: 0 4px 4px 0; + --flowchat-md-bq-fg: rgba(255, 255, 255, 0.7); + --flowchat-md-bq-padding: 1rem 1.2rem; + --flowchat-md-bq-margin: 1.5rem 0; +} + +:root[data-theme-type="light"] { + --flowchat-md-inline-code-bg: rgba(0, 0, 0, 0.05); + --flowchat-md-inline-code-border: rgba(0, 0, 0, 0.12); + --flowchat-md-inline-code-fg: #24292f; + --flowchat-md-inline-code-hover-bg: rgba(0, 0, 0, 0.08); + --flowchat-md-inline-code-hover-border: rgba(0, 0, 0, 0.18); + --flowchat-md-inline-code-hover-fg: #1f2328; + + --flowchat-md-pre-bg: #f6f8fa; + --flowchat-md-pre-border: rgba(0, 0, 0, 0.1); + --flowchat-md-pre-shadow: 0 2px 4px rgba(15, 23, 42, 0.06); + --flowchat-md-pre-hover-border: rgba(0, 0, 0, 0.15); + --flowchat-md-pre-hover-shadow: 0 6px 20px rgba(15, 23, 42, 0.08); + --flowchat-md-pre-code-fg: var(--color-text-primary, #24292f); + + --flowchat-md-bq-bg: rgba(0, 0, 0, 0.03); + --flowchat-md-bq-border: rgba(0, 0, 0, 0.12); + --flowchat-md-bq-fg: var(--color-text-muted, #57606a); +} diff --git a/src/web-ui/src/component-library/styles/flowchat-markdown-table-vars.scss b/src/web-ui/src/component-library/styles/flowchat-markdown-table-vars.scss new file mode 100644 index 00000000..4c02340c --- /dev/null +++ b/src/web-ui/src/component-library/styles/flowchat-markdown-table-vars.scss @@ -0,0 +1,32 @@ +/** + * Flow-chat markdown table surface (same palette as Markdown.scss `.table-wrapper`). + * Single source for `.markdown-renderer` and m-editor tables. + */ +:root { + --flowchat-md-table-radius: 12px; + --flowchat-md-table-border: rgba(255, 255, 255, 0.12); + --flowchat-md-table-shadow: 0 4px 20px rgba(0, 0, 0, 0.35); + --flowchat-md-table-surface: transparent; + --flowchat-md-table-cell-border: rgba(255, 255, 255, 0.1); + --flowchat-md-table-col-divider: transparent; + --flowchat-md-table-th-bg: rgba(255, 255, 255, 0.03); + --flowchat-md-table-th-fg: var(--color-text-primary); + --flowchat-md-table-td-fg: var(--color-text-primary); + --flowchat-md-table-row-base: transparent; + --flowchat-md-table-row-stripe: rgba(255, 255, 255, 0.02); + --flowchat-md-table-hover: rgba(255, 255, 255, 0.04); +} + +:root[data-theme-type="light"] { + --flowchat-md-table-border: rgba(15, 23, 42, 0.12); + --flowchat-md-table-shadow: 0 4px 14px rgba(15, 23, 42, 0.06); + --flowchat-md-table-surface: #ffffff; + --flowchat-md-table-cell-border: rgba(15, 23, 42, 0.1); + --flowchat-md-table-col-divider: rgba(15, 23, 42, 0.08); + --flowchat-md-table-th-bg: #f3f6fb; + --flowchat-md-table-th-fg: #0f172a; + --flowchat-md-table-td-fg: var(--color-text-primary, #1f2937); + --flowchat-md-table-row-base: #ffffff; + --flowchat-md-table-row-stripe: #f8fafc; + --flowchat-md-table-hover: #eef4ff; +} diff --git a/src/web-ui/src/flow_chat/components/modern/FlowChatHeader.scss b/src/web-ui/src/flow_chat/components/modern/FlowChatHeader.scss index b6f10e4d..467f8fc4 100644 --- a/src/web-ui/src/flow_chat/components/modern/FlowChatHeader.scss +++ b/src/web-ui/src/flow_chat/components/modern/FlowChatHeader.scss @@ -67,7 +67,7 @@ position: relative; display: flex; align-items: center; - gap: $size-gap-1; + gap: 0; } &__turn-nav-button { diff --git a/src/web-ui/src/tools/editor/components/MarkdownEditor.scss b/src/web-ui/src/tools/editor/components/MarkdownEditor.scss index 672c2097..8706386f 100644 --- a/src/web-ui/src/tools/editor/components/MarkdownEditor.scss +++ b/src/web-ui/src/tools/editor/components/MarkdownEditor.scss @@ -80,8 +80,8 @@ } } -// Markdown content styles -.markdown-body { +// Markdown preview inside m-editor only (align with flow-chat markdown chrome via shared CSS vars) +.m-editor .markdown-body { --m-editor-list-indent: 1.75rem; --m-editor-list-marker-width: 1.25rem; --m-editor-list-gap: 0.25rem; @@ -108,7 +108,7 @@ h1 { font-size: $font-size-3xl; color: var(--color-text-primary); - border-bottom: 1px solid $border-base; + border-bottom: 1px solid var(--border-subtle, #{$border-subtle}); padding-bottom: $size-gap-3; text-align: center; letter-spacing: 0.5px; @@ -116,7 +116,7 @@ h2 { font-size: $font-size-2xl; - border-bottom: 1px solid $border-base; + border-bottom: 1px solid var(--border-subtle, #{$border-subtle}); padding-bottom: $size-gap-2; } @@ -142,44 +142,84 @@ } code { - padding: 0.2em 0.4em; - margin: 0; - font-size: 100%; - background-color: $element-bg-subtle; - border-radius: $size-radius-sm; - font-family: $font-family-mono; - color: var(--color-accent-500); + padding: 0.1em 0.4em; + margin: 0 0.05em; + font-size: 0.9em; + font-weight: 500; + line-height: 1.4; + vertical-align: baseline; + white-space: nowrap; + font-family: var(--flowchat-md-code-font-mono); + color: var(--flowchat-md-inline-code-fg); + background: var(--flowchat-md-inline-code-bg); + border: 1px solid var(--flowchat-md-inline-code-border); + border-radius: 4px; + transition: all 0.15s ease; + box-decoration-break: clone; + -webkit-box-decoration-break: clone; + + &:hover { + background: var(--flowchat-md-inline-code-hover-bg); + border-color: var(--flowchat-md-inline-code-hover-border); + color: var(--flowchat-md-inline-code-hover-fg); + } } pre { - padding: $size-gap-4; + margin: 0.25rem 0.3rem $size-gap-4; + padding: var(--flowchat-md-pre-padding); overflow: auto; - font-size: 100%; - line-height: 1.45; - background-color: $element-bg-subtle; - border: 1px solid $border-base; - border-radius: $size-radius-base; - margin-bottom: $size-gap-4; + font-size: var(--flowchat-md-pre-font-size); + line-height: 1.5; + position: relative; + font-family: var(--flowchat-md-code-font-mono); + color: var(--flowchat-md-pre-code-fg); + background: var(--flowchat-md-pre-bg); + border: 1px var(--flowchat-md-pre-border-style) var(--flowchat-md-pre-border); + border-radius: var(--flowchat-md-pre-radius); + box-shadow: var(--flowchat-md-pre-shadow); + transition: all 0.3s ease; + + &:hover { + border-style: solid; + border-color: var(--flowchat-md-pre-hover-border); + box-shadow: var(--flowchat-md-pre-hover-shadow); + } code { + display: block; padding: 0; margin: 0; - background: transparent; - border: 0; - display: inline; + overflow: visible; + white-space: pre; + font-family: var(--flowchat-md-code-font-mono); + font-size: var(--flowchat-md-pre-font-size); + font-weight: inherit; line-height: inherit; word-wrap: normal; + color: inherit; + background: transparent; + border: 0; + border-radius: 0; + box-shadow: none; + + &:hover { + background: transparent !important; + border: none !important; + color: inherit !important; + } } } blockquote { - padding: $size-gap-3 $size-gap-4; - color: var(--color-text-muted); - border-left: 3px solid $border-base; - margin: 0 0 $size-gap-4 0; - background: $element-bg-subtle; - border-radius: 0 $size-radius-base $size-radius-base 0; + margin: var(--flowchat-md-bq-margin); + padding: var(--flowchat-md-bq-padding); font-style: italic; + position: relative; + color: var(--flowchat-md-bq-fg); + background: var(--flowchat-md-bq-bg); + border-left: 3px solid var(--flowchat-md-bq-border); + border-radius: var(--flowchat-md-bq-radius); > :first-child { margin-top: 0; @@ -188,6 +228,10 @@ > :last-child { margin-bottom: 0; } + + p { + margin: 0.5rem 0; + } } > ul:not(.contains-task-list), @@ -253,44 +297,118 @@ } } + /* Same palette as flow chat `.table-wrapper` (flowchat-markdown-table-vars.scss) */ table { border-spacing: 0; border-collapse: collapse; - display: block; width: max-content; max-width: 100%; - overflow: auto; margin-bottom: $size-gap-4; - border-radius: $size-radius-base; + font-size: 0.85rem; + border: 1px solid var(--flowchat-md-table-border); + border-radius: var(--flowchat-md-table-radius); + background: var(--flowchat-md-table-surface); + box-shadow: var(--flowchat-md-table-shadow); overflow: hidden; - border: 1px solid $border-base; + } - th { - font-weight: $font-weight-semibold; - padding: $size-gap-2 $size-gap-3; - border: 1px solid $border-base; - background: $element-bg-soft; - color: var(--color-text-primary); - } + table th, + table td { + padding: $size-gap-3 $size-gap-4; + border: none; + border-bottom: 1px solid var(--flowchat-md-table-cell-border); + text-align: left; + vertical-align: top; + background: transparent; + word-break: break-word; + overflow-wrap: anywhere; + transition: background-color $motion-fast $easing-standard; + } - td { - padding: $size-gap-2 $size-gap-3; - border: 1px solid $border-base; - } + table th { + font-weight: $font-weight-semibold; + background: var(--flowchat-md-table-th-bg); + color: var(--flowchat-md-table-th-fg); + } - tr { - background-color: transparent; - border-top: 1px solid $border-base; - transition: background-color $motion-fast $easing-standard; + table td { + color: var(--flowchat-md-table-td-fg); + } - &:nth-child(2n) { - background-color: color-mix(in srgb, var(--color-text-primary) 2%, transparent); - } + table th + th, + table td + td, + table th + td { + border-left: 1px solid var(--flowchat-md-table-col-divider); + } - &:hover { - background-color: $element-bg-subtle; - } - } + table tbody tr:last-child td, + table tbody tr:last-child th { + border-bottom: none; + } + + table tbody tr { + background: var(--flowchat-md-table-row-base); + } + + table tbody tr:nth-child(2n) { + background: var(--flowchat-md-table-row-stripe); + } + + table tbody tr:hover { + background: var(--flowchat-md-table-hover); + } + + table thead tr:hover { + background: var(--flowchat-md-table-hover); + } + + table thead tr:hover th, + table tbody tr:hover th, + table tbody tr:hover td { + background: transparent; + } + + table tbody tr:first-child th:first-child, + table thead th:first-child { + border-top-left-radius: var(--flowchat-md-table-radius); + } + + table tbody tr:first-child th:last-child, + table thead th:last-child { + border-top-right-radius: var(--flowchat-md-table-radius); + } + + table tbody tr:last-child td:first-child, + table tbody tr:last-child th:first-child { + border-bottom-left-radius: var(--flowchat-md-table-radius); + } + + table tbody tr:last-child td:last-child, + table tbody tr:last-child th:last-child { + border-bottom-right-radius: var(--flowchat-md-table-radius); + } + + table tbody tr:first-child th:only-child { + border-radius: var(--flowchat-md-table-radius) var(--flowchat-md-table-radius) 0 0; + } + + table tbody tr:last-child td:only-child { + border-radius: 0 0 var(--flowchat-md-table-radius) var(--flowchat-md-table-radius); + } + + table th code, + table td code { + white-space: normal; + word-break: break-word; + overflow-wrap: anywhere; + max-width: 100%; + } + + table th pre code, + table td pre code { + white-space: pre-wrap; + word-break: break-word; + max-width: 100%; } img { @@ -301,16 +419,13 @@ } hr { - height: 1px; + height: 0; padding: 0; - margin: $size-gap-6 0; - background: linear-gradient(90deg, - transparent 0%, - color-mix(in srgb, var(--color-text-muted) 30%, transparent) 20%, - color-mix(in srgb, var(--color-text-muted) 30%, transparent) 80%, - transparent 100%); + margin: $size-gap-5 0; border: 0; - border-radius: 1px; + border-top: 1px solid var(--border-subtle, #{$border-subtle}); + background: none; + overflow: visible; } // Task list diff --git a/src/web-ui/src/tools/editor/components/MarkdownEditor.tsx b/src/web-ui/src/tools/editor/components/MarkdownEditor.tsx index 64435b3d..2afdb5be 100644 --- a/src/web-ui/src/tools/editor/components/MarkdownEditor.tsx +++ b/src/web-ui/src/tools/editor/components/MarkdownEditor.tsx @@ -12,6 +12,7 @@ import { AlertCircle } from 'lucide-react'; import { createLogger } from '@/shared/utils/logger'; import { CubeLoading, Button } from '@/component-library'; import { useI18n } from '@/infrastructure/i18n'; +import { useTheme } from '@/infrastructure/theme/hooks/useTheme'; import './MarkdownEditor.scss'; const log = createLogger('MarkdownEditor'); @@ -53,6 +54,7 @@ const MarkdownEditor: React.FC = ({ jumpToColumn, }) => { const { t } = useI18n('tools'); + const { isLight } = useTheme(); const [content, setContent] = useState(initialContent); const [hasChanges, setHasChanges] = useState(false); const [loading, setLoading] = useState(!!filePath); @@ -291,7 +293,7 @@ const MarkdownEditor: React.FC = ({ onSave={handleSave} onDirtyChange={handleDirtyChange} mode="ir" - theme="dark" + theme={isLight ? 'light' : 'dark'} height="100%" width="100%" placeholder={t('editor.markdownEditor.placeholder')} diff --git a/src/web-ui/src/tools/editor/components/PlanViewer.tsx b/src/web-ui/src/tools/editor/components/PlanViewer.tsx index dcf8d79e..d90d6c4b 100644 --- a/src/web-ui/src/tools/editor/components/PlanViewer.tsx +++ b/src/web-ui/src/tools/editor/components/PlanViewer.tsx @@ -8,6 +8,7 @@ import type { EditorInstance } from '../meditor'; import { createLogger } from '@/shared/utils/logger'; import { CubeLoading, Button, Tooltip } from '@/component-library'; import { useI18n } from '@/infrastructure/i18n'; +import { useTheme } from '@/infrastructure/theme/hooks/useTheme'; import { workspaceAPI } from '@/infrastructure/api/service-api/WorkspaceAPI'; import { flowChatManager } from '@/flow_chat/services/FlowChatManager'; import { fileSystemService } from '@/tools/file-system/services/FileSystemService'; @@ -56,6 +57,8 @@ const PlanViewer: React.FC = ({ jumpToColumn: _jumpToColumn, }) => { const { t } = useI18n('tools'); + const { isLight } = useTheme(); + const mEditorTheme = isLight ? 'light' : 'dark'; const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [planData, setPlanData] = useState(null); @@ -593,7 +596,7 @@ const PlanViewer: React.FC = ({ onChange={handleYamlChange} onSave={handleSave} mode="edit" - theme="dark" + theme={mEditorTheme} height="200px" width="100%" placeholder={t('editor.planViewer.yamlPlaceholder')} @@ -807,7 +810,7 @@ ${JSON.stringify(simpleTodos, null, 2)} onChange={handleContentChange} onSave={handleSave} mode="ir" - theme="dark" + theme={mEditorTheme} height="auto" width="100%" placeholder={t('editor.planViewer.contentPlaceholder')} diff --git a/src/web-ui/src/tools/editor/meditor/components/MEditor.scss b/src/web-ui/src/tools/editor/meditor/components/MEditor.scss index db05a210..cec84342 100644 --- a/src/web-ui/src/tools/editor/meditor/components/MEditor.scss +++ b/src/web-ui/src/tools/editor/meditor/components/MEditor.scss @@ -55,19 +55,27 @@ &-light { background: var(--color-bg-flowchat); color: var(--color-text-primary); - border: 1px solid $border-base; + border: 1px solid var(--border-base); .m-editor-textarea { color: var(--color-text-secondary); } .m-editor-toolbar { - background: $element-bg-soft; - border-bottom-color: $border-base; + background: var(--element-bg-soft); + border-bottom-color: var(--border-base); } .m-editor-edit-panel { - border-right-color: $border-base !important; + border-right-color: var(--border-base) !important; + } + + .mermaid-container { + border-color: var(--border-medium); + } + + .m-editor-preview-loading { + border-color: var(--border-medium); } } } diff --git a/src/web-ui/src/tools/editor/meditor/components/Preview.scss b/src/web-ui/src/tools/editor/meditor/components/Preview.scss index 5ad51898..af7938d5 100644 --- a/src/web-ui/src/tools/editor/meditor/components/Preview.scss +++ b/src/web-ui/src/tools/editor/meditor/components/Preview.scss @@ -22,7 +22,8 @@ &-content { padding: $size-gap-4; - + overflow-x: auto; + &.markdown-body { } } 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 41b571e4..896bbf59 100644 --- a/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.scss +++ b/src/web-ui/src/tools/editor/meditor/components/TiptapEditor.scss @@ -19,14 +19,15 @@ font-size: $font-size-sm; line-height: $line-height-relaxed; - > [data-block-id] { + /* blockId lives on
itself — do not use paragraph chrome (padding + radius + hover fill) on hr */ + > [data-block-id]:not(hr) { margin-bottom: $size-gap-2; padding: $size-gap-2; border-radius: $size-radius-base; transition: background $motion-base $easing-standard; &:hover { - background: $element-bg-subtle; + background: var(--element-bg-subtle, #{$element-bg-subtle}); } &.m-editor-tiptap-block-highlighted { @@ -112,51 +113,216 @@ } blockquote { - margin: 0; - padding-left: $size-gap-3; - border-left: 3px solid $border-base; + margin: var(--flowchat-md-bq-margin); + padding: var(--flowchat-md-bq-padding); + font-style: italic; + color: var(--flowchat-md-bq-fg); + background: var(--flowchat-md-bq-bg); + border-left: 3px solid var(--flowchat-md-bq-border); + border-radius: var(--flowchat-md-bq-radius); } pre { - margin: 0; - padding: $size-gap-3; - background: $element-bg-subtle; - border-radius: $size-radius-base; + margin: 0.25rem 0.3rem; + padding: var(--flowchat-md-pre-padding); overflow-x: auto; - font-family: $font-family-mono; + font-size: var(--flowchat-md-pre-font-size); + line-height: 1.5; + font-family: var(--flowchat-md-code-font-mono); + color: var(--flowchat-md-pre-code-fg); + background: var(--flowchat-md-pre-bg); + border: 1px var(--flowchat-md-pre-border-style) var(--flowchat-md-pre-border); + border-radius: var(--flowchat-md-pre-radius); + box-shadow: var(--flowchat-md-pre-shadow); + transition: all 0.3s ease; + + &:hover { + border-style: solid; + border-color: var(--flowchat-md-pre-hover-border); + box-shadow: var(--flowchat-md-pre-hover-shadow); + } } code { - font-family: $font-family-mono; + padding: 0.1em 0.4em; + margin: 0 0.05em; + font-size: 0.9em; + font-weight: 500; + line-height: 1.4; + vertical-align: baseline; + white-space: nowrap; + font-family: var(--flowchat-md-code-font-mono); + color: var(--flowchat-md-inline-code-fg); + background: var(--flowchat-md-inline-code-bg); + border: 1px solid var(--flowchat-md-inline-code-border); + border-radius: 4px; + transition: all 0.15s ease; + box-decoration-break: clone; + -webkit-box-decoration-break: clone; + + &:hover { + background: var(--flowchat-md-inline-code-hover-bg); + border-color: var(--flowchat-md-inline-code-hover-border); + color: var(--flowchat-md-inline-code-hover-fg); + } } - hr { + pre code { + display: block; + padding: 0; + margin: 0; + font-size: var(--flowchat-md-pre-font-size); + font-weight: inherit; + white-space: pre-wrap; + word-break: break-word; + color: inherit; + background: transparent; border: none; - border-top: 1px solid $border-base; - margin: $size-gap-3 0; + border-radius: 0; + box-shadow: none; + + &:hover { + background: transparent !important; + border: none !important; + color: inherit !important; + } + } + + hr { + display: block; + height: 0; + margin: $size-gap-4 0; + padding: 0; + border: 0; + border-top: 1px solid var(--border-subtle, #{$border-subtle}); + background: none; + background-color: transparent; + overflow: visible; + box-shadow: none; + border-radius: 0; + + &[data-block-id] { + margin-bottom: $size-gap-2; + } + + &[data-block-id].m-editor-tiptap-block-highlighted { + border-top-color: rgba(255, 193, 7, 0.55); + } } + /* Same palette as flow chat `.markdown-renderer .table-wrapper` (see flowchat-markdown-table-vars.scss) */ table { width: 100%; border-collapse: collapse; - margin: $size-gap-2 0; + border-spacing: 0; + margin: $size-gap-4 0; table-layout: fixed; + font-size: 0.85rem; + border: 1px solid var(--flowchat-md-table-border); + border-radius: var(--flowchat-md-table-radius); + background: var(--flowchat-md-table-surface); + box-shadow: var(--flowchat-md-table-shadow); + overflow: hidden; } th, td { - border: 1px solid $border-base; - padding: $size-gap-2; + padding: $size-gap-3 $size-gap-4; + border: none; + border-bottom: 1px solid var(--flowchat-md-table-cell-border); vertical-align: top; min-width: 5rem; word-break: break-word; + overflow-wrap: anywhere; + text-align: left; + background: transparent; + transition: background-color $motion-fast $easing-standard; } th { - background: $element-bg-subtle; - color: var(--color-text-primary); font-weight: $font-weight-semibold; - text-align: left; + background: var(--flowchat-md-table-th-bg); + color: var(--flowchat-md-table-th-fg); + } + + td { + color: var(--flowchat-md-table-td-fg); + } + + th + th, + td + td, + th + td { + border-left: 1px solid var(--flowchat-md-table-col-divider); + } + + tbody tr:last-child td, + tbody tr:last-child th { + border-bottom: none; + } + + tbody tr { + background: var(--flowchat-md-table-row-base); + } + + tbody tr:nth-child(2n) { + background: var(--flowchat-md-table-row-stripe); + } + + tbody tr:hover { + background: var(--flowchat-md-table-hover); + } + + thead tr:hover { + background: var(--flowchat-md-table-hover); + } + + thead tr:hover th, + tbody tr:hover th, + tbody tr:hover td { + background: transparent; + } + + tbody tr:first-child th:first-child, + thead th:first-child { + border-top-left-radius: var(--flowchat-md-table-radius); + } + + tbody tr:first-child th:last-child, + thead th:last-child { + border-top-right-radius: var(--flowchat-md-table-radius); + } + + tbody tr:last-child td:first-child, + tbody tr:last-child th:first-child { + border-bottom-left-radius: var(--flowchat-md-table-radius); + } + + tbody tr:last-child td:last-child, + tbody tr:last-child th:last-child { + border-bottom-right-radius: var(--flowchat-md-table-radius); + } + + tbody tr:first-child th:only-child { + border-radius: var(--flowchat-md-table-radius) var(--flowchat-md-table-radius) 0 0; + } + + tbody tr:last-child td:only-child { + border-radius: 0 0 var(--flowchat-md-table-radius) var(--flowchat-md-table-radius); + } + + th code, + td code { + white-space: normal; + word-break: break-word; + overflow-wrap: anywhere; + max-width: 100%; + } + + th pre code, + td pre code { + white-space: pre-wrap; + word-break: break-word; + max-width: 100%; } > ul:not([data-type='taskList']), @@ -652,11 +818,14 @@ .m-editor-inline-ai-rendered__content.markdown-body blockquote { margin: 0 0 6px; - padding: 4px 8px; - border-left: 2px solid currentColor; - border-radius: 0 6px 6px 0; - opacity: 0.92; + padding: var(--flowchat-md-bq-padding); + font-style: italic; font-size: $font-size-2xs; + color: var(--flowchat-md-bq-fg); + background: var(--flowchat-md-bq-bg); + border-left: 3px solid var(--flowchat-md-bq-border); + border-radius: var(--flowchat-md-bq-radius); + opacity: 1; } .m-editor-inline-ai-rendered__content.markdown-body pre { @@ -664,15 +833,48 @@ 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: 8px; + font-family: var(--flowchat-md-code-font-mono); + color: var(--flowchat-md-pre-code-fg); + background: var(--flowchat-md-pre-bg); + border: 1px var(--flowchat-md-pre-border-style) var(--flowchat-md-pre-border); + border-radius: var(--flowchat-md-pre-radius); + box-shadow: var(--flowchat-md-pre-shadow); overflow-x: auto; + transition: all 0.3s ease; + + &:hover { + border-style: solid; + border-color: var(--flowchat-md-pre-hover-border); + box-shadow: var(--flowchat-md-pre-hover-shadow); + } } - .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 pre code { + display: block; + padding: 0; + margin: 0; + font-size: inherit; + font-weight: inherit; + white-space: pre-wrap; + color: inherit; + background: transparent; + border: none; + border-radius: 0; + } + + .m-editor-inline-ai-rendered__content.markdown-body :not(pre) > code, + .m-editor-inline-ai-rendered__content.markdown-body p code, + .m-editor-inline-ai-rendered__content.markdown-body li code, + .m-editor-inline-ai-rendered__content.markdown-body td code, + .m-editor-inline-ai-rendered__content.markdown-body th code { + padding: 0.1em 0.35em; + font-size: 0.9em; + font-weight: 500; + font-family: var(--flowchat-md-code-font-mono); + color: var(--flowchat-md-inline-code-fg); + background: var(--flowchat-md-inline-code-bg); + border: 1px solid var(--flowchat-md-inline-code-border); + border-radius: 4px; } .m-editor-inline-ai-rendered__content.markdown-body hr {