mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 02:42:25 +08:00
feat(editor): block comment extension (#12980)
#### PR Dependency Tree * **PR #12980** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal)
This commit is contained in:
@@ -17,6 +17,7 @@ import {
|
|||||||
AttachmentBlockStyles,
|
AttachmentBlockStyles,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
|
BlockCommentManager,
|
||||||
CitationProvider,
|
CitationProvider,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
FileSizeLimitProvider,
|
FileSizeLimitProvider,
|
||||||
@@ -92,6 +93,12 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
return this.citationService.isCitationModel(this.model);
|
return this.citationService.isCitationModel(this.model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCommentHighlighted() {
|
||||||
|
return this.std
|
||||||
|
.get(BlockCommentManager)
|
||||||
|
.isBlockCommentHighlighted(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
convertTo = () => {
|
convertTo = () => {
|
||||||
return this.std
|
return this.std
|
||||||
.get(AttachmentEmbedProvider)
|
.get(AttachmentEmbedProvider)
|
||||||
@@ -499,6 +506,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
|
|||||||
class=${classMap({
|
class=${classMap({
|
||||||
'affine-attachment-container': true,
|
'affine-attachment-container': true,
|
||||||
focused: this.selected$.value,
|
focused: this.selected$.value,
|
||||||
|
'comment-highlighted': this.isCommentHighlighted,
|
||||||
})}
|
})}
|
||||||
style=${this.containerStyleMap}
|
style=${this.containerStyleMap}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
|
blockCommentToolbarButton,
|
||||||
type ToolbarAction,
|
type ToolbarAction,
|
||||||
type ToolbarActionGroup,
|
type ToolbarActionGroup,
|
||||||
type ToolbarModuleConfig,
|
type ToolbarModuleConfig,
|
||||||
@@ -240,6 +241,10 @@ const builtinToolbarConfig = {
|
|||||||
replaceAction,
|
replaceAction,
|
||||||
downloadAction,
|
downloadAction,
|
||||||
captionAction,
|
captionAction,
|
||||||
|
{
|
||||||
|
id: 'f.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ export const styles = css`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.affine-attachment-container.comment-highlighted {
|
||||||
|
outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
}
|
||||||
|
|
||||||
.affine-attachment-card {
|
.affine-attachment-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import type {
|
|||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import { ImageProxyService } from '@blocksuite/affine-shared/adapters';
|
import { ImageProxyService } from '@blocksuite/affine-shared/adapters';
|
||||||
import {
|
import {
|
||||||
|
BlockCommentManager,
|
||||||
CitationProvider,
|
CitationProvider,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
LinkPreviewServiceIdentifier,
|
LinkPreviewServiceIdentifier,
|
||||||
@@ -128,6 +129,12 @@ export class BookmarkBlockComponent extends CaptionedBlockComponent<BookmarkBloc
|
|||||||
return this.std.get(ImageProxyService);
|
return this.std.get(ImageProxyService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCommentHighlighted() {
|
||||||
|
return this.std
|
||||||
|
.get(BlockCommentManager)
|
||||||
|
.isBlockCommentHighlighted(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
handleClick = (event: MouseEvent) => {
|
handleClick = (event: MouseEvent) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export class BookmarkCard extends SignalWatcher(
|
|||||||
[style]: true,
|
[style]: true,
|
||||||
selected: this.bookmark.selected$.value,
|
selected: this.bookmark.selected$.value,
|
||||||
edgeless: isGfxBlockComponent(this.bookmark),
|
edgeless: isGfxBlockComponent(this.bookmark),
|
||||||
|
'comment-highlighted': this.bookmark.isCommentHighlighted,
|
||||||
});
|
});
|
||||||
|
|
||||||
const domainName = url.match(
|
const domainName = url.match(
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
|
blockCommentToolbarButton,
|
||||||
EmbedIframeService,
|
EmbedIframeService,
|
||||||
EmbedOptionProvider,
|
EmbedOptionProvider,
|
||||||
type LinkEventType,
|
type LinkEventType,
|
||||||
@@ -288,6 +289,10 @@ const builtinToolbarConfig = {
|
|||||||
},
|
},
|
||||||
} satisfies ToolbarActionGroup<ToolbarAction>,
|
} satisfies ToolbarActionGroup<ToolbarAction>,
|
||||||
captionAction,
|
captionAction,
|
||||||
|
{
|
||||||
|
id: 'e.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { unsafeCSSVar } from '@blocksuite/affine-shared/theme';
|
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||||
import { baseTheme } from '@toeverything/theme';
|
import { baseTheme } from '@toeverything/theme';
|
||||||
import { css, unsafeCSS } from 'lit';
|
import { css, unsafeCSS } from 'lit';
|
||||||
|
|
||||||
@@ -158,6 +158,10 @@ export const styles = css`
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.affine-bookmark-card.comment-highlighted {
|
||||||
|
outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
}
|
||||||
|
|
||||||
.affine-bookmark-card.loading {
|
.affine-bookmark-card.loading {
|
||||||
.affine-bookmark-content-title-text {
|
.affine-bookmark-content-title-text {
|
||||||
color: var(--affine-placeholder-color);
|
color: var(--affine-placeholder-color);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
EDGELESS_TOP_CONTENTEDITABLE_SELECTOR,
|
EDGELESS_TOP_CONTENTEDITABLE_SELECTOR,
|
||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
|
BlockCommentManager,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
NotificationProvider,
|
NotificationProvider,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
@@ -390,6 +391,12 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCommentHighlighted() {
|
||||||
|
return this.std
|
||||||
|
.get(BlockCommentManager)
|
||||||
|
.isBlockCommentHighlighted(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
override async getUpdateComplete() {
|
override async getUpdateComplete() {
|
||||||
const result = await super.getUpdateComplete();
|
const result = await super.getUpdateComplete();
|
||||||
await this._richTextElement?.updateComplete;
|
await this._richTextElement?.updateComplete;
|
||||||
@@ -413,6 +420,7 @@ export class CodeBlockComponent extends CaptionedBlockComponent<CodeBlockModel>
|
|||||||
<div
|
<div
|
||||||
class=${classMap({
|
class=${classMap({
|
||||||
'affine-code-block-container': true,
|
'affine-code-block-container': true,
|
||||||
|
'highlight-comment': this.isCommentHighlighted,
|
||||||
mobile: IS_MOBILE,
|
mobile: IS_MOBILE,
|
||||||
wrap: this.model.props.wrap,
|
wrap: this.model.props.wrap,
|
||||||
'disable-line-numbers': !showLineNumbers,
|
'disable-line-numbers': !showLineNumbers,
|
||||||
|
|||||||
@@ -7,9 +7,10 @@ import {
|
|||||||
WrapIcon,
|
WrapIcon,
|
||||||
} from '@blocksuite/affine-components/icons';
|
} from '@blocksuite/affine-components/icons';
|
||||||
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
|
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
|
||||||
|
import { CommentProviderIdentifier } from '@blocksuite/affine-shared/services';
|
||||||
import { isInsidePageEditor } from '@blocksuite/affine-shared/utils';
|
import { isInsidePageEditor } from '@blocksuite/affine-shared/utils';
|
||||||
import { noop, sleep } from '@blocksuite/global/utils';
|
import { noop, sleep } from '@blocksuite/global/utils';
|
||||||
import { NumberedListIcon } from '@blocksuite/icons/lit';
|
import { CommentIcon, NumberedListIcon } from '@blocksuite/icons/lit';
|
||||||
import { BlockSelection } from '@blocksuite/std';
|
import { BlockSelection } from '@blocksuite/std';
|
||||||
import { html } from 'lit';
|
import { html } from 'lit';
|
||||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||||
@@ -113,6 +114,47 @@ export const PRIMARY_GROUPS: MenuItemGroup<CodeBlockToolbarContext>[] = [
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'comment',
|
||||||
|
label: 'Comment',
|
||||||
|
tooltip: 'Comment',
|
||||||
|
icon: CommentIcon({
|
||||||
|
width: '20',
|
||||||
|
height: '20',
|
||||||
|
}),
|
||||||
|
when: ({ std }) => !!std.getOptional(CommentProviderIdentifier),
|
||||||
|
generate: ({ blockComponent }) => {
|
||||||
|
return {
|
||||||
|
action: () => {
|
||||||
|
const commentProvider = blockComponent.std.getOptional(
|
||||||
|
CommentProviderIdentifier
|
||||||
|
);
|
||||||
|
if (!commentProvider) return;
|
||||||
|
|
||||||
|
commentProvider.addComment([
|
||||||
|
new BlockSelection({
|
||||||
|
blockId: blockComponent.model.id,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
render: item =>
|
||||||
|
html`<editor-icon-button
|
||||||
|
class="code-toolbar-button comment"
|
||||||
|
aria-label=${ifDefined(item.label)}
|
||||||
|
.tooltip=${item.label}
|
||||||
|
.tooltipOffset=${4}
|
||||||
|
.iconSize=${'16px'}
|
||||||
|
.iconContainerPadding=${4}
|
||||||
|
@click=${(e: MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
item.action();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
${item.icon}
|
||||||
|
</editor-icon-button>`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { scrollbarStyle } from '@blocksuite/affine-shared/styles';
|
import { scrollbarStyle } from '@blocksuite/affine-shared/styles';
|
||||||
|
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||||
import { css } from 'lit';
|
import { css } from 'lit';
|
||||||
|
|
||||||
export const codeBlockStyles = css`
|
export const codeBlockStyles = css`
|
||||||
@@ -20,6 +21,10 @@ export const codeBlockStyles = css`
|
|||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.affine-code-block-container.highlight-comment {
|
||||||
|
outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
}
|
||||||
|
|
||||||
${scrollbarStyle('.affine-code-block-container rich-text')}
|
${scrollbarStyle('.affine-code-block-container rich-text')}
|
||||||
|
|
||||||
.affine-code-block-container .inline-editor {
|
.affine-code-block-container .inline-editor {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||||
import { stopPropagation } from '@blocksuite/affine-shared/utils';
|
import { stopPropagation } from '@blocksuite/affine-shared/utils';
|
||||||
import type { DataViewUILogicBase } from '@blocksuite/data-view';
|
import type { DataViewUILogicBase } from '@blocksuite/data-view';
|
||||||
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
|
import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit';
|
||||||
@@ -72,6 +73,12 @@ export class DatabaseTitle extends SignalWatcher(
|
|||||||
.affine-database-title [data-title-focus='true']::before {
|
.affine-database-title [data-title-focus='true']::before {
|
||||||
color: var(--affine-placeholder-color);
|
color: var(--affine-placeholder-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.affine-database-title.comment-highlighted {
|
||||||
|
border-bottom: 2px solid
|
||||||
|
${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
background-color: ${unsafeCSSVarV2('block/comment/highlightActive')};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
private readonly compositionEnd = () => {
|
private readonly compositionEnd = () => {
|
||||||
@@ -134,6 +141,7 @@ export class DatabaseTitle extends SignalWatcher(
|
|||||||
const classList = classMap({
|
const classList = classMap({
|
||||||
'affine-database-title': true,
|
'affine-database-title': true,
|
||||||
ellipsis: !this.isFocus$.value,
|
ellipsis: !this.isFocus$.value,
|
||||||
|
'comment-highlighted': this.database?.isCommentHighlighted ?? false,
|
||||||
});
|
});
|
||||||
const untitledStyle = styleMap({
|
const untitledStyle = styleMap({
|
||||||
height: isEmpty ? 'auto' : 0,
|
height: isEmpty ? 'auto' : 0,
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { toast } from '@blocksuite/affine-components/toast';
|
|||||||
import type { DatabaseBlockModel } from '@blocksuite/affine-model';
|
import type { DatabaseBlockModel } from '@blocksuite/affine-model';
|
||||||
import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '@blocksuite/affine-shared/consts';
|
import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
|
BlockCommentManager,
|
||||||
|
CommentProviderIdentifier,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
NotificationProvider,
|
NotificationProvider,
|
||||||
type TelemetryEventMap,
|
type TelemetryEventMap,
|
||||||
@@ -34,11 +36,12 @@ import {
|
|||||||
import { widgetPresets } from '@blocksuite/data-view/widget-presets';
|
import { widgetPresets } from '@blocksuite/data-view/widget-presets';
|
||||||
import { Rect } from '@blocksuite/global/gfx';
|
import { Rect } from '@blocksuite/global/gfx';
|
||||||
import {
|
import {
|
||||||
|
CommentIcon,
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
DeleteIcon,
|
DeleteIcon,
|
||||||
MoreHorizontalIcon,
|
MoreHorizontalIcon,
|
||||||
} from '@blocksuite/icons/lit';
|
} from '@blocksuite/icons/lit';
|
||||||
import { type BlockComponent } from '@blocksuite/std';
|
import { type BlockComponent, BlockSelection } from '@blocksuite/std';
|
||||||
import { RANGE_SYNC_EXCLUDE_ATTR } from '@blocksuite/std/inline';
|
import { RANGE_SYNC_EXCLUDE_ATTR } from '@blocksuite/std/inline';
|
||||||
import { Slice } from '@blocksuite/store';
|
import { Slice } from '@blocksuite/store';
|
||||||
import { autoUpdate } from '@floating-ui/dom';
|
import { autoUpdate } from '@floating-ui/dom';
|
||||||
@@ -82,6 +85,18 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
menu.action({
|
||||||
|
prefix: CommentIcon(),
|
||||||
|
name: 'Comment',
|
||||||
|
hide: () => !this.std.getOptional(CommentProviderIdentifier),
|
||||||
|
select: () => {
|
||||||
|
this.std.getOptional(CommentProviderIdentifier)?.addComment([
|
||||||
|
new BlockSelection({
|
||||||
|
blockId: this.blockId,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
}),
|
||||||
menu.action({
|
menu.action({
|
||||||
prefix: CopyIcon(),
|
prefix: CopyIcon(),
|
||||||
name: 'Copy',
|
name: 'Copy',
|
||||||
@@ -297,6 +312,12 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent<DatabaseBloc
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCommentHighlighted() {
|
||||||
|
return this.std
|
||||||
|
.get(BlockCommentManager)
|
||||||
|
.isBlockCommentHighlighted(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
override get topContenteditableElement() {
|
override get topContenteditableElement() {
|
||||||
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
||||||
return this.closest<BlockComponent>(
|
return this.closest<BlockComponent>(
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
|
blockCommentToolbarButton,
|
||||||
DocDisplayMetaProvider,
|
DocDisplayMetaProvider,
|
||||||
EditorSettingProvider,
|
EditorSettingProvider,
|
||||||
type LinkEventType,
|
type LinkEventType,
|
||||||
@@ -305,6 +306,10 @@ const builtinToolbarConfig = {
|
|||||||
},
|
},
|
||||||
} satisfies ToolbarActionGroup<ToolbarAction>,
|
} satisfies ToolbarActionGroup<ToolbarAction>,
|
||||||
captionAction,
|
captionAction,
|
||||||
|
{
|
||||||
|
id: 'e.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
|
|||||||
'note-empty': this.isNoteContentEmpty,
|
'note-empty': this.isNoteContentEmpty,
|
||||||
'in-canvas': inCanvas,
|
'in-canvas': inCanvas,
|
||||||
[this._cardStyle]: true,
|
[this._cardStyle]: true,
|
||||||
|
'comment-highlighted': this.isCommentHighlighted,
|
||||||
});
|
});
|
||||||
|
|
||||||
const theme = this.std.get(ThemeProvider).theme;
|
const theme = this.std.get(ThemeProvider).theme;
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ export const styles = css`
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.affine-embed-linked-doc-block.comment-highlighted {
|
||||||
|
outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
}
|
||||||
|
|
||||||
.affine-embed-linked-doc-block.in-canvas {
|
.affine-embed-linked-doc-block.in-canvas {
|
||||||
border: 1px solid ${unsafeCSSVarV2('layer/insideBorder/border')};
|
border: 1px solid ${unsafeCSSVarV2('layer/insideBorder/border')};
|
||||||
background: ${unsafeCSSVarV2('layer/background/linkedDocOnEdgeless')};
|
background: ${unsafeCSSVarV2('layer/background/linkedDocOnEdgeless')};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
|
import { REFERENCE_NODE } from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
|
blockCommentToolbarButton,
|
||||||
EditorSettingProvider,
|
EditorSettingProvider,
|
||||||
type LinkEventType,
|
type LinkEventType,
|
||||||
type OpenDocMode,
|
type OpenDocMode,
|
||||||
@@ -225,6 +226,10 @@ const builtinToolbarConfig = {
|
|||||||
openDocActionGroup,
|
openDocActionGroup,
|
||||||
conversionsActionGroup,
|
conversionsActionGroup,
|
||||||
captionAction,
|
captionAction,
|
||||||
|
{
|
||||||
|
id: 'e.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
|
|||||||
surface: false,
|
surface: false,
|
||||||
selected: this.selected$.value,
|
selected: this.selected$.value,
|
||||||
'show-hover-border': true,
|
'show-hover-border': true,
|
||||||
|
'comment-highlighted': this.isCommentHighlighted,
|
||||||
})}
|
})}
|
||||||
@click=${this._handleClick}
|
@click=${this._handleClick}
|
||||||
style=${containerStyleMap}
|
style=${containerStyleMap}
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ export const blockStyles = css`
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
.affine-embed-synced-doc-container.comment-highlighted {
|
||||||
|
outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
}
|
||||||
.affine-embed-synced-doc-container.show-hover-border:hover {
|
.affine-embed-synced-doc-container.show-hover-border:hover {
|
||||||
border-color: var(--affine-border-color);
|
border-color: var(--affine-border-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,20 @@ import {
|
|||||||
CaptionedBlockComponent,
|
CaptionedBlockComponent,
|
||||||
SelectedStyle,
|
SelectedStyle,
|
||||||
} from '@blocksuite/affine-components/caption';
|
} from '@blocksuite/affine-components/caption';
|
||||||
import type { EmbedCardStyle } from '@blocksuite/affine-model';
|
import type { EmbedCardStyle, EmbedProps } from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
EMBED_CARD_HEIGHT,
|
EMBED_CARD_HEIGHT,
|
||||||
EMBED_CARD_MIN_WIDTH,
|
EMBED_CARD_MIN_WIDTH,
|
||||||
EMBED_CARD_WIDTH,
|
EMBED_CARD_WIDTH,
|
||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import { DocModeProvider } from '@blocksuite/affine-shared/services';
|
import {
|
||||||
|
BlockCommentManager,
|
||||||
|
DocModeProvider,
|
||||||
|
} from '@blocksuite/affine-shared/services';
|
||||||
|
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||||
import { findAncestorModel } from '@blocksuite/affine-shared/utils';
|
import { findAncestorModel } from '@blocksuite/affine-shared/utils';
|
||||||
import type { BlockService } from '@blocksuite/std';
|
import type { BlockService } from '@blocksuite/std';
|
||||||
import {
|
import {
|
||||||
type GfxCompatibleProps,
|
|
||||||
GfxViewInteractionExtension,
|
GfxViewInteractionExtension,
|
||||||
type ResizeConstraint,
|
type ResizeConstraint,
|
||||||
} from '@blocksuite/std/gfx';
|
} from '@blocksuite/std/gfx';
|
||||||
@@ -25,7 +28,7 @@ import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
|
|||||||
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
import { type StyleInfo, styleMap } from 'lit/directives/style-map.js';
|
||||||
|
|
||||||
export class EmbedBlockComponent<
|
export class EmbedBlockComponent<
|
||||||
Model extends BlockModel<GfxCompatibleProps> = BlockModel<GfxCompatibleProps>,
|
Model extends BlockModel<EmbedProps> = BlockModel<EmbedProps>,
|
||||||
Service extends BlockService = BlockService,
|
Service extends BlockService = BlockService,
|
||||||
WidgetName extends string = string,
|
WidgetName extends string = string,
|
||||||
> extends CaptionedBlockComponent<Model, Service, WidgetName> {
|
> extends CaptionedBlockComponent<Model, Service, WidgetName> {
|
||||||
@@ -59,6 +62,12 @@ export class EmbedBlockComponent<
|
|||||||
*/
|
*/
|
||||||
protected embedContainerStyle: StyleInfo = {};
|
protected embedContainerStyle: StyleInfo = {};
|
||||||
|
|
||||||
|
get isCommentHighlighted() {
|
||||||
|
return this.std
|
||||||
|
.get(BlockCommentManager)
|
||||||
|
.isBlockCommentHighlighted(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
renderEmbed = (content: () => TemplateResult) => {
|
renderEmbed = (content: () => TemplateResult) => {
|
||||||
if (
|
if (
|
||||||
this._cardStyle === 'horizontal' ||
|
this._cardStyle === 'horizontal' ||
|
||||||
@@ -90,6 +99,11 @@ export class EmbedBlockComponent<
|
|||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
height: `${this._cardHeight}px`,
|
height: `${this._cardHeight}px`,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
...(this.isCommentHighlighted
|
||||||
|
? {
|
||||||
|
border: `2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')}`,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
...this.embedContainerStyle,
|
...this.embedContainerStyle,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
import { EdgelessLegacySlotIdentifier } from '@blocksuite/affine-block-surface';
|
||||||
|
import type { EmbedProps } from '@blocksuite/affine-model';
|
||||||
import { Bound } from '@blocksuite/global/gfx';
|
import { Bound } from '@blocksuite/global/gfx';
|
||||||
import {
|
import {
|
||||||
blockComponentSymbol,
|
blockComponentSymbol,
|
||||||
@@ -7,16 +8,13 @@ import {
|
|||||||
GfxElementSymbol,
|
GfxElementSymbol,
|
||||||
toGfxBlockComponent,
|
toGfxBlockComponent,
|
||||||
} from '@blocksuite/std';
|
} from '@blocksuite/std';
|
||||||
import type {
|
import type { GfxBlockElementModel } from '@blocksuite/std/gfx';
|
||||||
GfxBlockElementModel,
|
|
||||||
GfxCompatibleProps,
|
|
||||||
} from '@blocksuite/std/gfx';
|
|
||||||
import type { StyleInfo } from 'lit/directives/style-map.js';
|
import type { StyleInfo } from 'lit/directives/style-map.js';
|
||||||
|
|
||||||
import type { EmbedBlockComponent } from './embed-block-element.js';
|
import type { EmbedBlockComponent } from './embed-block-element.js';
|
||||||
|
|
||||||
export function toEdgelessEmbedBlock<
|
export function toEdgelessEmbedBlock<
|
||||||
Model extends GfxBlockElementModel<GfxCompatibleProps>,
|
Model extends GfxBlockElementModel<EmbedProps>,
|
||||||
Service extends BlockService,
|
Service extends BlockService,
|
||||||
WidgetName extends string,
|
WidgetName extends string,
|
||||||
B extends typeof EmbedBlockComponent<Model, Service, WidgetName>,
|
B extends typeof EmbedBlockComponent<Model, Service, WidgetName>,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
|
blockCommentToolbarButton,
|
||||||
EmbedOptionProvider,
|
EmbedOptionProvider,
|
||||||
type LinkEventType,
|
type LinkEventType,
|
||||||
type ToolbarAction,
|
type ToolbarAction,
|
||||||
@@ -348,6 +349,10 @@ function createBuiltinToolbarConfigForExternal(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'e.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import type { BaseSelection } from '@blocksuite/store';
|
|||||||
import { computed } from '@preact/signals-core';
|
import { computed } from '@preact/signals-core';
|
||||||
import { css, html, type PropertyValues } from 'lit';
|
import { css, html, type PropertyValues } from 'lit';
|
||||||
import { property, query } from 'lit/decorators.js';
|
import { property, query } from 'lit/decorators.js';
|
||||||
|
import { classMap } from 'lit/directives/class-map.js';
|
||||||
import { styleMap } from 'lit/directives/style-map.js';
|
import { styleMap } from 'lit/directives/style-map.js';
|
||||||
import { when } from 'lit/directives/when.js';
|
import { when } from 'lit/directives/when.js';
|
||||||
|
|
||||||
@@ -76,6 +77,10 @@ export class ImageBlockPageComponent extends SignalWatcher(
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
affine-page-image .comment-highlighted {
|
||||||
|
outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
resizeable$ = computed(() => this.block.resizeable$.value);
|
resizeable$ = computed(() => this.block.resizeable$.value);
|
||||||
@@ -364,7 +369,13 @@ export class ImageBlockPageComponent extends SignalWatcher(
|
|||||||
const { loading, error, icon, description, needUpload } = this.state;
|
const { loading, error, icon, description, needUpload } = this.state;
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="resizable-img" style=${styleMap(imageSize)}>
|
<div
|
||||||
|
class=${classMap({
|
||||||
|
'resizable-img': true,
|
||||||
|
'comment-highlighted': this.block.isCommentHighlighted,
|
||||||
|
})}
|
||||||
|
style=${styleMap(imageSize)}
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
class="drag-target"
|
class="drag-target"
|
||||||
draggable="false"
|
draggable="false"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ImageBlockModel } from '@blocksuite/affine-model';
|
import { ImageBlockModel } from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
|
blockCommentToolbarButton,
|
||||||
type ToolbarModuleConfig,
|
type ToolbarModuleConfig,
|
||||||
ToolbarModuleExtension,
|
ToolbarModuleExtension,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
@@ -49,6 +50,10 @@ const builtinToolbarConfig = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'c.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
@@ -141,6 +146,10 @@ const builtinSurfaceToolbarConfig = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'c.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
when: ctx => ctx.getSurfaceModelsByType(ImageBlockModel).length === 1,
|
when: ctx => ctx.getSurfaceModelsByType(ImageBlockModel).length === 1,
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import { Peekable } from '@blocksuite/affine-components/peek';
|
|||||||
import { ResourceController } from '@blocksuite/affine-components/resource';
|
import { ResourceController } from '@blocksuite/affine-components/resource';
|
||||||
import type { ImageBlockModel } from '@blocksuite/affine-model';
|
import type { ImageBlockModel } from '@blocksuite/affine-model';
|
||||||
import { ImageSelection } from '@blocksuite/affine-shared/selection';
|
import { ImageSelection } from '@blocksuite/affine-shared/selection';
|
||||||
import { ToolbarRegistryIdentifier } from '@blocksuite/affine-shared/services';
|
import {
|
||||||
|
BlockCommentManager,
|
||||||
|
ToolbarRegistryIdentifier,
|
||||||
|
} from '@blocksuite/affine-shared/services';
|
||||||
import { formatSize } from '@blocksuite/affine-shared/utils';
|
import { formatSize } from '@blocksuite/affine-shared/utils';
|
||||||
import { IS_MOBILE } from '@blocksuite/global/env';
|
import { IS_MOBILE } from '@blocksuite/global/env';
|
||||||
import { BrokenImageIcon, ImageIcon } from '@blocksuite/icons/lit';
|
import { BrokenImageIcon, ImageIcon } from '@blocksuite/icons/lit';
|
||||||
@@ -65,6 +68,12 @@ export class ImageBlockComponent extends CaptionedBlockComponent<ImageBlockModel
|
|||||||
return this.pageImage?.resizeImg;
|
return this.pageImage?.resizeImg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCommentHighlighted() {
|
||||||
|
return this.std
|
||||||
|
.get(BlockCommentManager)
|
||||||
|
.isBlockCommentHighlighted(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
private _handleClick(event: MouseEvent) {
|
private _handleClick(event: MouseEvent) {
|
||||||
// the peek view need handle shift + click
|
// the peek view need handle shift + click
|
||||||
if (event.defaultPrevented) return;
|
if (event.defaultPrevented) return;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
EDGELESS_TOP_CONTENTEDITABLE_SELECTOR,
|
EDGELESS_TOP_CONTENTEDITABLE_SELECTOR,
|
||||||
} from '@blocksuite/affine-shared/consts';
|
} from '@blocksuite/affine-shared/consts';
|
||||||
import {
|
import {
|
||||||
|
BlockCommentManager,
|
||||||
CitationProvider,
|
CitationProvider,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
@@ -107,6 +108,12 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCommentHighlighted() {
|
||||||
|
return this.std
|
||||||
|
.get(BlockCommentManager)
|
||||||
|
.isBlockCommentHighlighted(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
override get topContenteditableElement() {
|
override get topContenteditableElement() {
|
||||||
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
|
||||||
return this.closest<BlockComponent>(
|
return this.closest<BlockComponent>(
|
||||||
@@ -268,7 +275,10 @@ export class ParagraphBlockComponent extends CaptionedBlockComponent<ParagraphBl
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div
|
<div
|
||||||
class="affine-paragraph-block-container"
|
class=${classMap({
|
||||||
|
'affine-paragraph-block-container': true,
|
||||||
|
'highlight-comment': this.isCommentHighlighted,
|
||||||
|
})}
|
||||||
data-has-collapsed-siblings="${collapsedSiblings.length > 0}"
|
data-has-collapsed-siblings="${collapsedSiblings.length > 0}"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { unsafeCSSVarV2 } from '@blocksuite/affine-shared/theme';
|
||||||
import { css } from 'lit';
|
import { css } from 'lit';
|
||||||
|
|
||||||
export const paragraphBlockStyles = css`
|
export const paragraphBlockStyles = css`
|
||||||
@@ -15,6 +16,11 @@ export const paragraphBlockStyles = css`
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.affine-paragraph-block-container.highlight-comment {
|
||||||
|
background-color: ${unsafeCSSVarV2('block/comment/highlightActive')};
|
||||||
|
outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
}
|
||||||
|
|
||||||
affine-paragraph code {
|
affine-paragraph code {
|
||||||
font-size: calc(var(--affine-font-base) - 3px);
|
font-size: calc(var(--affine-font-base) - 3px);
|
||||||
padding: 0px 4px 2px;
|
padding: 0px 4px 2px;
|
||||||
|
|||||||
@@ -40,11 +40,10 @@ import type {
|
|||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
CommentProviderIdentifier,
|
blockCommentToolbarButton,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
import { tableViewMeta } from '@blocksuite/data-view/view-presets';
|
import { tableViewMeta } from '@blocksuite/data-view/view-presets';
|
||||||
import {
|
import {
|
||||||
CommentIcon,
|
|
||||||
CopyIcon,
|
CopyIcon,
|
||||||
DatabaseTableViewIcon,
|
DatabaseTableViewIcon,
|
||||||
DeleteIcon,
|
DeleteIcon,
|
||||||
@@ -270,28 +269,17 @@ const turnIntoLinkedDoc = {
|
|||||||
},
|
},
|
||||||
} as const satisfies ToolbarAction;
|
} as const satisfies ToolbarAction;
|
||||||
|
|
||||||
const commentAction = {
|
|
||||||
id: 'd.comment',
|
|
||||||
when: ({ std, chain }) =>
|
|
||||||
isFormatSupported(chain).run()[0] &&
|
|
||||||
!!std.getOptional(CommentProviderIdentifier),
|
|
||||||
icon: CommentIcon(),
|
|
||||||
run: ({ std }) => {
|
|
||||||
const commentProvider = std.getOptional(CommentProviderIdentifier);
|
|
||||||
if (!commentProvider) return;
|
|
||||||
|
|
||||||
commentProvider.addComment(std.selection.value);
|
|
||||||
},
|
|
||||||
} as const satisfies ToolbarAction;
|
|
||||||
|
|
||||||
export const builtinToolbarConfig = {
|
export const builtinToolbarConfig = {
|
||||||
actions: [
|
actions: [
|
||||||
conversionsActionGroup,
|
conversionsActionGroup,
|
||||||
inlineTextActionGroup,
|
inlineTextActionGroup,
|
||||||
highlightActionGroup,
|
highlightActionGroup,
|
||||||
commentAction,
|
|
||||||
turnIntoDatabase,
|
turnIntoDatabase,
|
||||||
turnIntoLinkedDoc,
|
turnIntoLinkedDoc,
|
||||||
|
{
|
||||||
|
id: 'g.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
} from '@blocksuite/affine-shared/commands';
|
} from '@blocksuite/affine-shared/commands';
|
||||||
import {
|
import {
|
||||||
ActionPlacement,
|
ActionPlacement,
|
||||||
|
blockCommentToolbarButton,
|
||||||
type ToolbarModuleConfig,
|
type ToolbarModuleConfig,
|
||||||
} from '@blocksuite/affine-shared/services';
|
} from '@blocksuite/affine-shared/services';
|
||||||
import { CaptionIcon, CopyIcon, DeleteIcon } from '@blocksuite/icons/lit';
|
import { CaptionIcon, CopyIcon, DeleteIcon } from '@blocksuite/icons/lit';
|
||||||
@@ -61,6 +62,10 @@ export const surfaceRefToolbarModuleConfig: ToolbarModuleConfig = {
|
|||||||
surfaceRefBlock.captionElement.show();
|
surfaceRefBlock.captionElement.show();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'e.comment',
|
||||||
|
...blockCommentToolbarButton,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'a.clipboard',
|
id: 'a.clipboard',
|
||||||
placement: ActionPlacement.More,
|
placement: ActionPlacement.More,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
type SurfaceRefBlockModel,
|
type SurfaceRefBlockModel,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
import {
|
import {
|
||||||
|
BlockCommentManager,
|
||||||
DocModeProvider,
|
DocModeProvider,
|
||||||
EditPropsStore,
|
EditPropsStore,
|
||||||
type OpenDocMode,
|
type OpenDocMode,
|
||||||
@@ -76,6 +77,10 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
|
|||||||
border-color: ${unsafeCSSVarV2('edgeless/frame/border/active')};
|
border-color: ${unsafeCSSVarV2('edgeless/frame/border/active')};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.affine-surface-ref.comment-highlighted {
|
||||||
|
outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
|
||||||
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
.affine-surface-ref {
|
.affine-surface-ref {
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
@@ -137,6 +142,12 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
|
|||||||
return this._referencedModel;
|
return this._referencedModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isCommentHighlighted() {
|
||||||
|
return this.std
|
||||||
|
.get(BlockCommentManager)
|
||||||
|
.isBlockCommentHighlighted(this.model);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly _handleClick = () => {
|
private readonly _handleClick = () => {
|
||||||
this.selection.update(() => {
|
this.selection.update(() => {
|
||||||
return [this.selection.create(BlockSelection, { blockId: this.blockId })];
|
return [this.selection.create(BlockSelection, { blockId: this.blockId })];
|
||||||
@@ -456,6 +467,7 @@ export class SurfaceRefBlockComponent extends BlockComponent<SurfaceRefBlockMode
|
|||||||
class=${classMap({
|
class=${classMap({
|
||||||
'affine-surface-ref': true,
|
'affine-surface-ref': true,
|
||||||
focused: this.selected$.value,
|
focused: this.selected$.value,
|
||||||
|
'comment-highlighted': this.isCommentHighlighted,
|
||||||
})}
|
})}
|
||||||
@click=${this._handleClick}
|
@click=${this._handleClick}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
} from '@blocksuite/affine-ext-loader';
|
} from '@blocksuite/affine-ext-loader';
|
||||||
import {
|
import {
|
||||||
AutoClearSelectionService,
|
AutoClearSelectionService,
|
||||||
|
BlockCommentManager,
|
||||||
CitationService,
|
CitationService,
|
||||||
DefaultOpenDocExtension,
|
DefaultOpenDocExtension,
|
||||||
DNDAPIExtension,
|
DNDAPIExtension,
|
||||||
@@ -78,6 +79,7 @@ export class FoundationViewExtension extends ViewExtensionProvider<FoundationVie
|
|||||||
LinkPreviewCache,
|
LinkPreviewCache,
|
||||||
LinkPreviewService,
|
LinkPreviewService,
|
||||||
CitationService,
|
CitationService,
|
||||||
|
BlockCommentManager,
|
||||||
]);
|
]);
|
||||||
context.register(clipboardConfigs);
|
context.register(clipboardConfigs);
|
||||||
if (this.isEdgeless(context.scope)) {
|
if (this.isEdgeless(context.scope)) {
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ export type AttachmentBlockProps = {
|
|||||||
style?: (typeof AttachmentBlockStyles)[number];
|
style?: (typeof AttachmentBlockStyles)[number];
|
||||||
|
|
||||||
footnoteIdentifier: string | null;
|
footnoteIdentifier: string | null;
|
||||||
|
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & Omit<GfxCommonBlockProps, 'scale'> &
|
} & Omit<GfxCommonBlockProps, 'scale'> &
|
||||||
BlockMeta;
|
BlockMeta;
|
||||||
|
|
||||||
@@ -78,6 +80,7 @@ export const defaultAttachmentProps: AttachmentBlockProps = {
|
|||||||
'meta:createdBy': undefined,
|
'meta:createdBy': undefined,
|
||||||
'meta:updatedBy': undefined,
|
'meta:updatedBy': undefined,
|
||||||
footnoteIdentifier: null,
|
footnoteIdentifier: null,
|
||||||
|
comments: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AttachmentBlockSchema = defineBlockSchema({
|
export const AttachmentBlockSchema = defineBlockSchema({
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export type BookmarkBlockProps = {
|
|||||||
url: string;
|
url: string;
|
||||||
caption: string | null;
|
caption: string | null;
|
||||||
footnoteIdentifier: string | null;
|
footnoteIdentifier: string | null;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & LinkPreviewData &
|
} & LinkPreviewData &
|
||||||
Omit<GfxCommonBlockProps, 'scale'> &
|
Omit<GfxCommonBlockProps, 'scale'> &
|
||||||
BlockMeta;
|
BlockMeta;
|
||||||
@@ -52,6 +53,7 @@ const defaultBookmarkProps: BookmarkBlockProps = {
|
|||||||
'meta:updatedBy': undefined,
|
'meta:updatedBy': undefined,
|
||||||
|
|
||||||
footnoteIdentifier: null,
|
footnoteIdentifier: null,
|
||||||
|
comments: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const BookmarkBlockSchema = defineBlockSchema({
|
export const BookmarkBlockSchema = defineBlockSchema({
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type CodeBlockProps = {
|
|||||||
caption: string;
|
caption: string;
|
||||||
preview?: boolean;
|
preview?: boolean;
|
||||||
lineNumber?: boolean;
|
lineNumber?: boolean;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & BlockMeta;
|
} & BlockMeta;
|
||||||
|
|
||||||
export const CodeBlockSchema = defineBlockSchema({
|
export const CodeBlockSchema = defineBlockSchema({
|
||||||
@@ -26,6 +27,7 @@ export const CodeBlockSchema = defineBlockSchema({
|
|||||||
caption: '',
|
caption: '',
|
||||||
preview: undefined,
|
preview: undefined,
|
||||||
lineNumber: undefined,
|
lineNumber: undefined,
|
||||||
|
comments: undefined,
|
||||||
'meta:createdAt': undefined,
|
'meta:createdAt': undefined,
|
||||||
'meta:createdBy': undefined,
|
'meta:createdBy': undefined,
|
||||||
'meta:updatedAt': undefined,
|
'meta:updatedAt': undefined,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type DatabaseBlockProps = {
|
|||||||
title: Text;
|
title: Text;
|
||||||
cells: SerializedCells;
|
cells: SerializedCells;
|
||||||
columns: Array<ColumnDataType>;
|
columns: Array<ColumnDataType>;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class DatabaseBlockModel extends BlockModel<DatabaseBlockProps> {}
|
export class DatabaseBlockModel extends BlockModel<DatabaseBlockProps> {}
|
||||||
@@ -27,6 +28,7 @@ export const DatabaseBlockSchema = defineBlockSchema({
|
|||||||
title: internal.Text(),
|
title: internal.Text(),
|
||||||
cells: Object.create(null),
|
cells: Object.create(null),
|
||||||
columns: [],
|
columns: [],
|
||||||
|
comments: undefined,
|
||||||
}),
|
}),
|
||||||
metadata: {
|
metadata: {
|
||||||
role: 'hub',
|
role: 'hub',
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { DefaultTheme } from '../../themes/default';
|
|||||||
|
|
||||||
type EdgelessTextProps = {
|
type EdgelessTextProps = {
|
||||||
hasMaxWidth: boolean;
|
hasMaxWidth: boolean;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & Omit<TextStyleProps, 'fontSize'> &
|
} & Omit<TextStyleProps, 'fontSize'> &
|
||||||
GfxCommonBlockProps;
|
GfxCommonBlockProps;
|
||||||
|
|
||||||
@@ -54,6 +55,7 @@ export const EdgelessTextBlockSchema = defineBlockSchema({
|
|||||||
scale: 1,
|
scale: 1,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
hasMaxWidth: false,
|
hasMaxWidth: false,
|
||||||
|
comments: undefined,
|
||||||
...EdgelessTextZodSchema.parse(undefined),
|
...EdgelessTextZodSchema.parse(undefined),
|
||||||
}),
|
}),
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export type FrameBlockProps = {
|
|||||||
background: Color;
|
background: Color;
|
||||||
childElementIds?: Record<string, boolean>;
|
childElementIds?: Record<string, boolean>;
|
||||||
presentationIndex?: string;
|
presentationIndex?: string;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & GfxCompatibleProps;
|
} & GfxCompatibleProps;
|
||||||
|
|
||||||
export const FrameZodSchema = z
|
export const FrameZodSchema = z
|
||||||
@@ -50,6 +51,7 @@ export const FrameBlockSchema = defineBlockSchema({
|
|||||||
childElementIds: Object.create(null),
|
childElementIds: Object.create(null),
|
||||||
presentationIndex: generateKeyBetweenV2(null, null),
|
presentationIndex: generateKeyBetweenV2(null, null),
|
||||||
lockedBySelf: false,
|
lockedBySelf: false,
|
||||||
|
comments: undefined,
|
||||||
}),
|
}),
|
||||||
metadata: {
|
metadata: {
|
||||||
version: 1,
|
version: 1,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export type ImageBlockProps = {
|
|||||||
height?: number;
|
height?: number;
|
||||||
rotate: number;
|
rotate: number;
|
||||||
size?: number;
|
size?: number;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & Omit<GfxCommonBlockProps, 'scale'> &
|
} & Omit<GfxCommonBlockProps, 'scale'> &
|
||||||
BlockMeta;
|
BlockMeta;
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ const defaultImageProps: ImageBlockProps = {
|
|||||||
lockedBySelf: false,
|
lockedBySelf: false,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
size: -1,
|
size: -1,
|
||||||
|
comments: undefined,
|
||||||
'meta:createdAt': undefined,
|
'meta:createdAt': undefined,
|
||||||
'meta:createdBy': undefined,
|
'meta:createdBy': undefined,
|
||||||
'meta:updatedAt': undefined,
|
'meta:updatedAt': undefined,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
|
|
||||||
export type LatexProps = {
|
export type LatexProps = {
|
||||||
latex: string;
|
latex: string;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & GfxCommonBlockProps;
|
} & GfxCommonBlockProps;
|
||||||
|
|
||||||
export const LatexBlockSchema = defineBlockSchema({
|
export const LatexBlockSchema = defineBlockSchema({
|
||||||
@@ -22,6 +23,7 @@ export const LatexBlockSchema = defineBlockSchema({
|
|||||||
scale: 1,
|
scale: 1,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
latex: '',
|
latex: '',
|
||||||
|
comments: undefined,
|
||||||
}),
|
}),
|
||||||
metadata: {
|
metadata: {
|
||||||
version: 1,
|
version: 1,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type ListProps = {
|
|||||||
checked: boolean;
|
checked: boolean;
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
order: number | null;
|
order: number | null;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & BlockMeta;
|
} & BlockMeta;
|
||||||
|
|
||||||
export const ListBlockSchema = defineBlockSchema({
|
export const ListBlockSchema = defineBlockSchema({
|
||||||
@@ -29,6 +30,7 @@ export const ListBlockSchema = defineBlockSchema({
|
|||||||
|
|
||||||
// number type only for numbered list
|
// number type only for numbered list
|
||||||
order: null,
|
order: null,
|
||||||
|
comments: undefined,
|
||||||
'meta:createdAt': undefined,
|
'meta:createdAt': undefined,
|
||||||
'meta:createdBy': undefined,
|
'meta:createdBy': undefined,
|
||||||
'meta:updatedAt': undefined,
|
'meta:updatedAt': undefined,
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ export const NoteBlockSchema = defineBlockSchema({
|
|||||||
shadowType: DEFAULT_NOTE_SHADOW,
|
shadowType: DEFAULT_NOTE_SHADOW,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
comments: undefined,
|
||||||
}),
|
}),
|
||||||
metadata: {
|
metadata: {
|
||||||
version: 1,
|
version: 1,
|
||||||
@@ -91,6 +92,7 @@ export type NoteProps = {
|
|||||||
background: Color;
|
background: Color;
|
||||||
displayMode: NoteDisplayMode;
|
displayMode: NoteDisplayMode;
|
||||||
edgeless: NoteEdgelessProps;
|
edgeless: NoteEdgelessProps;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* use `displayMode` instead
|
* use `displayMode` instead
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export type ParagraphProps = {
|
|||||||
type: ParagraphType;
|
type: ParagraphType;
|
||||||
text: Text;
|
text: Text;
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
} & BlockMeta;
|
} & BlockMeta;
|
||||||
|
|
||||||
export const ParagraphBlockSchema = defineBlockSchema({
|
export const ParagraphBlockSchema = defineBlockSchema({
|
||||||
@@ -29,6 +30,7 @@ export const ParagraphBlockSchema = defineBlockSchema({
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
text: internal.Text(),
|
text: internal.Text(),
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
|
comments: undefined,
|
||||||
'meta:createdAt': undefined,
|
'meta:createdAt': undefined,
|
||||||
'meta:createdBy': undefined,
|
'meta:createdBy': undefined,
|
||||||
'meta:updatedAt': undefined,
|
'meta:updatedAt': undefined,
|
||||||
|
|||||||
@@ -8,14 +8,16 @@ export type SurfaceRefProps = {
|
|||||||
reference: string;
|
reference: string;
|
||||||
caption: string;
|
caption: string;
|
||||||
refFlavour: string;
|
refFlavour: string;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SurfaceRefBlockSchema = defineBlockSchema({
|
export const SurfaceRefBlockSchema = defineBlockSchema({
|
||||||
flavour: 'affine:surface-ref',
|
flavour: 'affine:surface-ref',
|
||||||
props: () => ({
|
props: (): SurfaceRefProps => ({
|
||||||
reference: '',
|
reference: '',
|
||||||
caption: '',
|
caption: '',
|
||||||
refFlavour: '',
|
refFlavour: '',
|
||||||
|
comments: undefined,
|
||||||
}),
|
}),
|
||||||
metadata: {
|
metadata: {
|
||||||
version: 1,
|
version: 1,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export interface TableBlockProps extends BlockMeta {
|
|||||||
columns: Record<string, TableColumn>;
|
columns: Record<string, TableColumn>;
|
||||||
// key = `${rowId}:${columnId}`
|
// key = `${rowId}:${columnId}`
|
||||||
cells: Record<string, TableCell>;
|
cells: Record<string, TableCell>;
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TableCellSerialized {
|
export interface TableCellSerialized {
|
||||||
@@ -51,6 +52,7 @@ export const TableBlockSchema = defineBlockSchema({
|
|||||||
rows: {},
|
rows: {},
|
||||||
columns: {},
|
columns: {},
|
||||||
cells: {},
|
cells: {},
|
||||||
|
comments: undefined,
|
||||||
'meta:createdAt': undefined,
|
'meta:createdAt': undefined,
|
||||||
'meta:createdBy': undefined,
|
'meta:createdBy': undefined,
|
||||||
'meta:updatedAt': undefined,
|
'meta:updatedAt': undefined,
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import {
|
|||||||
|
|
||||||
import type { BlockMeta } from './types';
|
import type { BlockMeta } from './types';
|
||||||
|
|
||||||
export type EmbedProps<Props = object> = Props & GfxCompatibleProps & BlockMeta;
|
export type EmbedProps<Props = object> = Props &
|
||||||
|
GfxCompatibleProps &
|
||||||
|
BlockMeta & {
|
||||||
|
comments?: Record<string, boolean>;
|
||||||
|
};
|
||||||
|
|
||||||
export function defineEmbedModel<
|
export function defineEmbedModel<
|
||||||
Props extends object,
|
Props extends object,
|
||||||
@@ -52,6 +56,7 @@ export function createEmbedBlockSchema<
|
|||||||
xywh: '[0,0,0,0]',
|
xywh: '[0,0,0,0]',
|
||||||
lockedBySelf: false,
|
lockedBySelf: false,
|
||||||
rotate: 0,
|
rotate: 0,
|
||||||
|
comments: undefined,
|
||||||
'meta:createdAt': undefined,
|
'meta:createdAt': undefined,
|
||||||
'meta:updatedAt': undefined,
|
'meta:updatedAt': undefined,
|
||||||
'meta:createdBy': undefined,
|
'meta:createdBy': undefined,
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
import { DividerBlockModel } from '@blocksuite/affine-model';
|
||||||
|
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||||
|
import {
|
||||||
|
BlockSelection,
|
||||||
|
LifeCycleWatcher,
|
||||||
|
TextSelection,
|
||||||
|
} from '@blocksuite/std';
|
||||||
|
import type { BaseSelection, BlockModel } from '@blocksuite/store';
|
||||||
|
import { signal } from '@preact/signals-core';
|
||||||
|
|
||||||
|
import { getSelectedBlocksCommand } from '../../commands';
|
||||||
|
import { ImageSelection } from '../../selection';
|
||||||
|
import { matchModels } from '../../utils';
|
||||||
|
import { type CommentId, CommentProviderIdentifier } from './comment-provider';
|
||||||
|
import { findCommentedBlocks } from './utils';
|
||||||
|
|
||||||
|
export class BlockCommentManager extends LifeCycleWatcher {
|
||||||
|
static override key = 'block-comment-manager';
|
||||||
|
|
||||||
|
private readonly _highlightedCommentId$ = signal<CommentId | null>(null);
|
||||||
|
|
||||||
|
private readonly _disposables = new DisposableGroup();
|
||||||
|
|
||||||
|
private get _provider() {
|
||||||
|
return this.std.getOptional(CommentProviderIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
isBlockCommentHighlighted(
|
||||||
|
block: BlockModel<{ comments?: Record<CommentId, boolean> }>
|
||||||
|
) {
|
||||||
|
const comments = block.props.comments;
|
||||||
|
if (!comments) return false;
|
||||||
|
return (
|
||||||
|
this._highlightedCommentId$.value !== null &&
|
||||||
|
Object.keys(comments).includes(this._highlightedCommentId$.value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override mounted() {
|
||||||
|
const provider = this._provider;
|
||||||
|
if (!provider) return;
|
||||||
|
|
||||||
|
this._disposables.add(provider.onCommentAdded(this._handleAddComment));
|
||||||
|
this._disposables.add(
|
||||||
|
provider.onCommentDeleted(this._handleDeleteAndResolve)
|
||||||
|
);
|
||||||
|
this._disposables.add(
|
||||||
|
provider.onCommentResolved(this._handleDeleteAndResolve)
|
||||||
|
);
|
||||||
|
this._disposables.add(
|
||||||
|
provider.onCommentHighlighted(this._handleHighlightComment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
override unmounted() {
|
||||||
|
this._disposables.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly _handleAddComment = (
|
||||||
|
id: CommentId,
|
||||||
|
selections: BaseSelection[]
|
||||||
|
) => {
|
||||||
|
const blocksFromTextRange = selections
|
||||||
|
.filter((s): s is TextSelection => s.is(TextSelection))
|
||||||
|
.map(s => {
|
||||||
|
const [_, { selectedBlocks }] = this.std.command.exec(
|
||||||
|
getSelectedBlocksCommand,
|
||||||
|
{
|
||||||
|
textSelection: s,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!selectedBlocks) return [];
|
||||||
|
return selectedBlocks.map(b => b.model);
|
||||||
|
});
|
||||||
|
|
||||||
|
const needCommentBlocks = [
|
||||||
|
...blocksFromTextRange.flat(),
|
||||||
|
...selections
|
||||||
|
.filter(s => s instanceof BlockSelection || s instanceof ImageSelection)
|
||||||
|
.map(({ blockId }) => this.std.store.getModelById(blockId))
|
||||||
|
.filter(
|
||||||
|
(m): m is BlockModel =>
|
||||||
|
m !== null && !matchModels(m, [DividerBlockModel])
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (needCommentBlocks.length === 0) return;
|
||||||
|
|
||||||
|
this.std.store.withoutTransact(() => {
|
||||||
|
needCommentBlocks.forEach(block => {
|
||||||
|
const comments = (
|
||||||
|
'comments' in block.props &&
|
||||||
|
typeof block.props.comments === 'object' &&
|
||||||
|
block.props.comments !== null
|
||||||
|
? block.props.comments
|
||||||
|
: {}
|
||||||
|
) as Record<CommentId, boolean>;
|
||||||
|
|
||||||
|
this.std.store.updateBlock(block, {
|
||||||
|
comments: { [id]: true, ...comments },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly _handleDeleteAndResolve = (id: CommentId) => {
|
||||||
|
const commentedBlocks = findCommentedBlocks(this.std.store, id);
|
||||||
|
this.std.store.withoutTransact(() => {
|
||||||
|
commentedBlocks.forEach(block => {
|
||||||
|
delete block.props.comments[id];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly _handleHighlightComment = (id: CommentId | null) => {
|
||||||
|
this._highlightedCommentId$.value = id;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1 +1,3 @@
|
|||||||
|
export * from './block-comment-manager';
|
||||||
export * from './comment-provider';
|
export * from './comment-provider';
|
||||||
|
export * from './utils';
|
||||||
|
|||||||
@@ -1,9 +1,45 @@
|
|||||||
import type { Store } from '@blocksuite/store';
|
import { CommentIcon } from '@blocksuite/icons/lit';
|
||||||
|
import { BlockSelection } from '@blocksuite/std';
|
||||||
|
import type { BlockModel, Store } from '@blocksuite/store';
|
||||||
|
|
||||||
import type { CommentId } from './comment-provider';
|
import type { ToolbarAction } from '../toolbar-service';
|
||||||
|
import { type CommentId, CommentProviderIdentifier } from './comment-provider';
|
||||||
|
|
||||||
export function findCommentedBlocks(store: Store, commentId: CommentId) {
|
export function findCommentedBlocks(store: Store, commentId: CommentId) {
|
||||||
return store.getAllModels().filter(block => {
|
type CommentedBlock = BlockModel<{ comments: Record<CommentId, boolean> }>;
|
||||||
return 'comment' in block.props && block.props.comment === commentId;
|
return store.getAllModels().filter((block): block is CommentedBlock => {
|
||||||
|
return (
|
||||||
|
'comments' in block.props &&
|
||||||
|
typeof block.props.comments === 'object' &&
|
||||||
|
block.props.comments !== null &&
|
||||||
|
commentId in block.props.comments
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const blockCommentToolbarButton: Omit<ToolbarAction, 'id'> = {
|
||||||
|
tooltip: 'Comment',
|
||||||
|
when: ({ std }) => !!std.getOptional(CommentProviderIdentifier),
|
||||||
|
icon: CommentIcon(),
|
||||||
|
run: ctx => {
|
||||||
|
const commentProvider = ctx.std.getOptional(CommentProviderIdentifier);
|
||||||
|
if (!commentProvider) return;
|
||||||
|
const selections = ctx.selection.value;
|
||||||
|
|
||||||
|
const model = ctx.getCurrentModel();
|
||||||
|
|
||||||
|
if (selections.length > 1) {
|
||||||
|
commentProvider.addComment(selections);
|
||||||
|
} else if (model) {
|
||||||
|
commentProvider.addComment([
|
||||||
|
new BlockSelection({
|
||||||
|
blockId: model.id,
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
} else if (selections.length === 1) {
|
||||||
|
commentProvider.addComment(selections);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user