From ddf72733e1c63f28dbc921d83fd4b036cb2a8d76 Mon Sep 17 00:00:00 2001 From: regischen <58546692+regischen@users.noreply.github.com> Date: Thu, 20 Jun 2024 02:52:14 +0000 Subject: [PATCH] feat: add ai-is-land (#7259) --- .../src/components/pure/ai-island/icons.tsx | 27 ++++++++ .../src/components/pure/ai-island/index.tsx | 65 +++++++++++++++++++ .../src/components/pure/ai-island/style.ts | 43 ++++++++++++ .../workbench/view/split-view/split-view.tsx | 2 - .../workspace/detail-page/detail-page.tsx | 7 +- tests/affine-local/e2e/ai-land.spec.ts | 16 +++++ tests/affine-local/e2e/contact-us.spec.ts | 17 ----- tests/affine-local/e2e/shortcuts.spec.ts | 23 ------- 8 files changed, 156 insertions(+), 44 deletions(-) create mode 100644 packages/frontend/core/src/components/pure/ai-island/icons.tsx create mode 100644 packages/frontend/core/src/components/pure/ai-island/index.tsx create mode 100644 packages/frontend/core/src/components/pure/ai-island/style.ts create mode 100644 tests/affine-local/e2e/ai-land.spec.ts delete mode 100644 tests/affine-local/e2e/contact-us.spec.ts delete mode 100644 tests/affine-local/e2e/shortcuts.spec.ts diff --git a/packages/frontend/core/src/components/pure/ai-island/icons.tsx b/packages/frontend/core/src/components/pure/ai-island/icons.tsx new file mode 100644 index 0000000000..35b00539b0 --- /dev/null +++ b/packages/frontend/core/src/components/pure/ai-island/icons.tsx @@ -0,0 +1,27 @@ +export const AIIcon = () => { + return ( + + + + + + + + + + + + ); +}; diff --git a/packages/frontend/core/src/components/pure/ai-island/index.tsx b/packages/frontend/core/src/components/pure/ai-island/index.tsx new file mode 100644 index 0000000000..a39b34a02f --- /dev/null +++ b/packages/frontend/core/src/components/pure/ai-island/index.tsx @@ -0,0 +1,65 @@ +import type { SidebarTabName } from '@affine/core/modules/multi-tab-sidebar'; +import { RightSidebarService } from '@affine/core/modules/right-sidebar'; +import { + DocsService, + GlobalContextService, + GlobalStateService, + LiveData, + useLiveData, + useService, +} from '@toeverything/infra'; +import { useCallback } from 'react'; + +import { ToolContainer } from '../../workspace'; +import { AIIcon } from './icons'; +import { StyledIsland, StyledTriggerWrapper } from './style'; + +export const RIGHT_SIDEBAR_TABS_ACTIVE_KEY = + 'app:settings:rightsidebar:tabs:active'; + +export const AIIsland = () => { + const docId = useLiveData( + useService(GlobalContextService).globalContext.docId.$ + ); + const docRecordList = useService(DocsService).list; + const doc = useLiveData(docId ? docRecordList.doc$(docId) : undefined); + const mode = useLiveData(doc?.mode$); + + const globalState = useService(GlobalStateService).globalState; + const activeTabName = useLiveData( + LiveData.from( + globalState.watch(RIGHT_SIDEBAR_TABS_ACTIVE_KEY), + 'journal' + ) + ); + const setActiveTabName = useCallback( + (name: string) => { + globalState.set(RIGHT_SIDEBAR_TABS_ACTIVE_KEY, name); + }, + [globalState] + ); + const rightSidebar = useService(RightSidebarService).rightSidebar; + const rightSidebarOpen = useLiveData(rightSidebar.isOpen$); + + return ( + + { + if (rightSidebarOpen) return; + rightSidebar.isOpen$; + rightSidebar.open(); + if (activeTabName !== 'chat') { + setActiveTabName('chat'); + } + }} + inEdgelessPage={!!docId && mode === 'edgeless'} + > + + + + + + ); +}; diff --git a/packages/frontend/core/src/components/pure/ai-island/style.ts b/packages/frontend/core/src/components/pure/ai-island/style.ts new file mode 100644 index 0000000000..d35f1059e8 --- /dev/null +++ b/packages/frontend/core/src/components/pure/ai-island/style.ts @@ -0,0 +1,43 @@ +import { displayFlex, positionAbsolute, styled } from '@affine/component'; + +export const StyledIsland = styled('div')<{ + spread: boolean; + inEdgelessPage?: boolean; +}>(({ spread, inEdgelessPage }) => { + return { + width: '44px', + position: 'relative', + boxShadow: spread + ? 'var(--affine-menu-shadow)' + : inEdgelessPage + ? 'var(--affine-menu-shadow)' + : 'unset', + padding: '0 4px 44px', + borderRadius: '50%', + background: spread + ? 'var(--affine-background-overlay-panel-color)' + : 'var(--affine-background-primary-color)', + ':hover': { + background: spread ? undefined : 'var(--affine-white)', + boxShadow: spread ? undefined : 'var(--affine-menu-shadow)', + }, + }; +}); + +export const StyledTriggerWrapper = styled('div')<{ + spread?: boolean; +}>(({ spread }) => { + return { + width: '36px', + height: '36px', + cursor: 'pointer', + color: 'var(--affine-icon-color)', + borderRadius: '5px', + fontSize: '24px', + ...displayFlex('center', 'center'), + ...positionAbsolute({ left: '4px', bottom: '4px' }), + ':hover': { + backgroundColor: spread ? 'var(--affine-hover-color)' : undefined, + }, + }; +}); diff --git a/packages/frontend/core/src/modules/workbench/view/split-view/split-view.tsx b/packages/frontend/core/src/modules/workbench/view/split-view/split-view.tsx index 65f3f49b46..ad8f12e0ff 100644 --- a/packages/frontend/core/src/modules/workbench/view/split-view/split-view.tsx +++ b/packages/frontend/core/src/modules/workbench/view/split-view/split-view.tsx @@ -1,4 +1,3 @@ -import { HubIsland } from '@affine/core/components/affine/hub-island'; import { useAppSettingHelper } from '@affine/core/hooks/affine/use-app-setting-helper'; import type { DragEndEvent } from '@dnd-kit/core'; import { @@ -116,7 +115,6 @@ export const SplitView = ({ data-client-border={appSettings.clientBorder} {...attrs} > -
+ {/* Add a key to force rerender when page changed, to avoid error boundary persisting. */} diff --git a/tests/affine-local/e2e/ai-land.spec.ts b/tests/affine-local/e2e/ai-land.spec.ts new file mode 100644 index 0000000000..cff6de869c --- /dev/null +++ b/tests/affine-local/e2e/ai-land.spec.ts @@ -0,0 +1,16 @@ +import { test } from '@affine-test/kit/playwright'; +import { openHomePage } from '@affine-test/kit/utils/load-page'; +import { + clickNewPageButton, + waitForEditorLoad, +} from '@affine-test/kit/utils/page-logic'; +import { expect } from '@playwright/test'; + +test('Click ai-land icon', async ({ page }) => { + await openHomePage(page); + await waitForEditorLoad(page); + await clickNewPageButton(page); + await page.locator('[data-testid=ai-island]').click(); + + await expect(page.locator('chat-panel')).toBeVisible(); +}); diff --git a/tests/affine-local/e2e/contact-us.spec.ts b/tests/affine-local/e2e/contact-us.spec.ts deleted file mode 100644 index 6e9c0875d4..0000000000 --- a/tests/affine-local/e2e/contact-us.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { test } from '@affine-test/kit/playwright'; -import { openHomePage } from '@affine-test/kit/utils/load-page'; -import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic'; -import { expect } from '@playwright/test'; - -test('Click right-bottom corner contact icon', async ({ page }) => { - await openHomePage(page); - await waitForEditorLoad(page); - await page.locator('[data-testid=help-island]').click(); - const rightBottomContactUs = page.getByTestId('right-bottom-contact-us-icon'); - await expect(rightBottomContactUs).toBeVisible(); - - await rightBottomContactUs.click(); - - const title = page.getByTestId('about-title'); - await expect(title).toBeVisible(); -}); diff --git a/tests/affine-local/e2e/shortcuts.spec.ts b/tests/affine-local/e2e/shortcuts.spec.ts deleted file mode 100644 index 384d8d13db..0000000000 --- a/tests/affine-local/e2e/shortcuts.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { test } from '@affine-test/kit/playwright'; -import { openHomePage } from '@affine-test/kit/utils/load-page'; -import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic'; -import { expect } from '@playwright/test'; - -test('Open shortcuts modal', async ({ page }) => { - await openHomePage(page); - await waitForEditorLoad(page); - await page.locator('[data-testid=help-island]').click(); - - const shortcutsIcon = page.locator('[data-testid=shortcuts-icon]'); - await page.waitForTimeout(1000); - await expect(shortcutsIcon).toBeVisible(); - - await shortcutsIcon.click(); - await page.waitForTimeout(1000); - - const settingModal = page.getByTestId('setting-modal'); - await expect(settingModal).toBeVisible(); - - const title = page.getByTestId('keyboard-shortcuts-title'); - await expect(title).toBeVisible(); -});