♻️ refactor(modal): migrate confirm modals to @lobehub/ui/base-ui (Phase 1) (#15259)

* ♻️ refactor(modal): migrate confirm modals to @lobehub/ui/base-ui

Replace all `App.useApp().modal.confirm`, `Modal.confirm` and `AntModal.confirm`
call sites with the headless `confirmModal` from `@lobehub/ui/base-ui`, dropping
antd-only props (`centered`, `type`, `width`, `okButtonProps.type='primary'`,
`okButtonProps.loading`, `classNames.root`) that the base-ui imperative API does
not accept.

- 82 files touched; `modal.confirm`/`Modal.confirm` call sites now zero
- `PageEditor/store/action.ts`: drop `modal` arg from `handleDelete`
- `ResourceManager/useUploadFolder`: replace dynamic `import('antd').Modal`
- `Eval/DatasetsTab`: migrate `modal.success` to `confirmModal`

Part of LOBE-9645 Phase 1.

* ♻️ refactor(ui): migrate select/modal call sites to @lobehub/ui/base-ui

- Convert imperative-modal factories (createXxxModal + Content split) for apikey,
  creds (Create/Edit/View), provider (CreateNewProvider), and messenger LinkModal.
- Switch Select usages to base-ui Select (Messenger AgentSelect, provider sdkType).
- Restructure CreateNewProvider form to vertical layout with manual section titles
  for tighter spacing; drop FormModal/Form group nesting.
- Standardize small ActionIcon sizing via DESKTOP_HEADER_ICON_SMALL_SIZE
  (WideScreenButton, ToggleRightPanelButton, ContextDropdown, AddNewProvider).
- Fix missing title on ResourceManager delete confirm modal so the header
  (title + close X) renders.
- Update react skill and AGENTS.md to require base-ui priority over root @lobehub/ui
  / antd; expand component table and Common Mistakes with explicit base-ui rules.

* ♻️ refactor(ui): swap antd Select to base-ui Select and migrate createStyles to createStaticStyles

*  test: update test mocks for base-ui confirmModal migration

*  test(e2e): switch delete confirm selector to base-ui dialog role
This commit is contained in:
Innei
2026-05-28 02:46:27 +08:00
committed by GitHub
parent 8c0e66b633
commit b4b1205ee9
142 changed files with 1290 additions and 1326 deletions
+33 -11
View File
@@ -1,6 +1,6 @@
---
name: react
description: "LobeHub React component conventions — styling via `antd-style` `createStaticStyles` + `cssVar.*` (zero-runtime preferred over `createStyles` + `token`), `@lobehub/ui` over antd when both exist, routing via `react-router-dom` (not `next/link`). Use when writing or editing any `.tsx` under `src/**`. Triggers on `createStaticStyles`, `createStyles`, `cssVar`, `antd-style`, `Flexbox`, `Center`, `Select`, `Modal`, `Drawer`, `Button`, `Tooltip`, `DropdownMenu`, `Popover`, `Switch`, `ScrollArea`, `Link`, `useNavigate`, `react-router-dom`, `next/link`, `desktopRouter`, `componentMap.desktop`, `.desktop.tsx`, 'new component', 'new page', 'edit layout', 'add styles', 'zustand selector', '@lobehub/ui', 'antd import'."
description: "LobeHub React component conventions — base-ui (`@lobehub/ui/base-ui`) first for headless primitives (Select, Modal, DropdownMenu, ContextMenu, Popover, ScrollArea, Switch, Toast, FloatingSheet), then `@lobehub/ui` root, antd as last resort; styling via `antd-style` `createStaticStyles` + `cssVar.*` (zero-runtime preferred over `createStyles` + `token`); routing via `react-router-dom` (not `next/link`). Use when writing or editing any `.tsx` under `src/**`. Triggers on `createStaticStyles`, `createStyles`, `cssVar`, `antd-style`, `Flexbox`, `Center`, `Select`, `Modal`, `Drawer`, `Button`, `Tooltip`, `DropdownMenu`, `ContextMenu`, `Popover`, `Switch`, `ScrollArea`, `Toast`, `FloatingSheet`, `Link`, `useNavigate`, `react-router-dom`, `next/link`, `desktopRouter`, `componentMap.desktop`, `.desktop.tsx`, `base-ui`, `@lobehub/ui/base-ui`, 'new component', 'new page', 'edit layout', 'add styles', 'zustand selector', '@lobehub/ui', 'antd import'."
user-invocable: false
---
@@ -17,22 +17,41 @@ user-invocable: false
## Component Priority
1. **`src/components`** — project-specific reusable components
2. **`@lobehub/ui/base-ui`** — headless primitives (Select, Modal, DropdownMenu, Popover, Switch, ScrollArea…)
3. **`@lobehub/ui`** — higher-level components (ActionIcon, Markdown, DragPage…)
4. **Custom implementation** — last resort; never reach for antd directly
2. **`@lobehub/ui/base-ui`** — headless primitives. **If the component lives here, use it. Do NOT import the same-named root export.**
3. **`@lobehub/ui`** — higher-level / antd-wrapping components (only when no base-ui equivalent)
4. **antd** — only when neither base-ui nor `@lobehub/ui` root provides it
5. **Custom implementation** — true last resort
If unsure about available components, search existing code or check `node_modules/@lobehub/ui/es/index.mjs`.
If unsure about available components, search existing code or check `node_modules/@lobehub/ui/es/index.mjs` and `node_modules/@lobehub/ui/es/base-ui/`.
### Common @lobehub/ui Components
### `@lobehub/ui/base-ui` — always prefer for these
| Component | Import |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| `Select` (+ `SelectProps`, `SelectOption`) | `import { Select } from '@lobehub/ui/base-ui';` |
| `Modal` (imperative API) | `import { createModal, confirmModal, useModalContext, type ModalInstance } from '@lobehub/ui/base-ui';` |
| `DropdownMenu` | `import { DropdownMenu } from '@lobehub/ui/base-ui';` |
| `ContextMenu` | `import { ContextMenu } from '@lobehub/ui/base-ui';` |
| `Popover` | `import { Popover } from '@lobehub/ui/base-ui';` |
| `ScrollArea` | `import { ScrollArea } from '@lobehub/ui/base-ui';` |
| `Switch` | `import { Switch } from '@lobehub/ui/base-ui';` |
| `Toast` | `import { Toast } from '@lobehub/ui/base-ui';` |
| `FloatingSheet` | `import { FloatingSheet } from '@lobehub/ui/base-ui';` |
For Modal specifically, see the dedicated **modal** skill — use the imperative `createModal({ content: … })` pattern over the legacy `<Modal open … />` declarative pattern. base-ui has its own `ModalHost` already mounted in `SPAGlobalProvider`.
> Common slip: `import { Select } from '@lobehub/ui'` looks fine but it's the antd-backed Select. Use base-ui Select. Same for `Modal`, `DropdownMenu`, etc.
### `@lobehub/ui` root — use when base-ui has no equivalent
| Category | Components |
| ------------ | ------------------------------------------------------------------------------- |
| ------------ | ------------------------------------------------------------------------------------- |
| General | ActionIcon, ActionIconGroup, Block, Button, Icon |
| Data Display | Avatar, Collapse, Empty, Highlighter, Markdown, Tag, Tooltip |
| Data Entry | CodeEditor, CopyButton, EditableText, Form, FormModal, Input, SearchBar, Select |
| Feedback | Alert, Drawer, Modal |
| Data Entry | CodeEditor, CopyButton, EditableText, Form, Input, InputPassword, SearchBar, TextArea |
| Feedback | Alert, Drawer |
| Layout | Center, DraggablePanel, Flexbox, Grid, Header, MaskShadow |
| Navigation | Burger, Dropdown, Menu, SideNav, Tabs |
| Navigation | Burger, Menu, SideNav, Tabs |
## Layout
@@ -86,9 +105,12 @@ errorElement: <ErrorBoundary />;
## Common Mistakes
| Mistake | Fix |
| ----------------------------------------------------------------- | ----------------------------------------------------------------- |
| ------------------------------------------------------------------ | --------------------------------------------------------------------------- |
| Using `next/link` in SPA | Use `react-router-dom` `Link` |
| Using antd directly | Use `@lobehub/ui/base-ui` first, then `@lobehub/ui` |
| `import { Select } from '@lobehub/ui'` | `import { Select } from '@lobehub/ui/base-ui'` |
| `import { Modal } from '@lobehub/ui'` + `<Modal open>` declarative | `createModal` / `confirmModal` from `@lobehub/ui/base-ui` (see modal skill) |
| `import { DropdownMenu/Popover/Switch } from '@lobehub/ui'` | Import same name from `@lobehub/ui/base-ui` instead |
| `createStyles` for static styles | Use `createStaticStyles` + `cssVar` |
| Editing only `desktopRouter.config.tsx` | Must edit both `.tsx` and `.desktop.tsx` |
| Using `margin` for flex spacing | Use `gap` prop on Flexbox |
+1
View File
@@ -7,6 +7,7 @@ Guidelines for using AI coding agents in this LobeHub repository.
- Next.js 16 + React 19 + TypeScript
- SPA inside Next.js with `react-router-dom`
- `@lobehub/ui`, antd for components; antd-style for CSS-in-JS — **prefer `createStaticStyles` with `cssVar.*`** (zero-runtime); only fall back to `createStyles` + `token` when styles genuinely need runtime computation. See `.cursor/docs/createStaticStyles_migration_guide.md`.
- **Component priority**: `@lobehub/ui/base-ui` (headless primitives) **first**, then `@lobehub/ui` root, then antd as last resort. When the component exists in base-ui, use it — never reach for the root or antd counterpart. Base-ui covers `Select`, `Modal` / `createModal` / `confirmModal`, `DropdownMenu`, `ContextMenu`, `Popover`, `ScrollArea`, `Switch`, `Toast`, `FloatingSheet`. Prefer `@lobehub/ui/base-ui` for new code and migrate root-package call sites opportunistically.
- react-i18next for i18n; zustand for state management
- SWR for data fetching; TRPC for type-safe backend
- Drizzle ORM with PostgreSQL; Vitest for testing
@@ -427,10 +427,10 @@ When('用户选择删除选项', async function (this: CustomWorld) {
When('用户确认删除', async function (this: CustomWorld) {
console.log(' 📍 Step: 确认删除...');
// A confirmation modal should appear
const confirmButton = this.page.locator('.ant-modal-confirm-btns button.ant-btn-dangerous');
const confirmButton = this.page
.getByRole('dialog')
.getByRole('button', { name: /^(ok|delete|删除|确认|确定)$/i });
// Wait for modal to appear
await expect(confirmButton).toBeVisible({ timeout: 5000 });
await confirmButton.click();
+3 -1
View File
@@ -294,7 +294,9 @@ When('用户在菜单中选择删除', async function (this: CustomWorld) {
When('用户在弹窗中确认删除', async function (this: CustomWorld) {
console.log(' 📍 Step: 确认删除...');
const confirmButton = this.page.locator('.ant-modal-confirm-btns button.ant-btn-dangerous');
const confirmButton = this.page
.getByRole('dialog')
.getByRole('button', { name: /^(ok|delete|删除|确认|确定)$/i });
await expect(confirmButton).toBeVisible({ timeout: 5000 });
await confirmButton.click();
await this.page.waitForTimeout(500);
+28 -27
View File
@@ -1,36 +1,32 @@
import { createStyles, keyframes } from 'antd-style';
import { createStaticStyles, cssVar, cx } from 'antd-style';
import { type CSSProperties, memo } from 'react';
const fade = keyframes`
0%, 100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
`;
interface StyleParams {
color?: string;
gap: number;
size: number;
}
const useStyles = createStyles(({ css, token }, { size, gap, color }: StyleParams) => ({
const styles = createStaticStyles(({ css }) => ({
container: css`
display: inline-flex;
flex-direction: row;
gap: ${gap}px;
gap: var(--dots-loading-gap);
align-items: center;
`,
dot: css`
width: ${size}px;
height: ${size}px;
width: var(--dots-loading-size);
height: var(--dots-loading-size);
border-radius: 50%;
background-color: ${color || token.colorTextSecondary};
background-color: var(--dots-loading-color);
animation: ${fade} 1.2s ease-in-out infinite;
animation: dots-loading-fade 1.2s ease-in-out infinite;
@keyframes dots-loading-fade {
0%,
100% {
opacity: 0.3;
}
50% {
opacity: 1;
}
}
`,
}));
@@ -46,12 +42,17 @@ interface DotsLoadingProps extends StyleArgs {
}
const DotsLoading = memo<DotsLoadingProps>(({ size = 4, gap = 3, color, className, style }) => {
const { styles: s, cx } = useStyles({ color, gap, size });
const cssVars = {
'--dots-loading-color': color || cssVar.colorTextSecondary,
'--dots-loading-gap': `${gap}px`,
'--dots-loading-size': `${size}px`,
} as CSSProperties;
return (
<div className={cx(s.container, className)} style={style}>
<div className={s.dot} style={{ animationDelay: '0s' }} />
<div className={s.dot} style={{ animationDelay: '0.15s' }} />
<div className={s.dot} style={{ animationDelay: '0.3s' }} />
<div className={cx(styles.container, className)} style={{ ...cssVars, ...style }}>
<div className={styles.dot} style={{ animationDelay: '0s' }} />
<div className={styles.dot} style={{ animationDelay: '0.15s' }} />
<div className={styles.dot} style={{ animationDelay: '0.3s' }} />
</div>
);
});
@@ -30,7 +30,6 @@ vi.mock('antd-style', async (importOriginal) => {
cssVar: {},
}),
),
createStyles: vi.fn(() => () => ({ styles: {} })),
useTheme: () => ({
colorTextSecondary: '#999',
}),
@@ -1,7 +1,8 @@
'use client';
import { confirmModal, Select } from '@lobehub/ui/base-ui';
import type { TableColumnsType } from 'antd';
import { App, Button, Popconfirm, Select, Space, Table, Tag, Typography } from 'antd';
import { App, Button, Popconfirm, Space, Table, Tag, Typography } from 'antd';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -28,7 +29,7 @@ const FILE_PREVIEW_LIMIT = 5;
const AgentDocuments = memo(() => {
const { t } = useTranslation(['setting', 'common']);
const { message, modal } = App.useApp();
const { message } = App.useApp();
const agentId = useAgentStore((s) => s.activeAgentId);
const [templateId, setTemplateId] = useState(DEFAULT_TEMPLATE_ID);
const [isInitializingTemplate, setIsInitializingTemplate] = useState(false);
@@ -160,7 +161,7 @@ const AgentDocuments = memo(() => {
const previewFilenames = overwrittenFilenames.slice(0, FILE_PREVIEW_LIMIT);
const remainingCount = overwrittenCount - previewFilenames.length;
modal.confirm({
confirmModal({
content: (
<Space direction={'vertical'} size={8}>
<Typography.Text>
@@ -12,7 +12,7 @@ import {
Markdown,
Text,
} from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { cssVar } from 'antd-style';
import { MessageCircle, MoreHorizontal, Pencil, Trash } from 'lucide-react';
import { memo, useCallback, useMemo, useState } from 'react';
@@ -29,7 +29,6 @@ interface CommentCardProps {
const CommentCard = memo<CommentCardProps>(({ activity }) => {
const { t } = useTranslation('chat');
const { modal } = App.useApp();
const deleteComment = useTaskStore((s) => s.deleteComment);
const updateComment = useTaskStore((s) => s.updateComment);
@@ -64,16 +63,14 @@ const CommentCard = memo<CommentCardProps>(({ activity }) => {
const handleDelete = useCallback(() => {
if (!commentId) return;
modal.confirm({
centered: true,
confirmModal({
content: t('taskDetail.comment.deleteConfirm.content'),
okButtonProps: { danger: true },
okText: t('taskDetail.comment.deleteConfirm.ok'),
onOk: () => deleteComment(commentId),
title: t('taskDetail.comment.deleteConfirm.title'),
type: 'error',
});
}, [commentId, deleteComment, modal, t]);
}, [commentId, deleteComment, t]);
const menuItems = useMemo<DropdownItem[]>(
() => [
@@ -9,7 +9,7 @@ import {
Tag,
Text,
} from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { cssVar } from 'antd-style';
import { FileTextIcon, MoreHorizontal, Package, Trash } from 'lucide-react';
import { memo, useCallback, useMemo, useState } from 'react';
@@ -30,7 +30,6 @@ const flattenWorkspace = (nodes: TaskDetailWorkspaceNode[]): TaskDetailWorkspace
const ArtifactCard = memo<{ node: TaskDetailWorkspaceNode }>(({ node }) => {
const { t } = useTranslation('chat');
const { modal } = App.useApp();
const openDocumentPreview = useDocumentStore((s) => s.openDocumentPreview);
const unpinDocument = useTaskStore((s) => s.unpinDocument);
const activeTaskId = useTaskStore(taskDetailSelectors.activeTaskId);
@@ -41,16 +40,14 @@ const ArtifactCard = memo<{ node: TaskDetailWorkspaceNode }>(({ node }) => {
const handleDelete = useCallback(() => {
const taskId = node.sourceTaskId ?? activeTaskId;
if (!taskId) return;
modal.confirm({
centered: true,
confirmModal({
content: t('taskDetail.artifactMenu.deleteConfirm.content'),
okButtonProps: { danger: true },
okText: t('taskDetail.artifactMenu.deleteConfirm.ok'),
onOk: () => unpinDocument(taskId, node.documentId),
title: t('taskDetail.artifactMenu.deleteConfirm.title'),
type: 'error',
});
}, [activeTaskId, modal, node.documentId, node.sourceTaskId, t, unpinDocument]);
}, [activeTaskId, node.documentId, node.sourceTaskId, t, unpinDocument]);
const menuItems = useMemo<DropdownItem[]>(
() => [
@@ -7,7 +7,7 @@ import {
Icon,
Text,
} from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { cssVar } from 'antd-style';
import { Check, ChevronDownIcon, ChevronUpIcon, MoreHorizontal, Trash } from 'lucide-react';
import { memo, useCallback, useMemo, useState } from 'react';
@@ -32,15 +32,13 @@ interface TaskBriefCardProps {
const TaskBriefCard = memo<TaskBriefCardProps>(
({ brief, onAfterResolve, onAfterAddComment, onAfterDelete }) => {
const { t } = useTranslation('home');
const { modal } = App.useApp();
const deleteBrief = useBriefStore((s) => s.deleteBrief);
const isResolved = Boolean(brief.resolvedAction);
const [expanded, setExpanded] = useState(false);
const showFull = !isResolved || expanded;
const handleDelete = useCallback(() => {
modal.confirm({
centered: true,
confirmModal({
content: t('brief.deleteConfirm.content'),
okButtonProps: { danger: true },
okText: t('brief.deleteConfirm.ok'),
@@ -49,9 +47,8 @@ const TaskBriefCard = memo<TaskBriefCardProps>(
await onAfterDelete?.();
},
title: t('brief.deleteConfirm.title'),
type: 'error',
});
}, [brief.id, deleteBrief, modal, onAfterDelete, t]);
}, [brief.id, deleteBrief, onAfterDelete, t]);
const menuItems = useMemo<DropdownItem[]>(
() => [
@@ -1,4 +1,5 @@
import { ActionIcon, copyToClipboard, type DropdownItem, DropdownMenu, Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { CopyIcon, LinkIcon, MoreHorizontal, Trash } from 'lucide-react';
import { memo, useCallback, useMemo } from 'react';
@@ -13,7 +14,7 @@ import { taskDetailPath } from '../shared/taskDetailPath';
const TaskDetailHeaderActions = memo(() => {
const { t } = useTranslation(['chat', 'common']);
const { modal, message } = App.useApp();
const { message } = App.useApp();
const navigate = useNavigate();
const appOrigin = useAppOrigin();
const taskId = useTaskStore(taskDetailSelectors.activeTaskId);
@@ -22,8 +23,7 @@ const TaskDetailHeaderActions = memo(() => {
const triggerDelete = useCallback(() => {
if (!taskId) return;
modal.confirm({
centered: true,
confirmModal({
content: t('taskDetail.deleteConfirm.content'),
okButtonProps: { danger: true },
okText: t('taskDetail.deleteConfirm.ok'),
@@ -32,9 +32,8 @@ const TaskDetailHeaderActions = memo(() => {
navigate('/tasks');
},
title: t('taskDetail.deleteConfirm.title'),
type: 'error',
});
}, [taskId, modal, t, deleteTask, navigate]);
}, [taskId, t, deleteTask, navigate]);
const menuItems = useMemo<DropdownItem[]>(() => {
if (!taskId) return [];
@@ -81,6 +81,10 @@ vi.mock('antd-style', () => ({
},
}));
vi.mock('@lobehub/ui/base-ui', () => ({
confirmModal: vi.fn(),
}));
vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: (key: string) => key }),
}));
@@ -1,5 +1,6 @@
import type { TaskDetailSubtask } from '@lobechat/types';
import { ActionIcon, Block, Flexbox, Icon, showContextMenu, Text } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, ConfigProvider, Tree } from 'antd';
import type { DataNode } from 'antd/es/tree';
import { cssVar } from 'antd-style';
@@ -127,7 +128,7 @@ const toTreeData = (tree: TaskTreeNode[]): DataNode[] => {
const TaskSubtasks = memo(() => {
const { t } = useTranslation('chat');
const { message, modal } = App.useApp();
const { message } = App.useApp();
const navigate = useNavigate();
const agentId = useTaskStore(taskDetailSelectors.activeTaskAgentId);
const subtasks = useTaskStore(taskDetailSelectors.activeTaskSubtasks);
@@ -210,9 +211,8 @@ const TaskSubtasks = memo(() => {
}
const canRun = plan.totalRunnable > 0;
modal.confirm({
confirmModal({
cancelText: t('taskDetail.runAll.cancel'),
centered: true,
content: <RunSubtasksPreview plan={plan} />,
okButtonProps: canRun ? undefined : { disabled: true },
okText: t('taskDetail.runAll.confirm', { count: plan.totalRunnable }),
@@ -234,7 +234,6 @@ const TaskSubtasks = memo(() => {
}
},
title: t('taskDetail.runAll.title'),
width: 520,
});
} catch (error) {
console.error('[TaskSubtasks] Failed to plan subtasks:', error);
@@ -242,7 +241,7 @@ const TaskSubtasks = memo(() => {
} finally {
setIsPlanning(false);
}
}, [taskId, isPlanning, message, modal, t, runReadySubtasks]);
}, [taskId, isPlanning, message, t, runReadySubtasks]);
if (!taskId) return null;
@@ -7,6 +7,7 @@ import {
Icon,
type MenuInfo,
} from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { cssVar } from 'antd-style';
import {
@@ -55,7 +56,7 @@ export interface TaskContextMenuActions {
export const useTaskContextMenuActions = (): TaskContextMenuActions => {
const { t } = useTranslation(['chat', 'common']);
const { modal, message } = App.useApp();
const { message } = App.useApp();
const appOrigin = useAppOrigin();
const updateTaskStatus = useTaskStore((s) => s.updateTaskStatus);
@@ -72,8 +73,7 @@ export const useTaskContextMenuActions = (): TaskContextMenuActions => {
return useMemo<TaskContextMenuActions>(() => {
const triggerDelete = (identifier: string) => {
modal.confirm({
centered: true,
confirmModal({
content: t('taskDetail.deleteConfirm.content'),
okButtonProps: { danger: true },
okText: t('taskDetail.deleteConfirm.ok'),
@@ -81,7 +81,6 @@ export const useTaskContextMenuActions = (): TaskContextMenuActions => {
await deleteTask(identifier);
},
title: t('taskDetail.deleteConfirm.title'),
type: 'error',
});
};
@@ -277,7 +276,6 @@ export const useTaskContextMenuActions = (): TaskContextMenuActions => {
return { buildItems, installKeyboardHandlers };
}, [
modal,
message,
t,
appOrigin,
@@ -1,7 +1,7 @@
'use client';
import { ActionIcon, Flexbox, Text } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { createStaticStyles, cssVar } from 'antd-style';
import { Archive, Star, Trash2, X } from 'lucide-react';
import { memo, useCallback } from 'react';
@@ -44,7 +44,6 @@ const styles = createStaticStyles(({ css }) => ({
const BulkActionBar = memo(() => {
const { t } = useTranslation('topic');
const { modal } = App.useApp();
const selectedIds = useTopicsViewStore((s) => s.selectedIds);
const exitSelectMode = useTopicsViewStore((s) => s.exitSelectMode);
@@ -68,7 +67,7 @@ const BulkActionBar = memo(() => {
}, [selectedIds, updateTopicStatus, exitSelectMode]);
const handleBatchDelete = useCallback(() => {
modal.confirm({
confirmModal({
content: t('management.bulk.deleteConfirm', { count: selectedIds.length }),
okButtonProps: { danger: true },
okText: t('management.bulk.delete'),
@@ -82,7 +81,7 @@ const BulkActionBar = memo(() => {
},
title: t('management.bulk.deleteTitle'),
});
}, [selectedIds, modal, t, removeTopic, exitSelectMode]);
}, [selectedIds, t, removeTopic, exitSelectMode]);
if (selectedIds.length === 0) return null;
@@ -1,6 +1,7 @@
'use client';
import { ActionIcon, type DropdownItem, DropdownMenu } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { Archive, MoreHorizontal } from 'lucide-react';
import { memo, useCallback, useMemo } from 'react';
@@ -13,7 +14,7 @@ const THREE_MONTHS_MS = 90 * 24 * 60 * 60 * 1000;
const ToolbarActions = memo(() => {
const { t } = useTranslation('topic');
const { modal, message } = App.useApp();
const { message } = App.useApp();
// Operate on the management page's own bucket — not the sidebar's — since
// the management view is the one the user is acting on here.
@@ -34,7 +35,7 @@ const ToolbarActions = memo(() => {
return;
}
modal.confirm({
confirmModal({
content: t('management.actionsMenu.archiveStale.confirm', { count: stale.length }),
okText: t('management.actionsMenu.archiveStale.confirmOk'),
onOk: async () => {
@@ -47,7 +48,7 @@ const ToolbarActions = memo(() => {
},
title: t('management.actionsMenu.archiveStale.title'),
});
}, [topics, updateTopicStatus, modal, message, t]);
}, [topics, updateTopicStatus, message, t]);
const items: DropdownItem[] = useMemo(
() => [
@@ -50,6 +50,7 @@ const styles = createStaticStyles(({ css, cssVar }) => ({
margin-block-start: -4px;
padding-block-end: 8px;
padding-inline: 16px;
font-size: ${cssVar.fontSizeSM};
line-height: 1.45;
color: ${cssVar.colorTextSecondary};
@@ -10,7 +10,7 @@ import {
Tabs,
type TabsProps,
} from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { createStaticStyles, cx } from 'antd-style';
import isEqual from 'fast-deep-equal';
import { ChevronDown, ChevronUp, History, Sparkles, Undo2 } from 'lucide-react';
@@ -68,7 +68,6 @@ export interface CompressedGroupMessageProps {
const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
const { t } = useTranslation('chat');
const { modal } = App.useApp();
const [activeTab, setActiveTab] = useState<string>(() => getStoredTab(id));
const handleTabChange = useCallback(
@@ -86,13 +85,12 @@ const CompressedGroupMessage = memo<CompressedGroupMessageProps>(({ id }) => {
const cancelCompression = useConversationStore((s) => s.cancelCompression);
const handleCancelCompression = useCallback(() => {
modal.confirm({
centered: true,
confirmModal({
content: t('compression.cancelConfirm'),
onOk: () => cancelCompression(id),
title: t('compression.cancel'),
});
}, [id, cancelCompression, modal, t]);
}, [id, cancelCompression, t]);
const content = message?.content;
const rawCompressedMessages = (message as UIChatMessage)?.compressedMessages;
+15 -15
View File
@@ -5,8 +5,9 @@ import {
type RemoteHeterogeneousAgentType,
} from '@lobechat/heterogeneous-agents';
import { Button, Flexbox, Icon } from '@lobehub/ui';
import { Alert, Input, Modal, Select, Steps, Tag, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { Select } from '@lobehub/ui/base-ui';
import { Alert, Input, Modal, Steps, Tag, Typography } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import {
BotIcon,
CheckCircle2,
@@ -23,7 +24,7 @@ import { lambdaClient, lambdaQuery } from '@/libs/trpc/client';
import { useAgentStore } from '@/store/agent';
import { useHomeStore } from '@/store/home';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css }) => ({
avatarPreview: css`
display: flex;
align-items: center;
@@ -31,12 +32,12 @@ const useStyles = createStyles(({ css, token }) => ({
width: 48px;
height: 48px;
border-radius: ${token.borderRadiusLG}px;
border-radius: ${cssVar.borderRadiusLG};
font-size: 28px;
line-height: 1;
background: ${token.colorFillSecondary};
background: ${cssVar.colorFillSecondary};
`,
deviceItem: css`
display: flex;
@@ -53,20 +54,20 @@ const useStyles = createStyles(({ css, token }) => ({
padding-block: 12px;
padding-inline: 16px;
border: 1.5px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadiusLG}px;
border: 1.5px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadiusLG};
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
transition: border-color 0.2s;
&:hover {
border-color: ${token.colorPrimary};
border-color: ${cssVar.colorPrimary};
}
&[data-selected='true'] {
border-color: ${token.colorPrimary};
background: ${token.colorPrimaryBg};
border-color: ${cssVar.colorPrimary};
background: ${cssVar.colorPrimaryBg};
}
&[data-disabled='true'] {
@@ -74,18 +75,18 @@ const useStyles = createStyles(({ css, token }) => ({
opacity: 0.5;
&:hover {
border-color: ${token.colorBorderSecondary};
border-color: ${cssVar.colorBorderSecondary};
}
}
`,
platformDesc: css`
font-size: 13px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
platformName: css`
font-size: 15px;
font-weight: 500;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
}));
@@ -104,7 +105,6 @@ interface CreatePlatformAgentModalProps {
const CreatePlatformAgentModal = memo<CreatePlatformAgentModalProps>(
({ open, onClose, groupId }) => {
const { t } = useTranslation('chat');
const { styles } = useStyles();
const navigate = useNavigate();
const storeCreateAgent = useAgentStore((s) => s.createAgent);
const refreshAgentList = useHomeStore((s) => s.refreshAgentList);
+2 -5
View File
@@ -1,12 +1,11 @@
import { useEditor } from '@lobehub/editor/react';
import { type ModalProps } from '@lobehub/ui';
import { createRawModal, Modal } from '@lobehub/ui';
import { Modal, type ModalComponentProps } from '@lobehub/ui/base-ui';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import EditorCanvas from './EditorCanvas';
interface EditorModalProps extends ModalProps {
interface EditorModalProps extends ModalComponentProps {
editorData?: unknown;
onConfirm?: (value: string, editorData?: unknown) => Promise<void>;
value?: string;
@@ -47,5 +46,3 @@ export const EditorModal = memo<EditorModalProps>(
);
},
);
export const createEditorModal = (props: EditorModalProps) => createRawModal(EditorModal, props);
+7 -17
View File
@@ -1,6 +1,7 @@
'use client';
import { Avatar, Flexbox, Select, type SelectProps, Text } from '@lobehub/ui';
import { Avatar, Flexbox, Text } from '@lobehub/ui';
import { Select, type SelectProps } from '@lobehub/ui/base-ui';
import { memo, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import useSWR from 'swr';
@@ -8,21 +9,11 @@ import useSWR from 'swr';
import { DEFAULT_AVATAR } from '@/const/meta';
import { messengerService } from '@/services/messenger';
interface AgentSelectProps extends Omit<
SelectProps,
'options' | 'showSearch' | 'optionFilterProp' | 'value' | 'onChange'
> {
interface AgentSelectProps extends Omit<SelectProps<string>, 'options' | 'value' | 'onChange'> {
onChange?: (agentId: string | undefined) => void;
value?: string;
}
/**
* Shared agent picker used wherever the messenger feature asks the user to
* pick which agent receives messages. Single source of truth for the option
* shape (avatar + title, locale-aware fallback) so verify-im and the Settings
* panel render identically. Fetches `messenger.listAgentsForBinding` (which
* already pins LobeAI to the top and matches the bot's `/agents` ordering).
*/
const AgentSelect = memo<AgentSelectProps>(({ value, onChange, ...rest }) => {
const { t: tCommon } = useTranslation('common');
const agentsSWR = useSWR('messenger:agentsForBinding', () =>
@@ -36,7 +27,7 @@ const AgentSelect = memo<AgentSelectProps>(({ value, onChange, ...rest }) => {
const title = agent.title || defaultAgentTitle;
return {
label: (
<Flexbox horizontal align="center" gap={8}>
<Flexbox horizontal align={'center'} gap={8}>
<Avatar
avatar={agent.avatar || DEFAULT_AVATAR}
background={agent.backgroundColor ?? undefined}
@@ -45,7 +36,6 @@ const AgentSelect = memo<AgentSelectProps>(({ value, onChange, ...rest }) => {
<Text ellipsis>{title}</Text>
</Flexbox>
),
searchValue: title,
title,
value: agent.id,
};
@@ -55,10 +45,10 @@ const AgentSelect = memo<AgentSelectProps>(({ value, onChange, ...rest }) => {
return (
<Select
optionFilterProp="searchValue"
showSearch
options={options}
value={value}
onChange={(next) => onChange?.(next as string | undefined)}
value={value ?? null}
onChange={(next) => onChange?.((next as string | null) ?? undefined)}
{...rest}
/>
);
@@ -2,11 +2,11 @@
import { Button, Icon, Text } from '@lobehub/ui';
import { LinkIcon, ServerIcon, Trash2Icon, UserIcon } from 'lucide-react';
import { memo, useState } from 'react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { buildDiscordOpenBotUrl } from '../constants';
import LinkModal from '../LinkModal';
import { createMessengerLinkModal } from '../LinkModal';
import {
ConnectionRow,
DetailLayout,
@@ -32,7 +32,6 @@ interface DiscordDetailProps {
// (`messenger.discord.connections.*`) makes that distinction explicit.
const DiscordDetail = memo<DiscordDetailProps>(({ appId, botUsername, name, onBack }) => {
const { t } = useTranslation('messenger');
const [linkOpen, setLinkOpen] = useState(false);
const data = useMessengerData('discord');
const { handleSetActive, handleUnlink } = useLinkActions({
@@ -61,18 +60,20 @@ const DiscordDetail = memo<DiscordDetailProps>(({ appId, botUsername, name, onBa
const hasLinks = links.length > 0;
const link = links[0];
const handleOpenLink = () =>
createMessengerLinkModal({ appId, botUsername, name, platform: 'discord' });
const headerAction = (
<Button
icon={<Icon icon={LinkIcon} />}
type={hasInstallations ? 'default' : 'primary'}
onClick={() => setLinkOpen(true)}
onClick={handleOpenLink}
>
{hasInstallations ? t('messenger.detail.addServer') : t('messenger.linkCta')}
</Button>
);
return (
<>
<DetailLayout
hasConnections={hasInstallations || hasLinks}
headerAction={headerAction}
@@ -135,16 +136,6 @@ const DiscordDetail = memo<DiscordDetailProps>(({ appId, botUsername, name, onBa
<div className={styles.emptyRow}>{t('messenger.detail.connections.empty')}</div>
)}
</DetailLayout>
<LinkModal
appId={appId}
botUsername={botUsername}
name={name}
open={linkOpen}
platform="discord"
onClose={() => setLinkOpen(false)}
/>
</>
);
});
@@ -2,10 +2,10 @@
import { Button, Icon } from '@lobehub/ui';
import { BriefcaseIcon, LinkIcon, Trash2Icon } from 'lucide-react';
import { memo, useState } from 'react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import LinkModal from '../LinkModal';
import { createMessengerLinkModal } from '../LinkModal';
import {
ConnectionRow,
DetailLayout,
@@ -26,7 +26,6 @@ interface SlackDetailProps {
const SlackDetail = memo<SlackDetailProps>(({ appId, botUsername, name, onBack }) => {
const { t } = useTranslation('messenger');
const [linkOpen, setLinkOpen] = useState(false);
const data = useMessengerData('slack');
const { handleSetActive, handleUnlink } = useLinkActions({
@@ -57,11 +56,14 @@ const SlackDetail = memo<SlackDetailProps>(({ appId, botUsername, name, onBack }
const hasInstallations = installations.length > 0;
const hasLinks = links.length > 0;
const handleOpenLink = () =>
createMessengerLinkModal({ appId, botUsername, name, platform: 'slack' });
const headerAction = (
<Button
icon={<Icon icon={LinkIcon} />}
type={hasInstallations ? 'default' : 'primary'}
onClick={() => setLinkOpen(true)}
onClick={handleOpenLink}
>
{hasInstallations ? t('messenger.detail.addWorkspace') : t('messenger.linkCta')}
</Button>
@@ -75,7 +77,6 @@ const SlackDetail = memo<SlackDetailProps>(({ appId, botUsername, name, onBack }
hasInstallations && installations.every((install) => !linkByTenantId.has(install.tenantId));
return (
<>
<DetailLayout
hasConnections={hasInstallations || hasLinks}
headerAction={headerAction}
@@ -114,21 +115,10 @@ const SlackDetail = memo<SlackDetailProps>(({ appId, botUsername, name, onBack }
/>
);
})}
{/* Installs without any user link yet — gentle nudge to /start in Slack. */}
{allInstallsUnlinked && !hasLinks && (
<div className={styles.emptyRow}>{t('messenger.detail.connections.linkHint')}</div>
)}
</DetailLayout>
<LinkModal
appId={appId}
botUsername={botUsername}
name={name}
open={linkOpen}
platform="slack"
onClose={() => setLinkOpen(false)}
/>
</>
);
});
@@ -2,10 +2,10 @@
import { Button, Icon } from '@lobehub/ui';
import { LinkIcon, Trash2Icon } from 'lucide-react';
import { memo, useState } from 'react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import LinkModal from '../LinkModal';
import { createMessengerLinkModal } from '../LinkModal';
import {
DetailLayout,
IntegrationDetailSkeleton,
@@ -27,7 +27,6 @@ interface TelegramDetailProps {
// header.
const TelegramDetail = memo<TelegramDetailProps>(({ appId, botUsername, name, onBack }) => {
const { t } = useTranslation('messenger');
const [linkOpen, setLinkOpen] = useState(false);
const data = useMessengerData('telegram');
const { handleSetActive, handleUnlink } = useLinkActions({
@@ -43,18 +42,20 @@ const TelegramDetail = memo<TelegramDetailProps>(({ appId, botUsername, name, on
const hasLinks = links.length > 0;
const link = links[0];
const handleOpenLink = () =>
createMessengerLinkModal({ appId, botUsername, name, platform: 'telegram' });
const headerAction = hasLinks ? (
<Button danger icon={<Icon icon={Trash2Icon} />} onClick={() => handleUnlink('')}>
{t('messenger.unlinkCta')}
</Button>
) : (
<Button icon={<Icon icon={LinkIcon} />} type="primary" onClick={() => setLinkOpen(true)}>
<Button icon={<Icon icon={LinkIcon} />} type="primary" onClick={handleOpenLink}>
{t('messenger.linkCta')}
</Button>
);
return (
<>
<DetailLayout
hasConnections={hasLinks}
headerAction={headerAction}
@@ -72,16 +73,6 @@ const TelegramDetail = memo<TelegramDetailProps>(({ appId, botUsername, name, on
<div className={styles.emptyRow}>{t('messenger.detail.connections.empty')}</div>
)}
</DetailLayout>
<LinkModal
appId={appId}
botUsername={botUsername}
name={name}
open={linkOpen}
platform="telegram"
onClose={() => setLinkOpen(false)}
/>
</>
);
});
@@ -1,6 +1,7 @@
'use client';
import { Block, Button, Flexbox, Icon, Skeleton, Tag, Text } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { createStaticStyles } from 'antd-style';
import { ArrowLeftIcon, CheckCircle2Icon, Trash2Icon, UserIcon } from 'lucide-react';
@@ -294,7 +295,7 @@ export const useLinkActions = ({
platform,
}: UseLinkActionsArgs) => {
const { t } = useTranslation('messenger');
const { message, modal } = App.useApp();
const { message } = App.useApp();
const handleSetActive = async (tenantId: string, agentId: string | null) => {
try {
@@ -311,7 +312,7 @@ export const useLinkActions = ({
};
const handleUnlink = (tenantId: string) => {
modal.confirm({
confirmModal({
content: t('messenger.unlinkConfirm', { platform: name }),
okButtonProps: { danger: true },
onOk: async () => {
@@ -359,10 +360,10 @@ export const useDisconnectInstallation = ({
linksMutate,
}: UseDisconnectInstallationArgs) => {
const { t } = useTranslation('messenger');
const { message, modal } = App.useApp();
const { message } = App.useApp();
return (id: string, copy: DisconnectInstallationCopy) => {
modal.confirm({
confirmModal({
content: copy.confirm,
okButtonProps: { danger: true },
onOk: async () => {
@@ -0,0 +1,42 @@
'use client';
import { Flexbox } from '@lobehub/ui';
import { memo } from 'react';
import type { MessengerPlatform } from '../constants';
import DiscordLinkBody from './Discord';
import SlackLinkBody from './Slack';
import TelegramLinkBody from './Telegram';
export interface LinkModalContentProps {
appId?: string;
botUsername?: string;
name: string;
platform: MessengerPlatform;
}
const LinkModalContent = memo<LinkModalContentProps>(({ appId, botUsername, name, platform }) => {
const renderBody = () => {
switch (platform) {
case 'slack': {
return <SlackLinkBody />;
}
case 'discord': {
return <DiscordLinkBody appId={appId} name={name} />;
}
case 'telegram': {
return <TelegramLinkBody botUsername={botUsername} name={name} />;
}
}
};
return (
<Flexbox align={'center'} gap={20} style={{ paddingBlockEnd: 16, paddingBlockStart: 24 }}>
{renderBody()}
</Flexbox>
);
});
LinkModalContent.displayName = 'MessengerLinkModalContent';
export default LinkModalContent;
+10 -43
View File
@@ -1,47 +1,14 @@
'use client';
import { Flexbox, Modal } from '@lobehub/ui';
import { memo } from 'react';
import { createModal, type ModalInstance } from '@lobehub/ui/base-ui';
import type { MessengerPlatform } from '../constants';
import DiscordLinkBody from './Discord';
import SlackLinkBody from './Slack';
import TelegramLinkBody from './Telegram';
import LinkModalContent, { type LinkModalContentProps } from './Content';
interface LinkModalProps {
appId?: string;
botUsername?: string;
/** Brand-name label (e.g. `"Slack"`) sourced from the registry. */
name: string;
onClose: () => void;
open: boolean;
platform: MessengerPlatform;
}
const LinkModal = memo<LinkModalProps>(({ appId, botUsername, name, onClose, open, platform }) => {
const renderBody = () => {
switch (platform) {
case 'slack': {
return <SlackLinkBody />;
}
case 'discord': {
return <DiscordLinkBody appId={appId} name={name} />;
}
case 'telegram': {
return <TelegramLinkBody botUsername={botUsername} name={name} />;
}
}
};
return (
<Modal footer={null} open={open} title={null} width={480} onCancel={onClose}>
<Flexbox align="center" gap={20} style={{ paddingBlockEnd: 16, paddingBlockStart: 40 }}>
{renderBody()}
</Flexbox>
</Modal>
);
});
LinkModal.displayName = 'MessengerLinkModal';
export default LinkModal;
export const createMessengerLinkModal = (props: LinkModalContentProps): ModalInstance =>
createModal({
content: <LinkModalContent {...props} />,
footer: null,
maskClosable: true,
title: null,
width: 'min(90vw, 480px)',
});
@@ -1,4 +1,4 @@
import { Select } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { memo, useMemo } from 'react';
import { useAgentId } from '@/features/ChatInput/hooks/useAgentId';
@@ -1,4 +1,4 @@
import { Select } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { memo, useMemo } from 'react';
import { useAgentId } from '@/features/ChatInput/hooks/useAgentId';
@@ -1,6 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { Trash2 } from 'lucide-react';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -20,13 +20,11 @@ export const useDropdownMenu = ({
onClose,
}: UseDropdownMenuProps): MenuProps['items'] => {
const { t } = useTranslation(['common', 'chat']);
const { modal } = App.useApp();
const removeAgent = useHomeStore((s) => s.removeAgent);
const handleDelete = () => {
modal.confirm({
confirmModal({
cancelText: t('cancel'),
centered: true,
okButtonProps: { danger: true },
okText: t('delete'),
onOk: async () => {
@@ -1,6 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { Trash2 } from 'lucide-react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -20,7 +20,6 @@ export const useDropdownMenu = ({
topicId,
}: UseDropdownMenuProps): (() => MenuProps['items']) => {
const { t } = useTranslation(['common', 'topic']);
const { modal } = App.useApp();
const removeTopic = useChatStore((s) => s.removeTopic);
return useCallback(
@@ -32,9 +31,8 @@ export const useDropdownMenu = ({
key: 'delete',
label: t('delete'),
onClick: () => {
modal.confirm({
confirmModal({
cancelText: t('cancel'),
centered: true,
content: t('actions.confirmRemoveTopic', { ns: 'topic' }),
okButtonProps: { danger: true },
okText: t('delete'),
@@ -48,6 +46,6 @@ export const useDropdownMenu = ({
},
},
].filter(Boolean) as MenuProps['items'],
[t, modal, removeTopic, topicId, onDelete, onClose],
[t, removeTopic, topicId, onDelete, onClose],
);
};
+2 -3
View File
@@ -21,7 +21,7 @@ import { usePageEditorStore, useStoreApi } from '../store';
*/
export const useMenu = (): { menuItems: any[] } => {
const { t } = useTranslation(['file', 'common', 'chat']);
const { message, modal } = App.useApp();
const { message } = App.useApp();
const storeApi = useStoreApi();
const { lg = true } = useResponsive();
@@ -137,7 +137,7 @@ export const useMenu = (): { menuItems: any[] } => {
label: t('delete', { ns: 'common' }),
onClick: async () => {
const state = storeApi.getState();
await state.handleDelete(t as any, message, modal, state.onDelete);
await state.handleDelete(t as any, message, state.onDelete);
},
},
{
@@ -185,7 +185,6 @@ export const useMenu = (): { menuItems: any[] } => {
storeApi,
t,
message,
modal,
setRightPanelMode,
wideScreen,
toggleWideScreen,
@@ -1,7 +1,7 @@
'use client';
import { Button, Flexbox, Text } from '@lobehub/ui';
import { createStyles, cssVar } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import dayjs from 'dayjs';
import { RotateCcwIcon } from 'lucide-react';
import { memo, useMemo, useState } from 'react';
@@ -16,10 +16,10 @@ import DocumentHistoryDiff from '../DocumentHistoryDiff';
import { formatHistoryAbsoluteTime } from '../formatHistoryDate';
import HistorySidebar from './HistorySidebar';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css }) => ({
arrow: css`
font-size: 12px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
badgeNew: css`
display: inline-flex;
@@ -33,9 +33,9 @@ const useStyles = createStyles(({ css, token }) => ({
font-size: 11px;
font-weight: 600;
line-height: 1.2;
color: ${token.colorSuccess};
color: ${cssVar.colorSuccess};
background: ${token.colorSuccessBg};
background: ${cssVar.colorSuccessBg};
`,
badgeOld: css`
display: inline-flex;
@@ -48,9 +48,9 @@ const useStyles = createStyles(({ css, token }) => ({
font-size: 11px;
font-weight: 600;
line-height: 1.2;
color: ${token.colorError};
color: ${cssVar.colorError};
background: ${token.colorErrorBg};
background: ${cssVar.colorErrorBg};
`,
cmpbar: css`
display: flex;
@@ -59,9 +59,9 @@ const useStyles = createStyles(({ css, token }) => ({
padding-block: 10px;
padding-inline: 16px;
border-block-end: 1px solid ${token.colorBorderSecondary};
border-block-end: 1px solid ${cssVar.colorBorderSecondary};
background: ${token.colorBgLayout};
background: ${cssVar.colorBgLayout};
`,
diffArea: css`
overflow: hidden;
@@ -104,7 +104,6 @@ export interface CompareContentProps {
const CompareContent = memo<CompareContentProps>(
({ documentId, initialHistoryId, items, onRestore, saveSourceLabels }) => {
const { t } = useTranslation('file');
const { styles } = useStyles();
const [selectedHistoryId, setSelectedHistoryId] = useState<string>(initialHistoryId);
@@ -1,7 +1,7 @@
'use client';
import { Flexbox, Tag, Text } from '@lobehub/ui';
import { createStyles, cssVar, cx } from 'antd-style';
import { createStaticStyles, cssVar, cx } from 'antd-style';
import dayjs from 'dayjs';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -13,7 +13,7 @@ import type {
import { formatHistoryRowTime } from '../formatHistoryDate';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css }) => ({
container: css`
overflow-y: auto;
flex-shrink: 0;
@@ -21,9 +21,9 @@ const useStyles = createStyles(({ css, token }) => ({
width: 232px;
padding-block: 4px 12px;
padding-inline: 8px;
border-inline-start: 1px solid ${token.colorBorderSecondary};
border-inline-start: 1px solid ${cssVar.colorBorderSecondary};
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
dot: css`
position: absolute;
@@ -32,19 +32,19 @@ const useStyles = createStyles(({ css, token }) => ({
width: 8px;
height: 8px;
border: 1px solid ${token.colorBorder};
border: 1px solid ${cssVar.colorBorder};
border-radius: 999px;
background: ${token.colorBgContainer};
box-shadow: 0 0 0 2px ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
box-shadow: 0 0 0 2px ${cssVar.colorBgContainer};
`,
dotCurrent: css`
border-color: ${token.colorSuccess};
background: ${token.colorSuccess};
border-color: ${cssVar.colorSuccess};
background: ${cssVar.colorSuccess};
`,
dotSelected: css`
border-color: ${token.colorPrimary};
background: ${token.colorPrimary};
border-color: ${cssVar.colorPrimary};
background: ${cssVar.colorPrimary};
`,
group: css`
position: relative;
@@ -61,7 +61,7 @@ const useStyles = createStyles(({ css, token }) => ({
font-weight: 500;
line-height: 1.2;
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
item: css`
cursor: pointer;
@@ -73,17 +73,17 @@ const useStyles = createStyles(({ css, token }) => ({
transition: background ${cssVar.motionDurationMid} ${cssVar.motionEaseInOut};
&:hover {
background: ${token.colorFillQuaternary};
background: ${cssVar.colorFillQuaternary};
}
`,
itemCurrent: css`
cursor: default;
`,
itemSelected: css`
background: ${token.colorFillSecondary};
background: ${cssVar.colorFillSecondary};
&:hover {
background: ${token.colorFillSecondary};
background: ${cssVar.colorFillSecondary};
}
`,
meta: css`
@@ -101,7 +101,7 @@ const useStyles = createStyles(({ css, token }) => ({
width: 1px;
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
`,
row: css`
position: relative;
@@ -160,7 +160,6 @@ interface HistorySidebarProps {
const HistorySidebar = memo<HistorySidebarProps>(
({ items, onSelect, saveSourceLabels, selectedHistoryId }) => {
const { t } = useTranslation('file');
const { styles } = useStyles();
const formatLabel = useCallback(
(savedAt: string) => {
+3 -3
View File
@@ -1,4 +1,5 @@
import { EDITOR_DEBOUNCE_TIME, EDITOR_MAX_WAIT, isDesktop } from '@lobechat/const';
import { confirmModal } from '@lobehub/ui/base-ui';
import debug from 'debug';
import { debounce } from 'es-toolkit/compat';
import { type StateCreator } from 'zustand';
@@ -19,7 +20,6 @@ export interface Action {
handleDelete: (
t: (key: string) => string,
message: any,
modal: any,
onDeleteCallback?: () => void,
) => Promise<void>;
handleTitleSubmit: () => Promise<void>;
@@ -75,12 +75,12 @@ export const store: (initState?: Partial<State>) => StateCreator<Store> =
}
},
handleDelete: async (t, message, modal, onDeleteCallback) => {
handleDelete: async (t, message, onDeleteCallback) => {
const { documentId } = get();
if (!documentId) return;
return new Promise((resolve, reject) => {
modal.confirm({
confirmModal({
cancelText: t('cancel'),
content: t('pageEditor.deleteConfirm.content'),
okButtonProps: { danger: true },
@@ -1,5 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { CopyPlus, PanelTop, Pencil, Trash2 } from 'lucide-react';
import { useCallback } from 'react';
@@ -20,14 +21,14 @@ export const useDropdownMenu = ({
toggleEditing,
}: ActionProps): (() => MenuProps['items']) => {
const { t } = useTranslation(['common', 'file']);
const { message, modal } = App.useApp();
const { message } = App.useApp();
const navigate = useNavigate();
const addTab = useElectronStore((s) => s.addTab);
const removePage = usePageStore((s) => s.removePage);
const duplicatePage = usePageStore((s) => s.duplicatePage);
const handleDelete = () => {
modal.confirm({
confirmModal({
cancelText: t('cancel'),
content: t('pageEditor.deleteConfirm.content', { ns: 'file' }),
okButtonProps: { danger: true },
@@ -1,6 +1,6 @@
import { type NotebookDocument } from '@lobechat/types';
import { ActionIcon, Flexbox, Text } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { createStaticStyles } from 'antd-style';
import { FileTextIcon, Trash2Icon } from 'lucide-react';
import { type MouseEvent } from 'react';
@@ -38,7 +38,6 @@ interface DocumentItemProps {
const DocumentItem = memo<DocumentItemProps>(({ document, topicId }) => {
const { t } = useTranslation('portal');
const { modal } = App.useApp();
const [deleting, setDeleting] = useState(false);
const openDocument = useChatStore((s) => s.openDocument);
@@ -51,8 +50,7 @@ const DocumentItem = memo<DocumentItemProps>(({ document, topicId }) => {
const handleDelete = async (e: MouseEvent) => {
e.stopPropagation();
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
setDeleting(true);
@@ -1,6 +1,7 @@
'use client';
import { ActionIcon, Flexbox } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { cssVar } from 'antd-style';
import { BookMinusIcon, FileBoxIcon, Trash2Icon } from 'lucide-react';
@@ -25,7 +26,7 @@ import SearchInput from './SearchInput';
*/
const Header = memo(() => {
const { t } = useTranslation(['components', 'common', 'file', 'knowledgeBase']);
const { modal, message } = App.useApp();
const { message } = App.useApp();
// Get state and actions from store
const [libraryId, category, onActionClick, selectAllState, selectFileIds] =
@@ -52,7 +53,7 @@ const Header = memo(() => {
icon={BookMinusIcon}
title={t('FileManager.actions.removeFromLibrary')}
onClick={() => {
modal.confirm({
confirmModal({
okButtonProps: {
danger: true,
},
@@ -80,7 +81,7 @@ const Header = memo(() => {
icon={Trash2Icon}
title={t('delete', { ns: 'common' })}
onClick={() => {
modal.confirm({
confirmModal({
okButtonProps: {
danger: true,
},
@@ -1,4 +1,5 @@
import { copyToClipboard, createRawModal, Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { type ItemType } from 'antd/es/menu/interface';
import {
@@ -54,7 +55,7 @@ export const useFileItemDropdown = ({
onRenameStart,
}: UseFileItemDropdownParams): UseFileItemDropdownReturn => {
const { t } = useTranslation(['components', 'common', 'knowledgeBase']);
const { message, modal } = App.useApp();
const { message } = App.useApp();
const appOrigin = useAppOrigin();
const { deleteResource, moveResource, refreshFileList } = useFileStore(
@@ -168,7 +169,7 @@ export const useFileItemDropdown = ({
onClick: async ({ domEvent }) => {
domEvent.stopPropagation();
modal.confirm({
confirmModal({
okButtonProps: {
danger: true,
},
@@ -301,11 +302,12 @@ export const useFileItemDropdown = ({
label: t('delete', { ns: 'common' }),
onClick: async ({ domEvent }) => {
domEvent.stopPropagation();
modal.confirm({
confirmModal({
content: isFolder
? t('FileManager.actions.confirmDeleteFolder')
: t('FileManager.actions.confirmDelete'),
okButtonProps: { danger: true },
title: t('delete', { ns: 'common' }),
onOk: async () => {
// Use optimistic delete - instant UI update, sync in background
await deleteResource(id);
@@ -335,7 +337,6 @@ export const useFileItemDropdown = ({
libraries,
libraryId,
message,
modal,
moveResource,
onRenameStart,
refreshFileList,
@@ -1,5 +1,6 @@
import { type DropdownItem } from '@lobehub/ui';
import { DropdownMenu, Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import {
BookMinusIcon,
@@ -33,7 +34,7 @@ interface BatchActionsDropdownProps {
const BatchActionsDropdown = memo<BatchActionsDropdownProps>(({ selectCount, onActionClick }) => {
const { t } = useTranslation(['components', 'common', 'file', 'knowledgeBase']);
const { modal, message } = App.useApp();
const { message } = App.useApp();
const libraryId = useResourceManagerStore((s) => s.libraryId);
const [resolveSelectedResourceIds, selectAllState] = useResourceManagerStore((s) => [
@@ -54,7 +55,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(({ selectCount, onA
key: 'deleteLibrary',
label: t('header.actions.deleteLibrary', { ns: 'file' }),
onClick: async () => {
modal.confirm({
confirmModal({
okButtonProps: {
danger: true,
},
@@ -100,7 +101,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(({ selectCount, onA
key: 'removeFromKnowledgeBase',
label: t('FileManager.actions.removeFromLibrary'),
onClick: () => {
modal.confirm({
confirmModal({
okButtonProps: {
danger: true,
},
@@ -154,7 +155,7 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(({ selectCount, onA
key: 'delete',
label: t('delete', { ns: 'common' }),
onClick: async () => {
modal.confirm({
confirmModal({
okButtonProps: {
danger: true,
},
@@ -177,7 +178,6 @@ const BatchActionsDropdown = memo<BatchActionsDropdownProps>(({ selectCount, onA
addFilesToKnowledgeBase,
resolveSelectedResourceIds,
t,
modal,
message,
knowledgeBases,
]);
@@ -1,4 +1,5 @@
import { Button, Checkbox, Flexbox, Icon, Skeleton } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import { BookMinusIcon, BookPlusIcon, FileBoxIcon, Trash2Icon } from 'lucide-react';
@@ -34,7 +35,7 @@ const MultiSelectActions = memo<MultiSelectActionsProps>(
const { t } = useTranslation(['components', 'common']);
const isSelectedFiles = selectCount > 0;
const { modal, message } = App.useApp();
const { message } = App.useApp();
const libraryId = useResourceManagerStore((s) => s.libraryId);
@@ -83,7 +84,7 @@ const MultiSelectActions = memo<MultiSelectActionsProps>(
icon={BookMinusIcon}
size={'small'}
onClick={() => {
modal.confirm({
confirmModal({
okButtonProps: {
danger: true,
},
@@ -142,7 +143,7 @@ const MultiSelectActions = memo<MultiSelectActionsProps>(
size={'small'}
variant={'filled'}
onClick={async () => {
modal.confirm({
confirmModal({
okButtonProps: {
danger: true,
},
@@ -1,3 +1,4 @@
import { confirmModal } from '@lobehub/ui/base-ui';
import { type TFunction } from 'i18next';
import { type ChangeEvent } from 'react';
import { useCallback } from 'react';
@@ -55,16 +56,13 @@ const useUploadFolder = ({
const gitignoreContent = await readGitignoreContent(gitignoreFile);
const gitignoreOriginalCount = files.length;
const { Modal } = await import('antd');
Modal.confirm({
confirmModal({
cancelText: t('header.actions.gitignore.cancel'),
content: t('header.actions.gitignore.content', {
count: gitignoreOriginalCount,
}),
okText: t('header.actions.gitignore.apply'),
onCancel: () => {
// Upload without awaiting - let it run in background
upload(files);
},
onOk: async () => {
@@ -8,7 +8,7 @@ import { type ReactNode } from 'react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
import { DESKTOP_HEADER_ICON_SMALL_SIZE } from '@/const/layoutTokens';
import { useGlobalStore } from '@/store/global';
import { systemStatusSelectors } from '@/store/global/selectors';
import { useUserStore } from '@/store/user';
@@ -55,7 +55,7 @@ const ToggleRightPanelButton = memo<ToggleRightPanelButtonProps>(
active={showActive ? expand : undefined}
icon={icon || (expand ? PanelRightClose : PanelRightOpen)}
id={TOGGLE_BUTTON_ID}
size={size || DESKTOP_HEADER_ICON_SIZE}
size={size || DESKTOP_HEADER_ICON_SMALL_SIZE}
title={title || t('toggleRightPanel.title', { ns: 'hotkey' })}
tooltipProps={{
hotkey,
+8 -11
View File
@@ -1,8 +1,8 @@
'use client';
import { type ConversationContext } from '@lobechat/types';
import { type ModalInstance } from '@lobehub/ui';
import { createModal, Flexbox, Segmented, Skeleton } from '@lobehub/ui';
import { Flexbox, Segmented, Skeleton } from '@lobehub/ui';
import { createModal, type ModalInstance } from '@lobehub/ui/base-ui';
import { t } from 'i18next';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -92,21 +92,18 @@ export const openShareModal = ({
context,
}: OpenShareModalOptions = {}): ModalInstance =>
createModal({
afterClose,
allowFullscreen: true,
centered: true,
children: (
content: (
<ShareDataProvider context={context}>
<ShareModalContent />
</ShareDataProvider>
),
destroyOnHidden: true,
footer: null,
height: '80vh',
styles: {
body: {
height: '80vh',
maskClosable: true,
onOpenChangeComplete: (open) => {
if (!open) afterClose?.();
},
styles: {
content: { height: 'min(80vh, 800px)' },
},
title: t('share', { ns: 'common' }),
width: 'min(90vw, 1024px)',
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { type ConversationContext } from '@lobechat/types';
import { type ModalInstance } from '@lobehub/ui';
import { type ModalInstance } from '@lobehub/ui/base-ui';
import { useCallback, useEffect, useRef } from 'react';
import { openShareModal as createShareModal } from './Modal';
+3 -6
View File
@@ -10,7 +10,7 @@ import {
Text,
usePopoverContext,
} from '@lobehub/ui';
import { Select } from '@lobehub/ui/base-ui';
import { confirmModal, Select } from '@lobehub/ui/base-ui';
import { App, Divider } from 'antd';
import { ExternalLinkIcon, LinkIcon, LockIcon } from 'lucide-react';
import { type ReactNode } from 'react';
@@ -36,7 +36,7 @@ interface SharePopoverContentProps {
const SharePopoverContent = memo<SharePopoverContentProps>(({ onOpenModal, topicId }) => {
const { t } = useTranslation('chat');
const { message, modal } = App.useApp();
const { message } = App.useApp();
const [updating, setUpdating] = useState(false);
const { close } = usePopoverContext();
const containerRef = useRef<HTMLDivElement>(null);
@@ -97,9 +97,8 @@ const SharePopoverContent = memo<SharePopoverContentProps>(({ onOpenModal, topic
) {
let doNotShowAgain = false;
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
centered: true,
content: (
<div>
<p>{t('shareModal.popover.privacyWarning.content')}</p>
@@ -122,7 +121,6 @@ const SharePopoverContent = memo<SharePopoverContentProps>(({ onOpenModal, topic
updateVisibility(visibility);
},
title: t('shareModal.popover.privacyWarning.title'),
type: 'warning',
});
} else {
updateVisibility(visibility);
@@ -131,7 +129,6 @@ const SharePopoverContent = memo<SharePopoverContentProps>(({ onOpenModal, topic
[
currentVisibility,
hideTopicSharePrivacyWarning,
modal,
t,
updateSystemStatus,
updateVisibility,
@@ -1,8 +1,8 @@
'use client';
import { ActionIcon, Block, DropdownMenu, Flexbox, Icon, Modal, Tag } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { SkillsIcon } from '@lobehub/ui/icons';
import { App } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import { DownloadIcon, MoreVerticalIcon, PackageSearch, Trash2 } from 'lucide-react';
import { lazy, memo, Suspense, useState } from 'react';
@@ -44,7 +44,6 @@ interface AgentSkillItemProps {
const AgentSkillItem = memo<AgentSkillItemProps>(({ skill }) => {
const { t } = useTranslation('plugin');
const { t: tc } = useTranslation('common');
const { modal } = App.useApp();
const [detailOpen, setDetailOpen] = useState(false);
const [editOpen, setEditOpen] = useState(false);
const [loading, setLoading] = useState(false);
@@ -65,14 +64,12 @@ const AgentSkillItem = memo<AgentSkillItemProps>(({ skill }) => {
};
const handleDelete = () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await deleteAgentSkill(skill.id);
},
title: t('store.actions.confirmUninstall'),
type: 'error',
});
};
@@ -9,7 +9,7 @@ import {
Icon,
stopPropagation,
} from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { MoreVerticalIcon, Plus, Trash2 } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -30,7 +30,6 @@ interface ItemProps {
const Item = memo<ItemProps>(({ avatar, description, identifier, onOpenDetail, title }) => {
const { t } = useTranslation(['setting', 'plugin']);
const styles = itemStyles;
const { modal } = App.useApp();
const [installBuiltinTool, uninstallBuiltinTool, isInstalled] = useToolStore((s) => [
s.installBuiltinTool,
@@ -43,14 +42,12 @@ const Item = memo<ItemProps>(({ avatar, description, identifier, onOpenDetail, t
};
const handleUninstall = () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await uninstallBuiltinTool(identifier);
},
title: t('store.actions.confirmUninstall', { ns: 'plugin' }),
type: 'error',
});
};
@@ -9,7 +9,8 @@ import {
Modal,
stopPropagation,
} from '@lobehub/ui';
import { App, Button } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { Button } from 'antd';
import isEqual from 'fast-deep-equal';
import { MoreVerticalIcon, Plus, Trash2 } from 'lucide-react';
import React, { memo, Suspense, useState } from 'react';
@@ -32,7 +33,6 @@ import { itemStyles } from '../style';
const Item = memo<DiscoverMcpItem>(({ name, description, icon, identifier }) => {
const styles = itemStyles;
const { t } = useTranslation('plugin');
const { modal } = App.useApp();
const [detailOpen, setDetailOpen] = useState(false);
const [installed, installing, installMCPPlugin, cancelInstallMCPPlugin, unInstallPlugin, plugin] =
@@ -91,8 +91,7 @@ const Item = memo<DiscoverMcpItem>(({ name, description, icon, identifier }) =>
key: 'uninstall',
label: t('store.actions.uninstall'),
onClick: () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
if (isPluginEnabledInAgent) {
@@ -101,7 +100,6 @@ const Item = memo<DiscoverMcpItem>(({ name, description, icon, identifier }) =>
await unInstallPlugin(identifier);
},
title: t('store.actions.confirmUninstall'),
type: 'error',
});
},
},
@@ -1,8 +1,8 @@
'use client';
import { ActionIcon, Avatar, Block, DropdownMenu, Flexbox, Icon, Modal, Tag } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { SkillsIcon } from '@lobehub/ui/icons';
import { App } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import { DownloadIcon, Loader2, MoreVerticalIcon, Plus, Trash2 } from 'lucide-react';
import { lazy, memo, Suspense, useCallback, useState } from 'react';
@@ -42,7 +42,6 @@ const MarketSkillItem = memo<DiscoverSkillItem>(({ name, icon, description, iden
const [detailOpen, setDetailOpen] = useState(false);
const [installing, setInstalling] = useState(false);
const [loading, setLoading] = useState(false);
const { modal } = App.useApp();
const installed = useToolStore(agentSkillsSelectors.isAgentSkill(identifier));
const installedSkill = useToolStore(agentSkillsSelectors.getAgentSkillByIdentifier(identifier));
@@ -66,16 +65,14 @@ const MarketSkillItem = memo<DiscoverSkillItem>(({ name, icon, description, iden
const handleUninstall = useCallback(() => {
if (!installedSkill) return;
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await deleteAgentSkill(installedSkill.id);
},
title: t('store.actions.confirmUninstall'),
type: 'error',
});
}, [installedSkill, deleteAgentSkill, modal, t]);
}, [installedSkill, deleteAgentSkill, t]);
const handleDownload = useCallback(async () => {
if (!installedSkill?.zipFileHash) return;
@@ -1,7 +1,7 @@
'use client';
import { ActionIcon, Block, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { createStaticStyles, cssVar } from 'antd-style';
import { MoreVerticalIcon, PackageSearch, Trash2 } from 'lucide-react';
import { memo, useState } from 'react';
@@ -45,7 +45,6 @@ interface ItemProps {
const Item = memo<ItemProps>(({ identifier, title, description, avatar }) => {
const { t } = useTranslation('plugin');
const { modal } = App.useApp();
const [configOpen, setConfigOpen] = useState(false);
const [detailOpen, setDetailOpen] = useState(false);
@@ -62,8 +61,7 @@ const Item = memo<ItemProps>(({ identifier, title, description, avatar }) => {
]);
const handleDelete = () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
if (isPluginEnabledInAgent) {
@@ -72,7 +70,6 @@ const Item = memo<ItemProps>(({ identifier, title, description, avatar }) => {
await uninstallPlugin(identifier);
},
title: t('store.actions.confirmUninstall'),
type: 'error',
});
};
@@ -1,7 +1,7 @@
'use client';
import { ActionIcon, Block, DropdownMenu, Flexbox, Icon, stopPropagation } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { cssVar } from 'antd-style';
import type { Klavis } from 'klavis';
import { Loader2, MoreVerticalIcon, Plus, Unplug } from 'lucide-react';
@@ -26,7 +26,6 @@ const Item = memo<ItemProps>(
({ description, icon, identifier, label, onOpenDetail, serverName, type }) => {
const { t } = useTranslation('setting');
const styles = itemStyles;
const { modal } = App.useApp();
const { handleConnect, handleDisconnect, isConnected, isConnecting } = useSkillConnect({
identifier,
@@ -42,9 +41,8 @@ const Item = memo<ItemProps>(
});
const confirmDisconnect = () => {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
centered: true,
content: t('tools.lobehubSkill.disconnectConfirm.desc', { name: label }),
okButtonProps: { danger: true },
okText: t('tools.lobehubSkill.disconnect'),
@@ -5,7 +5,7 @@ import { PanelLeftRightDashedIcon, SquareChartGanttIcon } from 'lucide-react';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { DESKTOP_HEADER_ICON_SIZE } from '@/const/layoutTokens';
import { DESKTOP_HEADER_ICON_SMALL_SIZE } from '@/const/layoutTokens';
import { useGlobalStore } from '@/store/global';
import { systemStatusSelectors } from '@/store/global/selectors';
@@ -20,7 +20,7 @@ const WideScreenButton = memo(() => {
return (
<ActionIcon
icon={wideScreen ? SquareChartGanttIcon : PanelLeftRightDashedIcon}
size={DESKTOP_HEADER_ICON_SIZE}
size={DESKTOP_HEADER_ICON_SMALL_SIZE}
title={t(wideScreen ? 'toggleWideScreen.off' : 'toggleWideScreen.on')}
tooltipProps={{
placement: 'bottom',
@@ -1,9 +1,9 @@
'use client';
import { Center, Flexbox, Icon, Input, Text, TextArea, Tooltip } from '@lobehub/ui';
import { Modal } from '@lobehub/ui/base-ui';
import { confirmModal, Modal } from '@lobehub/ui/base-ui';
import { type UploadProps } from 'antd';
import { App, Form, Modal as AntModal, Upload } from 'antd';
import { App, Form, Upload } from 'antd';
import { cssVar } from 'antd-style';
import { CircleHelp, Globe, ImagePlus, Trash2 } from 'lucide-react';
import { memo, useCallback, useEffect, useState } from 'react';
@@ -332,7 +332,7 @@ const ProfileSetupModal = memo<ProfileSetupModalProps>(
// If userName changed and it's not first-time setup, show confirmation
if (!isFirstTimeSetup && oldUserName && values.userName !== oldUserName) {
AntModal.confirm({
confirmModal({
cancelText: t('profileSetup.confirmChangeUserId.cancel'),
content: t('profileSetup.confirmChangeUserId.description', {
newId: values.userName,
@@ -369,20 +369,22 @@ const ProfileSetupModal = memo<ProfileSetupModalProps>(
maskClosable={!isFirstTimeSetup}
okText={isFirstTimeSetup ? t('profileSetup.getStarted') : t('profileSetup.save')}
open={open}
title={false}
width={640}
onCancel={handleCancel}
onOk={handleSubmit}
>
<Text strong fontSize={20} style={{ marginTop: 16 }}>
title={
<Flexbox gap={4}>
<Text strong fontSize={16} lineHeight={1.4}>
{isFirstTimeSetup ? t('profileSetup.titleFirstTime') : t('profileSetup.titleEdit')}
</Text>
<Text style={{ display: 'block', marginBottom: 24 }} type="secondary">
<Text fontSize={13} lineHeight={1.4} type="secondary">
{isFirstTimeSetup
? t('profileSetup.descriptionFirstTime')
: t('profileSetup.descriptionEdit')}
</Text>
</Flexbox>
}
onCancel={handleCancel}
onOk={handleSubmit}
>
<Form form={form} layout="vertical">
<Flexbox horizontal gap={24}>
<Flexbox flex={1}>
@@ -2,7 +2,7 @@
import { Icon } from '@lobehub/ui';
import { type MenuProps } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { Trash } from 'lucide-react';
import type { CSSProperties } from 'react';
import { memo, useState } from 'react';
@@ -23,7 +23,6 @@ interface TopicItemProps {
const TopicItem = memo<TopicItemProps>(({ topic, showMoreInfo, style }) => {
const { useStore, namespace } = useGenerationTopicContext();
const { t } = useTranslation(namespace);
const { modal } = App.useApp();
const [isUpdating, setIsUpdating] = useState(false);
const isLoading = useStore((s) => s.loadingGenerationTopicIds.includes(topic.id));
const removeGenerationTopic = useStore((s) => s.removeGenerationTopic);
@@ -40,7 +39,7 @@ const TopicItem = memo<TopicItemProps>(({ topic, showMoreInfo, style }) => {
e.stopPropagation();
e.preventDefault();
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
content: t('topic.deleteConfirmDesc'),
okButtonProps: { danger: true },
@@ -65,7 +64,7 @@ const TopicItem = memo<TopicItemProps>(({ topic, showMoreInfo, style }) => {
key: 'delete',
label: t('delete', { ns: 'common' }),
onClick: () => {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
content: t('topic.deleteConfirmDesc'),
okButtonProps: { danger: true },
@@ -26,13 +26,6 @@ vi.mock('antd-style', () => ({
neonDot: 'neonDot',
neonDotWrapper: 'neonDotWrapper',
}),
createStyles: () => () => ({
cx: (...classNames: Array<false | string | undefined>) => classNames.filter(Boolean).join(' '),
styles: {
container: 'container',
dot: 'dot',
},
}),
cssVar: {
colorInfo: '#00f',
colorTextDescription: '#999',
@@ -1,6 +1,7 @@
import type { ChatTopicStatus } from '@lobechat/types';
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import {
CheckCircle2,
@@ -44,7 +45,7 @@ export const useTopicItemDropdownMenu = ({
title,
}: TopicItemDropdownMenuProps) => {
const { t } = useTranslation(['topic', 'common']);
const { modal, message } = App.useApp();
const { message } = App.useApp();
const navigate = useNavigate();
const openTopicInNewWindow = useGlobalStore((s) => s.openTopicInNewWindow);
@@ -205,8 +206,7 @@ export const useTopicItemDropdownMenu = ({
key: 'delete',
label: t('delete', { ns: 'common' }),
onClick: () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeTopic(id);
@@ -234,7 +234,6 @@ export const useTopicItemDropdownMenu = ({
addTab,
navigate,
t,
modal,
message,
handleOpenShareModal,
]);
@@ -1,6 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { PencilLine, Trash } from 'lucide-react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -17,7 +17,6 @@ export const useThreadItemDropdownMenu = ({
toggleEditing,
}: ThreadItemDropdownMenuProps): (() => MenuProps['items']) => {
const { t } = useTranslation(['thread', 'common']);
const { modal } = App.useApp();
const [removeThread] = useChatStore((s) => [s.removeThread]);
@@ -40,8 +39,7 @@ export const useThreadItemDropdownMenu = ({
key: 'delete',
label: t('delete', { ns: 'common' }),
onClick: () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeThread(id);
@@ -51,5 +49,5 @@ export const useThreadItemDropdownMenu = ({
},
},
].filter(Boolean) as MenuProps['items'];
}, [id, removeThread, toggleEditing, t, modal]);
}, [id, removeThread, toggleEditing, t]);
};
@@ -1,5 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Upload } from 'antd';
import { css, cx } from 'antd-style';
import { Hash, Import, LucideCheck, Trash } from 'lucide-react';
@@ -100,9 +101,8 @@ export const useTopicActionsDropdownMenu = (
key: 'deleteUnstarred',
label: t('actions.removeUnstarred'),
onClick: () => {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
centered: true,
okButtonProps: { danger: true },
okText: t('ok', { ns: 'common' }),
onOk: removeUnstarredTopic,
@@ -116,9 +116,8 @@ export const useTopicActionsDropdownMenu = (
key: 'deleteAll',
label: t('actions.removeAll'),
onClick: () => {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
centered: true,
okButtonProps: { danger: true },
okText: t('ok', { ns: 'common' }),
onOk: removeAllTopic,
@@ -1,5 +1,6 @@
'use client';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Form } from 'antd';
import { createStaticStyles } from 'antd-style';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
@@ -69,7 +70,7 @@ interface PlatformDetailProps {
const PlatformDetail = memo<PlatformDetailProps>(
({ platformDef, agentId, currentConfig, runtimeStatus }) => {
const { t } = useTranslation('agent');
const { message: msg, modal } = App.useApp();
const { message: msg } = App.useApp();
const [form] = Form.useForm<ChannelFormValues>();
const [
@@ -402,7 +403,7 @@ const PlatformDetail = memo<PlatformDetailProps>(
const handleDelete = useCallback(async () => {
if (!currentConfig) return;
modal.confirm({
confirmModal({
content: t('channel.deleteConfirmDesc'),
okButtonProps: { danger: true },
onOk: async () => {
@@ -416,7 +417,7 @@ const PlatformDetail = memo<PlatformDetailProps>(
},
title: t('channel.deleteConfirm'),
});
}, [currentConfig, agentId, deleteBotProvider, msg, t, modal, form]);
}, [currentConfig, agentId, deleteBotProvider, msg, t, form]);
const handleToggleEnable = useCallback(
async (enabled: boolean) => {
+4 -4
View File
@@ -2,6 +2,7 @@
import { exportJSONFile } from '@lobechat/utils/client';
import { Icon, Tag } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Dropdown, type MenuProps } from 'antd';
import { createStaticStyles, cx, useTheme } from 'antd-style';
import { Book, Download, MoreHorizontal, Trash2, Upload } from 'lucide-react';
@@ -88,7 +89,7 @@ const PlatformList = memo<PlatformListProps>(
({ platforms, activeId, agentId, onSelect, providers, runtimeStatuses }) => {
const { t } = useTranslation('agent');
const theme = useTheme();
const { modal, message } = App.useApp();
const { message } = App.useApp();
const fileInputRef = useRef<HTMLInputElement>(null);
const deleteAllBotProviders = useAgentStore((s) => s.deleteAllBotProviders);
const createBotProvider = useAgentStore((s) => s.createBotProvider);
@@ -154,7 +155,7 @@ const PlatformList = memo<PlatformListProps>(
const handleDeleteAll = useCallback(() => {
if (!providers?.length) return;
modal.confirm({
confirmModal({
content: t('channel.deleteAllConfirmDesc'),
okButtonProps: { danger: true },
okText: t('channel.deleteAllChannels'),
@@ -167,9 +168,8 @@ const PlatformList = memo<PlatformListProps>(
}
},
title: t('channel.deleteAllConfirm'),
type: 'warning',
});
}, [agentId, deleteAllBotProviders, message, modal, providers, t]);
}, [agentId, deleteAllBotProviders, message, providers, t]);
const hasProviders = !!providers?.length;
const menuItems: MenuProps['items'] = [
@@ -36,7 +36,7 @@ import { systemStatusSelectors } from '@/store/global/selectors';
export const useMenu = (): { menuItems: DropdownItem[] } => {
const { t } = useTranslation(['chat', 'topic', 'common', 'file']);
const { modal, message } = App.useApp();
const { message } = App.useApp();
const { pathname } = useLocation();
const [wideScreen, toggleWideScreen] = useGlobalStore((s) => [
@@ -260,8 +260,7 @@ export const useMenu = (): { menuItems: DropdownItem[] } => {
key: 'delete',
label: t('delete', { ns: 'common' }),
onClick: () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeTopic(topicId);
@@ -291,7 +290,6 @@ export const useMenu = (): { menuItems: DropdownItem[] } => {
toggleWideScreen,
openCompareModal,
t,
modal,
message,
]);
@@ -10,6 +10,10 @@ const messageError = vi.hoisted(() => vi.fn());
const messageSuccess = vi.hoisted(() => vi.fn());
const removeDocumentMock = vi.hoisted(() => vi.fn());
vi.mock('@lobehub/ui/base-ui', () => ({
confirmModal: modalConfirm,
}));
vi.mock('@lobehub/ui', () => ({
Accordion: ({ children }: { children?: ReactNode }) => <div>{children}</div>,
AccordionItem: ({ children, title }: { children?: ReactNode; title?: ReactNode }) => (
@@ -45,7 +49,6 @@ vi.mock('antd', () => ({
App: {
useApp: () => ({
message: { error: messageError, success: messageSuccess },
modal: { confirm: modalConfirm },
}),
},
}));
@@ -1,5 +1,6 @@
import { buildAgentSkillIdentifier } from '@lobechat/const';
import { ActionIcon, Center, Empty, Flexbox, Text } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { SkillsIcon } from '@lobehub/ui/icons';
import { App } from 'antd';
import { createStaticStyles, cx } from 'antd-style';
@@ -112,7 +113,7 @@ interface DocumentItemProps {
const DocumentItem = memo<DocumentItemProps>(
({ agentId, document, hideDelete = false, mutate }) => {
const { t } = useTranslation(['chat', 'common']);
const { message, modal } = App.useApp();
const { message } = App.useApp();
const [deleting, setDeleting] = useState(false);
const openDocument = useChatStore((s) => s.openDocument);
const closeDocument = useChatStore((s) => s.closeDocument);
@@ -138,11 +139,10 @@ const DocumentItem = memo<DocumentItemProps>(
const handleDelete = (e: MouseEvent) => {
e.stopPropagation();
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
centered: true,
content: t('workingPanel.resources.deleteConfirm', { ns: 'chat' }),
okButtonProps: { danger: true, type: 'primary' },
okButtonProps: { danger: true },
okText: t('delete', { ns: 'common' }),
onOk: async () => {
setDeleting(true);
@@ -67,6 +67,10 @@ vi.mock('@lobehub/ui/icons', () => ({
ShapesUploadIcon: () => null,
}));
vi.mock('@lobehub/ui/base-ui', () => ({
confirmModal: vi.fn(),
}));
vi.mock('antd', () => ({
App: {
useApp: () => ({
@@ -1,6 +1,6 @@
import { ActionIcon, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { ShapesUploadIcon } from '@lobehub/ui/icons';
import { App, Modal } from 'antd';
import isEqual from 'fast-deep-equal';
import { BotMessageSquareIcon, MoreHorizontal, Settings2Icon, Trash } from 'lucide-react';
import { memo, useCallback, useMemo, useState } from 'react';
@@ -29,7 +29,6 @@ import AutoSaveHint from './AutoSaveHint';
const Header = memo(() => {
const { t } = useTranslation(['setting', 'marketAuth', 'chat']);
const { modal } = App.useApp();
const navigate = useNavigate();
const meta = useAgentStore(agentSelectors.currentAgentMeta, isEqual);
@@ -97,8 +96,7 @@ const Header = memo(() => {
return;
}
Modal.confirm({
okButtonProps: { type: 'primary' },
confirmModal({
onOk: async () => {
if (!isAuthenticated) {
try {
@@ -128,8 +126,7 @@ const Header = memo(() => {
const handleDelete = useCallback(() => {
if (!activeAgentId) return;
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeAgent(activeAgentId);
@@ -138,7 +135,7 @@ const Header = memo(() => {
},
title: t('confirmRemoveSessionItemAlert', { ns: 'chat' }),
});
}, [activeAgentId, modal, navigate, removeAgent, t]);
}, [activeAgentId, navigate, removeAgent, t]);
const menuItems = useMemo(
() => [
@@ -4,7 +4,7 @@ import { type HeterogeneousProviderConfig, type UserCredSummary } from '@lobecha
import { Github } from '@lobehub/icons';
import { Flexbox } from '@lobehub/ui';
import { Avatar, Button, Input, Select, Spin, Tag, Typography } from 'antd';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { CheckCircle2, KeyRound, X } from 'lucide-react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -15,14 +15,14 @@ import { lambdaClient, lambdaQuery } from '@/libs/trpc/client';
// Fixed cred key for Claude Code OAuth token — never changes
const CLAUDE_TOKEN_CRED_KEY = 'CLAUDE_CODE_OAUTH_TOKEN';
const useStyles = createStyles(({ css, token, cssVar }) => ({
const styles = createStaticStyles(({ css }) => ({
card: css`
padding-block: 16px 12px;
padding-inline: 16px;
border: 1px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadiusLG}px;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadiusLG};
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
credOption: css`
display: flex;
@@ -48,12 +48,12 @@ const useStyles = createStyles(({ css, token, cssVar }) => ({
min-height: 36px;
padding-block: 6px;
padding-inline: 8px;
border-radius: ${token.borderRadiusSM}px;
border-radius: ${cssVar.borderRadiusSM};
transition: background 0.15s;
&:hover {
background: ${token.colorFillTertiary};
background: ${cssVar.colorFillTertiary};
.repo-delete-btn {
opacity: 1;
@@ -61,7 +61,7 @@ const useStyles = createStyles(({ css, token, cssVar }) => ({
}
`,
repoItemActive: css`
background: ${token.colorFillSecondary};
background: ${cssVar.colorFillSecondary};
`,
repoDeleteBtn: css`
cursor: pointer;
@@ -73,7 +73,7 @@ const useStyles = createStyles(({ css, token, cssVar }) => ({
border: none;
border-radius: 4px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
opacity: 0;
background: transparent;
@@ -83,7 +83,7 @@ const useStyles = createStyles(({ css, token, cssVar }) => ({
color 0.15s;
&:hover {
color: ${token.colorError};
color: ${cssVar.colorError};
}
`,
repoList: css`
@@ -93,15 +93,15 @@ const useStyles = createStyles(({ css, token, cssVar }) => ({
`,
sectionDesc: css`
font-size: 12px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
sectionDivider: css`
margin-block: 12px;
border-block-start: 1px solid ${token.colorBorderSecondary};
border-block-start: 1px solid ${cssVar.colorBorderSecondary};
`,
sectionLabel: css`
font-size: 12px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
text-transform: uppercase;
letter-spacing: 0.04em;
`,
@@ -121,7 +121,6 @@ interface TokenSectionProps {
const TokenSection = memo<TokenSectionProps>(({ existingCred, onSaved, onEnvChange }) => {
const { t } = useTranslation('setting');
const { styles } = useStyles();
const [editing, setEditing] = useState(!existingCred);
const [tokenInput, setTokenInput] = useState('');
const [saving, setSaving] = useState(false);
@@ -211,7 +210,6 @@ interface RepoListSectionProps {
const RepoListSection = memo<RepoListSectionProps>(({ repos, onReposChange }) => {
const { t } = useTranslation('setting');
const { styles } = useStyles();
const [input, setInput] = useState('');
const addRepo = () => {
@@ -269,7 +267,6 @@ const RepoListSection = memo<RepoListSectionProps>(({ repos, onReposChange }) =>
const CloudHeterogeneousConfig = memo<CloudHeterogeneousConfigProps>(
({ provider, onEnvChange }) => {
const { t } = useTranslation('setting');
const { styles } = useStyles();
const navigate = useNavigate();
const currentEnv = provider.env ?? {};
@@ -83,13 +83,12 @@ vi.mock('@lobehub/ui', () => ({
}));
vi.mock('antd-style', () => ({
createStyles: () => () => ({
styles: {
createStaticStyles: () => ({
card: 'card',
label: 'label',
path: 'path',
},
}),
cssVar: new Proxy({}, { get: (_, key) => `var(--${String(key)})` }),
}));
vi.mock('lucide-react', () => ({
@@ -8,7 +8,7 @@ import {
} from '@lobechat/heterogeneous-agents/client';
import type { HeterogeneousProviderConfig } from '@lobechat/types';
import { ActionIcon, CopyButton, Flexbox, Icon, Input, Tag, Text, Tooltip } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { createStaticStyles, cssVar } from 'antd-style';
import { Loader2Icon, PencilLine, RefreshCw, XCircle } from 'lucide-react';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -19,14 +19,14 @@ import { toolDetectorService } from '@/services/electron/toolDetector';
const COMMAND_LINE_HEIGHT = 28;
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css }) => ({
card: css`
padding-block: 16px 4px;
padding-inline: 16px;
border: 1px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadiusLG}px;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadiusLG};
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
cardHeader: css`
display: flex;
@@ -57,7 +57,7 @@ const useStyles = createStyles(({ css, token }) => ({
`,
metaText: css`
font-size: 13px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
pathWrap: css`
display: flex;
@@ -69,7 +69,7 @@ const useStyles = createStyles(({ css, token }) => ({
`,
detailList: css`
margin-block-start: 4px;
border-block-start: 1px solid ${token.colorBorderSecondary};
border-block-start: 1px solid ${cssVar.colorBorderSecondary};
`,
detailRow: css`
display: flex;
@@ -80,7 +80,7 @@ const useStyles = createStyles(({ css, token }) => ({
padding-block: 8px;
& + & {
border-block-start: 1px solid ${token.colorBorderSecondary};
border-block-start: 1px solid ${cssVar.colorBorderSecondary};
}
`,
detailLabel: css`
@@ -89,7 +89,7 @@ const useStyles = createStyles(({ css, token }) => ({
width: 96px;
font-size: 12px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
text-transform: uppercase;
letter-spacing: 0.04em;
`,
@@ -111,7 +111,7 @@ const useStyles = createStyles(({ css, token }) => ({
`,
commandInput: css`
width: 100%;
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
&,
&.ant-input,
@@ -127,7 +127,7 @@ const useStyles = createStyles(({ css, token }) => ({
max-height: ${COMMAND_LINE_HEIGHT}px;
border-radius: 999px !important;
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
font-size: 14px;
line-height: ${COMMAND_LINE_HEIGHT - 2}px;
}
@@ -174,10 +174,10 @@ const useStyles = createStyles(({ css, token }) => ({
height: ${COMMAND_LINE_HEIGHT}px;
padding-block: 0;
padding-inline: 12px;
border: 1px solid ${token.colorBorderSecondary};
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: 999px;
background: ${token.colorFillSecondary};
background: ${cssVar.colorFillSecondary};
`,
commandEditButton: css`
pointer-events: none;
@@ -187,23 +187,23 @@ const useStyles = createStyles(({ css, token }) => ({
commandText: css`
min-width: 0;
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
font-size: 14px;
line-height: 20px;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
accountValue: css`
font-size: 15px;
color: ${token.colorText};
color: ${cssVar.colorText};
`,
path: css`
font-family: ${token.fontFamilyCode};
font-family: ${cssVar.fontFamilyCode};
font-size: 12px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
`,
unavailableText: css`
font-size: 13px;
color: ${token.colorTextSecondary};
color: ${cssVar.colorTextSecondary};
`,
}));
@@ -215,7 +215,6 @@ interface HeterogeneousAgentStatusCardProps {
const HeterogeneousAgentStatusCard = memo<HeterogeneousAgentStatusCardProps>(
({ provider, onCommandChange }) => {
const { t } = useTranslation('setting');
const { styles } = useStyles();
const navigate = useNavigate();
const providerConfig = getHeterogeneousAgentClientConfig(provider.type);
const defaultCommand = providerConfig?.command || '';
@@ -6,8 +6,9 @@ import {
} from '@lobechat/heterogeneous-agents';
import type { HeterogeneousProviderConfig } from '@lobechat/types';
import { ActionIcon, Flexbox, Icon, Text, Tooltip } from '@lobehub/ui';
import { Button, Modal, Select, Tag } from 'antd';
import { createStyles } from 'antd-style';
import { Select } from '@lobehub/ui/base-ui';
import { Button, Modal, Tag } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import { BotIcon, CheckCircle2, MonitorSmartphone, RefreshCw, XCircle } from 'lucide-react';
import { memo, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -15,14 +16,14 @@ import { useTranslation } from 'react-i18next';
import { lambdaClient, lambdaQuery } from '@/libs/trpc/client';
import { useAgentStore } from '@/store/agent';
const useStyles = createStyles(({ css, token }) => ({
const styles = createStaticStyles(({ css }) => ({
card: css`
padding-block: 16px 4px;
padding-inline: 16px;
border: 1px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadiusLG}px;
border: 1px solid ${cssVar.colorBorderSecondary};
border-radius: ${cssVar.borderRadiusLG};
background: ${token.colorBgContainer};
background: ${cssVar.colorBgContainer};
`,
cardHeader: css`
display: flex;
@@ -37,7 +38,7 @@ const useStyles = createStyles(({ css, token }) => ({
font-weight: 500;
`,
detailList: css`
border-block-start: 1px solid ${token.colorBorderSecondary};
border-block-start: 1px solid ${cssVar.colorBorderSecondary};
`,
detailRow: css`
display: flex;
@@ -48,7 +49,7 @@ const useStyles = createStyles(({ css, token }) => ({
padding-block: 6px;
& + & {
border-block-start: 1px solid ${token.colorBorderSecondary};
border-block-start: 1px solid ${cssVar.colorBorderSecondary};
}
`,
detailLabel: css`
@@ -57,7 +58,7 @@ const useStyles = createStyles(({ css, token }) => ({
width: 96px;
font-size: 12px;
color: ${token.colorTextTertiary};
color: ${cssVar.colorTextTertiary};
text-transform: uppercase;
letter-spacing: 0.04em;
`,
@@ -85,7 +86,6 @@ interface RemoteAgentConfigCardProps {
const RemoteAgentConfigCard = memo<RemoteAgentConfigCardProps>(
({ provider, onBoundDeviceChange }) => {
const { t } = useTranslation('setting');
const { styles } = useStyles();
const agentId = useAgentStore((s) => s.activeAgentId);
const boundDeviceId = useAgentStore((s) =>
@@ -1,6 +1,7 @@
'use client';
import { Button, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { createStaticStyles } from 'antd-style';
import { ChevronDownIcon } from 'lucide-react';
@@ -37,7 +38,7 @@ const AddAgent = memo<{ mobile?: boolean }>(({ mobile }) => {
const [isLoading, setIsLoading] = useState(false);
const createAgent = useAgentStore((s) => s.createAgent);
const refreshAgentList = useHomeStore((s) => s.refreshAgentList);
const { message, modal } = App.useApp();
const { message } = App.useApp();
const navigate = useNavigate();
const { t } = useTranslation('discover');
@@ -56,7 +57,7 @@ const AddAgent = memo<{ mobile?: boolean }>(({ mobile }) => {
};
const showDuplicateConfirmation = (callback: () => void) => {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
content: t('assistants.duplicateAdd.content', { title }),
okText: t('assistants.duplicateAdd.ok'),
@@ -7,11 +7,11 @@ import {
CopyButton,
Flexbox,
Input,
Modal,
Skeleton,
Tag,
Text,
} from '@lobehub/ui';
import { Modal } from '@lobehub/ui/base-ui';
import { createStaticStyles, cssVar } from 'antd-style';
import { startCase } from 'es-toolkit/compat';
import { LinkIcon, Share2Icon } from 'lucide-react';
@@ -1,6 +1,7 @@
'use client';
import { Button, DropdownMenu, Flexbox, Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { createStaticStyles } from 'antd-style';
import { ChevronDownIcon } from 'lucide-react';
@@ -42,7 +43,7 @@ const AddGroupAgent = memo<{ mobile?: boolean }>(() => {
memberAgents = [],
} = useDetailContext();
const [isLoading, setIsLoading] = useState(false);
const { message, modal } = App.useApp();
const { message } = App.useApp();
const { t } = useTranslation('discover');
const navigate = useNavigate();
const loadGroups = useAgentGroupStore((s) => s.loadGroups);
@@ -67,7 +68,7 @@ const AddGroupAgent = memo<{ mobile?: boolean }>(() => {
};
const showDuplicateConfirmation = (callback: () => void) => {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
content: t('groupAgents.duplicateAdd.content', {
defaultValue: 'This group agent has already been added. Do you want to add it again?',
@@ -1,6 +1,6 @@
'use client';
import { Select } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -1,5 +1,6 @@
'use client';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -17,7 +18,7 @@ interface UseUserDetailOptions {
export const useUserDetail = ({ onMutate }: UseUserDetailOptions = {}) => {
const { t } = useTranslation('setting');
const { message, modal } = App.useApp();
const { message } = App.useApp();
const { session } = useMarketAuth();
const enableMarketTrustedClient = useServerConfigStore(
serverConfigSelectors.enableMarketTrustedClient,
@@ -88,7 +89,7 @@ export const useUserDetail = ({ onMutate }: UseUserDetailOptions = {}) => {
}
if (action === 'deprecate') {
modal.confirm({
confirmModal({
cancelText: t('myAgents.actions.cancel'),
content: t('myAgents.actions.deprecateConfirmContent'),
okButtonProps: { danger: true },
@@ -103,7 +104,7 @@ export const useUserDetail = ({ onMutate }: UseUserDetailOptions = {}) => {
await executeStatusChange(identifier, action, type);
},
[enableMarketTrustedClient, session?.accessToken, message, modal, t, onMutate],
[enableMarketTrustedClient, session?.accessToken, message, t, onMutate],
);
return {
@@ -1,6 +1,7 @@
'use client';
import { Button, Flexbox } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Typography } from 'antd';
import { ArrowLeft, Database, Pencil, Plus, Trash2 } from 'lucide-react';
import { memo, useCallback, useMemo, useState } from 'react';
@@ -24,7 +25,7 @@ const DatasetDetail = memo(() => {
const { t } = useTranslation('eval');
const { benchmarkId, datasetId } = useParams<{ benchmarkId: string; datasetId: string }>();
const navigate = useNavigate();
const { modal, message } = App.useApp();
const { message } = App.useApp();
const [pagination, setPagination] = useState({ current: 1, pageSize: 10 });
const [search, setSearch] = useState('');
@@ -78,7 +79,7 @@ const DatasetDetail = memo(() => {
const handleDeleteCase = useCallback(
(testCase: any) => {
modal.confirm({
confirmModal({
content: t('testCase.delete.confirm'),
okButtonProps: { danger: true },
okText: t('common.delete'),
@@ -94,11 +95,11 @@ const DatasetDetail = memo(() => {
title: t('common.delete'),
});
},
[handleRefresh, message, modal, t],
[handleRefresh, message, t],
);
const handleDelete = useCallback(() => {
modal.confirm({
confirmModal({
content: t('dataset.delete.confirm'),
okButtonProps: { danger: true },
okText: t('common.delete'),
@@ -113,7 +114,7 @@ const DatasetDetail = memo(() => {
},
title: t('common.delete'),
});
}, [benchmarkId, datasetId, message, modal, navigate, t]);
}, [benchmarkId, datasetId, message, navigate, t]);
if (!dataset) return null;
@@ -3,7 +3,8 @@
import type { AgentEvalRunListItem } from '@lobechat/types';
import { formatCost } from '@lobechat/utils';
import { Button, Flexbox, Icon } from '@lobehub/ui';
import { App, Badge, Dropdown } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { Badge, Dropdown } from 'antd';
import { createStaticStyles, cssVar } from 'antd-style';
import {
CircleDollarSign,
@@ -97,7 +98,6 @@ const BenchmarkHeader = memo<BenchmarkHeaderProps>(
totalCases,
}) => {
const { t } = useTranslation('eval');
const { modal } = App.useApp();
const navigate = useNavigate();
const deleteBenchmark = useEvalStore((s) => s.deleteBenchmark);
const refreshBenchmarkDetail = useEvalStore((s) => s.refreshBenchmarkDetail);
@@ -109,7 +109,7 @@ const BenchmarkHeader = memo<BenchmarkHeaderProps>(
};
const handleDelete = () => {
modal.confirm({
confirmModal({
content: t('benchmark.actions.delete.confirm'),
okButtonProps: { danger: true },
okText: t('benchmark.actions.delete'),
@@ -1,4 +1,5 @@
import { Button, Flexbox, Tag } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Card, Dropdown } from 'antd';
import { createStaticStyles } from 'antd-style';
import { ArrowRight, ChevronRight, Database, Ellipsis, Pencil, Play, Trash2 } from 'lucide-react';
@@ -135,10 +136,10 @@ const DatasetCard = memo<DatasetCardProps>(
onRun,
}) => {
const { t } = useTranslation('eval');
const { modal, message } = App.useApp();
const { message } = App.useApp();
const handleDelete = useCallback(() => {
modal.confirm({
confirmModal({
content: t('dataset.delete.confirm'),
okButtonProps: { danger: true },
okText: t('common.delete'),
@@ -153,7 +154,7 @@ const DatasetCard = memo<DatasetCardProps>(
},
title: t('common.delete'),
});
}, [dataset.id, message, modal, onRefresh, t]);
}, [dataset.id, message, onRefresh, t]);
return (
<Card className={styles.card}>
@@ -1,6 +1,7 @@
'use client';
import { Button, Flexbox } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Card, Skeleton } from 'antd';
import { createStaticStyles } from 'antd-style';
import { Plus } from 'lucide-react';
@@ -52,7 +53,7 @@ interface DatasetsTabProps {
const DatasetsTab = memo<DatasetsTabProps>(
({ benchmarkId, datasets, loading: datasetsLoading, onImport, onRefresh }) => {
const { t } = useTranslation('eval');
const { modal, message } = App.useApp();
const { message } = App.useApp();
const [expandedDs, setExpandedDs] = useState<string | null>(null);
const [pagination, setPagination] = useState({ current: 1, pageSize: 5 });
const [search, setSearch] = useState('');
@@ -115,7 +116,7 @@ const DatasetsTab = memo<DatasetsTabProps>(
const handleDeleteCase = useCallback(
(testCase: any) => {
modal.confirm({
confirmModal({
content: t('testCase.delete.confirm'),
okButtonProps: { danger: true },
okText: t('common.delete'),
@@ -132,7 +133,7 @@ const DatasetsTab = memo<DatasetsTabProps>(
title: t('common.delete'),
});
},
[expandedDs, message, modal, onRefresh, refreshTestCases, t],
[expandedDs, message, onRefresh, refreshTestCases, t],
);
return (
@@ -218,10 +219,9 @@ const DatasetsTab = memo<DatasetsTabProps>(
onSuccess={(dataset) => {
onRefresh();
// Ask if user wants to import data immediately
modal.success({
confirmModal({
cancelText: t('common.later'),
content: t('dataset.create.importNow'),
okCancel: true,
okText: t('dataset.actions.import'),
onOk: () => {
setImportDatasetId(dataset.id);
@@ -1,5 +1,6 @@
import type { AgentEvalRunListItem } from '@lobechat/types';
import { Flexbox, Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Card, Dropdown, Progress } from 'antd';
import { createStaticStyles } from 'antd-style';
import {
@@ -122,7 +123,7 @@ interface RunCardProps {
const RunCard = memo<RunCardProps>(({ benchmarkId, run, onRefresh, onEdit }) => {
const { t } = useTranslation('eval');
const { modal, message } = App.useApp();
const { message } = App.useApp();
const deleteRun = useEvalStore((s) => s.deleteRun);
const startRun = useEvalStore((s) => s.startRun);
const abortRun = useEvalStore((s) => s.abortRun);
@@ -148,7 +149,7 @@ const RunCard = memo<RunCardProps>(({ benchmarkId, run, onRefresh, onEdit }) =>
const handleStart = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
modal.confirm({
confirmModal({
content: t('run.actions.start.confirm'),
okText: t('run.actions.start'),
onOk: async () => {
@@ -166,7 +167,7 @@ const RunCard = memo<RunCardProps>(({ benchmarkId, run, onRefresh, onEdit }) =>
const handleAbort = (e?: React.MouseEvent) => {
e?.preventDefault();
e?.stopPropagation();
modal.confirm({
confirmModal({
content: t('run.actions.abort.confirm'),
okText: t('run.actions.abort'),
okButtonProps: { danger: true },
@@ -181,7 +182,7 @@ const RunCard = memo<RunCardProps>(({ benchmarkId, run, onRefresh, onEdit }) =>
const handleDelete = (e?: React.MouseEvent) => {
e?.preventDefault();
e?.stopPropagation();
modal.confirm({
confirmModal({
content: t('run.actions.delete.confirm'),
okButtonProps: { danger: true },
okText: t('run.actions.delete'),
@@ -2,7 +2,7 @@
import type { AgentEvalRunListItem } from '@lobechat/types';
import { Button, Flexbox } from '@lobehub/ui';
import { Select } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { Plus } from 'lucide-react';
import { memo, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -3,7 +3,8 @@
import type { EvalThreadResult } from '@lobechat/types';
import { formatCost, formatShortenNumber } from '@lobechat/utils';
import { ActionIcon, Flexbox, Icon, Tag } from '@lobehub/ui';
import { Badge, Input, Select, Table, Tooltip } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { Badge, Input, Table, Tooltip } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { createStaticStyles, cssVar } from 'antd-style';
import { Footprints, Play, RotateCcw } from 'lucide-react';
@@ -1,6 +1,7 @@
'use client';
import { Button, Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { createStaticStyles, cx } from 'antd-style';
import { Brain, ChartBar, MessageSquare, Play } from 'lucide-react';
@@ -105,12 +106,12 @@ interface IdleStateProps {
const IdleState = memo<IdleStateProps>(({ run }) => {
const { t } = useTranslation('eval');
const { modal, message } = App.useApp();
const { message } = App.useApp();
const startRun = useEvalStore((s) => s.startRun);
const [starting, setStarting] = useState(false);
const handleStart = () => {
modal.confirm({
confirmModal({
content: t('run.actions.start.confirm'),
okText: t('run.actions.start'),
onOk: async () => {
@@ -3,6 +3,7 @@
import { AGENT_PROFILE_URL } from '@lobechat/const';
import type { AgentEvalRunDetail } from '@lobechat/types';
import { ActionIcon, Avatar, copyToClipboard, Flexbox, Highlighter, Markdown } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Button, Card, Tag, Typography } from 'antd';
import { createStaticStyles } from 'antd-style';
import {
@@ -115,7 +116,7 @@ interface RunHeaderProps {
const RunHeader = memo<RunHeaderProps>(({ run, benchmarkId, hideStart }) => {
const { t } = useTranslation('eval');
const { modal, message } = App.useApp();
const { message } = App.useApp();
const navigate = useNavigate();
const abortRun = useEvalStore((s) => s.abortRun);
const deleteRun = useEvalStore((s) => s.deleteRun);
@@ -133,7 +134,7 @@ const RunHeader = memo<RunHeaderProps>(({ run, benchmarkId, hideStart }) => {
const agentProvider = snapshot?.provider || run.targetAgent?.provider;
const handleAbort = () => {
modal.confirm({
confirmModal({
content: t('run.actions.abort.confirm'),
okButtonProps: { danger: true },
okText: t('run.actions.abort'),
@@ -143,7 +144,7 @@ const RunHeader = memo<RunHeaderProps>(({ run, benchmarkId, hideStart }) => {
};
const handleDelete = () => {
modal.confirm({
confirmModal({
content: t('run.actions.delete.confirm'),
okButtonProps: { danger: true },
okText: t('run.actions.delete'),
@@ -156,7 +157,7 @@ const RunHeader = memo<RunHeaderProps>(({ run, benchmarkId, hideStart }) => {
};
const handleStart = () => {
modal.confirm({
confirmModal({
content: t('run.actions.start.confirm'),
okText: t('run.actions.start'),
onOk: async () => {
@@ -1,7 +1,8 @@
'use client';
import { Flexbox } from '@lobehub/ui';
import { App, Button, Card, Progress, Typography } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { Button, Card, Progress, Typography } from 'antd';
import { Play, RotateCcw } from 'lucide-react';
import { memo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -23,7 +24,6 @@ const POLLING_INTERVAL = 3000;
const RunDetail = memo(() => {
const { t } = useTranslation('eval');
const { modal } = App.useApp();
const { benchmarkId, runId } = useParams<{ benchmarkId: string; runId: string }>();
const useFetchRunDetail = useEvalStore((s) => s.useFetchRunDetail);
const useFetchRunResults = useEvalStore((s) => s.useFetchRunResults);
@@ -165,7 +165,7 @@ const RunDetail = memo(() => {
loading={retrying}
size="small"
onClick={() => {
modal.confirm({
confirmModal({
content: t('run.actions.retryErrors.confirm'),
onOk: async () => {
setRetrying(true);
@@ -1,7 +1,8 @@
'use client';
import { Center, Flexbox, Icon, Modal, Text } from '@lobehub/ui';
import { App, Form, Input, Select } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { App, Form, Input } from 'antd';
import { cssVar } from 'antd-style';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -1,7 +1,8 @@
'use client';
import { Center, Flexbox, Icon, Input, Modal, type ModalProps, Text, TextArea } from '@lobehub/ui';
import { App, Form, Select } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { App, Form } from 'antd';
import { cssVar } from 'antd-style';
import { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -1,7 +1,8 @@
'use client';
import { Flexbox } from '@lobehub/ui';
import { Checkbox, Input, Select, Table } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { Checkbox, Input, Table } from 'antd';
import { cssVar } from 'antd-style';
import { memo, type ReactNode, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -1,7 +1,8 @@
'use client';
import { Accordion, AccordionItem, Flexbox, Modal, Text } from '@lobehub/ui';
import { App, Form, Input, Select } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { App, Form, Input } from 'antd';
import { memo, useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -1,7 +1,8 @@
'use client';
import { Accordion, AccordionItem, Flexbox, Modal, Text } from '@lobehub/ui';
import { App, Form, Input, Select } from 'antd';
import { Select } from '@lobehub/ui/base-ui';
import { App, Form, Input } from 'antd';
import { memo, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -1,6 +1,7 @@
import type { ChatTopicStatus } from '@lobechat/types';
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import {
CheckCircle2,
@@ -37,7 +38,7 @@ export const useTopicItemDropdownMenu = ({
toggleEditing,
}: TopicItemDropdownMenuProps): (() => MenuProps['items']) => {
const { t } = useTranslation(['topic', 'common']);
const { modal, message } = App.useApp();
const { message } = App.useApp();
const navigate = useNavigate();
const openGroupTopicInNewWindow = useGlobalStore((s) => s.openGroupTopicInNewWindow);
@@ -162,8 +163,7 @@ export const useTopicItemDropdownMenu = ({
key: 'delete',
label: t('delete', { ns: 'common' }),
onClick: () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeTopic(id);
@@ -188,7 +188,6 @@ export const useTopicItemDropdownMenu = ({
navigate,
toggleEditing,
t,
modal,
message,
]);
};
@@ -1,6 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { PencilLine, Trash } from 'lucide-react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -17,7 +17,6 @@ export const useThreadItemDropdownMenu = ({
toggleEditing,
}: ThreadItemDropdownMenuProps): (() => MenuProps['items']) => {
const { t } = useTranslation(['thread', 'common']);
const { modal } = App.useApp();
const [removeThread] = useChatStore((s) => [s.removeThread]);
@@ -40,8 +39,7 @@ export const useThreadItemDropdownMenu = ({
key: 'delete',
label: t('delete', { ns: 'common' }),
onClick: () => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeThread(id);
@@ -51,5 +49,5 @@ export const useThreadItemDropdownMenu = ({
},
},
].filter(Boolean) as MenuProps['items'];
}, [id, removeThread, toggleEditing, t, modal]);
}, [id, removeThread, toggleEditing, t]);
};
@@ -1,5 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App, Upload } from 'antd';
import { css, cx } from 'antd-style';
import { Hash, Import, LucideCheck, Trash } from 'lucide-react';
@@ -100,9 +101,8 @@ export const useTopicActionsDropdownMenu = (
key: 'deleteUnstarred',
label: t('actions.removeUnstarred'),
onClick: () => {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
centered: true,
okButtonProps: { danger: true },
okText: t('ok', { ns: 'common' }),
onOk: removeUnstarredTopic,
@@ -116,9 +116,8 @@ export const useTopicActionsDropdownMenu = (
key: 'deleteAll',
label: t('actions.removeAll'),
onClick: () => {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
centered: true,
okButtonProps: { danger: true },
okText: t('ok', { ns: 'common' }),
onOk: removeAllTopic,
@@ -1,5 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { LucideCopy, Pen, PictureInPicture2Icon, Pin, PinOff, Trash } from 'lucide-react';
import { useMemo } from 'react';
@@ -29,7 +30,7 @@ export const useGroupDropdownMenu = ({
title,
}: UseGroupDropdownMenuParams): (() => MenuProps['items']) => {
const { t } = useTranslation('chat');
const { modal, message } = App.useApp();
const { message } = App.useApp();
const openAgentInNewWindow = useGlobalStore((s) => s.openAgentInNewWindow);
const [pinAgentGroup, duplicateAgentGroup, removeAgentGroup] = useHomeStore((s) => [
@@ -92,8 +93,7 @@ export const useGroupDropdownMenu = ({
label: t('delete', { ns: 'common' }),
onClick: ({ domEvent }: any) => {
domEvent.stopPropagation();
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeAgentGroup(id);
@@ -116,7 +116,6 @@ export const useGroupDropdownMenu = ({
title,
duplicateAgentGroup,
openAgentInNewWindow,
modal,
removeAgentGroup,
message,
],
@@ -1,6 +1,7 @@
import { SessionDefaultGroup } from '@lobechat/types';
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import isEqual from 'fast-deep-equal';
import {
@@ -42,7 +43,7 @@ export const useAgentDropdownMenu = ({
title,
}: UseAgentDropdownMenuParams): (() => MenuProps['items']) => {
const { t } = useTranslation('chat');
const { modal, message } = App.useApp();
const { message } = App.useApp();
const openAgentInNewWindow = useGlobalStore((s) => s.openAgentInNewWindow);
const sessionCustomGroups = useHomeStore(homeAgentListSelectors.agentGroups, isEqual);
@@ -131,8 +132,7 @@ export const useAgentDropdownMenu = ({
label: t('delete', { ns: 'common' }),
onClick: ({ domEvent }: any) => {
domEvent.stopPropagation();
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeAgent(id);
@@ -1,4 +1,5 @@
import { ActionIcon, EditableText, SortableList } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { createStaticStyles } from 'antd-style';
import { PencilLine, Trash } from 'lucide-react';
@@ -24,7 +25,7 @@ const styles = createStaticStyles(({ css }) => ({
const GroupItem = memo<SessionGroupItemBase>(({ id, name }) => {
const { t } = useTranslation('chat');
const { message, modal } = App.useApp();
const { message } = App.useApp();
const [editing, setEditing] = useState(false);
const [updateGroupName, removeGroup] = useHomeStore((s) => [s.updateGroupName, s.removeGroup]);
@@ -40,11 +41,9 @@ const GroupItem = memo<SessionGroupItemBase>(({ id, name }) => {
icon={Trash}
size={'small'}
onClick={() => {
modal.confirm({
centered: true,
confirmModal({
okButtonProps: {
danger: true,
type: 'primary',
},
onOk: async () => {
await removeGroup(id);
@@ -1,6 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { PencilLine, Trash } from 'lucide-react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -18,7 +18,6 @@ export const useProjectItemDropdownMenu = ({
}: ProjectItemDropdownMenuProps): (() => MenuProps['items']) => {
const { t } = useTranslation(['home', 'common']);
const [removeKnowledgeBase] = useKnowledgeBaseStore((s) => [s.removeKnowledgeBase]);
const { modal } = App.useApp();
return useCallback(
() => [
@@ -40,8 +39,7 @@ export const useProjectItemDropdownMenu = ({
label: t('delete', { ns: 'common' }),
onClick: () => {
if (!id) return;
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeKnowledgeBase(id);
@@ -51,6 +49,6 @@ export const useProjectItemDropdownMenu = ({
},
},
],
[t, id, modal, removeKnowledgeBase, toggleEditing],
[t, id, removeKnowledgeBase, toggleEditing],
);
};
@@ -1,4 +1,5 @@
import { Icon } from '@lobehub/ui';
import { confirmModal } from '@lobehub/ui/base-ui';
import { App } from 'antd';
import { type ItemType } from 'antd/es/menu/interface';
import { createStaticStyles } from 'antd-style';
@@ -25,7 +26,7 @@ const styles = createStaticStyles(({ css }) => ({
*/
export const useSessionGroupMenuItems = () => {
const { t } = useTranslation('chat');
const { modal, message } = App.useApp();
const { message } = App.useApp();
const groupTemplates = useGroupTemplates();
const [storeCreateAgent] = useAgentStore((s) => [s.createAgent]);
@@ -88,11 +89,7 @@ export const useSessionGroupMenuItems = () => {
label: t('delete', { ns: 'common' }),
onClick: (info: any) => {
info.domEvent?.stopPropagation();
modal.confirm({
centered: true,
classNames: {
root: styles.modalRoot,
},
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
await removeGroup(groupId);
@@ -102,7 +99,7 @@ export const useSessionGroupMenuItems = () => {
},
};
},
[t, modal, removeGroup, styles.modalRoot],
[t, removeGroup],
);
/**
@@ -1,6 +1,6 @@
import { type MenuProps } from '@lobehub/ui';
import { Icon } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { PencilLineIcon, Trash } from 'lucide-react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
@@ -16,7 +16,6 @@ export const useRecentItemDropdownMenu = (
toggleEditing: (visible?: boolean) => void,
) => {
const { t } = useTranslation(['common', 'topic', 'components']);
const { modal } = App.useApp();
const [updateRecentTitle, refreshRecents] = useHomeStore((s) => [
s.updateRecentTitle,
s.refreshRecents,
@@ -52,8 +51,7 @@ export const useRecentItemDropdownMenu = (
topic: t('actions.confirmRemoveTopic', { ns: 'topic' }),
};
modal.confirm({
centered: true,
confirmModal({
okButtonProps: { danger: true },
onOk: async () => {
switch (item.type) {
@@ -75,7 +73,7 @@ export const useRecentItemDropdownMenu = (
},
title: confirmMessages[item.type] || t('delete', { ns: 'common' }),
});
}, [item, modal, t, refreshRecents]);
}, [item, t, refreshRecents]);
const dropdownMenu = useCallback((): MenuProps['items'] => {
const canRename = true;
@@ -1,6 +1,6 @@
import { type ActionIconProps } from '@lobehub/ui';
import { ActionIcon, DropdownMenu } from '@lobehub/ui';
import { App } from 'antd';
import { confirmModal } from '@lobehub/ui/base-ui';
import { MoreHorizontal, Pencil, Trash2 } from 'lucide-react';
import { type KeyboardEvent, type MouseEvent } from 'react';
import { memo } from 'react';
@@ -15,7 +15,6 @@ interface ActivityDropdownProps {
const ActivityDropdown = memo<ActivityDropdownProps>(({ id, size = 'small' }) => {
const { t } = useTranslation(['memory', 'common']);
const { modal } = App.useApp();
const activities = useUserMemoryStore((s) => s.activities);
const deleteActivity = useUserMemoryStore((s) => s.deleteActivity);
@@ -30,7 +29,7 @@ const ActivityDropdown = memo<ActivityDropdownProps>(({ id, size = 'small' }) =>
setEditingMemory(id, activity.narrative || activity.notes || '', 'activity');
}
} else if (info.key === 'delete') {
modal.confirm({
confirmModal({
cancelText: t('cancel', { ns: 'common' }),
content: t('activity.deleteConfirm'),
okButtonProps: { danger: true },
@@ -39,7 +38,6 @@ const ActivityDropdown = memo<ActivityDropdownProps>(({ id, size = 'small' }) =>
await deleteActivity(id);
},
title: t('activity.deleteTitle'),
type: 'warning',
});
}
};

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