diff --git a/blocksuite/blocks/src/effects.ts b/blocksuite/blocks/src/effects.ts
index dd4cf14af..840c41039 100644
--- a/blocksuite/blocks/src/effects.ts
+++ b/blocksuite/blocks/src/effects.ts
@@ -22,6 +22,7 @@ import { effects as componentCaptionEffects } from '@blocksuite/affine-component
import { effects as componentColorPickerEffects } from '@blocksuite/affine-components/color-picker';
import { effects as componentContextMenuEffects } from '@blocksuite/affine-components/context-menu';
import { effects as componentDatePickerEffects } from '@blocksuite/affine-components/date-picker';
+import { effects as componentDocTitleEffects } from '@blocksuite/affine-components/doc-title';
import { effects as componentDropIndicatorEffects } from '@blocksuite/affine-components/drop-indicator';
import { effects as componentEmbedCardModalEffects } from '@blocksuite/affine-components/embed-card-modal';
import { FilterableListComponent } from '@blocksuite/affine-components/filterable-list';
@@ -213,6 +214,7 @@ export function effects() {
componentAiItemEffects();
componentColorPickerEffects();
componentEmbedCardModalEffects();
+ componentDocTitleEffects();
widgetScrollAnchoringEffects();
widgetMobileToolbarEffects();
diff --git a/blocksuite/blocks/src/index.ts b/blocksuite/blocks/src/index.ts
index 441542749..04f3da59d 100644
--- a/blocksuite/blocks/src/index.ts
+++ b/blocksuite/blocks/src/index.ts
@@ -58,6 +58,10 @@ export {
type MenuOptions,
onMenuOpen,
} from '@blocksuite/affine-components/context-menu';
+export {
+ DocTitle,
+ getDocTitleByEditorHost,
+} from '@blocksuite/affine-components/doc-title';
export {
HoverController,
whenHover,
diff --git a/blocksuite/presets/src/effects.ts b/blocksuite/presets/src/effects.ts
index 1847c2ab1..6952043bd 100644
--- a/blocksuite/presets/src/effects.ts
+++ b/blocksuite/presets/src/effects.ts
@@ -7,8 +7,6 @@ import {
PageEditor,
} from './editors/index.js';
import { CommentInput } from './fragments/comment/comment-input.js';
-import { BacklinkButton } from './fragments/doc-meta-tags/backlink-popover.js';
-import { effects as docTitleEffects } from './fragments/doc-title/index.js';
import {
AFFINE_FRAME_PANEL_BODY,
FramePanelBody,
@@ -70,8 +68,6 @@ import {
} from './fragments/outline/header/outline-setting-menu.js';
export function effects() {
- docTitleEffects();
-
customElements.define('page-editor', PageEditor);
customElements.define('comment-input', CommentInput);
customElements.define(
@@ -82,7 +78,6 @@ export function effects() {
customElements.define(AFFINE_OUTLINE_NOTICE, OutlineNotice);
customElements.define('comment-panel', CommentPanel);
customElements.define(AFFINE_OUTLINE_PANEL, OutlinePanel);
- customElements.define('backlink-button', BacklinkButton);
customElements.define(AFFINE_OUTLINE_PANEL_HEADER, OutlinePanelHeader);
customElements.define('affine-editor-container', AffineEditorContainer);
customElements.define(AFFINE_OUTLINE_NOTE_CARD, OutlineNoteCard);
diff --git a/blocksuite/presets/src/fragments/doc-meta-tags/backlink-popover.ts b/blocksuite/presets/src/fragments/doc-meta-tags/backlink-popover.ts
deleted file mode 100644
index 3fdf7885c..000000000
--- a/blocksuite/presets/src/fragments/doc-meta-tags/backlink-popover.ts
+++ /dev/null
@@ -1,148 +0,0 @@
-import { DualLinkIcon16, scrollbarStyle } from '@blocksuite/blocks';
-import { WithDisposable } from '@blocksuite/global/utils';
-import { baseTheme } from '@toeverything/theme';
-import { css, html, LitElement, unsafeCSS } from 'lit';
-import { state } from 'lit/decorators.js';
-
-import { type BacklinkData, DEFAULT_DOC_NAME } from './utils.js';
-
-export class BacklinkButton extends WithDisposable(LitElement) {
- static override styles = css`
- :host {
- position: relative;
- display: flex;
- }
-
- .btn {
- padding: 0 12px;
- box-sizing: border-box;
- display: inline-flex;
- align-items: center;
- border: none;
- height: 30px;
- border-radius: 8px;
- gap: 4px;
- background: transparent;
- cursor: pointer;
-
- user-select: none;
- font-size: var(--affine-font-sm);
- font-family: ${unsafeCSS(baseTheme.fontSansFamily)};
- fill: var(--affine-text-secondary-color);
- color: var(--affine-text-secondary-color);
- pointer-events: auto;
- }
-
- .btn > span {
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
- }
-
- .btn:hover {
- background: var(--affine-hover-color);
- }
-
- .btn:active {
- background: var(--affine-hover-color);
- }
-
- .backlink-popover {
- position: absolute;
- left: 0;
- bottom: 0;
- transform: translateY(100%);
- z-index: 1;
- padding-top: 8px;
- }
-
- .menu {
- display: flex;
- flex-direction: column;
- padding: 8px 4px;
- background: var(--affine-white);
- box-shadow: var(--affine-menu-shadow);
- border-radius: 12px;
- }
-
- .backlink-popover .group-title {
- color: var(--affine-text-secondary-color);
- margin: 8px 12px;
- }
-
- .backlink-popover icon-button {
- padding: 8px;
- justify-content: flex-start;
- gap: 8px;
- }
-
- ${scrollbarStyle('.backlink-popover .group')}
- `;
-
- private readonly _backlinks: BacklinkData[];
-
- // Handle click outside
- private readonly _onClickAway = (e: Event) => {
- if (e.target === this) return;
- if (!this._showPopover) return;
- this._showPopover = false;
- document.removeEventListener('mousedown', this._onClickAway);
- };
-
- constructor(backlinks: BacklinkData[]) {
- super();
-
- this._backlinks = backlinks;
- }
-
- override connectedCallback() {
- super.connectedCallback();
-
- this.tabIndex = 0;
- }
-
- onClick() {
- this._showPopover = !this._showPopover;
- document.addEventListener('mousedown', this._onClickAway);
- }
-
- override render() {
- // Only show linked doc backlinks
- const backlinks = this._backlinks;
- if (!backlinks.length) return null;
-
- return html`
-
- ${DualLinkIcon16}Backlinks (${backlinks.length})
- ${this._showPopover ? backlinkPopover(backlinks) : null}
-
- `;
- }
-
- @state()
- private accessor _showPopover = false;
-}
-
-function backlinkPopover(backlinks: BacklinkData[]) {
- return html` e.stopPropagation()}
- >
-
-
`;
-}
diff --git a/blocksuite/presets/src/fragments/doc-meta-tags/utils.ts b/blocksuite/presets/src/fragments/doc-meta-tags/utils.ts
deleted file mode 100644
index f5697ee8e..000000000
--- a/blocksuite/presets/src/fragments/doc-meta-tags/utils.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
-import type { DocMeta } from '@blocksuite/store';
-import type { TemplateResult } from 'lit';
-
-export const DOC_BLOCK_CHILD_PADDING = 24;
-
-export const DEFAULT_DOC_NAME = 'Untitled';
-
-export type BackLink = {
- pageId: string;
- blockId: string;
- type: NonNullable['type'];
-};
-
-export type BacklinkData = BackLink &
- DocMeta & {
- jump: () => void;
- icon: TemplateResult;
- };
diff --git a/blocksuite/presets/src/fragments/doc-title/index.ts b/blocksuite/presets/src/fragments/doc-title/index.ts
deleted file mode 100644
index 31bed24c2..000000000
--- a/blocksuite/presets/src/fragments/doc-title/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from '@blocksuite/affine-components/doc-title';
diff --git a/blocksuite/presets/src/fragments/index.ts b/blocksuite/presets/src/fragments/index.ts
index 607923fde..2f78e9f9c 100644
--- a/blocksuite/presets/src/fragments/index.ts
+++ b/blocksuite/presets/src/fragments/index.ts
@@ -1,4 +1,3 @@
export * from './comment/index.js';
-export * from './doc-title/index.js';
export * from './frame-panel/index.js';
export * from './outline/index.js';
diff --git a/blocksuite/presets/src/fragments/outline/utils/scroll.ts b/blocksuite/presets/src/fragments/outline/utils/scroll.ts
index 711146e38..c1fedbd3c 100644
--- a/blocksuite/presets/src/fragments/outline/utils/scroll.ts
+++ b/blocksuite/presets/src/fragments/outline/utils/scroll.ts
@@ -1,9 +1,8 @@
import type { EditorHost } from '@blocksuite/block-std';
-import { NoteDisplayMode } from '@blocksuite/blocks';
+import { getDocTitleByEditorHost, NoteDisplayMode } from '@blocksuite/blocks';
import { clamp, DisposableGroup } from '@blocksuite/global/utils';
import type { AffineEditorContainer } from '../../../editors/editor-container.js';
-import { getDocTitleByEditorHost } from '../../doc-title/index.js';
import { getHeadingBlocksFromDoc } from './query.js';
export function scrollToBlock(editor: AffineEditorContainer, blockId: string) {
diff --git a/packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/ai-chat-block.ts b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/ai-chat-block.ts
similarity index 92%
rename from packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/ai-chat-block.ts
rename to packages/frontend/core/src/blocksuite/blocks/ai-chat-block/ai-chat-block.ts
index c461a1d2a..3536ff77e 100644
--- a/packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/ai-chat-block.ts
+++ b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/ai-chat-block.ts
@@ -3,8 +3,8 @@ import { Peekable } from '@blocksuite/affine/blocks';
import { computed } from '@preact/signals-core';
import { html } from 'lit';
-import { type AIChatBlockModel, ChatMessagesSchema } from '../../../blocks';
-import { ChatWithAIIcon } from '../_common/icon';
+import { ChatWithAIIcon } from './components/icon';
+import { type AIChatBlockModel, ChatMessagesSchema } from './model';
import { AIChatBlockStyles } from './styles';
@Peekable({
diff --git a/packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/ai-chat-edgeless-block.ts b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/ai-chat-edgeless-block.ts
similarity index 100%
rename from packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/ai-chat-edgeless-block.ts
rename to packages/frontend/core/src/blocksuite/blocks/ai-chat-block/ai-chat-edgeless-block.ts
diff --git a/packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/ai-chat-spec.ts b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/ai-chat-spec.ts
similarity index 100%
rename from packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/ai-chat-spec.ts
rename to packages/frontend/core/src/blocksuite/blocks/ai-chat-block/ai-chat-spec.ts
diff --git a/packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/components/ai-chat-messages.ts b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/ai-chat-messages.ts
similarity index 96%
rename from packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/components/ai-chat-messages.ts
rename to packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/ai-chat-messages.ts
index 87169255b..a8d7dee47 100644
--- a/packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/components/ai-chat-messages.ts
+++ b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/ai-chat-messages.ts
@@ -1,3 +1,4 @@
+import type { TextRendererOptions } from '@affine/core/blocksuite/presets';
import type { EditorHost } from '@blocksuite/affine/block-std';
import type { AffineAIPanelState } from '@blocksuite/affine/blocks';
import { css, html, LitElement } from 'lit';
@@ -5,8 +6,7 @@ import { property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { repeat } from 'lit/directives/repeat.js';
-import type { ChatMessage, MessageRole } from '../../../../blocks';
-import type { TextRendererOptions } from '../../../_common/components/text-renderer';
+import type { ChatMessage, MessageRole } from '../model';
import { UserInfoTemplate } from './user-info';
export class AIChatMessage extends LitElement {
diff --git a/packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/components/chat-images.ts b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/chat-images.ts
similarity index 95%
rename from packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/components/chat-images.ts
rename to packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/chat-images.ts
index bd6faeb24..2f87c77d0 100644
--- a/packages/frontend/core/src/blocksuite/presets/blocks/ai-chat-block/components/chat-images.ts
+++ b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/chat-images.ts
@@ -1,9 +1,10 @@
+import { LoadingIcon } from '@blocksuite/affine/blocks';
import { css, html, LitElement, nothing } from 'lit';
import { property } from 'lit/decorators.js';
import { choose } from 'lit/directives/choose.js';
import { repeat } from 'lit/directives/repeat.js';
-import { ImageLoadingFailedIcon, LoadingIcon } from '../../_common/icon';
+import { ImageLoadingFailedIcon } from './icon';
export class ChatImage extends LitElement {
static override styles = css`
diff --git a/packages/frontend/core/src/blocksuite/presets/blocks/_common/icon.ts b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/icon.ts
similarity index 79%
rename from packages/frontend/core/src/blocksuite/presets/blocks/_common/icon.ts
rename to packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/icon.ts
index bb6199f5f..adcf38061 100644
--- a/packages/frontend/core/src/blocksuite/presets/blocks/_common/icon.ts
+++ b/packages/frontend/core/src/blocksuite/blocks/ai-chat-block/components/icon.ts
@@ -57,35 +57,6 @@ export const ImageLoadingFailedIcon = html`
`;
-export const LoadingIcon = html``;
-
export const SmallHintIcon = html`