♻️ refactor: refactor static style (#11010)

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style

* refactor: refactor static style
This commit is contained in:
CanisMinor
2025-12-27 23:51:21 +08:00
committed by GitHub
parent ba4834ff84
commit d865e27d58
687 changed files with 6542 additions and 5726 deletions
@@ -0,0 +1,959 @@
# createStaticStyles 迁移指南
## 📖 概述
`createStaticStyles``antd-style` 提供的静态样式创建函数,相比 `createStyles`(hook 方案)具有零运行时开销的优势。样式在模块加载时计算一次,而不是每次组件渲染时计算。
## 🎯 适用场景
### ✅ 可以优化的场景
1. **纯静态样式**:不依赖运行时动态值
2. **使用标准 token**:所有 token 都在 `cssVar.json` 中有对应项
3. **简单的条件逻辑**:可以通过静态样式拆分处理
### ❌ 无法优化的场景
1. **JS 计算函数**`readableColor()`, `chroma()`, `mix()`, `calc()` 中使用 token 数值
2. **复杂的动态 props**:需要运行时计算的复杂逻辑
3. **动态 prefixCls**:需要运行时传入的类名前缀(但可以硬编码为 `'ant'`
## 🔄 基本转换步骤
### 1. 样式文件转换
**之前(createStyles):**
```typescript
import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css, token }) => {
return {
root: css`
color: ${token.colorText};
font-size: ${token.fontSize}px;
`,
};
});
```
**之后(createStaticStyles):**
```typescript
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => {
return {
root: css`
color: ${cssVar.colorText};
font-size: ${cssVar.fontSize};
`,
};
});
```
### 2. 组件文件转换
**之前:**
```typescript
import { useStyles } from './style';
const Component = () => {
const { styles, cx } = useStyles();
return <div className={cx(styles.root, className)} />;
};
```
**之后:**
```typescript
import { cx } from 'antd-style';
import { styles } from './style';
const Component = () => {
return <div className={cx(styles.root, className)} />;
};
```
## 🛠️ 常见场景处理
### 场景 1: Token 转换
**规则:**
- `token.xxx``cssVar.xxx`
- 注意:`cssVar.fontSize` 已经包含 `px` 单位,不需要再加 `px`
**示例:**
```typescript
// ❌ 错误
font-size: ${cssVar.fontSize}px; // cssVar.fontSize 已经是 "14px"
// ✅ 正确
font-size: ${cssVar.fontSize}; // 直接使用
```
**特殊情况 - calc ()**
```typescript
// ❌ 错误
calc(${token.fontSize}px * 2.5)
// ✅ 正确
calc(${cssVar.fontSize} * 2.5) // cssVar.fontSize 已经包含单位
```
### 场景 2: 动态 Props → CSS 变量
**适用:** 数值、字符串类型的 props
**步骤:**
1. 在样式文件中使用 CSS 变量(带默认值)
2. 在组件中通过 `style` prop 设置 CSS 变量
**示例:**
**样式文件:**
```typescript
export const styles = createStaticStyles(({ css }) => {
return {
root: css`
width: var(--component-size, 24px);
height: var(--component-size, 24px);
`,
};
});
```
**组件文件:**
```typescript
import { useMemo } from 'react';
const Component = ({ size = 24, style, ...rest }) => {
const cssVariables = useMemo<Record<string, string>>(
() => ({
'--component-size': `${size}px`,
}),
[size],
);
return (
<div
className={styles.root}
style={{
...cssVariables,
...style,
}}
{...rest}
/>
);
};
```
**已优化示例:**
- `Video`: `maxHeight`, `maxWidth`, `minHeight`, `minWidth`
- `ScrollShadow`: `size`
- `MaskShadow`: `size`
- `ColorSwatches`: `size`
- `Grid`: `rows`, `maxItemWidth`, `gap`
- `Layout`: `headerHeight`
- `Footer`: `contentMaxWidth`
### 场景 3: 布尔值 Props → 静态样式拆分
**适用:** 简单的布尔值 props(2-3 个)
**步骤:**
1. 创建所有可能的组合样式
2. 运行时使用 `cx` 组合
**示例:**
**样式文件:**
```typescript
export const styles = createStaticStyles(({ css }) => {
return {
root: css`
/* base styles */
`,
root_closable_true: css`
/* closable styles */
`,
root_closable_false: css`
/* no closable styles */
`,
root_hasTitle_true: css`
/* has title styles */
`,
root_hasTitle_false: css`
/* no title styles */
`,
};
});
```
**组件文件:**
```typescript
const Component = ({ closable, hasTitle }) => {
const className = cx(
styles.root,
styles[`root_closable_${!!closable}`],
styles[`root_hasTitle_${!!hasTitle}`],
);
return <div className={className} />;
};
```
**已优化示例:**
- `Alert`: `closable`, `hasTitle`, `showIcon` → 8 个组合(2×2×2
- `Image`: `alwaysShowActions` → 2 个样式
- `StoryBook`: `noPadding` → 2 个样式
### 场景 4: isDarkMode → 静态样式拆分
**适用:** 依赖 `isDarkMode` 的条件样式
**有两种处理方式:**
#### 方式 A: 直接条件选择(简单场景)
**步骤:**
1. 创建 `Dark``Light` 两个静态样式
2. 运行时根据 `theme.isDarkMode` 选择
**示例:**
**样式文件:**
```typescript
export const styles = createStaticStyles(({ css, cssVar }) => {
return {
rootDark: css`
background: ${cssVar.colorFillTertiary};
color: ${cssVar.colorTextLightSolid};
`,
rootLight: css`
background: ${cssVar.colorFillQuaternary};
color: ${cssVar.colorText};
`,
};
});
```
**组件文件:**
```typescript
import { useThemeMode } from 'antd-style';
const Component = () => {
const { isDarkMode } = useThemeMode();
return (
<div
className={cx(
isDarkMode ? styles.rootDark : styles.rootLight
)}
/>
);
};
```
#### 方式 B: 使用 cva 将 isDarkMode 作为 variant(推荐,适用于复杂场景)
**步骤:**
1. 创建 `Dark``Light` 两个静态样式
2.`cva` 中将 `isDarkMode` 作为 variant prop
3. 运行时直接传入 `isDarkMode`
**示例:**
**样式文件:**
```typescript
import { createStaticStyles } from 'antd-style';
import { cva } from 'class-variance-authority';
export const styles = createStaticStyles(({ css, cssVar }) => {
return {
filledDark: css`
background: ${cssVar.colorFillTertiary};
color: ${cssVar.colorTextLightSolid};
`,
filledLight: css`
background: ${cssVar.colorFillQuaternary};
color: ${cssVar.colorText};
`,
outlined: css`
border: 1px solid ${cssVar.colorBorder};
`,
root: css`
/* base styles */
`,
};
});
export const variants = cva(styles.root, {
defaultVariants: {
isDarkMode: false,
variant: 'filled',
},
variants: {
isDarkMode: {
false: null,
true: null, // isDarkMode 本身不添加样式,通过 compoundVariants 组合
},
variant: {
filled: null, // variant 本身不添加样式,通过 compoundVariants 组合
outlined: styles.outlined,
},
},
compoundVariants: [
{
class: styles.filledDark,
isDarkMode: true,
variant: 'filled',
},
{
class: styles.filledLight,
isDarkMode: false,
variant: 'filled',
},
],
});
```
**组件文件:**
```typescript
import { useThemeMode } from 'antd-style';
import { variants } from './style';
const Component = ({ variant = 'filled' }) => {
const { isDarkMode } = useThemeMode();
return (
<div
className={variants({ isDarkMode, variant })}
/>
);
};
```
**优势:**
- ✅ 不需要 `useMemo` 动态创建 variants
- ✅ 更符合 `cva` 的设计理念
- ✅ 代码更简洁,性能更好
- ✅ 类型安全,IDE 自动补全
**已优化示例:**
- `TypewriterEffect`: `textDark` / `textLight`(方式 A
- `Collapse`: `filledDark` / `filledLight`(可优化为方式 B
- `Hotkey`: `inverseThemeDark` / `inverseThemeLight`(可优化为方式 B
- `GuideCard`: `filledDark` / `filledLight`(可优化为方式 B
- `GradientButton`: `buttonDark` / `buttonLight`(方式 A
### 场景 5: responsive → 静态 responsive
**适用:** 使用响应式断点
**步骤:**
1. 导入静态 `responsive` from `antd-style`
2. 使用 `responsive.sm` 替代 `responsive.mobile`
3.`createStyles` 参数中移除 `responsive`
**示例:**
**之前:**
```typescript
import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css, responsive }) => ({
root: css`
${responsive.mobile} {
padding: 12px;
}
`,
}));
```
**之后:**
```typescript
import { createStaticStyles } from 'antd-style';
import { responsive } from 'antd-style';
export const styles = createStaticStyles(({ css }) => ({
root: css`
${responsive.sm} {
padding: 12px;
}
`,
}));
```
**注意:**
- `responsive.mobile``responsive.sm`
- 静态 `responsive` 提供:`xs`, `sm`, `md`, `lg`, `xl`, `xxl`
**已优化示例:**
- `Header`: `responsive.mobile``responsive.sm`
- `FormModal`: `responsive.mobile``responsive.sm`
- `Hero`: `responsive.mobile``responsive.sm`
### 场景 6: stylish → lobeStaticStylish
**适用:** 使用自定义 `stylish` 工具
**步骤:**
1. 导入 `lobeStaticStylish` from `@/styles`
2. 替换 `stylish.xxx``lobeStaticStylish.xxx`
**示例:**
**之前:**
```typescript
import { createStyles } from 'antd-style';
export const useStyles = createStyles(({ css, stylish }) => ({
root: css`
${stylish.blur};
${stylish.variantFilled};
`,
}));
```
**之后:**
```typescript
import { createStaticStyles } from 'antd-style';
import { lobeStaticStylish } from '@/styles';
export const styles = createStaticStyles(({ css }) => ({
root: css`
${lobeStaticStylish.blur};
${lobeStaticStylish.variantFilled};
`,
}));
```
**已优化示例:**
- `Button`: `stylish.blur``lobeStaticStylish.blur`
- `Hero`: `stylish.gradientAnimation``lobeStaticStylish.gradientAnimation`
### 场景 7: prefixCls → 硬编码
**适用:** 使用动态 `prefixCls` 参数
**步骤:**
1. 在文件顶部硬编码 `const prefixCls = 'ant'`
2.`createStyles` 参数中移除 `prefixCls`
**示例:**
**之前:**
```typescript
export const useStyles = createStyles(({ css }, prefixCls: string) => ({
root: css`
.${prefixCls}-button {
/* styles */
}
`,
}));
```
**之后:**
```typescript
const prefixCls = 'ant';
export const styles = createStaticStyles(({ css }) => ({
root: css`
.${prefixCls}-button {
/* styles */
}
`,
}));
```
**已优化示例:**
- `Alert`, `Collapse`, `FormModal`, `Image`, `Burger`, `DraggablePanel`, `DraggableSideNav`, `Toc`, `ColorSwatches`, `EmojiPicker`, `Form`, `awesome/Features`
### 场景 8: readableColor () → Token 替换
**适用:** 使用 `readableColor()` 计算对比色
**规则:**
- `readableColor(token.colorPrimary)``cssVar.colorTextLightSolid`(主色背景用白色文字)
- `readableColor(token.colorTextQuaternary)``cssVar.colorText`(浅色背景用深色文字)
**示例:**
**之前:**
```typescript
import { readableColor } from 'polished';
export const useStyles = createStyles(({ css, token }) => ({
checked: css`
background-color: ${token.colorPrimary};
color: ${readableColor(token.colorPrimary)};
`,
}));
```
**之后:**
```typescript
export const styles = createStaticStyles(({ css, cssVar }) => ({
checked: css`
background-color: ${cssVar.colorPrimary};
color: ${cssVar.colorTextLightSolid};
`,
}));
```
**已优化示例:**
- `Checkbox`: `readableColor(token.colorPrimary)``cssVar.colorTextLightSolid`
### 场景 9: rgba () → color-mix ()
**适用:** 使用 `rgba()` 设置透明度
**步骤:**
1. 使用 CSS 原生的 `color-mix()` 函数
2. 格式:`color-mix(in srgb, ${cssVar.xxx} alpha%, transparent)`
**示例:**
**之前:**
```typescript
import { rgba } from 'polished';
export const useStyles = createStyles(({ css, token }) => ({
root: css`
background-color: ${rgba(token.colorBgLayout, 0.4)};
`,
}));
```
**之后:**
```typescript
export const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
background-color: color-mix(in srgb, ${cssVar.colorBgLayout} 40%, transparent);
`,
}));
```
**已优化示例:**
- `Header`: `rgba(cssVar.colorBgLayout, 0.4)``color-mix(...)`
- `FormModal`: `rgba(cssVar.colorBgContainer, 0)``color-mix(...)`
### 场景 10: keyframes → css
**适用:** 使用 `keyframes` 创建动画
**步骤:**
1.`createStaticStyles` 外部定义 `keyframes`
2. 在样式内部使用
**示例:**
**之前:**
```typescript
export const useStyles = createStyles(({ css, keyframes }) => {
const spin = keyframes`
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
`;
return {
icon: css`
animation: ${spin} 1s linear infinite;
`,
};
});
```
**之后:**
```typescript
import { keyframes } from 'antd-style';
const spin = keyframes`
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
`;
export const styles = createStaticStyles(({ css }) => ({
icon: css`
animation: ${spin} 1s linear infinite;
`,
}));
```
**已优化示例:**
- `Icon`: `keyframes` 动画
- `Skeleton`: `keyframes` shimmer 动画
## ⚠️ 反模式:避免使用 createVariants (isDarkMode)
**不推荐的做法:**
```typescript
// ❌ 不推荐:在组件中动态创建 variants
export const createVariants = (isDarkMode: boolean) =>
cva(styles.root, {
variants: {
variant: {
filled: isDarkMode ? styles.filledDark : styles.filledLight,
},
},
});
// 组件中
const variants = useMemo(() => createVariants(isDarkMode), [isDarkMode]);
```
**推荐的做法:**
`isDarkMode` 作为 `cva` 的 variant prop(见场景 4 方式 B),这样:
- ✅ 不需要 `useMemo` 动态创建
- ✅ 更符合 `cva` 的设计理念
- ✅ 代码更简洁,性能更好
- ✅ 类型安全,IDE 自动补全
```typescript
// ✅ 推荐:将 isDarkMode 作为 variant prop
export const variants = cva(styles.root, {
variants: {
isDarkMode: {
false: null,
true: null,
},
variant: {
filled: null,
},
},
compoundVariants: [
{
class: styles.filledDark,
isDarkMode: true,
variant: 'filled',
},
{
class: styles.filledLight,
isDarkMode: false,
variant: 'filled',
},
],
});
// 组件中
const { isDarkMode } = useThemeMode();
const className = variants({ isDarkMode, variant: 'filled' });
```
## ⚠️ 无法优化的场景
### 1. JS 计算函数
**无法优化:**
- `chroma()` - 颜色计算库
- `readableColor()` - 需要运行时计算(但可以用 token 替代)
- `mix()` - 颜色混合计算
- `calc()` 中使用 token 数值进行复杂计算
**示例:**
```typescript
// ❌ 无法优化
const scale = chroma.bezier([token.colorText, backgroundColor]).scale().colors(6);
```
### 2. 复杂的动态 Props
**无法优化:**
- 需要复杂计算的 props
- 对象 / 数组类型的 props
- 函数类型的 props
### 3. useTheme Hook
**无法优化:**
- 直接使用 `useTheme()` hook 获取运行时值
- 例如:`awesome/Giscus/style.ts` 使用 `useTheme()` 获取主题值
## 📋 迁移检查清单
### 样式文件检查
- [ ] `createStyles``createStaticStyles`
- [ ] `token.xxx``cssVar.xxx`
- [ ] 移除 `px` 后缀(`cssVar` 已包含单位)
- [ ] `responsive.mobile``responsive.sm`(如果使用)
- [ ] `stylish.xxx``lobeStaticStylish.xxx`(如果使用)
- [ ] `rgba()``color-mix()`(如果使用)
- [ ] `readableColor()` → token 替换(如果使用)
- [ ] `prefixCls` 参数 → 硬编码 `const prefixCls = 'ant'`(如果使用)
- [ ] `isDarkMode` → 静态样式拆分(如果使用)
- [ ] 动态 props → CSS 变量(如果使用)
### 组件文件检查
- [ ] `useStyles()``import { styles } from './style'`
- [ ] `import { cx } from 'antd-style'`(如果需要)
- [ ] `import { useTheme } from 'antd-style'`(如果需要 `theme.isDarkMode`
- [ ] 动态 props → CSS 变量设置(如果使用)
- [ ] `isDarkMode` 条件 → `theme.isDarkMode` 判断(如果使用)
## 🎯 优化优先级
### 高优先级(简单优化)
1. ✅ 纯静态样式(无动态 props)
2.`isDarkMode` 拆分
3.`responsive.mobile``responsive.sm`
4.`stylish``lobeStaticStylish`
5.`readableColor()` → token 替换
### 中优先级(需要转换)
6. ✅ 简单的动态 props → CSS 变量(1-2 个)
7. ✅ 布尔值 props → 静态样式拆分(2-3 个)
### 低优先级(复杂优化)
8. ⚠️ 多个动态 props → CSS 变量(3+ 个)
9. ⚠️ 复杂的条件逻辑拆分
## 📚 参考示例
### 完整示例 1: 简单组件
**样式文件:**
```typescript
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
padding: ${cssVar.padding};
color: ${cssVar.colorText};
border-radius: ${cssVar.borderRadius};
`,
}));
```
**组件文件:**
```typescript
import { cx } from 'antd-style';
import { styles } from './style';
const Component = ({ className }) => {
return <div className={cx(styles.root, className)} />;
};
```
### 完整示例 2: 带动态 Props
**样式文件:**
```typescript
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
width: var(--component-size, 24px);
height: var(--component-size, 24px);
background: ${cssVar.colorBgContainer};
`,
}));
```
**组件文件:**
```typescript
import { cx } from 'antd-style';
import { useMemo } from 'react';
import { styles } from './style';
const Component = ({ size = 24, className, style, ...rest }) => {
const cssVariables = useMemo<Record<string, string>>(
() => ({
'--component-size': `${size}px`,
}),
[size],
);
return (
<div
className={cx(styles.root, className)}
style={{
...cssVariables,
...style,
}}
{...rest}
/>
);
};
```
### 完整示例 3: 带 isDarkMode
**样式文件:**
```typescript
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
rootDark: css`
background: ${cssVar.colorFillTertiary};
color: ${cssVar.colorTextLightSolid};
`,
rootLight: css`
background: ${cssVar.colorFillQuaternary};
color: ${cssVar.colorText};
`,
}));
```
**组件文件:**
```typescript
import { cx, useTheme } from 'antd-style';
import { styles } from './style';
const Component = ({ className }) => {
const { theme } = useTheme();
return (
<div
className={cx(
theme.isDarkMode ? styles.rootDark : styles.rootLight,
className
)}
/>
);
};
```
## 🔍 验证步骤
1. **类型检查:** `pnpm run type-check`
2. **运行时测试:** 确保视觉效果一致
3. **性能验证:** 检查样式计算是否在模块加载时完成
## 📊 优化效果
-**零运行时开销**:样式在模块加载时计算一次
-**减少重新渲染**:组件不再依赖样式 hook
-**更好的性能**:减少每次渲染的计算开销
-**代码更简洁**:直接导入样式对象
## 🔧 场景 11: useTheme () → useThemeMode () /cssVar
**适用:** 组件中只使用 `theme.isDarkMode` 或其他 token 值
**规则:**
- 如果只使用 `theme.isDarkMode`,使用 `const { isDarkMode } = useThemeMode()` 替代
- 如果使用其他 token(如 `theme.colorText`, `theme.borderRadius` 等),使用 `cssVar` 替代
- `useThemeMode()``useTheme()` 更轻量,只返回 `isDarkMode`
**示例:**
**之前:**
```typescript
import { useTheme } from 'antd-style';
const Component = () => {
const theme = useTheme();
return (
<div className={theme.isDarkMode ? styles.dark : styles.light}>
{theme.colorText}
</div>
);
};
```
**之后:**
```typescript
import { cssVar, useThemeMode } from 'antd-style';
const Component = () => {
const { isDarkMode } = useThemeMode();
return (
<div className={isDarkMode ? styles.dark : styles.light}>
{cssVar.colorText}
</div>
);
};
```
**已优化示例:**
- `AuroraBackground`, `Select`, `Input`, `Button`, `DatePicker`, `AutoComplete`, `InputNumber`, `InputPassword`, `InputOPT`, `TextArea`, `SpotlightCardItem`, `Spotlight`, `HotkeyInput` - 只使用 `isDarkMode``useThemeMode()`
- `Image`, `GradientButton`, `Empty`, `FileTypeIcon`, `FormSubmitFooter`, `CodeEditor`, `LobeChat`, `Drawer`, `Modal`, `Avatar`, `AvatarGroup`, `SkeletonAvatar`, `SkeletonButton`, `SkeletonTags`, `Callout`, `LobeHub`, `GridBackground`, `FolderIcon`, `FileIcon`, `TokenTag`, `ChatSendButton`, `AvatarUploader` - 使用 token → `cssVar`
**无法优化的文件(需要保留 `useTheme()`):**
- `useMermaid`, `useStreamMermaid`, `useHighlight`, `useStreamHighlight` - 需要完整的 theme 对象传给第三方库
- `Alert`, `Tag`, `Menu`, `EmojiPicker` - 需要实际颜色值传给颜色计算函数
- `SkeletonTitle`, `SkeletonTags` - 需要数值进行数学运算
- `GridShowcase`, `GridBackground/demos` - 需要实际颜色值传给 `rgba()` 函数
- `CustomFonts` - 需要实际字符串值进行字符串拼接
- `Giscus/style.ts` - 需要实际颜色值传给 `readableColor()``rgba()` 函数(其他 token 已优化为 `cssVar`
**注意事项:**
- `useThemeMode()` 只返回 `{ isDarkMode }`,不返回完整的 theme 对象
- `cssVar` 的值是字符串(如 `"14px"`, `"#ffffff"`),可以直接在 JSX 中使用
- 如果 token 需要用于数值计算(如 `Math.round(theme.fontSize * 1.5)`),需要保留 `useTheme()`
## 🎉 总结
`createStaticStyles` 迁移是一个渐进式的优化过程。对于简单的静态样式,可以直接转换;对于复杂的动态场景,需要根据具体情况选择合适的优化策略。关键是要理解每种场景的处理方式,并灵活运用 CSS 变量、静态样式拆分等技术。
### useTheme () 优化总结
-**使用 `useThemeMode()`**:当组件只使用 `theme.isDarkMode`
-**使用 `cssVar`**:当组件使用其他 token 值(颜色、尺寸等)时
- ⚠️ **保留 `useTheme()`**:当 token 需要用于数值计算或传给第三方库时
+9 -6
View File
@@ -132,6 +132,8 @@
]
},
"overrides": {
"@lobehub/ui": "^4.3.11",
"antd-style": "^4.1.0",
"stylelint-config-clean-order": "7.0.0"
},
"dependencies": {
@@ -195,15 +197,15 @@
"@lobechat/utils": "workspace:*",
"@lobechat/web-crawler": "workspace:*",
"@lobehub/analytics": "^1.6.0",
"@lobehub/charts": "^4.0.0",
"@lobehub/charts": "^4.0.2",
"@lobehub/chat-plugin-sdk": "^1.32.4",
"@lobehub/chat-plugins-gateway": "^1.9.0",
"@lobehub/desktop-ipc-typings": "workspace:*",
"@lobehub/editor": "^3.1.1",
"@lobehub/icons": "^4.0.0",
"@lobehub/editor": "^3.2.1",
"@lobehub/icons": "^4.0.2",
"@lobehub/market-sdk": "^0.25.0",
"@lobehub/tts": "^4.0.0",
"@lobehub/ui": "^4.3.1",
"@lobehub/tts": "^4.0.2",
"@lobehub/ui": "^4.3.11",
"@modelcontextprotocol/sdk": "^1.25.1",
"@neondatabase/serverless": "^1.0.2",
"@next/third-parties": "^16.1.0",
@@ -232,13 +234,14 @@
"@zumer/snapdom": "^1.9.14",
"ahooks": "^3.9.6",
"antd": "^6.1.1",
"antd-style": "^3.7.1",
"antd-style": "^4.1.0",
"async-retry": "^1.3.3",
"bcryptjs": "^3.0.3",
"better-auth": "^1.4.7",
"better-auth-harmony": "^1.2.5",
"brotli-wasm": "^3.0.1",
"chroma-js": "^3.2.0",
"class-variance-authority": "^0.7.1",
"cmdk": "^1.1.1",
"cookie": "^1.1.1",
"countries-and-timezones": "^3.8.0",
@@ -1,45 +1,47 @@
import { BuiltinRenderProps } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { CheckCircle, FileText } from 'lucide-react';
import { memo } from 'react';
import type { UpdatePromptParams, UpdatePromptState } from '../../types';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
font-size: 13px;
`,
fileIcon: css`
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
promptCard: css`
background: ${token.colorFillTertiary};
border-left: 3px solid ${token.colorSuccess};
margin-left: 12px;
margin-inline-start: 12px;
padding: 12px;
border-inline-start: 3px solid ${cssVar.colorSuccess};
background: ${cssVar.colorFillTertiary};
`,
promptContent: css`
color: ${token.colorText};
overflow: auto;
max-height: 200px;
margin-inline: -12px;
margin-inline-start: 20px;
padding-inline: 12px;
font-size: 13px;
line-height: 1.6;
margin-left: 20px;
max-height: 200px;
overflow: auto;
white-space: pre-wrap;
color: ${cssVar.colorText};
word-break: break-word;
margin-inline: -12px;
padding-inline: 12px;
white-space: pre-wrap;
`,
promptLabel: css`
color: ${token.colorTextSecondary};
font-size: 12px;
font-weight: 500;
color: ${cssVar.colorTextSecondary};
`,
statusRow: css`
color: ${token.colorSuccess};
margin-left: 9px;
margin-bottom: 6px;
margin-block-end: 6px;
margin-inline-start: 9px;
color: ${cssVar.colorSuccess};
`,
statusText: css`
font-weight: 500;
@@ -49,7 +51,6 @@ const useStyles = createStyles(({ css, token }) => ({
const UpdatePrompt = memo<BuiltinRenderProps<UpdatePromptParams, UpdatePromptState>>(
({ pluginState }) => {
const { newPrompt } = pluginState || {};
const { styles } = useStyles();
return (
<Flexbox className={styles.container} gap={8}>
@@ -1,9 +1,9 @@
'use client';
import { BuiltinInterventionProps } from '@lobechat/types';
import { Avatar, Flexbox , Tooltip } from '@lobehub/ui';
import { Avatar, Flexbox, Tooltip } from '@lobehub/ui';
import { Input, InputNumber } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { Clock } from 'lucide-react';
import { ChangeEvent, memo, useCallback, useEffect, useState } from 'react';
@@ -14,11 +14,11 @@ import { agentGroupSelectors } from '@/store/agentGroup/selectors';
import type { ExecuteTaskParams } from '../../types';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
agentCard: css`
padding: 4px;
border-radius: ${token.borderRadius}px;
background: ${token.colorFillTertiary};
border-radius: ${cssVar.borderRadius};
background: ${cssVar.colorFillTertiary};
`,
agentDescription: css`
overflow: hidden;
@@ -28,16 +28,16 @@ const useStyles = createStyles(({ css, token }) => ({
font-size: 12px;
line-height: 1.4;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
agentTitle: css`
font-size: 14px;
font-weight: 600;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
container: css`
padding-block: 12px;
border-radius: ${token.borderRadius}px;
border-radius: ${cssVar.borderRadius};
`,
header: css`
font-size: 14px;
@@ -50,11 +50,11 @@ const useStyles = createStyles(({ css, token }) => ({
width: 32px;
height: 32px;
border-radius: ${token.borderRadius}px;
border-radius: ${cssVar.borderRadius};
color: ${token.colorPrimary};
color: ${cssVar.colorPrimary};
background: ${token.colorPrimaryBg};
background: ${cssVar.colorPrimaryBg};
`,
timeoutInput: css`
width: 100px;
@@ -70,7 +70,6 @@ const DEFAULT_TIMEOUT = 1_800_000; // 30 minutes
*/
const ExecuteTaskIntervention = memo<BuiltinInterventionProps<ExecuteTaskParams>>(
({ args, onArgsChange, registerBeforeApprove }) => {
const { styles } = useStyles();
const { t } = useTranslation('tool');
// Get agent info from store
@@ -2,7 +2,7 @@
import { BuiltinRenderProps } from '@lobechat/types';
import { Avatar, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { Clock } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -12,23 +12,23 @@ import { agentGroupSelectors } from '@/store/agentGroup/selectors';
import type { ExecuteTaskParams, ExecuteTaskState } from '../../../types';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
agentTitle: css`
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
container: css`
padding-block: 12px;
border-radius: ${token.borderRadius}px;
border-radius: ${cssVar.borderRadius};
`,
taskContent: css`
padding-block: 8px;
padding-inline: 12px;
border-radius: ${token.borderRadius}px;
background: ${token.colorFillTertiary};
border-radius: ${cssVar.borderRadius};
background: ${cssVar.colorFillTertiary};
`,
timeout: css`
font-size: 12px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
}));
@@ -38,7 +38,6 @@ const useStyles = createStyles(({ css, token }) => ({
*/
const ExecuteTaskRender = memo<BuiltinRenderProps<ExecuteTaskParams, ExecuteTaskState>>(
({ args }) => {
const { styles } = useStyles();
const { t } = useTranslation('tool');
// Get agent info from store
@@ -3,29 +3,29 @@
import { BuiltinInterventionProps } from '@lobechat/types';
import { Flexbox } from '@lobehub/ui';
import { Radio, RadioChangeEvent } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { Trash2 } from 'lucide-react';
import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import type { ClearTodosParams } from '../../types';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
padding: 12px;
border-radius: ${token.borderRadius}px;
background: ${token.colorFillTertiary};
border-radius: ${cssVar.borderRadius};
background: ${cssVar.colorFillTertiary};
`,
dangerText: css`
font-size: 13px;
color: ${token.colorError};
color: ${cssVar.colorError};
`,
header: css`
color: ${token.colorWarning};
color: ${cssVar.colorWarning};
`,
label: css`
font-size: 13px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
normalText: css`
font-size: 13px;
@@ -38,7 +38,6 @@ const useStyles = createStyles(({ css, token }) => ({
*/
const ClearTodosIntervention = memo<BuiltinInterventionProps<ClearTodosParams>>(
({ args, onArgsChange }) => {
const { styles } = useStyles();
const { t } = useTranslation('tool');
const [mode, setMode] = useState<ClearTodosParams['mode']>(args?.mode || 'completed');
@@ -2,7 +2,7 @@
import { type BuiltinRenderProps } from '@lobechat/types';
import { Block, Checkbox } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { memo } from 'react';
import type { TodoItem, TodoList as TodoListType } from '../../types';
@@ -12,19 +12,19 @@ export interface TodoListRenderState {
}
// Styles matching TodoItemRow in SortableTodoList
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
itemRow: css`
width: 100%;
padding-block: 10px;
padding-inline: 12px;
border-block-end: 1px dashed ${token.colorBorderSecondary};
border-block-end: 1px dashed ${cssVar.colorBorderSecondary};
&:last-child {
border-block-end: none;
}
`,
textChecked: css`
color: ${token.colorTextQuaternary};
color: ${cssVar.colorTextQuaternary};
text-decoration: line-through;
`,
}));
@@ -38,11 +38,9 @@ interface ReadOnlyTodoItemProps {
* Read-only todo item row, matching the style of TodoItemRow in SortableTodoList
*/
const ReadOnlyTodoItem = memo<ReadOnlyTodoItemProps>(({ text, completed }) => {
const { styles, theme } = useStyles();
return (
<Checkbox
backgroundColor={theme.colorSuccess}
backgroundColor={cssVar.colorSuccess}
checked={completed}
classNames={{ text: completed ? styles.textChecked : undefined, wrapper: styles.itemRow }}
shape={'circle'}
@@ -2,14 +2,14 @@
import { ActionIcon, Checkbox, Flexbox, Input } from '@lobehub/ui';
import { InputRef } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import { Plus } from 'lucide-react';
import { ChangeEvent, KeyboardEvent, memo, useCallback, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { ADD_ITEM_ID, useTodoListStore } from './store';
const useStyles = createStyles(({ css }) => ({
const styles = createStaticStyles(({ css }) => ({
addRow: css`
padding-block: 10px;
padding-inline: 12px;
@@ -26,7 +26,6 @@ interface AddItemRowProps {
}
const AddItemRow = memo<AddItemRowProps>(({ placeholder, showDragHandle = true, className }) => {
const { styles, cx } = useStyles();
const { t } = useTranslation('tool');
const inputRef = useRef<InputRef>(null);
const defaultPlaceholder = placeholder || t('lobe-gtd.addTodo.placeholder');
@@ -2,14 +2,14 @@
import { ActionIcon, Checkbox, Flexbox, SortableList } from '@lobehub/ui';
import { Input, InputRef } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar, cx } from 'antd-style';
import { Trash2 } from 'lucide-react';
import { ChangeEvent, KeyboardEvent, memo, useCallback, useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { useTodoListStore } from './store';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
deleteIcon: css`
flex-shrink: 0;
opacity: 0;
@@ -25,7 +25,7 @@ const useStyles = createStyles(({ css, token }) => ({
width: 100%;
padding-block: 10px;
padding-inline: 4px 12px;
border-block-end: 1px dashed ${token.colorBorderSecondary};
border-block-end: 1px dashed ${cssVar.colorBorderSecondary};
&:hover {
.drag-handle,
@@ -35,7 +35,7 @@ const useStyles = createStyles(({ css, token }) => ({
}
`,
textChecked: css`
color: ${token.colorTextQuaternary};
color: ${cssVar.colorTextQuaternary};
text-decoration: line-through;
`,
}));
@@ -46,7 +46,6 @@ interface TodoItemRowProps {
}
const TodoItemRow = memo<TodoItemRowProps>(({ id, placeholder }) => {
const { styles, cx, theme } = useStyles();
const { t } = useTranslation('tool');
const inputRef = useRef<InputRef>(null);
const defaultPlaceholder = placeholder || t('lobe-gtd.todoItem.placeholder');
@@ -125,7 +124,7 @@ const TodoItemRow = memo<TodoItemRowProps>(({ id, placeholder }) => {
<Flexbox align="center" className={styles.itemRow} gap={4} horizontal width="100%">
<SortableList.DragHandle className={cx(styles.dragHandle, 'drag-handle')} size="small" />
<Checkbox
backgroundColor={theme.colorSuccess}
backgroundColor={cssVar.colorSuccess}
checked={completed}
onChange={handleToggle}
shape={'circle'}
@@ -2,70 +2,68 @@
import { Alert, Flexbox, Text } from '@lobehub/ui';
import { Descriptions } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { ComponentType, memo } from 'react';
import { FileContentDetail } from '../../../types';
const useStyles = createStyles(({ token, css }) => {
return {
cardBody: css`
padding-block: 12px 8px;
padding-inline: 16px;
`,
container: css`
overflow: hidden;
const styles = createStaticStyles(({ css, cssVar }) => ({
cardBody: css`
padding-block: 12px 8px;
padding-inline: 16px;
`,
container: css`
overflow: hidden;
min-width: 360px;
max-width: 360px;
border: 1px solid ${token.colorBorderSecondary};
border-radius: 12px;
`,
description: css`
margin-block: 0 4px !important;
color: ${token.colorTextTertiary};
`,
footer: css`
padding-block: 8px;
padding-inline: 16px;
border-radius: 8px;
min-width: 360px;
max-width: 360px;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: 12px;
`,
description: css`
margin-block: 0 4px !important;
color: ${cssVar.colorTextTertiary};
`,
footer: css`
padding-block: 8px;
padding-inline: 16px;
border-radius: 8px;
text-align: center;
text-align: center;
background-color: ${token.colorFillQuaternary};
`,
footerText: css`
font-size: 12px !important;
color: ${token.colorTextTertiary} !important;
`,
icon: css`
color: ${token.colorTextSecondary};
`,
preview: css`
overflow: hidden;
background-color: ${cssVar.colorFillQuaternary};
`,
footerText: css`
font-size: 12px !important;
color: ${cssVar.colorTextTertiary} !important;
`,
icon: css`
color: ${cssVar.colorTextSecondary};
`,
preview: css`
overflow: hidden;
max-height: 80px;
padding: 8px;
border-radius: 6px;
max-height: 80px;
padding: 8px;
border-radius: 6px;
font-family: ${token.fontFamilyCode};
font-size: 12px;
line-height: 1.5;
color: ${token.colorTextSecondary};
`,
title: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
font-family: ${cssVar.fontFamilyCode};
font-size: 12px;
line-height: 1.5;
color: ${cssVar.colorTextSecondary};
`,
title: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
margin-block-end: 0;
`,
titleRow: css`
color: ${token.colorText};
`,
};
});
margin-block-end: 0;
`,
titleRow: css`
color: ${cssVar.colorText};
`,
}));
interface FileCardProps {
FileIcon: ComponentType<{ fileName: string; size: number }>;
@@ -77,8 +75,6 @@ interface FileCardProps {
}
const FileCard = memo<FileCardProps>(({ file, FileIcon, labels }) => {
const { styles } = useStyles();
if (file.error) {
return (
<Flexbox className={styles.container} gap={8}>
@@ -2,9 +2,10 @@
import { FileSearchResult } from '@lobechat/types';
import { Center, Flexbox, Text, Tooltip } from '@lobehub/ui';
import { cx, useThemeMode } from 'antd-style';
import { ComponentType, memo } from 'react';
import { useStyles } from './style';
import { styles } from './style';
export interface FileItemProps extends FileSearchResult {
FileIcon: ComponentType<{
@@ -19,7 +20,7 @@ export interface FileItemProps extends FileSearchResult {
const FileItem = memo<FileItemProps>(
({ fileId, fileName, relevanceScore, topChunks, FileIcon, isMobile, onFileClick }) => {
const { styles, cx } = useStyles();
const { isDarkMode } = useThemeMode();
// Use the first chunk for preview
const firstChunk = topChunks[0];
@@ -27,7 +28,11 @@ const FileItem = memo<FileItemProps>(
return (
<Flexbox
align={'center'}
className={cx(styles.container, isMobile && styles.mobile)}
className={cx(
styles.container,
isDarkMode ? styles.containerDark : styles.containerLight,
isMobile && styles.mobile,
)}
gap={4}
horizontal
key={fileId}
@@ -1,7 +1,6 @@
import { createStyles } from 'antd-style';
import { lighten } from 'polished';
import { createStaticStyles } from 'antd-style';
export const useStyles = createStyles(({ css, token, isDarkMode }) => ({
export const styles = createStaticStyles(({ css, cssVar }) => ({
badge: css`
padding-block: 4px;
padding-inline: 6px;
@@ -9,9 +8,9 @@ export const useStyles = createStyles(({ css, token, isDarkMode }) => ({
font-size: 12px;
line-height: 12px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
background: ${token.colorFillSecondary};
background: ${cssVar.colorFillSecondary};
`,
container: css`
@@ -23,16 +22,25 @@ export const useStyles = createStyles(({ css, token, isDarkMode }) => ({
padding-inline-end: 12px;
border-radius: 8px;
color: ${token.colorText};
color: ${cssVar.colorText};
background: ${lighten(0.1, token.colorBgElevated)};
box-shadow: ${token.boxShadowTertiary};
background: color-mix(in srgb, ${cssVar.colorBgElevated} 90%, white);
box-shadow: ${cssVar.boxShadowTertiary};
transition: all 0.2s;
&:hover {
background: ${isDarkMode ? lighten(0.15, token.colorBgElevated) : ''};
box-shadow: ${token.boxShadowSecondary};
box-shadow: ${cssVar.boxShadowSecondary};
}
`,
containerDark: css`
&:hover {
background: color-mix(in srgb, ${cssVar.colorBgElevated} 85%, white);
}
`,
containerLight: css`
&:hover {
background: color-mix(in srgb, ${cssVar.colorBgElevated} 90%, white);
}
`,
filename: css`
@@ -1,7 +1,7 @@
'use client';
import { Flexbox, Tag, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { FileText, NotebookText } from 'lucide-react';
import { memo } from 'react';
@@ -9,54 +9,51 @@ import { useChatStore } from '@/store/chat';
import { NotebookDocument } from '../../../types';
const useStyles = createStyles(({ token, css }) => {
return {
container: css`
cursor: pointer;
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
cursor: pointer;
overflow: hidden;
overflow: hidden;
width: 100%;
padding-block: 12px;
padding-inline: 12px;
border: 1px solid ${token.colorBorderSecondary};
border-radius: 8px;
width: 100%;
padding-block: 12px;
padding-inline: 12px;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: 8px;
background: ${token.colorBgElevated};
background: ${cssVar.colorBgElevated};
&:hover {
background: ${token.colorFillSecondary};
}
`,
description: css`
font-size: 12px;
line-height: 1.5;
color: ${token.colorTextSecondary};
`,
icon: css`
color: ${token.colorPrimary};
`,
title: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
&:hover {
background: ${cssVar.colorFillSecondary};
}
`,
description: css`
font-size: 12px;
line-height: 1.5;
color: ${cssVar.colorTextSecondary};
`,
icon: css`
color: ${cssVar.colorPrimary};
`,
title: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
font-weight: 500;
color: ${token.colorText};
`,
typeTag: css`
font-size: 11px;
`,
};
});
font-weight: 500;
color: ${cssVar.colorText};
`,
typeTag: css`
font-size: 11px;
`,
}));
interface DocumentCardProps {
document: NotebookDocument;
}
const DocumentCard = memo<DocumentCardProps>(({ document }) => {
const { styles } = useStyles();
const openDocument = useChatStore((s) => s.openDocument);
const handleClick = () => {
@@ -1,18 +1,19 @@
'use client';
import type { BuiltinInspectorProps } from '@lobechat/types';
import { createStyles } from 'antd-style';
import { rgba } from 'polished';
import { createStaticStyles, cx } from 'antd-style';
import { memo } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { shinyTextStyles } from '@/styles';
import type { EditTitleArgs, EditTitleState } from '../../../types';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
highlight: css`
padding-block-end: 1px;
color: ${token.colorText};
background: linear-gradient(to top, ${token.gold3} 40%, transparent 40%);
color: ${cssVar.colorText};
background: linear-gradient(to top, ${cssVar.gold3} 40%, transparent 40%);
`,
root: css`
overflow: hidden;
@@ -20,43 +21,18 @@ const useStyles = createStyles(({ css, token }) => ({
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
color: ${token.colorTextSecondary};
`,
shinyText: css`
color: ${rgba(token.colorText, 0.45)};
background: linear-gradient(
120deg,
${rgba(token.colorTextBase, 0)} 40%,
${token.colorTextSecondary} 50%,
${rgba(token.colorTextBase, 0)} 60%
);
background-clip: text;
background-size: 200% 100%;
animation: shine 1.5s linear infinite;
@keyframes shine {
0% {
background-position: 100%;
}
100% {
background-position: -100%;
}
}
color: ${cssVar.colorTextSecondary};
`,
}));
export const EditTitleInspector = memo<BuiltinInspectorProps<EditTitleArgs, EditTitleState>>(
({ args, partialArgs, isArgumentsStreaming }) => {
const { t } = useTranslation('plugin');
const { styles, cx } = useStyles();
const title = args?.title || partialArgs?.title;
return (
<div className={cx(styles.root, isArgumentsStreaming && styles.shinyText)}>
<div className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}>
{title ? (
<Trans
components={{ title: <span className={styles.highlight} /> }}
@@ -1,52 +1,28 @@
'use client';
import type { BuiltinInspectorProps } from '@lobechat/types';
import { createStyles } from 'antd-style';
import { rgba } from 'polished';
import { createStaticStyles, cx } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
const useStyles = createStyles(({ css, token }) => ({
import { shinyTextStyles } from '@/styles';
const styles = createStaticStyles(({ css, cssVar }) => ({
root: css`
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
color: ${token.colorTextDescription};
`,
shinyText: css`
color: ${rgba(token.colorText, 0.45)};
background: linear-gradient(
120deg,
${rgba(token.colorTextBase, 0)} 40%,
${token.colorTextSecondary} 50%,
${rgba(token.colorTextBase, 0)} 60%
);
background-clip: text;
background-size: 200% 100%;
animation: shine 1.5s linear infinite;
@keyframes shine {
0% {
background-position: 100%;
}
100% {
background-position: -100%;
}
}
color: ${cssVar.colorTextDescription};
`,
}));
export const GetPageContentInspector = memo<BuiltinInspectorProps>(({ isArgumentsStreaming }) => {
const { t } = useTranslation('plugin');
const { styles, cx } = useStyles();
return (
<div className={cx(styles.root, isArgumentsStreaming && styles.shinyText)}>
<div className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-page-agent.apiName.getPageContent')}</span>
</div>
);
@@ -2,23 +2,24 @@
import type { BuiltinInspectorProps } from '@lobechat/types';
import { Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import { Plus } from 'lucide-react';
import { rgba } from 'polished';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { shinyTextStyles } from '@/styles';
import type { InitDocumentArgs, InitDocumentState } from '../../../types';
import { AnimatedNumber } from '../../components/AnimatedNumber';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
chars: css`
font-family: ${token.fontFamilyCode};
color: ${token.colorTextDescription};
font-family: ${cssVar.fontFamilyCode};
color: ${cssVar.colorTextDescription};
`,
lines: css`
font-family: ${token.fontFamilyCode};
color: ${token.colorSuccess};
font-family: ${cssVar.fontFamilyCode};
color: ${cssVar.colorSuccess};
`,
root: css`
overflow: hidden;
@@ -26,40 +27,15 @@ const useStyles = createStyles(({ css, token }) => ({
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
`,
shinyText: css`
color: ${rgba(token.colorText, 0.45)};
background: linear-gradient(
120deg,
${rgba(token.colorTextBase, 0)} 40%,
${token.colorTextSecondary} 50%,
${rgba(token.colorTextBase, 0)} 60%
);
background-clip: text;
background-size: 200% 100%;
animation: shine 1.5s linear infinite;
@keyframes shine {
0% {
background-position: 100%;
}
100% {
background-position: -100%;
}
}
`,
title: css`
margin-inline-end: 8px;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
}));
export const InitPageInspector = memo<BuiltinInspectorProps<InitDocumentArgs, InitDocumentState>>(
({ args, partialArgs, isArgumentsStreaming, pluginState }) => {
const { t } = useTranslation('plugin');
const { styles, cx } = useStyles();
// Calculate lines and chars from markdown content
const markdown = args?.markdown || partialArgs?.markdown || '';
@@ -73,14 +49,14 @@ export const InitPageInspector = memo<BuiltinInspectorProps<InitDocumentArgs, In
// During streaming without content, show init
if (isArgumentsStreaming && !hasContent) {
return (
<div className={cx(styles.root, styles.shinyText)}>
<div className={cx(styles.root, shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-page-agent.apiName.initPage')}</span>
</div>
);
}
return (
<div className={cx(styles.root, isArgumentsStreaming && styles.shinyText)}>
<div className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}>
<span className={styles.title}>
{t('builtins.lobe-page-agent.apiName.initPage.result')}
</span>
@@ -2,26 +2,27 @@
import type { BuiltinInspectorProps } from '@lobechat/types';
import { Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import { DiffIcon, Minus, Plus } from 'lucide-react';
import { rgba } from 'polished';
import { type ReactNode, memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { shinyTextStyles } from '@/styles';
import type { ModifyNodesArgs, ModifyNodesState } from '../../../types';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
insert: css`
font-family: ${token.fontFamilyCode};
color: ${token.colorSuccess};
font-family: ${cssVar.fontFamilyCode};
color: ${cssVar.colorSuccess};
`,
modify: css`
font-family: ${token.fontFamilyCode};
color: ${token.colorWarning};
font-family: ${cssVar.fontFamilyCode};
color: ${cssVar.colorWarning};
`,
remove: css`
font-family: ${token.fontFamilyCode};
color: ${token.colorError};
font-family: ${cssVar.fontFamilyCode};
color: ${cssVar.colorError};
`,
root: css`
overflow: hidden;
@@ -31,42 +32,17 @@ const useStyles = createStyles(({ css, token }) => ({
`,
separator: css`
margin-inline: 2px;
color: ${token.colorTextQuaternary};
`,
shinyText: css`
color: ${rgba(token.colorText, 0.45)};
background: linear-gradient(
120deg,
${rgba(token.colorTextBase, 0)} 40%,
${token.colorTextSecondary} 50%,
${rgba(token.colorTextBase, 0)} 60%
);
background-clip: text;
background-size: 200% 100%;
animation: shine 1.5s linear infinite;
@keyframes shine {
0% {
background-position: 100%;
}
100% {
background-position: -100%;
}
}
color: ${cssVar.colorTextQuaternary};
`,
title: css`
margin-inline-end: 8px;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
}));
export const ModifyNodesInspector = memo<BuiltinInspectorProps<ModifyNodesArgs, ModifyNodesState>>(
({ args, partialArgs, isArgumentsStreaming }) => {
const { t } = useTranslation('plugin');
const { styles, cx } = useStyles();
// Count operations by type
const counts = useMemo(() => {
@@ -101,7 +77,7 @@ export const ModifyNodesInspector = memo<BuiltinInspectorProps<ModifyNodesArgs,
// During streaming without operations yet, show init message
if (isArgumentsStreaming && !hasOperations) {
return (
<div className={cx(styles.root, styles.shinyText)}>
<div className={cx(styles.root, shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-page-agent.apiName.modifyNodes.init')}</span>
</div>
);
@@ -135,7 +111,7 @@ export const ModifyNodesInspector = memo<BuiltinInspectorProps<ModifyNodesArgs,
}
return (
<div className={cx(styles.root, isArgumentsStreaming && styles.shinyText)}>
<div className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}>
<span className={styles.title}>{t('builtins.lobe-page-agent.apiName.modifyNodes')}</span>
{statsParts.length > 0 && (
<>
@@ -2,27 +2,28 @@
import type { BuiltinInspectorProps } from '@lobechat/types';
import { Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import { ArrowRight } from 'lucide-react';
import { rgba } from 'polished';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { shinyTextStyles } from '@/styles';
import type { ReplaceTextArgs, ReplaceTextState } from '../../../types';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
arrow: css`
margin-inline: 4px;
color: ${token.colorTextQuaternary};
color: ${cssVar.colorTextQuaternary};
`,
from: css`
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
text-decoration: line-through;
`,
highlight: css`
padding-block-end: 1px;
color: ${token.colorText};
background: linear-gradient(to top, ${token.gold3} 40%, transparent 40%);
color: ${cssVar.colorText};
background: linear-gradient(to top, ${cssVar.gold3} 40%, transparent 40%);
`,
root: css`
overflow: hidden;
@@ -30,46 +31,21 @@ const useStyles = createStyles(({ css, token }) => ({
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
color: ${token.colorTextSecondary};
`,
shinyText: css`
color: ${rgba(token.colorText, 0.45)};
background: linear-gradient(
120deg,
${rgba(token.colorTextBase, 0)} 40%,
${token.colorTextSecondary} 50%,
${rgba(token.colorTextBase, 0)} 60%
);
background-clip: text;
background-size: 200% 100%;
animation: shine 1.5s linear infinite;
@keyframes shine {
0% {
background-position: 100%;
}
100% {
background-position: -100%;
}
}
color: ${cssVar.colorTextSecondary};
`,
stats: css`
font-family: ${token.fontFamilyCode};
color: ${token.colorTextSecondary};
font-family: ${cssVar.fontFamilyCode};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin-inline-end: 8px;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
}));
export const ReplaceTextInspector = memo<BuiltinInspectorProps<ReplaceTextArgs, ReplaceTextState>>(
({ args, partialArgs, isArgumentsStreaming, pluginState }) => {
const { t } = useTranslation('plugin');
const { styles, cx } = useStyles();
const from = args?.searchText || partialArgs?.searchText;
const to = args?.newText ?? partialArgs?.newText;
@@ -77,7 +53,7 @@ export const ReplaceTextInspector = memo<BuiltinInspectorProps<ReplaceTextArgs,
// During streaming without searchText yet, show init message
if (isArgumentsStreaming && !from) {
return (
<div className={cx(styles.root, styles.shinyText)}>
<div className={cx(styles.root, shinyTextStyles.shinyText)}>
<span>{t('builtins.lobe-page-agent.apiName.replaceText.init')}</span>
</div>
);
@@ -87,7 +63,7 @@ export const ReplaceTextInspector = memo<BuiltinInspectorProps<ReplaceTextArgs,
const hasResult = from && to !== undefined;
return (
<div className={cx(styles.root, isArgumentsStreaming && styles.shinyText)}>
<div className={cx(styles.root, isArgumentsStreaming && shinyTextStyles.shinyText)}>
<span className={styles.title}>{t('builtins.lobe-page-agent.apiName.replaceText')}</span>
{hasResult && (
<>
+10 -26
View File
@@ -2,33 +2,22 @@
import { Center, Flexbox, Text } from '@lobehub/ui';
import { Divider } from 'antd';
import { useTheme } from 'antd-style';
import { type PropsWithChildren, memo } from 'react';
import { cx, useThemeMode } from 'antd-style';
import type { FC, PropsWithChildren } from 'react';
import { ProductLogo } from '@/components/Branding';
import LangButton from '@/features/User/UserPanel/LangButton';
import ThemeButton from '@/features/User/UserPanel/ThemeButton';
const AuthContainer = memo(({ children }: PropsWithChildren) => {
const theme = useTheme();
import { styles } from './style';
const AuthContainer: FC<PropsWithChildren> = ({ children }) => {
const { isDarkMode } = useThemeMode();
return (
<Flexbox
height={'100%'}
padding={8}
style={{
position: 'relative',
}}
width={'100%'}
>
<Flexbox className={styles.outerContainer} height={'100%'} padding={8} width={'100%'}>
<Flexbox
className={cx(isDarkMode ? styles.innerContainerDark : styles.innerContainerLight)}
height={'100%'}
style={{
background: theme.colorBgContainer,
border: `1px solid ${theme.isDarkMode ? theme.colorBorderSecondary : theme.colorBorder}`,
borderRadius: theme.borderRadius,
overflow: 'hidden',
position: 'relative',
}}
width={'100%'}
>
<Flexbox
@@ -42,12 +31,7 @@ const AuthContainer = memo(({ children }: PropsWithChildren) => {
<ProductLogo size={40} />
<Flexbox align={'center'} horizontal>
<LangButton placement={'bottomRight'} size={18} />
<Divider
style={{
height: 24,
}}
type={'vertical'}
/>
<Divider className={styles.divider} orientation={'vertical'} />
<ThemeButton placement={'bottomRight'} size={18} />
</Flexbox>
</Flexbox>
@@ -62,7 +46,7 @@ const AuthContainer = memo(({ children }: PropsWithChildren) => {
</Flexbox>
</Flexbox>
);
});
};
AuthContainer.displayName = 'AuthContainer';
@@ -0,0 +1,47 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
// Divider 样式
divider: css`
height: 24px;
`,
// 内层容器 - 深色模式
innerContainerDark: css`
position: relative;
overflow: hidden;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadius};
background: ${cssVar.colorBgContainer};
`,
// 内层容器 - 浅色模式
innerContainerLight: css`
position: relative;
overflow: hidden;
border: 1px solid ${cssVar.colorBorder};
border-radius: ${cssVar.borderRadius};
background: ${cssVar.colorBgContainer};
`,
// 外层容器
outerContainer: css`
position: relative;
`,
}));
@@ -5,7 +5,7 @@ import { DOCUMENTS_REFER_URL, PRIVACY_URL, TERMS_URL } from '@lobechat/const';
import { Button, Skeleton, Text } from '@lobehub/ui';
import { LobeHub } from '@lobehub/ui/brand';
import { Col, Flex, Row } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { AuthError } from 'next-auth';
import { signIn } from 'next-auth/react';
import { useRouter, useSearchParams } from 'next/navigation';
@@ -16,15 +16,15 @@ import BrandWatermark from '@/components/BrandWatermark';
import AuthIcons from '@/components/NextAuth/AuthIcons';
import { useUserStore } from '@/store/user';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
button: css`
text-transform: capitalize;
`,
container: css`
min-width: 360px;
border: 1px solid ${token.colorBorder};
border-radius: ${token.borderRadiusLG}px;
background: ${token.colorBgContainer};
border: 1px solid ${cssVar.colorBorder};
border-radius: ${cssVar.borderRadiusLG}px;
background: ${cssVar.colorBgContainer};
`,
contentCard: css`
padding-block: 2.5rem;
@@ -32,23 +32,23 @@ const useStyles = createStyles(({ css, token }) => ({
`,
description: css`
margin: 0;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
footer: css`
padding: 1rem;
border-block-start: 1px solid ${token.colorBorder};
border-block-start: 1px solid ${cssVar.colorBorder};
border-radius: 0 0 8px 8px;
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
background: ${token.colorBgElevated};
background: ${cssVar.colorBgElevated};
`,
text: css`
text-align: center;
`,
title: css`
margin: 0;
color: ${token.colorTextHeading};
color: ${cssVar.colorTextHeading};
`,
}));
@@ -68,7 +68,6 @@ const BtnListLoading = memo(() => {
* ref: https://authjs.dev/guides/pages/signin
*/
export default memo(() => {
const { styles } = useStyles();
const { t } = useTranslation('clerk');
const router = useRouter();
const [loadingProvider, setLoadingProvider] = useState<string | null>(null);
@@ -1,11 +1,11 @@
import { Avatar, Center, Flexbox, FluentEmoji, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { Link2Icon } from 'lucide-react';
import React, { memo } from 'react';
import { ProductLogo } from '@/components/Branding';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
connector: css`
width: 40px;
height: 40px;
@@ -18,7 +18,7 @@ const useStyles = createStyles(({ css, token }) => ({
connectorLine: css`
width: 24px;
height: 1px;
background-color: ${token.colorBorderSecondary};
background-color: ${cssVar.colorBorderSecondary};
@media (max-width: 768px) {
width: 24px;
@@ -35,7 +35,6 @@ interface OAuthApplicationLogoProps {
const OAuthApplicationLogo = memo<OAuthApplicationLogoProps>(
({ isFirstParty, clientDisplayName, logoUrl, size = 72 }) => {
const { styles, theme } = useStyles();
return isFirstParty ? (
<Avatar alt={clientDisplayName} avatar={logoUrl!} shape={'square'} size={size} />
) : (
@@ -47,7 +46,7 @@ const OAuthApplicationLogo = memo<OAuthApplicationLogoProps>(
)}
<div className={styles.connectorLine} />
<Center className={styles.connector}>
<Icon icon={Link2Icon} style={{ color: theme.colorTextSecondary, fontSize: 20 }} />
<Icon icon={Link2Icon} style={{ color: cssVar.colorTextSecondary, fontSize: 20 }} />
</Center>
<div className={styles.connectorLine} />
<ProductLogo size={size} />
@@ -1,7 +1,7 @@
import { Button, Icon, InputPassword, Text } from '@lobehub/ui';
import { Form } from 'antd';
import type { FormInstance, InputRef } from 'antd';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { ChevronLeft, ChevronRight, Lock } from 'lucide-react';
import { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next';
@@ -25,7 +25,6 @@ export const SignInPasswordStep = ({
onForgotPassword,
onSubmit,
}: SignInPasswordStepProps) => {
const theme = useTheme();
const { t } = useTranslation('auth');
const passwordInputRef = useRef<InputRef>(null);
@@ -90,7 +89,7 @@ export const SignInPasswordStep = ({
icon={ChevronRight}
loading={loading}
onClick={() => form.submit()}
style={{ color: theme.colorPrimary }}
style={{ color: cssVar.colorPrimary }}
title={t('betterAuth.signin.submit')}
variant={'filled'}
/>
@@ -1,40 +1,50 @@
import { Flexbox } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { type FC, type PropsWithChildren } from 'react';
import { cssVar, useThemeMode } from 'antd-style';
import { type FC, type PropsWithChildren, useMemo } from 'react';
import { isDesktop } from '@/const/version';
import { useGlobalStore } from '@/store/global';
import { systemStatusSelectors } from '@/store/global/selectors';
import { styles } from './DesktopLayoutContainer/style';
const DesktopLayoutContainer: FC<PropsWithChildren> = ({ children }) => {
const theme = useTheme();
const { isDarkMode } = useThemeMode();
const [expand] = useGlobalStore((s) => [systemStatusSelectors.showLeftPanel(s)]);
// CSS 变量用于动态样式
const outerCssVariables = useMemo<Record<string, string>>(
() => ({
'--container-padding-left': expand ? '0px' : '8px',
'--container-padding-top': isDesktop ? '0px' : '8px',
}),
[expand, isDesktop],
);
const innerCssVariables = useMemo<Record<string, string>>(() => {
const borderRadius =
typeof window !== 'undefined' && (window.lobeEnv?.darwinMajorVersion ?? 0) >= 25
? '12px'
: cssVar.borderRadius;
return {
'--container-border-color': isDarkMode ? cssVar.colorBorderSecondary : cssVar.colorBorder,
'--container-border-radius': borderRadius,
};
}, [isDarkMode]);
return (
<Flexbox
className={styles.outerContainer}
height={'100%'}
padding={8}
style={{
overflow: 'hidden',
paddingLeft: expand ? 0 : 8,
paddingTop: isDesktop ? 0 : 8,
position: 'relative',
}}
style={outerCssVariables}
width={'100%'}
>
<Flexbox
className={styles.innerContainer}
height={'100%'}
style={{
background: theme.colorBgContainer,
border: `1px solid ${theme.isDarkMode ? theme.colorBorderSecondary : theme.colorBorder}`,
borderRadius:
// macOS 26's darwin is 25
typeof window !== 'undefined' && (window.lobeEnv?.darwinMajorVersion ?? 0) >= 25
? 12
: theme.borderRadius,
overflow: 'hidden',
position: 'relative',
}}
style={innerCssVariables}
width={'100%'}
>
{children}
@@ -0,0 +1,27 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
// 内层容器
innerContainer: css`
position: relative;
overflow: hidden;
border: 1px solid var(--container-border-color, ${cssVar.colorBorder});
border-radius: var(--container-border-radius, ${cssVar.borderRadius});
background: ${cssVar.colorBgContainer};
`,
// 外层容器
outerContainer: css`
position: relative;
overflow: hidden;
padding-block-start: var(--container-padding-top, 8px);
padding-inline-start: var(--container-padding-left, 8px);
background: ${cssVar.colorBgLayout};
`,
}));
+3 -6
View File
@@ -2,7 +2,7 @@
import { ENABLE_BUSINESS_FEATURES } from '@lobechat/business-const';
import { Flexbox } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cx } from 'antd-style';
import dynamic from 'next/dynamic';
import { type FC, Suspense } from 'react';
import { HotkeysProvider } from 'react-hotkeys-hook';
@@ -28,12 +28,12 @@ import DesktopHomeLayout from '../home/_layout';
import DesktopAutoOidcOnFirstOpen from './DesktopAutoOidcOnFirstOpen';
import DesktopLayoutContainer from './DesktopLayoutContainer';
import RegisterHotkeys from './RegisterHotkeys';
import { styles } from './style';
const CloudBanner = dynamic(() => import('@/features/AlertBanner/CloudBanner'));
const Layout: FC = () => {
const { isPWA } = usePlatform();
const theme = useTheme();
const { showCloudPromotion } = useServerConfigStore(featureFlagsSelectors);
const { isChecking, loadingComponent } = useMainLayoutRenderingChecking();
@@ -51,6 +51,7 @@ const Layout: FC = () => {
</Suspense>
<DndContextWrapper>
<Flexbox
className={cx(isPWA ? styles.mainContainerPWA : styles.mainContainer)}
height={
isDesktop
? `calc(100% - ${TITLE_BAR_HEIGHT}px)`
@@ -59,10 +60,6 @@ const Layout: FC = () => {
: '100%'
}
horizontal
style={{
borderTop: isPWA ? `1px solid ${theme.colorBorder}` : undefined,
position: 'relative',
}}
width={'100%'}
>
<NavPanel />
@@ -0,0 +1,14 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
// 主容器 - 非 PWA 模式(无顶部边框)
mainContainer: css`
position: relative;
`,
// 主容器 - PWA 模式(带顶部边框)
mainContainerPWA: css`
position: relative;
border-block-start: 1px solid ${cssVar.colorBorder};
`,
}));
@@ -1,5 +1,5 @@
import { ActionIcon, Dropdown, Flexbox, Icon, Skeleton, Tag } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { MessageSquareDashed, Star } from 'lucide-react';
import { Suspense, memo, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -26,8 +26,6 @@ interface TopicItemProps {
const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) => {
const { t } = useTranslation('topic');
const theme = useTheme();
const openTopicInNewWindow = useGlobalStore((s) => s.openTopicInNewWindow);
const activeAgentId = useAgentStore((s) => s.activeAgentId);
@@ -69,7 +67,9 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
return (
<NavItem
active={active && !isInAgentSubRoute}
icon={<Icon color={theme.colorTextDescription} icon={MessageSquareDashed} size={'small'} />}
icon={
<Icon color={cssVar.colorTextDescription} icon={MessageSquareDashed} size={'small'} />
}
loading={isLoading}
onClick={handleClick}
title={
@@ -78,7 +78,7 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
<Tag
size={'small'}
style={{
color: theme.colorTextDescription,
color: cssVar.colorTextDescription,
fontSize: 10,
}}
>
@@ -104,8 +104,8 @@ const TopicItem = memo<TopicItemProps>(({ id, title, fav, active, threadId }) =>
disabled={editing}
icon={
<ActionIcon
color={fav ? theme.colorWarning : undefined}
fill={fav ? theme.colorWarning : 'transparent'}
color={fav ? cssVar.colorWarning : undefined}
fill={fav ? cssVar.colorWarning : 'transparent'}
icon={Star}
onClick={(e) => {
e.stopPropagation();
@@ -1,6 +1,6 @@
import { Dropdown, Icon } from '@lobehub/ui';
import { TreeDownRightIcon } from '@lobehub/ui/icons';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { memo, useCallback } from 'react';
import NavItem from '@/features/NavPanel/components/NavItem';
@@ -18,7 +18,6 @@ export interface ThreadItemProps {
}
const ThreadItem = memo<ThreadItemProps>(({ title, id }) => {
const theme = useTheme();
const [editing, activeThreadId] = useChatStore((s) => [
s.threadRenamingId === id,
s.activeThreadId,
@@ -57,7 +56,9 @@ const ThreadItem = memo<ThreadItemProps>(({ title, id }) => {
actions={<Actions dropdownMenu={dropdownMenu} />}
active={active && !isInAgentSubRoute}
disabled={editing}
icon={<Icon color={theme.colorTextDescription} icon={TreeDownRightIcon} size={'small'} />}
icon={
<Icon color={cssVar.colorTextDescription} icon={TreeDownRightIcon} size={'small'} />
}
onClick={handleClick}
title={title}
/>
@@ -1,5 +1,4 @@
import { Flexbox } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { type FC } from 'react';
import { Outlet } from 'react-router-dom';
@@ -10,23 +9,15 @@ import { useInitAgentConfig } from '@/hooks/useInitAgentConfig';
import RegisterHotkeys from './RegisterHotkeys';
import Sidebar from './Sidebar';
import { styles } from './style';
const Layout: FC = () => {
const theme = useTheme();
useInitAgentConfig();
return (
<>
<Sidebar />
<Flexbox
flex={1}
height={'100%'}
style={{
background: theme.colorBgContainer,
overflow: 'hidden',
position: 'relative',
}}
>
<Flexbox className={styles.mainContainer} flex={1} height={'100%'}>
<Outlet />
</Flexbox>
{/* ↓ cloud slot ↓ */}
@@ -0,0 +1,10 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
// 主容器
mainContainer: css`
position: relative;
overflow: hidden;
background: ${cssVar.colorBgContainer};
`,
}));
@@ -1,7 +1,7 @@
'use client';
import { Block, Flexbox } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles , responsive } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -9,13 +9,13 @@ import { useConversationStore } from '@/features/Conversation';
// import { useSend } from '../../features/ChatInput/useSend';
const useStyles = createStyles(({ css, token, responsive }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
card: css`
padding-block: 8px;
padding-inline: 16px;
border-radius: 48px;
${responsive.mobile} {
${responsive.sm} {
padding-block: 8px;
padding-inline: 16px;
}
@@ -27,7 +27,7 @@ const useStyles = createStyles(({ css, token, responsive }) => ({
`,
title: css`
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
}));
@@ -40,8 +40,6 @@ const OpeningQuestions = memo<OpeningQuestionsProps>(({ mobile, questions }) =>
const { t } = useTranslation('welcome');
const [sendMessage] = useConversationStore((s) => [s.sendMessage]);
const { styles } = useStyles();
return (
<div className={styles.container}>
<p className={styles.title}>{t('guide.questions.title')}</p>
@@ -3,7 +3,7 @@
import { KLAVIS_SERVER_TYPES, type KlavisServerType } from '@lobechat/const';
import { Alert, Avatar, Button, Flexbox, Icon, Text } from '@lobehub/ui';
import { Divider } from 'antd';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { PlusIcon } from 'lucide-react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
@@ -55,7 +55,6 @@ interface KlavisToolAuthItemProps {
const KlavisToolAuthItem = memo<KlavisToolAuthItemProps>(({ tool, onAuthComplete }) => {
const { t } = useTranslation('chat');
const theme = useTheme();
const [isConnecting, setIsConnecting] = useState(false);
const [isWaitingAuth, setIsWaitingAuth] = useState(false);
@@ -196,7 +195,7 @@ const KlavisToolAuthItem = memo<KlavisToolAuthItemProps>(({ tool, onAuthComplete
if (typeof tool.icon === 'string') {
return <Avatar alt={tool.label} avatar={tool.icon} size={20} style={{ flex: 'none' }} />;
}
return <Icon fill={theme.colorText} icon={tool.icon} size={20} />;
return <Icon fill={cssVar.colorText} icon={tool.icon} size={20} />;
};
const isLoading = isConnecting || isWaitingAuth;
@@ -1,7 +1,7 @@
'use client';
import { Flexbox } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { memo } from 'react';
import NavHeader from '@/features/NavHeader';
@@ -12,16 +12,15 @@ import ShareButton from './ShareButton';
import Tags from './Tags';
const Header = memo(() => {
const theme = useTheme();
return (
<NavHeader
left={
<Flexbox style={{ backgroundColor: theme.colorBgContainer }}>
<Flexbox style={{ backgroundColor: cssVar.colorBgContainer }}>
<Tags />
</Flexbox>
}
right={
<Flexbox horizontal style={{ backgroundColor: theme.colorBgContainer }}>
<Flexbox horizontal style={{ backgroundColor: cssVar.colorBgContainer }}>
<WideScreenButton />
<NotebookButton />
<ShareButton />
@@ -1,21 +1,20 @@
'use client';
import { Flexbox, Modal } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import { type ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { PortalContent } from '@/features/Portal/router';
import { useChatStore } from '@/store/chat';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
container: css`
background: linear-gradient(${token.colorBgElevated}, ${token.colorBgContainer}) !important;
background: linear-gradient(${cssVar.colorBgElevated}, ${cssVar.colorBgContainer}) !important;
`,
}));
const Layout = () => {
const { styles, cx } = useStyles();
const [showMobilePortal, isPortalThread, togglePortal] = useChatStore((s) => [
s.showPortal,
!!s.portalThreadId,
@@ -24,12 +23,7 @@ const Layout = () => {
const { t } = useTranslation('portal');
const renderBody = (body: ReactNode) => (
<Flexbox
gap={8}
height={'calc(100% - 52px)'}
padding={'0 8px'}
style={{ overflow: 'hidden' }}
>
<Flexbox gap={8} height={'calc(100% - 52px)'} padding={'0 8px'} style={{ overflow: 'hidden' }}>
<Flexbox
height={'100%'}
style={{ marginInline: -8, overflow: 'hidden', position: 'relative' }}
@@ -2,7 +2,7 @@
import { DraggablePanel, DraggablePanelContainer, type DraggablePanelProps } from '@lobehub/ui';
import { Flexbox } from '@lobehub/ui';
import { createStyles, useResponsive } from 'antd-style';
import { createStaticStyles, useResponsive } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { type PropsWithChildren, memo, useState } from 'react';
@@ -16,7 +16,7 @@ import { chatPortalSelectors, portalThreadSelectors } from '@/store/chat/selecto
import { useGlobalStore } from '@/store/global';
import { systemStatusSelectors } from '@/store/global/selectors';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
content: css`
display: flex;
flex-direction: column;
@@ -25,17 +25,16 @@ const useStyles = createStyles(({ css, token }) => ({
drawer: css`
z-index: 10;
height: 100%;
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
panel: css`
overflow: hidden;
height: 100%;
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
}));
const PortalPanel = memo(({ children }: PropsWithChildren) => {
const { styles } = useStyles();
const { md = true } = useResponsive();
const [showPortal, showToolUI, showArtifactUI, showThread] = useChatStore((s) => [
@@ -2,7 +2,7 @@
import { BRANDING_NAME } from '@lobechat/business-const';
import { Avatar, Button, Flexbox, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { LucideArrowUpRightFromSquare, TelescopeIcon } from 'lucide-react';
import Link from 'next/link';
import { memo } from 'react';
@@ -13,9 +13,9 @@ import { PRIVACY_URL } from '@/const/url';
import { useUserStore } from '@/store/user';
import { preferenceSelectors } from '@/store/user/selectors';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
desc: css`
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
font-size: 18px;
@@ -24,8 +24,6 @@ const useStyles = createStyles(({ css, token }) => ({
}));
const TelemetryNotification = memo<{ mobile?: boolean }>(({ mobile }) => {
const { styles, theme } = useStyles();
const { t } = useTranslation('common');
const isPreferenceInit = useUserStore(preferenceSelectors.isPreferenceInit);
@@ -46,8 +44,8 @@ const TelemetryNotification = memo<{ mobile?: boolean }>(({ mobile }) => {
<Flexbox>
<Avatar
avatar={<TelescopeIcon />}
background={theme.geekblue1}
style={{ color: theme.geekblue7 }}
background={cssVar.geekblue1}
style={{ color: cssVar.geekblue7 }}
/>
</Flexbox>
<Flexbox gap={16}>
@@ -2,7 +2,7 @@
import { KLAVIS_SERVER_TYPES, type KlavisServerType } from '@lobechat/const';
import { Avatar, Button, Flexbox, Icon, type ItemType, Segmented } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { ArrowRight, PlusIcon, Store, ToyBrick } from 'lucide-react';
import Image from 'next/image';
@@ -36,8 +36,6 @@ type TabType = 'all' | 'installed';
* 对于 IconType 类型的 icon,使用 Icon 组件渲染,并根据主题设置填充色
*/
const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label }) => {
const theme = useTheme();
if (typeof icon === 'string') {
return (
<Image alt={label} height={18} src={icon} style={{ flex: 'none' }} unoptimized width={18} />
@@ -45,15 +43,13 @@ const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label
}
// 使用主题色填充,在深色模式下自动适应
return <Icon fill={theme.colorText} icon={icon} size={18} />;
return <Icon fill={cssVar.colorText} icon={icon} size={18} />;
});
const AgentTool = memo(() => {
const { t } = useTranslation('setting');
const config = useAgentStore(agentSelectors.currentAgentConfig, isEqual);
const theme = useTheme();
// Plugin state management
const plugins = config?.plugins || [];
@@ -356,7 +352,7 @@ const AgentTool = memo(() => {
icon={PlusIcon}
loading={updating}
size={'small'}
style={{ color: theme.colorTextSecondary }}
style={{ color: cssVar.colorTextSecondary }}
type={'text'}
>
{t('tools.add', { defaultValue: 'Add' })}
@@ -1,6 +1,6 @@
import { type MenuRenderProps } from '@lobehub/editor/es/plugins/slash/react/type';
import { Flexbox } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { type ReactNode, memo, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { type MentionListOption } from './types';
@@ -23,7 +23,6 @@ const getCursorPosition = (): { x: number; y: number } | null => {
const MentionDropdown = memo<MenuRenderProps>(
({ activeKey, onSelect, open, options, setActiveKey }) => {
const theme = useTheme();
const activeItemRef = useRef<HTMLDivElement>(null);
const [position, setPosition] = useState<{ x: number; y: number } | null>(null);
@@ -51,10 +50,10 @@ const MentionDropdown = memo<MenuRenderProps>(
return (
<Flexbox
style={{
background: theme.colorBgElevated,
border: `1px solid ${theme.colorBorderSecondary}`,
background: cssVar.colorBgElevated,
border: `1px solid ${cssVar.colorBorderSecondary}`,
borderRadius: 12,
boxShadow: theme.boxShadowSecondary,
boxShadow: cssVar.boxShadowSecondary,
left: position.x,
maxHeight: 260,
maxWidth: 400,
@@ -70,7 +69,7 @@ const MentionDropdown = memo<MenuRenderProps>(
return (
<div
key={`divider-${(option as any)?.key ?? 'divider'}`}
style={{ borderTop: `1px solid ${theme.colorBorderSecondary}` }}
style={{ borderTop: `1px solid ${cssVar.colorBorderSecondary}` }}
/>
);
}
@@ -93,14 +92,14 @@ const MentionDropdown = memo<MenuRenderProps>(
paddingInline={12}
ref={isActive ? activeItemRef : null}
style={{
background: isActive ? theme.colorFillSecondary : undefined,
background: isActive ? cssVar.colorFillSecondary : undefined,
cursor: 'pointer',
}}
>
{item.icon && <Flexbox style={{ flex: 'none' }}>{item?.icon as ReactNode}</Flexbox>}
<div
style={{
color: theme.colorText,
color: cssVar.colorText,
fontSize: 14,
overflow: 'hidden',
textOverflow: 'ellipsis',
@@ -4,7 +4,7 @@ import { type API, apiPrompt, toolPrompt } from '@lobechat/prompts';
import { type LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
import { type IEditor, INSERT_MENTION_COMMAND } from '@lobehub/editor';
import { Icon, Image } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { memo, useCallback, useMemo } from 'react';
@@ -28,14 +28,12 @@ const getKlavisServerType = (identifier: string) =>
* 对于 IconType 类型的 icon,使用 Icon 组件渲染,并根据主题设置填充色
*/
const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label }) => {
const theme = useTheme();
if (typeof icon === 'string') {
return <Image alt={label} height={20} src={icon} style={{ flex: 'none' }} width={20} />;
}
// 使用主题色填充,在深色模式下自动适应
return <Icon fill={theme.colorText} icon={icon} size={20} />;
return <Icon fill={cssVar.colorText} icon={icon} size={20} />;
});
const toolNameResolver = new ToolNameResolver();
@@ -2,7 +2,7 @@
import { KLAVIS_SERVER_TYPES, type KlavisServerType } from '@lobechat/const';
import { Avatar, Icon, Tag } from '@lobehub/ui';
import { createStyles, useTheme } from 'antd-style';
import { createStaticStyles, cssVar, useThemeMode } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { AlertCircle, X } from 'lucide-react';
import Image from 'next/image';
@@ -23,29 +23,27 @@ import {
* Klavis 服务器图标组件
*/
const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label }) => {
const theme = useTheme();
if (typeof icon === 'string') {
return (
<Image alt={label} height={16} src={icon} style={{ flexShrink: 0 }} unoptimized width={16} />
);
}
return <Icon fill={theme.colorText} icon={icon} size={16} />;
return <Icon fill={cssVar.colorText} icon={icon} size={16} />;
});
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
notInstalledTag: css`
border-color: ${token.colorWarningBorder};
background: ${token.colorWarningBg};
border-color: ${cssVar.colorWarningBorder};
background: ${cssVar.colorWarningBg};
`,
tag: css`
height: 28px !important;
border-radius: ${token.borderRadiusSM}px !important;
border-radius: ${cssVar.borderRadiusSM} !important;
`,
warningIcon: css`
flex-shrink: 0;
color: ${token.colorWarning};
color: ${cssVar.colorWarning};
`,
}));
@@ -55,7 +53,7 @@ interface PluginTagProps {
}
const PluginTag = memo<PluginTagProps>(({ pluginId, onRemove }) => {
const { styles, theme } = useStyles();
const { isDarkMode } = useThemeMode();
const { t } = useTranslation('setting');
// Extract identifier
@@ -167,7 +165,7 @@ const PluginTag = memo<PluginTagProps>(({ pluginId, onRemove }) => {
? undefined
: t('tools.notInstalledWarning', { defaultValue: 'This tool is not installed' })
}
variant={theme.isDarkMode ? 'filled' : 'outlined'}
variant={isDarkMode ? 'filled' : 'outlined'}
>
{!meta.isInstalled
? `${displayTitle} (${t('tools.notInstalled', { defaultValue: 'Not Installed' })})`
@@ -1,7 +1,7 @@
'use client';
import { ActionIcon, Flexbox } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { ArrowLeft } from 'lucide-react';
import { memo } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
@@ -11,8 +11,9 @@ import StoreSearchBar from '@/app/[variants]/(main)/community/features/Search';
import UserAvatar from '@/app/[variants]/(main)/community/features/UserAvatar';
import NavHeader from '@/features/NavHeader';
import { styles } from './Header/style';
const Header = memo(() => {
const theme = useTheme();
const location = useLocation();
const navigate = useNavigate();
@@ -26,8 +27,13 @@ const Header = memo(() => {
}
};
const cssVariables: Record<string, string> = {
'--header-border-color': cssVar.colorBorderSecondary,
};
return (
<NavHeader
className={styles.headerContainer}
left={
<Flexbox align={'center'} flex={1} gap={8} horizontal>
<ActionIcon icon={ArrowLeft} onClick={handleGoBack} size={'small'} />
@@ -35,9 +41,7 @@ const Header = memo(() => {
</Flexbox>
}
right={<UserAvatar />}
style={{
borderBottom: `1px solid ${theme.colorBorderSecondary}`,
}}
style={cssVariables}
styles={{
left: { flex: 1 },
}}
@@ -0,0 +1,8 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
// Header 容器
headerContainer: css`
border-block-end: 1px solid var(--header-border-color, ${cssVar.colorBorderSecondary});
`,
}));
@@ -9,6 +9,7 @@ import WideScreenContainer from '@/features/WideScreenContainer';
import { MAX_WIDTH, SCROLL_PARENT_ID } from '../../features/const';
import Header from './Header';
import { styles } from './style';
/**
* Desktop Discover Detail Layout
@@ -18,21 +19,24 @@ const DesktopDiscoverDetailLayout = memo(() => {
return (
<>
<Header />
<Flexbox height={'100%'} id={SCROLL_PARENT_ID} style={{ overflowY: 'auto' }} width={'100%'}>
<Flexbox
className={styles.mainContainer}
height={'100%'}
id={SCROLL_PARENT_ID}
width={'100%'}
>
<WideScreenContainer
className={styles.contentContainer}
gap={32}
minWidth={MAX_WIDTH}
paddingBlock={16}
style={{
minHeight: '100%',
}}
wrapperStyle={{
minHeight: '100%',
position: 'relative',
}}
>
<Outlet />
<div style={{ flex: 1 }} />
<div className={styles.spacer} />
<Footer />
</WideScreenContainer>
</Flexbox>
@@ -0,0 +1,18 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css }) => ({
// 内容容器
contentContainer: css`
min-height: 100%;
`,
// 主容器
mainContainer: css`
overflow-y: auto;
`,
// 占位符
spacer: css`
flex: 1;
`,
}));
@@ -1,14 +1,14 @@
import { Avatar, Block, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
font-size: 14px !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin: 0 !important;
@@ -16,7 +16,7 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
@@ -24,8 +24,6 @@ const useStyles = createStyles(({ css, token }) => {
const KnowledgeItem = memo<{ avatar?: string; description?: string; title: string }>(
({ avatar, title, description }) => {
const { styles } = useStyles();
return (
<Block gap={12} horizontal padding={12} variant={'outlined'}>
<Avatar avatar={avatar} shape={'square'} size={40} style={{ flex: 'none' }} />
@@ -36,7 +34,7 @@ const KnowledgeItem = memo<{ avatar?: string; description?: string; title: strin
overflow: 'hidden',
}}
>
<Text as={'h2'} className={title} ellipsis>
<Text as={'h2'} className={styles.title} ellipsis>
{title}
</Text>
<Text
@@ -1,7 +1,7 @@
import { KLAVIS_SERVER_TYPES, type KlavisServerType } from '@lobechat/const';
import { type DiscoverPluginDetail, type PluginSource } from '@lobechat/types';
import { Avatar, Block, Flexbox, Icon, Image, Skeleton, Tag, Text } from '@lobehub/ui';
import { createStyles, useTheme } from 'antd-style';
import { createStaticStyles, cssVar, cx } from 'antd-style';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
@@ -15,26 +15,24 @@ import { useDiscoverStore } from '@/store/discover';
* For IconType type icon, use Icon component to render with theme fill color
*/
const KlavisIcon = memo<Pick<KlavisServerType, 'icon' | 'label'>>(({ icon, label }) => {
const theme = useTheme();
if (typeof icon === 'string') {
return <Image alt={label} height={40} src={icon} style={{ flex: 'none' }} width={40} />;
}
// Use theme color fill, automatically adapts in dark mode
return <Icon fill={theme.colorText} icon={icon} size={40} />;
return <Icon fill={cssVar.colorText} icon={icon} size={40} />;
});
KlavisIcon.displayName = 'KlavisIcon';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
clickable: css`
cursor: pointer;
&:hover {
.plugin-title {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
}
`,
@@ -42,7 +40,7 @@ const useStyles = createStyles(({ css, token }) => {
flex: 1;
margin: 0 !important;
font-size: 14px !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
noLink: css`
cursor: default;
@@ -71,7 +69,6 @@ const PluginItem = memo<PluginItemProps>(({ identifier }) => {
const { t } = useTranslation('discover');
const usePluginDetail = useDiscoverStore((s) => s.usePluginDetail);
const { data: apiData, isLoading } = usePluginDetail({ identifier, withManifest: false });
const { styles, cx } = useStyles();
// Try to get Klavis tool info if API returns no data
const klavisTool = useMemo(() => {
@@ -2,7 +2,7 @@
import { SOCIAL_URL } from '@lobechat/business-const';
import { Flexbox, Icon, Tabs, Tag } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { BookOpenIcon, HistoryIcon, LayersIcon, ListIcon, SquareUserIcon } from 'lucide-react';
import Link from 'next/link';
import { memo } from 'react';
@@ -15,17 +15,17 @@ import { AssistantNavKey } from '@/types/discover';
import { useDetailContext } from '../DetailProvider';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
link: css`
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
&:hover {
color: ${token.colorInfo};
color: ${cssVar.colorInfo};
}
`,
nav: css`
border-block-end: 1px solid ${token.colorBorder};
border-block-end: 1px solid ${cssVar.colorBorder};
`,
};
});
@@ -38,7 +38,6 @@ interface NavProps {
const Nav = memo<NavProps>(({ mobile, setActiveTab, activeTab = AssistantNavKey.Overview }) => {
const { t } = useTranslation('discover');
const { pluginCount, knowledgeCount, identifier } = useDetailContext();
const { styles } = useStyles();
const { source } = useQuery() as { source?: string };
const isLegacy = source === 'legacy';
const marketplaceLink = identifier
@@ -1,7 +1,7 @@
'use client';
import { Flexbox, Tag } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import qs from 'query-string';
import { memo } from 'react';
import { Link } from 'react-router-dom';
@@ -9,7 +9,7 @@ import { Link } from 'react-router-dom';
import { useQuery } from '@/hooks/useQuery';
import { type AssistantMarketSource } from '@/types/discover';
const useStyles = createStyles(({ token, css }) => {
const styles = createStaticStyles(({ cssVar, css }) => {
return {
tag: css`
margin: 0;
@@ -17,13 +17,12 @@ const useStyles = createStyles(({ token, css }) => {
padding-inline: 12px;
border-radius: 16px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
};
});
const TagList = memo<{ tags: string[] }>(({ tags }) => {
const { styles } = useStyles();
const { source } = useQuery() as { source?: AssistantMarketSource };
const marketSource = source === 'legacy' ? 'legacy' : undefined;
const showTags = Boolean(tags?.length && tags?.length > 0);
@@ -1,7 +1,7 @@
'use client';
import { Flexbox, Tag } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import qs from 'query-string';
import { memo } from 'react';
import { Link } from 'react-router-dom';
@@ -9,7 +9,7 @@ import { Link } from 'react-router-dom';
import { useQuery } from '@/hooks/useQuery';
import { type AssistantMarketSource } from '@/types/discover';
const useStyles = createStyles(({ token, css }) => {
const styles = createStaticStyles(({ cssVar, css }) => {
return {
tag: css`
margin: 0;
@@ -17,13 +17,12 @@ const useStyles = createStyles(({ token, css }) => {
padding-inline: 12px;
border-radius: 16px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
};
});
const TagList = memo<{ tags: string[] }>(({ tags }) => {
const { styles } = useStyles();
const { source } = useQuery() as { source?: AssistantMarketSource };
const marketSource = source === 'legacy' ? 'legacy' : undefined;
const showTags = Boolean(tags?.length && tags?.length > 0);
@@ -1,5 +1,5 @@
import { Block, Flexbox, Icon, Tag } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { MessageCircleHeartIcon, MessageCircleQuestionIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -13,7 +13,6 @@ import TagList from './TagList';
const Overview = memo(() => {
const { t } = useTranslation('discover');
const { tokenUsage, tags = [], config } = useDetailContext();
const theme = useTheme();
const { systemRole, openingMessage, openingQuestions } = config || {};
return (
@@ -34,7 +33,7 @@ const Overview = memo(() => {
<Title>{t('assistants.details.systemRole.openingMessage')}</Title>
<Block align={'flex-start'} gap={12} horizontal padding={16} variant={'outlined'}>
<Icon
color={theme.colorError}
color={cssVar.colorError}
icon={MessageCircleHeartIcon}
size={20}
style={{
@@ -53,7 +52,7 @@ const Overview = memo(() => {
<Flexbox gap={8}>
{openingQuestions?.map((item, key) => (
<Block gap={12} horizontal key={key} padding={16} variant={'outlined'}>
<Icon color={theme.colorWarning} icon={MessageCircleQuestionIcon} size={20} />
<Icon color={cssVar.colorWarning} icon={MessageCircleQuestionIcon} size={20} />
<MarkdownRender>{item}</MarkdownRender>
</Block>
))}
@@ -1,5 +1,5 @@
import { Block, Flexbox, Icon, Tag } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { CheckIcon, MinusIcon } from 'lucide-react';
import qs from 'query-string';
import { memo, useMemo } from 'react';
@@ -17,7 +17,6 @@ import { useDetailContext } from '../../DetailProvider';
const Versions = memo(() => {
const { t } = useTranslation('discover');
const theme = useTheme();
const pathname = usePathname();
const { versions = [], currentVersion } = useDetailContext();
const { source } = useQuery() as { source?: AssistantMarketSource };
@@ -107,7 +106,7 @@ const Versions = memo(() => {
dataIndex: 'isValidated',
render: (_: any, record: any) => (
<Icon
color={record.isValidated ? theme.colorSuccess : theme.colorTextDescription}
color={record.isValidated ? cssVar.colorSuccess : cssVar.colorTextDescription}
icon={record.isValidated ? CheckIcon : MinusIcon}
/>
),
@@ -12,7 +12,7 @@ import {
TooltipGroup,
} from '@lobehub/ui';
import { App } from 'antd';
import { createStyles, useResponsive } from 'antd-style';
import { createStaticStyles, cssVar, useResponsive } from 'antd-style';
import { BookTextIcon, BookmarkCheckIcon, BookmarkIcon, CoinsIcon, DotIcon } from 'lucide-react';
import qs from 'query-string';
import { memo, useState } from 'react';
@@ -29,21 +29,12 @@ import { useCategory } from '../../../(list)/assistant/features/Category/useCate
import PublishedTime from '../../../../../../../components/PublishedTime';
import { useDetailContext } from './DetailProvider';
const useStyles = createStyles(({ css, token }) => {
return {
desc: css`
color: ${token.colorTextSecondary};
`,
time: css`
font-size: 12px;
color: ${token.colorTextDescription};
`,
version: css`
font-family: ${token.fontFamilyCode};
font-size: 13px;
`,
};
});
const styles = createStaticStyles(({ css, cssVar }) => ({
time: css`
font-size: 12px;
color: ${cssVar.colorTextDescription};
`,
}));
const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
const { t } = useTranslation('discover');
@@ -60,7 +51,6 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
knowledgeCount,
userName,
} = useDetailContext();
const { styles, theme } = useStyles();
const { mobile = isMobile } = useResponsive();
const { isAuthenticated, signIn, session } = useMarketAuth();
const [favoriteLoading, setFavoriteLoading] = useState(false);
@@ -228,7 +218,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
gap={mobile ? 12 : 24}
horizontal
style={{
color: theme.colorTextSecondary,
color: cssVar.colorTextSecondary,
}}
>
{!mobile && cateButton}
@@ -249,7 +239,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
title={t('assistants.withPlugin')}
>
<Flexbox align={'center'} gap={6} horizontal>
<Icon fill={theme.colorTextSecondary} icon={MCP} />
<Icon fill={cssVar.colorTextSecondary} icon={MCP} />
{pluginCount}
</Flexbox>
</Tooltip>
@@ -2,7 +2,7 @@
import { Icon } from '@lobehub/ui';
import { App, Dropdown } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { ChevronDownIcon } from 'lucide-react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -16,7 +16,7 @@ import { useHomeStore } from '@/store/home';
import { useDetailContext } from '../../DetailProvider';
const useStyles = createStyles(({ css }) => ({
const styles = createStaticStyles(({ css }) => ({
button: css`
button {
width: 100%;
@@ -27,8 +27,6 @@ const useStyles = createStyles(({ css }) => ({
const AddAgent = memo<{ mobile?: boolean }>(({ mobile }) => {
const { avatar, description, tags, title, config, backgroundColor, identifier, editorData } =
useDetailContext();
const { styles } = useStyles();
const [isLoading, setIsLoading] = useState(false);
const createAgent = useAgentStore((s) => s.createAgent);
const refreshAgentList = useHomeStore((s) => s.refreshAgentList);
@@ -1,16 +1,16 @@
import { Avatar, Block, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { type DiscoverAssistantItem } from '@/types/discover';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
font-size: 14px !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin: 0 !important;
@@ -18,14 +18,13 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
});
const RelatedItem = memo<DiscoverAssistantItem>(({ avatar, title, description, identifier }) => {
const { styles } = useStyles();
return (
<Block gap={12} horizontal key={identifier} padding={12} variant={'outlined'}>
<Avatar avatar={avatar} shape={'square'} size={40} style={{ flex: 'none' }} />
@@ -1,5 +1,5 @@
import { Collapse } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -8,7 +8,6 @@ import { useDetailContext } from '../../DetailProvider';
const Summary = memo(() => {
const { description, summary } = useDetailContext();
const { t } = useTranslation('discover');
const theme = useTheme();
return (
<Collapse
defaultActiveKey={['summary']}
@@ -18,7 +17,7 @@ const Summary = memo(() => {
children: (
<p
style={{
color: theme.colorTextSecondary,
color: cssVar.colorTextSecondary,
margin: 0,
}}
>
@@ -1,19 +1,19 @@
'use client';
import { Flexbox, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { ArrowLeft } from 'lucide-react';
import { type CSSProperties, memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
back: css`
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
&:hover {
color: ${token.colorText};
color: ${cssVar.colorText};
}
`,
};
@@ -21,7 +21,6 @@ const useStyles = createStyles(({ css, token }) => {
const Back = memo<{ href: string; style?: CSSProperties }>(({ href, style }) => {
const { t } = useTranslation('discover');
const { styles } = useStyles();
return (
<Link className={styles.back} style={{ marginBottom: 8, ...style }} to={href}>
@@ -1,14 +1,14 @@
import { Button, Flexbox, type FlexboxProps, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { ChevronRight } from 'lucide-react';
import { memo } from 'react';
import { Link } from 'react-router-dom';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
more: css`
display: flex;
align-items: center;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin-block: 0;
@@ -25,8 +25,6 @@ interface BlockProps extends FlexboxProps {
}
const Block = memo<BlockProps>(({ title, more, moreLink, children, ...rest }) => {
const { styles } = useStyles();
return (
<Flexbox gap={16} style={{ position: 'relative' }} width={'100%'}>
<Flexbox align={'center'} gap={16} horizontal justify={'space-between'} width={'100%'}>
@@ -2,7 +2,7 @@
import { CopyButton, Flexbox } from '@lobehub/ui';
import { Breadcrumb as AntdBreadcrumb, type BreadcrumbProps } from 'antd';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
@@ -10,7 +10,6 @@ import { Link } from 'react-router-dom';
import { DiscoverTab } from '@/types/discover';
const Breadcrumb = memo<{ identifier: string; tab: DiscoverTab }>(({ tab, identifier }) => {
const theme = useTheme();
const { t } = useTranslation('discover');
const tabLabel = useMemo(() => {
@@ -33,7 +32,7 @@ const Breadcrumb = memo<{ identifier: string; tab: DiscoverTab }>(({ tab, identi
gap={4}
horizontal
style={{
color: theme.colorTextSecondary,
color: cssVar.colorTextSecondary,
}}
>
@{identifier}
@@ -57,7 +56,7 @@ const Breadcrumb = memo<{ identifier: string; tab: DiscoverTab }>(({ tab, identi
gap={4}
horizontal
style={{
color: theme.colorTextSecondary,
color: cssVar.colorTextSecondary,
}}
>
{identifier}
@@ -72,7 +71,7 @@ const Breadcrumb = memo<{ identifier: string; tab: DiscoverTab }>(({ tab, identi
),
},
];
}, [tab, identifier, tabLabel, theme.colorTextSecondary]);
}, [tab, identifier, tabLabel]);
return <AntdBreadcrumb items={items} />;
});
@@ -1,16 +1,15 @@
'use client';
import { Flexbox, type FlexboxProps, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { type LucideIcon } from 'lucide-react';
import { rgba } from 'polished';
import { type ReactNode, memo } from 'react';
import { useServerConfigStore } from '@/store/serverConfig';
import CardBanner from '../../components/CardBanner';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
background: css`
pointer-events: none;
@@ -31,22 +30,22 @@ const useStyles = createStyles(({ css, token }) => ({
width: 100%;
height: 100%;
background: ${rgba(token.colorBgContainer, 0.5)};
background: color-mix(in srgb, ${cssVar.colorBgContainer} 50%, transparent);
}
`,
container: css`
position: relative;
overflow: hidden;
border-radius: ${token.borderRadiusLG}px;
border-radius: ${cssVar.borderRadiusLG};
box-shadow:
0 0 0 1px ${token.colorFill} inset,
${token.boxShadowTertiary};
0 0 0 1px ${cssVar.colorFill} inset,
${cssVar.boxShadowTertiary};
`,
header: css`
position: relative;
overflow: hidden;
height: 58px;
border-block-end: 1px solid ${token.colorBorderSecondary};
border-block-end: 1px solid ${cssVar.colorBorderSecondary};
`,
}));
@@ -57,7 +56,6 @@ interface HighlightBlockProps extends FlexboxProps {
}
const HighlightBlock = memo<HighlightBlockProps>(({ avatar, title, icon, children, ...rest }) => {
const { styles } = useStyles();
const mobile = useServerConfigStore((s) => s.isMobile);
return (
<Flexbox className={styles.container} flex={'none'} width={'100%'} {...rest}>
@@ -12,7 +12,7 @@ import {
Tag,
Text,
} from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { startCase } from 'es-toolkit/compat';
import { LinkIcon, Share2Icon } from 'lucide-react';
import Link from 'next/link';
@@ -23,36 +23,36 @@ import { useShare } from '@/hooks/useShare';
import CardBanner from '../../components/CardBanner';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
banner: css`
overflow: hidden;
border: 1px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadiusLG}px;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadiusLG};
background: ${token.colorBgContainer};
box-shadow: ${token.boxShadowTertiary};
background: ${cssVar.colorBgContainer};
box-shadow: ${cssVar.boxShadowTertiary};
`,
copy: css`
background: ${token.colorPrimary};
background: ${cssVar.colorPrimary};
&:hover {
background: ${token.colorPrimaryHover};
background: ${cssVar.colorPrimaryHover};
}
`,
icon: css`
border: 1px solid ${token.colorFillSecondary};
border: 1px solid ${cssVar.colorFillSecondary};
svg {
fill: ${token.colorTextSecondary};
fill: ${cssVar.colorTextSecondary};
}
&:hover {
border: 1px solid ${token.colorBorderSecondary};
border: 1px solid ${cssVar.colorBorderSecondary};
svg {
fill: ${token.colorText};
fill: ${cssVar.colorText};
}
}
`,
@@ -80,7 +80,6 @@ const ShareButton = memo<ShareButtonProps>(({ meta, ...rest }) => {
...meta,
});
const { t } = useTranslation('common');
const { styles, theme } = useStyles();
const [open, setOpen] = useState(false);
let content;
@@ -94,7 +93,7 @@ const ShareButton = memo<ShareButtonProps>(({ meta, ...rest }) => {
flex={'none'}
height={72}
style={{
backgroundColor: theme.colorBgContainer,
backgroundColor: cssVar.colorBgContainer,
borderRadius: '50%',
overflow: 'hidden',
zIndex: 2,
@@ -105,7 +104,7 @@ const ShareButton = memo<ShareButtonProps>(({ meta, ...rest }) => {
</Center>
<Center padding={12} width={'100%'}>
<h3 style={{ fontWeight: 'bold', textAlign: 'center' }}>{meta.title}</h3>
<Text as={'p'} style={{ color: theme.colorTextSecondary, textAlign: 'center' }}>
<Text as={'p'} style={{ color: cssVar.colorTextSecondary, textAlign: 'center' }}>
{meta.desc}
</Text>
{meta.hashtags && (
@@ -137,7 +136,7 @@ const ShareButton = memo<ShareButtonProps>(({ meta, ...rest }) => {
<Input value={meta.url} variant={'filled'} />
<CopyButton
className={styles.copy}
color={theme.colorBgLayout}
color={cssVar.colorBgLayout}
content={meta.url}
icon={LinkIcon}
size={{ blockSize: 36, size: 16 }}
@@ -1,11 +1,19 @@
'use client';
import { Icon } from '@lobehub/ui';
import { createStyles, useTheme } from 'antd-style';
import { createStaticStyles, cssVar, cx } from 'antd-style';
import { kebabCase } from 'es-toolkit/compat';
import { Heading2, Heading3, Heading4, Heading5 } from 'lucide-react';
import Link from 'next/link';
import { Children, type ComponentProps, type FC, type ReactNode, isValidElement, useEffect, useMemo } from 'react';
import {
Children,
type ComponentProps,
type FC,
type ReactNode,
isValidElement,
useEffect,
useMemo,
} from 'react';
import { useToc } from './useToc';
@@ -30,31 +38,29 @@ const HeadingIcon: any = {
h5: Heading5,
};
const useStyles = createStyles(({ css, cx, token }) => {
const anchor = cx(css`
display: none;
margin-inline-start: 0.5rem;
color: ${token.colorTextDescription} !important;
`);
return {
anchor,
container: css`
&:hover {
.${anchor} {
display: inline;
}
}
const styles = createStaticStyles(({ cx, css, cssVar }) => ({
anchor: cx(
'toc-anchor',
css`
display: none;
margin-inline-start: 0.5rem;
color: ${cssVar.colorTextDescription} !important;
`,
};
});
),
container: css`
&:hover {
.toc-anchor {
display: inline;
}
}
`,
}));
const createHeading = (Tag: `h${1 | 2 | 3 | 4 | 5 | 6}`) => {
const Heading: FC<ComponentProps<'h2'>> = ({ children, className, style, ...props }) => {
const { setToc, setFinished } = useToc();
const { cx, styles } = useStyles();
const text = useMemo(() => extractTextChildren(children), [children]);
const id = kebabCase(text);
const theme = useTheme();
useEffect(() => {
if (!setToc) return;
@@ -73,7 +79,7 @@ const createHeading = (Tag: `h${1 | 2 | 3 | 4 | 5 | 6}`) => {
if (Tag === 'h1')
return (
<Tag style={{ color: theme.colorText, ...style }} {...props} id={id}>
<Tag style={{ color: cssVar.colorText, ...style }} {...props} id={id}>
{children}
</Tag>
);
@@ -81,7 +87,7 @@ const createHeading = (Tag: `h${1 | 2 | 3 | 4 | 5 | 6}`) => {
return (
<Tag
className={cx(styles.container, className)}
style={{ color: theme.colorText, ...style }}
style={{ color: cssVar.colorText, ...style }}
{...props}
id={id}
>
@@ -1,7 +1,7 @@
'use client';
import { Anchor, type AnchorProps } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx, responsive } from 'antd-style';
import { memo, useMemo } from 'react';
import { SCROLL_PARENT_ID } from '@/app/[variants]/(main)/community/features/const';
@@ -9,7 +9,9 @@ import { isOnServerSide } from '@/utils/env';
import { createTOCTree } from './useToc';
const useStyles = createStyles(({ css, token, responsive, prefixCls }) => {
const prefixCls = 'ant';
const styles = createStaticStyles(({ css, cssVar }) => {
return {
toc: css`
a {
@@ -52,14 +54,14 @@ const useStyles = createStyles(({ css, token, responsive, prefixCls }) => {
}
.${prefixCls}-anchor-link-title-active {
color: ${token.colorText} !important;
color: ${cssVar.colorText} !important;
}
.${prefixCls}-anchor-link-title:not(.${prefixCls}-anchor-link-title-active) {
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
&:hover {
color: ${token.colorText};
color: ${cssVar.colorText};
}
}
}
@@ -72,8 +74,6 @@ const useStyles = createStyles(({ css, token, responsive, prefixCls }) => {
});
const Toc = memo<AnchorProps>(({ items, className, ...rest }) => {
const { cx, styles } = useStyles();
const toc = useMemo(() => createTOCTree(items as any), [items]);
return (
@@ -1,5 +1,5 @@
import { Block, Flexbox, Icon, Tag } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { CheckIcon, MinusIcon } from 'lucide-react';
import qs from 'query-string';
import { memo } from 'react';
@@ -14,7 +14,6 @@ import { useDetailContext } from '../../../../../../../../../features/MCPPluginD
import Title from '../../../../../features/Title';
const Versions = memo(() => {
const theme = useTheme();
const { t } = useTranslation('discover');
const { versions } = useDetailContext();
const pathname = usePathname();
@@ -50,7 +49,7 @@ const Versions = memo(() => {
dataIndex: 'isValidated',
render: (_, record) => (
<Icon
color={record.isValidated ? theme.colorSuccess : theme.colorTextDescription}
color={record.isValidated ? cssVar.colorSuccess : cssVar.colorTextDescription}
icon={record.isValidated ? CheckIcon : MinusIcon}
/>
),
@@ -1,7 +1,7 @@
'use client';
import { Button, Flexbox, Icon } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { Trash2Icon } from 'lucide-react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -12,7 +12,7 @@ import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
import { useToolStore } from '@/store/tool';
import { pluginSelectors } from '@/store/tool/slices/plugin/selectors';
const useStyles = createStyles(({ css }) => ({
const styles = createStaticStyles(({ css }) => ({
button: css`
button {
width: 100%;
@@ -24,7 +24,6 @@ const ActionButton = memo(() => {
const { t } = useTranslation(['discover', 'plugin']);
const detailContext = useDetailContext();
const { identifier, haveCloudEndpoint } = detailContext;
const { styles } = useStyles();
const [isLoading, setIsLoading] = useState(false);
const { isAuthenticated, isLoading: isAuthLoading, signIn } = useMarketAuth();
@@ -1,16 +1,16 @@
import { Avatar, Block, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { type DiscoverMcpItem } from '@/types/discover';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
font-size: 14px !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin: 0 !important;
@@ -18,14 +18,13 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
});
const RelatedItem = memo<DiscoverMcpItem>(({ name, icon, description, identifier }) => {
const { styles } = useStyles();
return (
<Block gap={12} horizontal key={identifier} padding={12} variant={'outlined'}>
<Avatar avatar={icon} shape={'square'} size={40} style={{ flex: 'none' }} />
@@ -2,7 +2,7 @@
import { SOCIAL_URL } from '@lobechat/business-const';
import { Flexbox, Icon, Tabs } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { BookOpenIcon, ListIcon, Settings2Icon } from 'lucide-react';
import Link from 'next/link';
import { memo } from 'react';
@@ -10,17 +10,17 @@ import { useTranslation } from 'react-i18next';
import { ModelNavKey } from '@/types/discover';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
link: css`
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
&:hover {
color: ${token.colorInfo};
color: ${cssVar.colorInfo};
}
`,
nav: css`
border-block-end: 1px solid ${token.colorBorder};
border-block-end: 1px solid ${cssVar.colorBorder};
`,
};
});
@@ -33,7 +33,6 @@ interface NavProps {
const Nav = memo<NavProps>(({ mobile, setActiveTab, activeTab = ModelNavKey.Overview }) => {
const { t } = useTranslation('discover');
const { styles } = useStyles();
const nav = (
<Tabs
@@ -2,7 +2,7 @@
import { ProviderIcon } from '@lobehub/icons';
import { ActionIcon, Block, Flexbox, Icon, Tooltip, TooltipGroup } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { BadgeCheck, BookIcon, ChevronRightIcon, KeyIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -20,7 +20,6 @@ import { useDetailContext } from '../../../DetailProvider';
const ProviderList = memo(() => {
const { providers = [] } = useDetailContext();
const { t } = useTranslation('discover');
const theme = useTheme();
return (
<TooltipGroup>
@@ -140,7 +139,7 @@ const ProviderList = memo(() => {
{isLobeHub && (
<Tooltip title={t('models.providerInfo.officialTooltip')}>
<ActionIcon
color={theme.colorSuccess}
color={cssVar.colorSuccess}
icon={BadgeCheck}
size={'small'}
variant={'filled'}
@@ -170,7 +169,7 @@ const ProviderList = memo(() => {
to={urlJoin('/community/provider', record.id)}
>
<ActionIcon
color={theme.colorTextDescription}
color={cssVar.colorTextDescription}
icon={ChevronRightIcon}
size={'small'}
variant={'filled'}
@@ -1,6 +1,6 @@
import { Flexbox } from '@lobehub/ui';
import { Divider } from 'antd';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import Link from 'next/link';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -24,11 +24,10 @@ const formatNum = (num: string | number) => {
const ParameterItem = memo<ParameterItemProps>(
({ docUrl = DEFAULT_DOC_URL, desc, type, defaultValue, range }) => {
const { t } = useTranslation('discover');
const theme = useTheme();
return (
<Flexbox align={'flex-start'} gap={16}>
<p style={{ color: theme.colorTextSecondary, margin: 0 }}>
<p style={{ color: cssVar.colorTextSecondary, margin: 0 }}>
{desc}{' '}
<Link href={docUrl} target={'_blank'}>
{t('models.parameterList.docs')}
@@ -2,7 +2,7 @@
import { ModelIcon } from '@lobehub/icons';
import { Flexbox, Icon, Text } from '@lobehub/ui';
import { createStyles, useResponsive } from 'antd-style';
import { createStaticStyles, cssVar, useResponsive } from 'antd-style';
import { DotIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -13,17 +13,17 @@ import { ModelInfoTags } from '@/components/ModelSelect';
import PublishedTime from '../../../../../../../components/PublishedTime';
import { useDetailContext } from './DetailProvider';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
time: css`
font-size: 12px;
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
version: css`
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
font-size: 13px;
`,
};
@@ -32,7 +32,6 @@ const useStyles = createStyles(({ css, token }) => {
const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
const { identifier, releasedAt, displayName, type, abilities, contextWindowTokens } =
useDetailContext();
const { styles, theme } = useStyles();
const { mobile = isMobile } = useResponsive();
const { t } = useTranslation('models');
@@ -99,7 +98,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
</Flexbox>
<div
style={{
color: theme.colorTextSecondary,
color: cssVar.colorTextSecondary,
}}
>
{t(`${identifier}.description`)}
@@ -3,16 +3,16 @@
import { ProviderIcon } from '@lobehub/icons';
import { Button, Icon } from '@lobehub/ui';
import { Dropdown } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { ChevronDownIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Link , useNavigate } from 'react-router-dom';
import { Link, useNavigate } from 'react-router-dom';
import urlJoin from 'url-join';
import { useDetailContext } from '../../DetailProvider';
const useStyles = createStyles(({ css }) => ({
const styles = createStaticStyles(({ css }) => ({
button: css`
button {
width: 100%;
@@ -21,7 +21,6 @@ const useStyles = createStyles(({ css }) => ({
}));
const ChatWithModel = memo(() => {
const { styles } = useStyles();
const { t } = useTranslation('discover');
const { providers = [] } = useDetailContext();
const includeLobeHub = providers.some((item) => item.id === 'lobehub');
@@ -1,18 +1,18 @@
import { ModelIcon } from '@lobehub/icons';
import { Block, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { type DiscoverModelItem } from '@/types/discover';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
font-size: 14px !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin: 0 !important;
@@ -20,14 +20,13 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
});
const RelatedItem = memo<DiscoverModelItem>(({ identifier, displayName }) => {
const { styles } = useStyles();
const { t } = useTranslation('models');
return (
<Block gap={12} horizontal key={identifier} padding={12} variant={'outlined'}>
@@ -1,18 +1,18 @@
import { ProviderIcon } from '@lobehub/icons';
import { Block, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { type DiscoverModelDetailProviderItem } from '@/types/discover';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
font-size: 14px !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin: 0 !important;
@@ -20,14 +20,13 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
});
const RelatedItem = memo<DiscoverModelDetailProviderItem>(({ id, name }) => {
const { styles } = useStyles();
const { t } = useTranslation('providers');
return (
<Block gap={12} horizontal key={id} padding={12} variant={'outlined'}>
@@ -2,7 +2,7 @@
import { SOCIAL_URL } from '@lobechat/business-const';
import { Flexbox, Icon, Tabs } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { BookOpenIcon, BrainCircuitIcon, ListIcon } from 'lucide-react';
import Link from 'next/link';
import { memo } from 'react';
@@ -13,17 +13,17 @@ import { ProviderNavKey } from '@/types/discover';
import { useDetailContext } from '../DetailProvider';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
link: css`
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
&:hover {
color: ${token.colorInfo};
color: ${cssVar.colorInfo};
}
`,
nav: css`
border-block-end: 1px solid ${token.colorBorder};
border-block-end: 1px solid ${cssVar.colorBorder};
`,
};
});
@@ -37,7 +37,6 @@ interface NavProps {
const Nav = memo<NavProps>(({ mobile, setActiveTab, activeTab = ProviderNavKey.Overview }) => {
const { t } = useTranslation('discover');
const { identifier } = useDetailContext();
const { styles } = useStyles();
const nav = (
<Tabs
@@ -2,7 +2,7 @@
import { ModelIcon } from '@lobehub/icons';
import { ActionIcon, Block, Flexbox, Tooltip, TooltipGroup } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { ChevronRightIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -19,7 +19,6 @@ import { useDetailContext } from '../../../DetailProvider';
const ModelList = memo(() => {
const { models = [] } = useDetailContext();
const { t } = useTranslation('discover');
const theme = useTheme();
return (
<TooltipGroup>
@@ -36,7 +35,7 @@ const ModelList = memo(() => {
<ModelIcon model={record.id} size={24} type={'avatar'} />
<Flexbox style={{ overflow: 'hidden' }}>
<div style={{ fontWeight: 500 }}>{record.displayName}</div>
<div style={{ color: theme.colorTextSecondary, fontSize: 12 }}>
<div style={{ color: cssVar.colorTextSecondary, fontSize: 12 }}>
{record.id}
</div>
</Flexbox>
@@ -132,7 +131,7 @@ const ModelList = memo(() => {
<Flexbox align="center" gap={4} horizontal justify={'flex-end'}>
<Link style={{ color: 'inherit' }} to={urlJoin('/community/model', record.id)}>
<ActionIcon
color={theme.colorTextDescription}
color={cssVar.colorTextDescription}
icon={ChevronRightIcon}
size={'small'}
variant={'filled'}
@@ -2,7 +2,7 @@
import { Github, ProviderCombine } from '@lobehub/icons';
import { ActionIcon, Flexbox } from '@lobehub/ui';
import { createStyles, useResponsive } from 'antd-style';
import { cssVar, useResponsive } from 'antd-style';
import { GlobeIcon } from 'lucide-react';
import Link from 'next/link';
import { memo } from 'react';
@@ -11,26 +11,9 @@ import urlJoin from 'url-join';
import { useDetailContext } from './DetailProvider';
const useStyles = createStyles(({ css, token }) => {
return {
desc: css`
color: ${token.colorTextSecondary};
`,
time: css`
font-size: 12px;
color: ${token.colorTextDescription};
`,
version: css`
font-family: ${token.fontFamilyCode};
font-size: 13px;
`,
};
});
const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
const { t } = useTranslation('providers');
const { identifier, url, modelsUrl, name } = useDetailContext();
const { theme } = useStyles();
const { mobile = isMobile } = useResponsive();
return (
@@ -64,7 +47,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
onClick={(e) => e.stopPropagation()}
target={'_blank'}
>
<ActionIcon color={theme.colorTextDescription} icon={GlobeIcon} />
<ActionIcon color={cssVar.colorTextDescription} icon={GlobeIcon} />
</Link>
)}
@@ -76,7 +59,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
onClick={(e) => e.stopPropagation()}
target={'_blank'}
>
<ActionIcon fill={theme.colorTextDescription} icon={Github} />
<ActionIcon fill={cssVar.colorTextDescription} icon={Github} />
</Link>
</Flexbox>
</Flexbox>
@@ -86,7 +69,7 @@ const Header = memo<{ mobile?: boolean }>(({ mobile: isMobile }) => {
gap={mobile ? 12 : 24}
horizontal
style={{
color: theme.colorTextSecondary,
color: cssVar.colorTextSecondary,
}}
>
{t(`${identifier}.description`)}
@@ -3,7 +3,7 @@
import { isDesktop } from '@lobechat/const';
import { Button, Icon } from '@lobehub/ui';
import { Dropdown } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { ChevronDownIcon, SquareArrowOutUpRight } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -11,7 +11,7 @@ import { Link, useNavigate } from 'react-router-dom';
import { useDetailContext } from '../../DetailProvider';
const useStyles = createStyles(({ css }) => ({
const styles = createStaticStyles(({ css }) => ({
button: css`
button {
width: 100%;
@@ -20,7 +20,6 @@ const useStyles = createStyles(({ css }) => ({
}));
const ProviderConfig = memo(() => {
const { styles } = useStyles();
const { t } = useTranslation('discover');
const { url, modelsUrl, identifier } = useDetailContext();
const navigate = useNavigate();
@@ -1,18 +1,18 @@
import { ProviderIcon } from '@lobehub/icons';
import { Block, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { type DiscoverProviderItem } from '@/types/discover';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
font-size: 14px !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin: 0 !important;
@@ -20,14 +20,13 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
});
const RelatedItem = memo<DiscoverProviderItem>(({ identifier, name }) => {
const { styles } = useStyles();
const { t } = useTranslation('providers');
return (
<Block gap={12} horizontal key={identifier} padding={12} variant={'outlined'}>
@@ -1,18 +1,18 @@
import { ModelIcon } from '@lobehub/icons';
import { Block, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { type DiscoverProviderDetailModelItem } from '@/types/discover';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
font-size: 14px !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
title: css`
margin: 0 !important;
@@ -20,14 +20,13 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
});
const RelatedItem = memo<DiscoverProviderDetailModelItem>(({ id, displayName }) => {
const { styles } = useStyles();
const { t } = useTranslation('models');
return (
<Block gap={12} horizontal key={id} padding={12} variant={'outlined'}>
@@ -1,10 +1,10 @@
'use client';
import { Center } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles , responsive } from 'antd-style';
import { memo } from 'react';
const useStyles = createStyles(({ css, token, responsive }) => ({
const styles = createStaticStyles(({ css, cssVar }) => ({
banner: css`
position: absolute;
inset-block-start: 0;
@@ -14,7 +14,7 @@ const useStyles = createStyles(({ css, token, responsive }) => ({
height: 160px;
padding: 16px;
${responsive.mobile} {
${responsive.sm} {
position: relative;
width: calc(100% + 32px);
@@ -38,9 +38,9 @@ const useStyles = createStyles(({ css, token, responsive }) => ({
width: 100%;
height: 100%;
border-radius: ${token.borderRadiusLG}px;
border-radius: ${cssVar.borderRadiusLG};
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
@media (max-width: 1720px) {
border-radius: 0;
@@ -57,7 +57,7 @@ const useStyles = createStyles(({ css, token, responsive }) => ({
height: 64px;
min-height: 64px;
${responsive.mobile} {
${responsive.sm} {
display: none;
}
`,
@@ -69,7 +69,6 @@ interface BannerProps {
}
const Banner = memo<BannerProps>(({ avatar, bannerUrl }) => {
const { styles } = useStyles();
// Use bannerUrl if available, otherwise fall back to blurred avatar
const backgroundImage = bannerUrl || avatar;
const shouldBlur = !bannerUrl && !!avatar;
@@ -2,7 +2,7 @@
import { SiGithub, SiX } from '@icons-pack/react-simple-icons';
import { ActionIcon, Avatar, Button, Flexbox, Text, Tooltip, TooltipGroup } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { Globe } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -14,7 +14,6 @@ import Banner from './Banner';
const UserHeader = memo(() => {
const { t } = useTranslation('discover');
const theme = useTheme();
const { user, isOwner, onEditProfile } = useUserDetailContext();
const displayName = user.displayName || user.userName || user.namespace;
@@ -28,7 +27,7 @@ const UserHeader = memo(() => {
avatar={user.avatarUrl || undefined}
shape={'square'}
size={64}
style={{ boxShadow: `0 0 0 4px ${theme.colorBgContainer}`, flexShrink: 0 }}
style={{ boxShadow: `0 0 0 4px ${cssVar.colorBgContainer}`, flexShrink: 0 }}
/>
<Flexbox align={'flex-start'} gap={16} horizontal justify={'space-between'}>
<Flexbox
@@ -12,7 +12,7 @@ import {
TooltipGroup,
} from '@lobehub/ui';
import { App, Dropdown } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import {
AlertTriangle,
ClockIcon,
@@ -60,20 +60,20 @@ const getStatusTagColor = (status?: AgentStatus) => {
}
};
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
author: css`
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
desc: css`
flex: 1;
margin: 0 !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
footer: css`
margin-block-start: 16px;
border-block-start: 1px dashed ${token.colorBorder};
background: ${token.colorBgContainerSecondary};
border-block-start: 1px dashed ${cssVar.colorBorder};
background: ${cssVar.colorBgContainer};
`,
moreButton: css`
position: absolute;
@@ -86,16 +86,16 @@ const useStyles = createStyles(({ css, token }) => {
`,
secondaryDesc: css`
font-size: 12px;
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
statTag: css`
border-radius: 4px;
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
font-size: 11px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
`,
title: css`
margin: 0 !important;
@@ -103,7 +103,7 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
wrapper: css`
@@ -129,7 +129,6 @@ const UserAgentCard = memo<UserAgentCardProps>(
status,
identifier,
}) => {
const { styles } = useStyles();
const { t } = useTranslation(['discover', 'setting']);
const navigate = useNavigate();
const { message } = App.useApp();
@@ -261,7 +260,7 @@ const UserAgentCard = memo<UserAgentCardProps>(
{isOwner && (
<Dropdown menu={{ items: menuItems }} trigger={['click']}>
<div
className={`more-button ${styles.moreButton}`}
className={cx('more-button', styles.moreButton)}
onClick={(e) => e.stopPropagation()}
>
<Icon icon={MoreVerticalIcon} size={16} style={{ cursor: 'pointer' }} />
@@ -2,7 +2,7 @@
import { Avatar, Block, Flexbox, Grid, Icon, Tag, Text, Tooltip } from '@lobehub/ui';
import { App } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import { ClockIcon, DownloadIcon, Heart } from 'lucide-react';
import qs from 'query-string';
import { memo, useCallback } from 'react';
@@ -18,12 +18,12 @@ import { formatIntergerNumber } from '@/utils/format';
import { useUserDetailContext } from './DetailProvider';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
favoriteButton: css`
cursor: pointer;
@@ -32,7 +32,7 @@ const useStyles = createStyles(({ css, token }) => {
inset-block-start: 12px;
inset-inline-end: 12px;
color: ${token.colorError};
color: ${cssVar.colorError};
opacity: 0;
@@ -40,21 +40,21 @@ const useStyles = createStyles(({ css, token }) => {
`,
footer: css`
margin-block-start: 16px;
border-block-start: 1px dashed ${token.colorBorder};
background: ${token.colorBgContainerSecondary};
border-block-start: 1px dashed ${cssVar.colorBorder};
background: ${cssVar.colorBgContainer};
`,
secondaryDesc: css`
font-size: 12px;
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
statTag: css`
border-radius: 4px;
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
font-size: 11px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
`,
title: css`
margin: 0 !important;
@@ -62,7 +62,7 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
wrapper: css`
@@ -90,7 +90,6 @@ const FavoriteAgentCard = memo<FavoriteAgentCardProps>(
onUnfavorite,
showUnfavorite,
}) => {
const { styles } = useStyles();
const { t } = useTranslation('discover');
const navigate = useNavigate();
@@ -119,7 +118,7 @@ const FavoriteAgentCard = memo<FavoriteAgentCardProps>(
{showUnfavorite && (
<Tooltip title={t('user.unfavorite')}>
<div
className={`favorite-button ${styles.favoriteButton}`}
className={cx('favorite-button', styles.favoriteButton)}
onClick={(e) => {
e.stopPropagation();
onUnfavorite(identifier);
@@ -2,7 +2,7 @@
import { Avatar, Block, Flexbox, Grid, Icon, Tag, Text, Tooltip } from '@lobehub/ui';
import { App } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx } from 'antd-style';
import { ClockIcon, Heart } from 'lucide-react';
import qs from 'query-string';
import { memo, useCallback } from 'react';
@@ -17,12 +17,12 @@ import { useDiscoverStore } from '@/store/discover';
import { useUserDetailContext } from './DetailProvider';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
desc: css`
flex: 1;
margin: 0 !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
favoriteButton: css`
cursor: pointer;
@@ -31,7 +31,7 @@ const useStyles = createStyles(({ css, token }) => {
inset-block-start: 12px;
inset-inline-end: 12px;
color: ${token.colorError};
color: ${cssVar.colorError};
opacity: 0;
@@ -39,12 +39,12 @@ const useStyles = createStyles(({ css, token }) => {
`,
footer: css`
margin-block-start: 16px;
border-block-start: 1px dashed ${token.colorBorder};
background: ${token.colorBgContainerSecondary};
border-block-start: 1px dashed ${cssVar.colorBorder};
background: ${cssVar.colorBgContainer};
`,
secondaryDesc: css`
font-size: 12px;
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
title: css`
margin: 0 !important;
@@ -52,7 +52,7 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
wrapper: css`
@@ -79,7 +79,6 @@ const FavoritePluginCard = memo<FavoritePluginCardProps>(
onUnfavorite,
showUnfavorite,
}) => {
const { styles } = useStyles();
const { t } = useTranslation('discover');
const navigate = useNavigate();
@@ -107,7 +106,7 @@ const FavoritePluginCard = memo<FavoritePluginCardProps>(
{showUnfavorite && (
<Tooltip title={t('user.unfavorite')}>
<div
className={`favorite-button ${styles.favoriteButton}`}
className={cx('favorite-button', styles.favoriteButton)}
onClick={(e) => {
e.stopPropagation();
onUnfavorite(identifier);
@@ -1,7 +1,7 @@
'use client';
import { Flexbox, Skeleton } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { memo } from 'react';
import ListLoading from '@/app/[variants]/(main)/community/components/ListLoading';
@@ -9,7 +9,6 @@ import ListLoading from '@/app/[variants]/(main)/community/components/ListLoadin
import Banner from './features/Header/Banner';
const Loading = memo(() => {
const theme = useTheme();
return (
<Flexbox gap={24} width={'100%'}>
{/* User Header Skeleton */}
@@ -18,7 +17,7 @@ const Loading = memo(() => {
<Skeleton.Avatar
shape={'square'}
size={64}
style={{ boxShadow: `0 0 0 4px ${theme.colorBgContainer}`, flexShrink: 0 }}
style={{ boxShadow: `0 0 0 4px ${cssVar.colorBgContainer}`, flexShrink: 0 }}
/>
<Skeleton paragraph={{ rows: 1 }} />
</Flexbox>
@@ -1,32 +1,33 @@
import { Button, Flexbox, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cx, useThemeMode } from 'antd-style';
import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import DefaultFooter from '@/features/Setting/Footer';
import { useMarketAuth } from '@/layout/AuthProvider/MarketAuth';
const useStyles = createStyles(({ css, isDarkMode }) => {
const image = isDarkMode
? '/images/community_footer_dark.webp'
: '/images/community_footer_light.webp';
return {
footer: css`
min-height: 320px;
padding-block-start: 32px;
const styles = createStaticStyles(({ css }) => ({
footer: css`
min-height: 320px;
padding-block-start: 32px;
background-image: url(${image});
background-repeat: no-repeat;
background-position: center bottom;
background-size: 512px auto;
background-blend-mode: ${isDarkMode ? 'screen' : 'multiply'};
`,
};
});
background-repeat: no-repeat;
background-position: center bottom;
background-size: 512px auto;
`,
footer_dark: css`
background-image: url('/images/community_footer_dark.webp');
background-blend-mode: screen;
`,
footer_light: css`
background-image: url('/images/community_footer_light.webp');
background-blend-mode: multiply;
`,
}));
const Footer = memo(() => {
const { t } = useTranslation('discover');
const { styles } = useStyles();
const { isDarkMode } = useThemeMode();
const { isAuthenticated, signIn } = useMarketAuth();
const [loading, setLoading] = useState(false);
const handleSignIn = useCallback(async () => {
@@ -42,7 +43,12 @@ const Footer = memo(() => {
if (isAuthenticated) return <DefaultFooter />;
return (
<Flexbox align={'center'} className={styles.footer} flex={'none'} gap={4}>
<Flexbox
align={'center'}
className={cx(styles.footer, isDarkMode ? styles.footer_dark : styles.footer_light)}
flex={'none'}
gap={4}
>
<Text align={'center'} as={'h2'} fontSize={22} strong>
{t('footer.title')}
</Text>
@@ -1,6 +1,6 @@
'use client';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { memo } from 'react';
import { useLocation } from 'react-router-dom';
@@ -9,15 +9,20 @@ import UserAvatar from '@/app/[variants]/(main)/community/features/UserAvatar';
import NavHeader from '@/features/NavHeader';
import SortButton from '../features/SortButton';
import { styles } from './Header/style';
const Header = memo(() => {
const theme = useTheme();
const location = useLocation();
// const { activeKey } = useNav();
const isHome = location.pathname === '/';
const cssVariables: Record<string, string> = {
'--header-border-color': cssVar.colorBorderSecondary,
};
return (
<NavHeader
className={styles.headerContainer}
left={<StoreSearchBar />}
right={
!isHome && (
@@ -28,9 +33,7 @@ const Header = memo(() => {
</>
)
}
style={{
borderBottom: `1px solid ${theme.colorBorderSecondary}`,
}}
style={cssVariables}
styles={{
left: { flex: 1 },
}}
@@ -0,0 +1,8 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css, cssVar }) => ({
// Header 容器
headerContainer: css`
border-block-end: 1px solid var(--header-border-color, ${cssVar.colorBorderSecondary});
`,
}));
@@ -6,26 +6,25 @@ import WideScreenContainer from '@/features/WideScreenContainer';
import { MAX_WIDTH } from '../../features/const';
import Footer from './Footer';
import Header from './Header';
import { styles } from './style';
const Layout = () => {
return (
<>
<Header />
<Flexbox height={'100%'} style={{ overflowY: 'auto' }} width={'100%'}>
<Flexbox className={styles.mainContainer} height={'100%'} width={'100%'}>
<WideScreenContainer
className={styles.contentContainer}
gap={16}
minWidth={MAX_WIDTH}
paddingBlock={16}
style={{
minHeight: '100%',
}}
wrapperStyle={{
minHeight: '100%',
position: 'relative',
}}
>
<Outlet />
<div style={{ flex: 1 }} />
<div className={styles.spacer} />
<Footer />
</WideScreenContainer>
</Flexbox>
@@ -0,0 +1,20 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css }) => ({
// 内容容器
contentContainer: css`
min-height: 100%;
`,
// 主容器
mainContainer: css`
overflow-y: auto;
`,
// 占位符
spacer: css`
flex: 1;
`,
}));
@@ -3,10 +3,11 @@ import { Outlet } from 'react-router-dom';
import CategoryContainer from '../../../components/CategoryContainer';
import Category from '../features/Category';
import { styles } from './style';
const Layout = () => {
return (
<Flexbox gap={24} horizontal style={{ position: 'relative' }} width={'100%'}>
<Flexbox className={styles.mainContainer} gap={24} horizontal width={'100%'}>
<CategoryContainer>
<Category />
</CategoryContainer>
@@ -0,0 +1,8 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css }) => ({
// 主容器
mainContainer: css`
position: relative;
`,
}));
@@ -1,5 +1,5 @@
import { Avatar, Block, Flexbox, Icon, Text } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles } from 'antd-style';
import { ClockIcon } from 'lucide-react';
import qs from 'query-string';
import React, { memo, useCallback } from 'react';
@@ -13,32 +13,32 @@ import { type AssistantMarketSource, type DiscoverAssistantItem } from '@/types/
import TokenTag from './TokenTag';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
author: css`
cursor: pointer;
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
&:hover {
color: ${token.colorPrimary};
color: ${cssVar.colorPrimary};
}
`,
code: css`
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
`,
desc: css`
flex: 1;
margin: 0 !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
footer: css`
margin-block-start: 16px;
border-block-start: 1px dashed ${token.colorBorder};
background: ${token.colorBgContainerSecondary};
border-block-start: 1px dashed ${cssVar.colorBorder};
background: ${cssVar.colorBgContainer};
`,
secondaryDesc: css`
font-size: 12px;
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
title: css`
margin: 0 !important;
@@ -46,7 +46,7 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
@@ -68,7 +68,6 @@ const AssistantItem = memo<DiscoverAssistantItem>(
backgroundColor,
userName,
}) => {
const { styles } = useStyles();
const navigate = useNavigate();
const { source } = useQuery() as { source?: AssistantMarketSource };
const link = qs.stringifyUrl(
@@ -1,22 +1,22 @@
import { MCP } from '@lobehub/icons';
import { Flexbox, Icon, Tag, Tooltip } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { BookTextIcon, CoinsIcon, DownloadIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { formatIntergerNumber } from '@/utils/format';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
token: css`
border-radius: 4px;
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
font-size: 11px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
`,
};
});
@@ -31,7 +31,6 @@ interface TokenTagProps {
const TokenTag = memo<TokenTagProps>(
({ tokenUsage, pluginCount, knowledgeCount, installCount, placement = 'right' }) => {
const { styles, theme } = useStyles();
const { t } = useTranslation('discover');
return (
<Flexbox align={'center'} gap={4} horizontal>
@@ -61,7 +60,7 @@ const TokenTag = memo<TokenTagProps>(
styles={{ root: { pointerEvents: 'none' } }}
title={t('assistants.withPlugin')}
>
<Tag icon={<Icon fill={theme.colorTextSecondary} icon={MCP} />}>{pluginCount}</Tag>
<Tag icon={<Icon fill={cssVar.colorTextSecondary} icon={MCP} />}>{pluginCount}</Tag>
</Tooltip>
)}
{Boolean(knowledgeCount && knowledgeCount > 0) && (
@@ -1,7 +1,7 @@
'use client';
import { Pagination as Page } from 'antd';
import { createStyles, useResponsive } from 'antd-style';
import { createStaticStyles, useResponsive } from 'antd-style';
import { memo } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
@@ -11,16 +11,18 @@ import { type DiscoverTab } from '@/types/discover';
const SCROLL_CONTAINER_ID = 'lobe-mobile-scroll-container';
const useStyles = createStyles(({ css, token, prefixCls }) => {
const prefixCls = 'ant';
const styles = createStaticStyles(({ css, cssVar }) => {
return {
page: css`
.${prefixCls}-pagination-item-active {
border-color: ${token.colorFillSecondary};
background: ${token.colorFillSecondary};
border-color: ${cssVar.colorFillSecondary};
background: ${cssVar.colorFillSecondary};
&:hover {
border-color: ${token.colorFill};
background: ${token.colorFill};
border-color: ${cssVar.colorFill};
background: ${cssVar.colorFill};
}
}
`,
@@ -35,7 +37,6 @@ interface PaginationProps {
}
const Pagination = memo<PaginationProps>(({ tab, currentPage, total, pageSize }) => {
const { styles } = useStyles();
const { page } = useQuery();
const navigate = useNavigate();
const location = useLocation();
@@ -3,10 +3,11 @@ import { Outlet } from 'react-router-dom';
import CategoryContainer from '../../../components/CategoryContainer';
import Category from '../features/Category';
import { styles } from './style';
const Layout = () => {
return (
<Flexbox gap={24} horizontal style={{ position: 'relative' }} width={'100%'}>
<Flexbox className={styles.mainContainer} gap={24} horizontal width={'100%'}>
<CategoryContainer>
<Category />
</CategoryContainer>
@@ -0,0 +1,8 @@
import { createStaticStyles } from 'antd-style';
export const styles = createStaticStyles(({ css }) => ({
// 主容器
mainContainer: css`
position: relative;
`,
}));
@@ -1,5 +1,5 @@
import { Flexbox, Icon, Tooltip } from '@lobehub/ui';
import { useTheme } from 'antd-style';
import { cssVar } from 'antd-style';
import { Blend, Cloud, LaptopMinimalIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -10,19 +10,18 @@ interface ConnectionTypeTagProps {
const ConnectionTypeTag = memo<ConnectionTypeTagProps>(({ type }) => {
const { t } = useTranslation('discover');
const theme = useTheme();
const icons = {
hybrid: {
color: theme.purple,
color: cssVar.purple,
icon: Blend,
},
local: {
color: theme.colorWarning,
color: cssVar.colorWarning,
icon: LaptopMinimalIcon,
},
remote: {
color: theme.colorInfo,
color: cssVar.colorInfo,
icon: Cloud,
},
};
@@ -36,7 +35,7 @@ const ConnectionTypeTag = memo<ConnectionTypeTagProps>(({ type }) => {
gap={6}
horizontal
style={{
color: theme.colorTextSecondary,
color: cssVar.colorTextSecondary,
fontSize: 12,
}}
>
@@ -3,7 +3,7 @@
import { Github } from '@lobehub/icons';
import { ActionIcon, Avatar, Block, Flexbox, Icon, Tag, Text, Tooltip } from '@lobehub/ui';
import { Spotlight } from '@lobehub/ui/awesome';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { ClockIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -19,27 +19,27 @@ import { type DiscoverMcpItem } from '@/types/discover';
import ConnectionTypeTag from './ConnectionTypeTag';
import MetaInfo from './MetaInfo';
const useStyles = createStyles(({ css, token }) => {
const styles = createStaticStyles(({ css, cssVar }) => {
return {
author: css`
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
code: css`
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
`,
desc: css`
flex: 1;
margin: 0 !important;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
footer: css`
margin-block-start: 16px;
border-block-start: 1px dashed ${token.colorBorder};
background: ${token.colorBgContainerSecondary};
border-block-start: 1px dashed ${cssVar.colorBorder};
background: ${cssVar.colorBgContainer};
`,
secondaryDesc: css`
font-size: 12px;
color: ${token.colorTextDescription};
color: ${cssVar.colorTextDescription};
`,
title: css`
margin: 0 !important;
@@ -47,7 +47,7 @@ const useStyles = createStyles(({ css, token }) => {
font-weight: 500 !important;
&:hover {
color: ${token.colorLink};
color: ${cssVar.colorLink};
}
`,
};
@@ -75,7 +75,6 @@ const McpItem = memo<DiscoverMcpItem>(
github,
}) => {
const { t } = useTranslation('discover');
const { styles, theme } = useStyles();
const navigate = useNavigate();
const link = urlJoin('/community/mcp', identifier);
return (
@@ -150,7 +149,7 @@ const McpItem = memo<DiscoverMcpItem>(
rel="noopener noreferrer"
target={'_blank'}
>
<ActionIcon fill={theme.colorTextDescription} icon={Github} />
<ActionIcon fill={cssVar.colorTextDescription} icon={Github} />
</a>
)}
</Flexbox>
@@ -3,15 +3,16 @@ import { Outlet } from 'react-router-dom';
import CategoryContainer from '../../../components/CategoryContainer';
import Category from '../features/Category';
import { styles } from './style';
const Layout = () => {
return (
<Flexbox gap={24} horizontal style={{ position: 'relative' }} width={'100%'}>
<Flexbox className={styles.mainContainer} gap={24} horizontal width={'100%'}>
<CategoryContainer>
<Category />
</CategoryContainer>
<Flexbox flex={1} gap={16}>
{<Outlet />}
<Outlet />
</Flexbox>
</Flexbox>
);

Some files were not shown because too many files have changed in this diff Show More