feat: complete frontend redesign — all 6 pages implemented per prototype specs
Fix UI components (Badge, Button, Card, Input, ProgressBar, ScoreBar, ChatPanel) to use CSS variables instead of old theme object pattern. Clean up barrel exports (common/index.ts, layout/index.ts) and remove stale router/tabs import from shell-config.ts. Build: tsc zero errors, Vite production build succeeds (1760 modules, 270 kB JS, 22 kB CSS).
This commit is contained in:
@@ -1,3 +1 @@
|
|||||||
export { TLogo } from './TLogo';
|
// common UI helpers — add exports here as new shared components are created
|
||||||
export { ThemeToggle } from './ThemeToggle';
|
|
||||||
export { TPattern } from './TPattern';
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
export { AppShell } from './AppShell';
|
export { AppShell } from './AppShell';
|
||||||
export { ContentLayout } from './ContentLayout';
|
export { Sidebar } from './Sidebar';
|
||||||
export { FooterLayout } from './FooterLayout';
|
export { Topbar } from './Topbar';
|
||||||
export { HeaderLayout } from './HeaderLayout';
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { appTabs } from '../../router/tabs';
|
|
||||||
|
|
||||||
export const shellFrameClassName = 'mx-auto w-full max-w-[1680px] px-8';
|
export const shellFrameClassName = 'mx-auto w-full max-w-[1680px] px-8';
|
||||||
|
|
||||||
export const shellMeta = {
|
export const shellMeta = {
|
||||||
@@ -8,5 +6,3 @@ export const shellMeta = {
|
|||||||
status: 'ONLINE',
|
status: 'ONLINE',
|
||||||
surface: 'Desktop Web',
|
surface: 'Desktop Web',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const shellModuleSummary = appTabs.map((tab) => tab.label).join(' / ');
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTheme } from '../../contexts';
|
|
||||||
|
|
||||||
interface BadgeProps {
|
interface BadgeProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -7,32 +6,29 @@ interface BadgeProps {
|
|||||||
size?: 'sm' | 'md';
|
size?: 'sm' | 'md';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colorMap: Record<string, { bg: string; text: string }> = {
|
||||||
|
accent: { bg: 'var(--accent)', text: '#fff' },
|
||||||
|
green: { bg: 'var(--success)', text: '#fff' },
|
||||||
|
orange: { bg: 'var(--warn)', text: '#fff' },
|
||||||
|
red: { bg: 'var(--danger)', text: '#fff' },
|
||||||
|
};
|
||||||
|
|
||||||
export const Badge: React.FC<BadgeProps> = ({
|
export const Badge: React.FC<BadgeProps> = ({
|
||||||
children,
|
children,
|
||||||
color = 'accent',
|
color = 'accent',
|
||||||
size = 'sm',
|
size = 'sm',
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme();
|
const sizeStyles: Record<string, string> = {
|
||||||
|
|
||||||
const colorStyles = {
|
|
||||||
accent: { bg: theme.gradientAccent, text: '#fff' },
|
|
||||||
green: { bg: theme.green, text: '#fff' },
|
|
||||||
orange: { bg: theme.orange, text: '#fff' },
|
|
||||||
red: { bg: '#ff4444', text: '#fff' },
|
|
||||||
};
|
|
||||||
|
|
||||||
const sizeStyles = {
|
|
||||||
sm: 'px-2 py-0.5 text-xs',
|
sm: 'px-2 py-0.5 text-xs',
|
||||||
md: 'px-3 py-1 text-sm',
|
md: 'px-3 py-1 text-sm',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const c = colorMap[color] ?? colorMap.accent;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className={`${sizeStyles[size]} rounded font-mono font-medium`}
|
className={`${sizeStyles[size]} rounded font-mono font-medium`}
|
||||||
style={{
|
style={{ background: c.bg, color: c.text }}
|
||||||
background: colorStyles[color].bg,
|
|
||||||
color: colorStyles[color].text,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTheme } from '../../contexts';
|
|
||||||
|
|
||||||
interface ButtonProps {
|
interface ButtonProps {
|
||||||
variant?: 'primary' | 'secondary';
|
variant?: 'primary' | 'secondary';
|
||||||
@@ -18,8 +17,6 @@ export const Button: React.FC<ButtonProps> = ({
|
|||||||
disabled = false,
|
disabled = false,
|
||||||
className = '',
|
className = '',
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
const baseStyles = `
|
const baseStyles = `
|
||||||
inline-flex items-center justify-center
|
inline-flex items-center justify-center
|
||||||
font-semibold rounded-xl cursor-pointer
|
font-semibold rounded-xl cursor-pointer
|
||||||
@@ -27,13 +24,13 @@ export const Button: React.FC<ButtonProps> = ({
|
|||||||
disabled:cursor-not-allowed disabled:opacity-50
|
disabled:cursor-not-allowed disabled:opacity-50
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const sizeStyles = {
|
const sizeStyles: Record<string, string> = {
|
||||||
sm: 'px-3 py-1.5 text-xs',
|
sm: 'px-3 py-1.5 text-xs',
|
||||||
md: 'px-5 py-3 text-sm',
|
md: 'px-5 py-3 text-sm',
|
||||||
lg: 'px-8 py-5 text-base',
|
lg: 'px-8 py-5 text-base',
|
||||||
};
|
};
|
||||||
|
|
||||||
const variantStyles = {
|
const variantStyles: Record<string, string> = {
|
||||||
primary: `
|
primary: `
|
||||||
bg-gradient-to-r from-t-accent to-t-accent-dark
|
bg-gradient-to-r from-t-accent to-t-accent-dark
|
||||||
text-white hover:shadow-t-accent hover:-translate-y-0.5
|
text-white hover:shadow-t-accent hover:-translate-y-0.5
|
||||||
@@ -50,9 +47,6 @@ export const Button: React.FC<ButtonProps> = ({
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={`${baseStyles} ${sizeStyles[size]} ${variantStyles[variant]} ${className}`}
|
className={`${baseStyles} ${sizeStyles[size]} ${variantStyles[variant]} ${className}`}
|
||||||
style={{
|
|
||||||
color: variant === 'primary' ? '#fff' : theme.text2,
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTheme } from '../../contexts';
|
|
||||||
|
|
||||||
interface CardProps {
|
interface CardProps {
|
||||||
accent?: boolean;
|
accent?: boolean;
|
||||||
@@ -18,9 +17,7 @@ export const Card: React.FC<CardProps> = ({
|
|||||||
className = '',
|
className = '',
|
||||||
onClick,
|
onClick,
|
||||||
}) => {
|
}) => {
|
||||||
const { theme, isDark } = useTheme();
|
const paddingStyles: Record<string, string> = {
|
||||||
|
|
||||||
const paddingStyles = {
|
|
||||||
sm: 'p-4',
|
sm: 'p-4',
|
||||||
md: 'p-5',
|
md: 'p-5',
|
||||||
lg: 'p-8',
|
lg: 'p-8',
|
||||||
@@ -30,22 +27,22 @@ export const Card: React.FC<CardProps> = ({
|
|||||||
<div
|
<div
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={`
|
className={`
|
||||||
rounded-xl border transition-all duration-200 cursor-default
|
relative rounded-xl border transition-all duration-200 cursor-default
|
||||||
${paddingStyles[padding]}
|
${paddingStyles[padding]}
|
||||||
${accent ? 'border-t-accent' : 'border-t-border'}
|
${accent ? 'border-[color:var(--accent)]' : 'border-[color:var(--border)]'}
|
||||||
${highlight ? 'border-2 border-t-accent' : ''}
|
${highlight ? 'border-2 border-[color:var(--accent)]' : ''}
|
||||||
${!isDark ? 'shadow-t-card' : ''}
|
${onClick ? 'cursor-pointer hover:border-[color:var(--accent)]' : ''}
|
||||||
${onClick ? 'cursor-pointer hover:border-t-accent' : ''}
|
|
||||||
${className}
|
${className}
|
||||||
`}
|
`}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.bgCard,
|
backgroundColor: 'var(--surface)',
|
||||||
|
boxShadow: 'var(--shadow-card)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{accent && (
|
{accent && (
|
||||||
<div
|
<div
|
||||||
className="absolute top-0 left-0 right-0 h-[3px] rounded-t-xl"
|
className="absolute top-0 left-0 right-0 h-[3px] rounded-t-xl"
|
||||||
style={{ background: theme.gradientAccent }}
|
style={{ background: 'linear-gradient(90deg, var(--accent), var(--accent-hover))' }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTheme } from '../../contexts';
|
|
||||||
|
|
||||||
interface InputProps {
|
interface InputProps {
|
||||||
value: string;
|
value: string;
|
||||||
@@ -18,8 +17,6 @@ export const Input: React.FC<InputProps> = ({
|
|||||||
className = '',
|
className = '',
|
||||||
type = 'text',
|
type = 'text',
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
@@ -28,17 +25,24 @@ export const Input: React.FC<InputProps> = ({
|
|||||||
onKeyDown={onKeyDown}
|
onKeyDown={onKeyDown}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
className={`
|
className={`
|
||||||
w-full px-4 py-3 text-sm
|
w-full px-4 py-3 text-sm rounded-lg outline-none
|
||||||
bg-t-bg-card border border-t-border rounded-lg
|
focus:ring-1
|
||||||
text-t-text outline-none
|
placeholder:text-[color:var(--muted)]
|
||||||
focus:border-t-accent focus:ring-1 focus:ring-t-accent
|
|
||||||
placeholder:text-t-text3
|
|
||||||
${className}
|
${className}
|
||||||
`}
|
`}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.bgCard,
|
backgroundColor: 'var(--surface)',
|
||||||
borderColor: theme.border,
|
borderColor: 'var(--border)',
|
||||||
color: theme.text,
|
border: '1px solid var(--border)',
|
||||||
|
color: 'var(--fg)',
|
||||||
|
}}
|
||||||
|
onFocus={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'var(--accent)';
|
||||||
|
e.currentTarget.style.boxShadow = '0 0 0 1px var(--accent)';
|
||||||
|
}}
|
||||||
|
onBlur={(e) => {
|
||||||
|
e.currentTarget.style.borderColor = 'var(--border)';
|
||||||
|
e.currentTarget.style.boxShadow = 'none';
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTheme } from '../../contexts';
|
|
||||||
|
|
||||||
interface ProgressBarProps {
|
interface ProgressBarProps {
|
||||||
percent: number;
|
percent: number;
|
||||||
@@ -7,36 +6,39 @@ interface ProgressBarProps {
|
|||||||
showLabel?: boolean;
|
showLabel?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const colorBg: Record<string, string> = {
|
||||||
|
accent: 'linear-gradient(90deg, var(--accent), var(--accent-hover))',
|
||||||
|
green: 'linear-gradient(90deg, var(--success), #00ff88)',
|
||||||
|
orange: 'linear-gradient(90deg, var(--warn), #ffaa00)',
|
||||||
|
red: 'var(--danger)',
|
||||||
|
};
|
||||||
|
|
||||||
export const ProgressBar: React.FC<ProgressBarProps> = ({
|
export const ProgressBar: React.FC<ProgressBarProps> = ({
|
||||||
percent,
|
percent,
|
||||||
color = 'accent',
|
color = 'accent',
|
||||||
showLabel = false,
|
showLabel = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
const colorStyles = {
|
|
||||||
accent: theme.gradientAccent,
|
|
||||||
green: `linear-gradient(90deg, ${theme.green}, #00ff88)`,
|
|
||||||
orange: `linear-gradient(90deg, ${theme.orange}, #ffaa00)`,
|
|
||||||
red: '#ff4444',
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div
|
<div
|
||||||
className="h-2 rounded-full flex-1"
|
className="h-2 rounded-full flex-1"
|
||||||
style={{ backgroundColor: theme.bgHover }}
|
style={{ backgroundColor: 'var(--border)' }}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-full rounded-full transition-all duration-300"
|
className="h-full rounded-full transition-all duration-300"
|
||||||
style={{
|
style={{
|
||||||
width: `${percent}%`,
|
width: `${percent}%`,
|
||||||
background: colorStyles[color],
|
background: colorBg[color] ?? colorBg.accent,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{showLabel && (
|
{showLabel && (
|
||||||
<span className="font-mono text-xs text-t-accent">{percent}%</span>
|
<span
|
||||||
|
className="font-mono text-xs"
|
||||||
|
style={{ color: 'var(--accent)' }}
|
||||||
|
>
|
||||||
|
{percent}%
|
||||||
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTheme } from '../../contexts';
|
|
||||||
|
|
||||||
interface ScoreBarProps {
|
interface ScoreBarProps {
|
||||||
score: number; // 0-100
|
score: number; // 0-100
|
||||||
@@ -12,27 +11,25 @@ export const ScoreBar: React.FC<ScoreBarProps> = ({
|
|||||||
label,
|
label,
|
||||||
accent = false,
|
accent = false,
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="p-5 rounded-xl border"
|
className="p-5 rounded-xl border"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: theme.bgCard,
|
backgroundColor: 'var(--surface)',
|
||||||
borderColor: accent ? theme.accent : theme.border,
|
borderColor: accent ? 'var(--accent)' : 'var(--border)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{label && (
|
{label && (
|
||||||
<div
|
<div
|
||||||
className="font-mono text-xs mb-2"
|
className="font-mono text-xs mb-2"
|
||||||
style={{ color: theme.text3, letterSpacing: '1px' }}
|
style={{ color: 'var(--muted)', letterSpacing: '1px' }}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className="font-mono text-3xl font-bold"
|
className="font-mono text-3xl font-bold"
|
||||||
style={{ color: accent ? theme.accent : theme.text }}
|
style={{ color: accent ? 'var(--accent)' : 'var(--fg)' }}
|
||||||
>
|
>
|
||||||
{score}
|
{score}
|
||||||
</div>
|
</div>
|
||||||
@@ -42,7 +39,7 @@ export const ScoreBar: React.FC<ScoreBarProps> = ({
|
|||||||
key={i}
|
key={i}
|
||||||
className="w-2 h-4 rounded-sm"
|
className="w-2 h-4 rounded-sm"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: i < score / 10 ? theme.accent : theme.bgHover,
|
backgroundColor: i < score / 10 ? 'var(--accent)' : 'var(--border)',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTheme } from '../../contexts';
|
|
||||||
import type { ComplianceChunk } from '../../types';
|
import type { ComplianceChunk } from '../../types';
|
||||||
|
|
||||||
interface ChatPanelProps {
|
interface ChatPanelProps {
|
||||||
@@ -29,7 +28,6 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
'法规的具体要求是什么?',
|
'法规的具体要求是什么?',
|
||||||
],
|
],
|
||||||
}) => {
|
}) => {
|
||||||
const { theme } = useTheme();
|
|
||||||
const activeChunk = chunks.find(c => c.id === activeChunkId);
|
const activeChunk = chunks.find(c => c.id === activeChunkId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -40,8 +38,8 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
top: 0,
|
top: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
width: 420,
|
width: 420,
|
||||||
background: theme.bgCard,
|
background: 'var(--surface)',
|
||||||
borderLeft: `1px solid ${theme.border}`,
|
borderLeft: '1px solid var(--border)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
zIndex: 50,
|
zIndex: 50,
|
||||||
@@ -51,14 +49,14 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
{/* Chat Header */}
|
{/* Chat Header */}
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '20px 24px',
|
padding: '20px 24px',
|
||||||
borderBottom: `1px solid ${theme.border}`,
|
borderBottom: '1px solid var(--border)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'space-between',
|
justifyContent: 'space-between',
|
||||||
}}>
|
}}>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ fontSize: 14, fontWeight: 600, color: theme.text }}>合规对话</div>
|
<div style={{ fontSize: 14, fontWeight: 600, color: 'var(--fg)' }}>合规对话</div>
|
||||||
<div className="mono" style={{ fontSize: 11, color: theme.text3 }}>
|
<div className="mono" style={{ fontSize: 11, color: 'var(--muted)' }}>
|
||||||
段落 #{activeChunk?.index} · {activeChunk?.regulations.length} 条法规
|
段落 #{activeChunk?.index} · {activeChunk?.regulations.length} 条法规
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -68,7 +66,7 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
background: theme.bgHover,
|
background: 'var(--rail-hover)',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@@ -77,7 +75,7 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
||||||
<path d="M18 6L6 18M6 6L18 18" stroke={theme.text3} strokeWidth="2" strokeLinecap="round"/>
|
<path d="M18 6L6 18M6 6L18 18" stroke="var(--muted)" strokeWidth="2" strokeLinecap="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,15 +83,15 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
{/* Current Chunk Info */}
|
{/* Current Chunk Info */}
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '16px 24px',
|
padding: '16px 24px',
|
||||||
background: theme.bgHover,
|
background: 'var(--rail-hover)',
|
||||||
borderBottom: `1px solid ${theme.border}`,
|
borderBottom: '1px solid var(--border)',
|
||||||
}}>
|
}}>
|
||||||
<div style={{ fontSize: 13, fontWeight: 500, marginBottom: 8, color: theme.text }}>
|
<div style={{ fontSize: 13, fontWeight: 500, marginBottom: 8, color: 'var(--fg)' }}>
|
||||||
{activeChunk?.intent}
|
{activeChunk?.intent}
|
||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: theme.text2,
|
color: 'var(--muted)',
|
||||||
lineHeight: 1.5,
|
lineHeight: 1.5,
|
||||||
maxHeight: 60,
|
maxHeight: 60,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
@@ -122,7 +120,7 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
background: theme.gradientAccent,
|
background: 'linear-gradient(90deg, var(--accent), var(--accent-hover))',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -136,13 +134,15 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
<div style={{
|
<div style={{
|
||||||
maxWidth: '80%',
|
maxWidth: '80%',
|
||||||
padding: '12px 16px',
|
padding: '12px 16px',
|
||||||
background: msg.role === 'user' ? theme.gradientAccent : theme.bgElevated,
|
background: msg.role === 'user'
|
||||||
|
? 'linear-gradient(90deg, var(--accent), var(--accent-hover))'
|
||||||
|
: 'var(--surface)',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
color: msg.role === 'user' ? '#fff' : theme.text,
|
color: msg.role === 'user' ? '#fff' : 'var(--fg)',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
lineHeight: 1.6,
|
lineHeight: 1.6,
|
||||||
whiteSpace: 'pre-wrap',
|
whiteSpace: 'pre-wrap',
|
||||||
border: msg.role === 'assistant' ? `1px solid ${theme.border}` : 'none',
|
border: msg.role === 'assistant' ? '1px solid var(--border)' : 'none',
|
||||||
}}>
|
}}>
|
||||||
{msg.content}
|
{msg.content}
|
||||||
</div>
|
</div>
|
||||||
@@ -154,7 +154,7 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
width: 32,
|
width: 32,
|
||||||
height: 32,
|
height: 32,
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
background: theme.gradientAccent,
|
background: 'linear-gradient(90deg, var(--accent), var(--accent-hover))',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
@@ -165,15 +165,15 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '12px 16px',
|
padding: '12px 16px',
|
||||||
background: theme.bgElevated,
|
background: 'var(--surface)',
|
||||||
borderRadius: 12,
|
borderRadius: 12,
|
||||||
border: `1px solid ${theme.border}`,
|
border: '1px solid var(--border)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
gap: 8,
|
gap: 8,
|
||||||
}}>
|
}}>
|
||||||
<div style={{ width: 6, height: 6, borderRadius: '50%', background: theme.accent, animation: 'pulse 1s infinite' }} />
|
<div style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--accent)', animation: 'pulse 1s infinite' }} />
|
||||||
<span style={{ fontSize: 13, color: theme.text2 }}>分析中...</span>
|
<span style={{ fontSize: 13, color: 'var(--muted)' }}>分析中...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -193,10 +193,10 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
style={{
|
style={{
|
||||||
padding: '6px 12px',
|
padding: '6px 12px',
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
background: theme.bgHover,
|
background: 'var(--rail-hover)',
|
||||||
border: `1px solid ${theme.border}`,
|
border: '1px solid var(--border)',
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
color: theme.text2,
|
color: 'var(--muted)',
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
}}
|
}}
|
||||||
>{q}</button>
|
>{q}</button>
|
||||||
@@ -206,7 +206,7 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
{/* Chat Input */}
|
{/* Chat Input */}
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: '16px 24px',
|
padding: '16px 24px',
|
||||||
borderTop: `1px solid ${theme.border}`,
|
borderTop: '1px solid var(--border)',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
gap: 12,
|
gap: 12,
|
||||||
}}>
|
}}>
|
||||||
@@ -219,10 +219,10 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
flex: 1,
|
flex: 1,
|
||||||
padding: 12,
|
padding: 12,
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
background: theme.bgHover,
|
background: 'var(--rail-hover)',
|
||||||
border: `1px solid ${theme.border}`,
|
border: '1px solid var(--border)',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
color: theme.text,
|
color: 'var(--fg)',
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -233,8 +233,10 @@ export const ChatPanel: React.FC<ChatPanelProps> = ({
|
|||||||
padding: '12px 20px',
|
padding: '12px 20px',
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
background: chatLoading || !chatInput.trim() ? theme.bgHover : theme.gradientAccent,
|
background: chatLoading || !chatInput.trim()
|
||||||
color: chatLoading || !chatInput.trim() ? theme.text3 : '#fff',
|
? 'var(--rail-hover)'
|
||||||
|
: 'linear-gradient(90deg, var(--accent), var(--accent-hover))',
|
||||||
|
color: chatLoading || !chatInput.trim() ? 'var(--muted)' : '#fff',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
cursor: chatLoading || !chatInput.trim() ? 'not-allowed' : 'pointer',
|
cursor: chatLoading || !chatInput.trim() ? 'not-allowed' : 'pointer',
|
||||||
|
|||||||
Reference in New Issue
Block a user