From ec510bc1409de86bff89485f9f4e6e6bced46a02 Mon Sep 17 00:00:00 2001 From: Peng Xiao Date: Fri, 4 Jul 2025 20:46:45 +0800 Subject: [PATCH] fix(core): some style issues (#13039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### PR Dependency Tree * **PR #13039** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) ## Summary by CodeRabbit * **Refactor** * Streamlined and unified the rendering and update flow for the document compose tool, improving clarity and responsiveness of the UI. * Enhanced error handling and updated preview panel interactions for a smoother user experience. * **Style** * Improved sidebar header layout: increased header height, added sticky positioning, adjusted padding, and updated background color for better visibility and usability. --- .../ai/components/ai-tools/doc-compose.ts | 278 ++++++++++-------- .../components/comment/sidebar/style.css.ts | 9 +- 2 files changed, 160 insertions(+), 127 deletions(-) diff --git a/packages/frontend/core/src/blocksuite/ai/components/ai-tools/doc-compose.ts b/packages/frontend/core/src/blocksuite/ai/components/ai-tools/doc-compose.ts index 959831fc40..16c4480def 100644 --- a/packages/frontend/core/src/blocksuite/ai/components/ai-tools/doc-compose.ts +++ b/packages/frontend/core/src/blocksuite/ai/components/ai-tools/doc-compose.ts @@ -1,7 +1,7 @@ import { getStoreManager } from '@affine/core/blocksuite/manager/store'; import { getAFFiNEWorkspaceSchema } from '@affine/core/modules/workspace'; import { getEmbedLinkedDocIcons } from '@blocksuite/affine/blocks/embed-doc'; -import { DocIcon } from '@blocksuite/affine/components/icons'; +import { LoadingIcon } from '@blocksuite/affine/components/icons'; import { toast } from '@blocksuite/affine/components/toast'; import { WithDisposable } from '@blocksuite/affine/global/lit'; import { RefNodeSlotsProvider } from '@blocksuite/affine/inlines/reference'; @@ -15,11 +15,14 @@ import { type BlockStdScope, ShadowlessElement } from '@blocksuite/affine/std'; import { MarkdownTransformer } from '@blocksuite/affine/widgets/linked-doc'; import { CopyIcon, PageIcon, ToolIcon } from '@blocksuite/icons/lit'; import { type Signal } from '@preact/signals-core'; -import { css, html, nothing } from 'lit'; +import { css, html, nothing, type PropertyValues } from 'lit'; import { property } from 'lit/decorators.js'; import { getCustomPageEditorBlockSpecs } from '../text-renderer'; -import { renderPreviewPanel } from './artifacts-preview-panel'; +import { + isPreviewPanelOpen, + renderPreviewPanel, +} from './artifacts-preview-panel'; import type { ToolError } from './type'; interface DocComposeToolCall { @@ -60,6 +63,7 @@ export class DocComposeTool extends WithDisposable(ShadowlessElement) { .doc-compose-result-preview { padding: 24px; + height: 100%; } .doc-compose-result-preview-title { @@ -82,6 +86,18 @@ export class DocComposeTool extends WithDisposable(ShadowlessElement) { font-weight: 500; } + .doc-compose-result-preview-loading { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + } + .doc-compose-result-save-as-doc:hover { background: ${unsafeCSSVarV2('switch/buttonBackground/hover')}; } @@ -99,145 +115,159 @@ export class DocComposeTool extends WithDisposable(ShadowlessElement) { @property({ attribute: false }) accessor std: BlockStdScope | undefined; - private renderToolCall() { - const { args } = this.data as DocComposeToolCall; - const name = `Composing document "${args.title}"`; - return html``; + override updated(changedProperties: PropertyValues) { + super.updated(changedProperties); + if (changedProperties.has('data') && isPreviewPanelOpen(this)) { + this.updatePreviewPanel(); + } } - private renderToolResult() { - if (!this.std) return nothing; - if (this.data.type !== 'tool-result') return nothing; + private updatePreviewPanel() { + if (!this.std) return; const std = this.std; - const resultData = this.data as DocComposeToolResult; - const result = resultData.result; + const resultData = this.data; + const composing = resultData.type === 'tool-call'; + const title = this.data.args.title; + const result = resultData.type === 'tool-result' ? resultData.result : null; + const successResult = result && 'markdown' in result ? result : null; - if (result && typeof result === 'object' && 'title' in result) { - const { title } = result as { title: string }; + const copyMarkdown = async () => { + if (!successResult) { + return; + } + await navigator.clipboard + .writeText(successResult.markdown) + .catch(console.error); + toast(std.host, 'Copied markdown to clipboard'); + }; - const theme = this.std.get(ThemeProvider).theme; - - const { LinkedDocEmptyBanner } = getEmbedLinkedDocIcons( - theme, - 'page', - 'horizontal' - ); - - const onClick = () => { - const copyMarkdown = async () => { - await navigator.clipboard - .writeText(result.markdown) - .catch(console.error); - toast(std.host, 'Copied markdown to clipboard'); - }; - - const saveAsDoc = async () => { - try { - const workspace = std.store.workspace; - const notificationService = std.get(NotificationProvider); - const refNodeSlots = std.getOptional(RefNodeSlotsProvider); - const docId = await MarkdownTransformer.importMarkdownToDoc({ - collection: workspace, - schema: getAFFiNEWorkspaceSchema(), - markdown: result.markdown, - fileName: title, - extensions: getStoreManager().config.init().value.get('store'), + const saveAsDoc = async () => { + try { + if (!successResult) { + return; + } + const workspace = std.store.workspace; + const notificationService = std.get(NotificationProvider); + const refNodeSlots = std.getOptional(RefNodeSlotsProvider); + const docId = await MarkdownTransformer.importMarkdownToDoc({ + collection: workspace, + schema: getAFFiNEWorkspaceSchema(), + markdown: successResult.markdown, + fileName: title, + extensions: getStoreManager().config.init().value.get('store'), + }); + if (docId) { + const open = await notificationService.confirm({ + title: 'Open the doc you just created', + message: 'Doc saved successfully! Would you like to open it now?', + cancelText: 'Cancel', + confirmText: 'Open', + }); + if (open) { + refNodeSlots?.docLinkClicked.next({ + pageId: docId, + openMode: 'open-in-active-view', + host: std.host, }); - if (docId) { - const open = await notificationService.confirm({ - title: 'Open the doc you just created', - message: - 'Doc saved successfully! Would you like to open it now?', - cancelText: 'Cancel', - confirmText: 'Open', - }); - if (open) { - refNodeSlots?.docLinkClicked.next({ - pageId: docId, - openMode: 'open-in-active-view', - host: std.host, - }); - } - } else { - toast(std.host, 'Failed to create document'); - } - } catch (e) { - console.error(e); - toast(std.host, 'Failed to create document'); } - }; + } else { + toast(std.host, 'Failed to create document'); + } + } catch (e) { + console.error(e); + toast(std.host, 'Failed to create document'); + } + }; - const controls = html` - - - ${CopyIcon({ width: '20', height: '20' })} - - `; + const controls = html` + + + ${CopyIcon({ width: '20', height: '20' })} + + `; - renderPreviewPanel( - this, - html`
-
${title}
- +
${title}
+ ${successResult + ? html` -
`, - controls - ); - }; - - return html` -
-
-
-
- ${DocIcon} -
-
- ${title} -
-
-
-
- ${LinkedDocEmptyBanner} -
-
- `; - } - - // failed - return html``; + >` + : html`
+ ${LoadingIcon({ + size: '32px', + })} +
`} + `, + composing ? undefined : controls + ); } protected override render() { - if (this.data.type === 'tool-call') { - return this.renderToolCall(); + if (!this.std) return nothing; + const resultData = this.data; + const composing = resultData.type === 'tool-call'; + + const title = this.data.args.title; + + if ( + resultData.type === 'tool-result' && + resultData.result && + 'type' in resultData.result && + resultData.result.type === 'error' + ) { + // failed + return html``; } - if (this.data.type === 'tool-result') { - return this.renderToolResult(); - } - return nothing; + + const theme = this.std.get(ThemeProvider).theme; + + const { LinkedDocEmptyBanner } = getEmbedLinkedDocIcons( + theme, + 'page', + 'horizontal' + ); + + return html` +
+
+
+
+ ${composing + ? LoadingIcon({ + size: '20px', + }) + : PageIcon()} +
+
+ ${title} +
+
+
+
+ ${LinkedDocEmptyBanner} +
+
+ `; } } diff --git a/packages/frontend/core/src/components/comment/sidebar/style.css.ts b/packages/frontend/core/src/components/comment/sidebar/style.css.ts index 564f5f20ed..610b7eec76 100644 --- a/packages/frontend/core/src/components/comment/sidebar/style.css.ts +++ b/packages/frontend/core/src/components/comment/sidebar/style.css.ts @@ -6,7 +6,6 @@ export const container = style({ display: 'flex', flexDirection: 'column', alignItems: 'stretch', - paddingTop: '8px', paddingBottom: '64px', position: 'relative', minHeight: '100%', @@ -16,9 +15,13 @@ export const header = style({ display: 'flex', justifyContent: 'space-between', alignItems: 'center', - padding: '0 16px', + padding: '8px 16px 0 16px', gap: '8px', - height: '32px', + height: '40px', + position: 'sticky', + top: 0, + backgroundColor: cssVarV2('layer/background/overlayPanel'), + zIndex: 2, }); export const headerTitle = style({