From 562c6b1bdb188b7fa22cfc78f6d183482c3de85c Mon Sep 17 00:00:00 2001 From: yanweidong Date: Sat, 25 Jul 2026 23:46:24 +0800 Subject: [PATCH] feat(desktop): add document upload and markdown editor --- apps/desktop/scripts/visual-check.mjs | 13 ++++++++- apps/desktop/src/App.tsx | 39 +++++++++++++++++++++------ apps/desktop/src/api.ts | 5 ++++ apps/desktop/src/styles.css | 12 ++++++--- 4 files changed, 57 insertions(+), 12 deletions(-) diff --git a/apps/desktop/scripts/visual-check.mjs b/apps/desktop/scripts/visual-check.mjs index b6f42de..9a89fc3 100644 --- a/apps/desktop/scripts/visual-check.mjs +++ b/apps/desktop/scripts/visual-check.mjs @@ -65,6 +65,16 @@ await page.getByRole('option', { name: '森林项目' }).click() await page.waitForSelector('.channel-rail') await page.locator('.channel-rail button[title="资料"]').click() const documents = await page.locator('.detail-row').count() +const documentActions = await page.locator('.document-actions .arco-btn').count() +await page.locator('.document-actions .arco-btn').nth(1).click() +await page.waitForSelector('.markdown-editor-page') +await page.screenshot({ path: 'test-results/app-markdown-new.png', fullPage: true }) +const markdownEditor = await page.locator('.markdown-editor-page').count() +const markdownLayout = await page.evaluate(() => { + const rect = (selector) => { const node = document.querySelector(selector); const value = node?.getBoundingClientRect(); return value ? { left: value.left, width: value.width } : null } + return { scrollX: window.scrollX, scrollWidth: document.documentElement.scrollWidth, clientWidth: document.documentElement.clientWidth, shell: rect('.app-shell'), main: rect('.app-main'), rail: rect('.channel-rail'), editor: rect('.markdown-editor-page') } +}) +await page.locator('.markdown-editor-page .content-heading .arco-btn').click() await page.locator('.channel-rail button[title="AI"]').click() await page.waitForSelector('.expert-chips') const experts = await page.locator('.expert-chips span').count() @@ -83,8 +93,9 @@ if (home.pending !== 1 || home.completed !== 0) failures.push(`all-project task if (taskModal !== 1) failures.push('task editor did not open') if (exploreSources !== 2 || exploreDetail !== 1) failures.push(`explore reader did not render: ${JSON.stringify({ exploreSources, exploreDetail })}`) if (documents !== 1) failures.push(`expected one document, got ${documents}`) +if (documentActions !== 2 || markdownEditor !== 1) failures.push(`document actions did not render: ${JSON.stringify({ documentActions, markdownEditor })}`) if (experts !== 1) failures.push(`expected one AI expert, got ${experts}`) if (profileMenu !== 1) failures.push('profile menu did not open') if (errors.length) failures.push(`console errors: ${errors.join('; ')}`) -console.log(JSON.stringify({ home, documents, experts, taskModal, exploreSources, exploreDetail, profileMenu, errors }, null, 2)) +console.log(JSON.stringify({ home, documents, documentActions, markdownEditor, markdownLayout, experts, taskModal, exploreSources, exploreDetail, profileMenu, errors }, null, 2)) if (failures.length) throw new Error(failures.join('\n')) diff --git a/apps/desktop/src/App.tsx b/apps/desktop/src/App.tsx index af223cb..d53c4d7 100644 --- a/apps/desktop/src/App.tsx +++ b/apps/desktop/src/App.tsx @@ -1,13 +1,14 @@ -import { useEffect, useMemo, useState } from 'react' +import { useEffect, useMemo, useRef, useState } from 'react' import { Alert, Avatar, Button, Dropdown, Empty, Form, Input, Menu, Modal, Select, Spin, Switch, Tag, Typography } from '@arco-design/web-react' import { IconApps, IconBook, IconCheckCircle, IconCheckCircleFill, IconClockCircle, IconCompass, IconDown, IconFile, IconFolder, IconLeft, IconList, IconMoon, IconPlus, IconRefresh, IconRight, IconRobot, - IconSun, IconUser, + IconSun, IconUpload, IconUser, } from '@arco-design/web-react/icon' import { - ApiError, createProject, fetchProjectWorkspace, fetchProjects, getCurrentUser, + ApiError, createMarkdownDocument, createProject, fetchProjectWorkspace, fetchProjects, getCurrentUser, listAIExperts, listDatasetItems, listDatasetSources, login, setApiBaseUrl, updateCurrentUser, updateTask, + uploadDocument, type ApiSession, type CurrentUser, type DatasetItem, type DatasetSource, type Expert, type Project, type Workspace, type WorkspaceTask, } from './api' @@ -114,7 +115,7 @@ export function App() { {error ? setError('')} /> : null} -
{workspaceMode === 'explore' ? : !projectId ? setProjectModalOpen(true)} /> : visibleWorkspaces.length ? : }
+
{workspaceMode === 'explore' ? : !projectId ? setProjectModalOpen(true)} /> : visibleWorkspaces.length ? { await uploadDocument(session, projectId, file); await loadWorkspaces() }} onCreateMarkdown={async (input) => { await createMarkdownDocument(session, projectId, input); await loadWorkspaces() }} /> : }
{projectId && workspaceMode === 'workbench' ?