Skip to content

Commit ee807e4

Browse files
L-SunSaul-Mirone
andauthored
chore: sync affine blocksuite to packages (#9144)
Co-authored-by: Saul-Mirone <[email protected]>
1 parent cf6d8a4 commit ee807e4

File tree

66 files changed

+750
-727
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+750
-727
lines changed

packages/affine/blocks/attachment/src/attachment-block.ts

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ import {
2222
FileSizeLimitProvider,
2323
TelemetryProvider,
2424
} from '@blocksuite/affine-shared/services';
25-
import { formatSize } from '@blocksuite/affine-shared/utils';
25+
import {
26+
formatSize,
27+
openSingleFileWith,
28+
} from '@blocksuite/affine-shared/utils';
2629
import {
2730
AttachmentIcon,
2831
ResetIcon,
@@ -31,7 +34,7 @@ import {
3134
} from '@blocksuite/icons/lit';
3235
import { BlockSelection } from '@blocksuite/std';
3336
import { nanoid, Slice } from '@blocksuite/store';
34-
import { computed, signal } from '@preact/signals-core';
37+
import { batch, computed, signal } from '@preact/signals-core';
3538
import { html, type TemplateResult } from 'lit';
3639
import { choose } from 'lit/directives/choose.js';
3740
import { type ClassInfo, classMap } from 'lit/directives/class-map.js';
@@ -42,7 +45,7 @@ import { filter } from 'rxjs/operators';
4245

4346
import { AttachmentEmbedProvider } from './embed';
4447
import { styles } from './styles';
45-
import { downloadAttachmentBlob, refreshData } from './utils';
48+
import { downloadAttachmentBlob, getFileType, refreshData } from './utils';
4649

4750
type AttachmentResolvedStateInfo = ResolvedStateInfo & {
4851
kind?: TemplateResult;
@@ -129,12 +132,50 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
129132

130133
// Refreshes the embed component.
131134
reload = () => {
132-
if (this.model.props.embed) {
133-
this._refreshKey$.value = nanoid();
134-
return;
135-
}
135+
batch(() => {
136+
if (this.model.props.embed$.value) {
137+
this._refreshKey$.value = nanoid();
138+
return;
139+
}
140+
141+
this.refreshData();
142+
});
143+
};
144+
145+
// Replaces the current attachment.
146+
replace = async () => {
147+
const state = this.resourceController.state$.peek();
148+
if (state.uploading) return;
136149

137-
this.refreshData();
150+
const file = await openSingleFileWith();
151+
if (!file) return;
152+
153+
const sourceId = await this.std.store.blobSync.set(file);
154+
const type = await getFileType(file);
155+
const { name, size } = file;
156+
157+
let embed = this.model.props.embed$.value ?? false;
158+
159+
this.std.store.captureSync();
160+
this.std.store.transact(() => {
161+
this.std.store.updateBlock(this.blockId, {
162+
name,
163+
size,
164+
type,
165+
sourceId,
166+
embed: false,
167+
});
168+
169+
const provider = this.std.get(AttachmentEmbedProvider);
170+
embed &&= provider.embedded(this.model);
171+
172+
if (embed) {
173+
provider.convertTo(this.model);
174+
}
175+
176+
// Reloads
177+
this.reload();
178+
});
138179
};
139180

140181
private _selectBlock() {
@@ -403,7 +444,7 @@ export class AttachmentBlockComponent extends CaptionedBlockComponent<Attachment
403444

404445
protected renderEmbedView = () => {
405446
const { model, blobUrl } = this;
406-
if (!model.props.embed || !blobUrl) return null;
447+
if (!model.props.embed$.value || !blobUrl) return null;
407448

408449
const { std, _maxFileSize } = this;
409450
const provider = std.get(AttachmentEmbedProvider);

packages/affine/blocks/attachment/src/configs/toolbar.ts

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
DownloadIcon,
2525
DuplicateIcon,
2626
EditIcon,
27+
ReplaceIcon,
2728
ResetIcon,
2829
} from '@blocksuite/icons/lit';
2930
import { BlockFlavourIdentifier } from '@blocksuite/std';
@@ -139,27 +140,42 @@ export const attachmentViewDropdownMenu = {
139140
});
140141
};
141142

142-
return html`${keyed(
143-
model,
144-
html`<affine-view-dropdown-menu
145-
@toggle=${onToggle}
146-
.actions=${actions.value}
147-
.context=${ctx}
148-
.viewType$=${viewType$}
149-
></affine-view-dropdown-menu>`
150-
)}`;
143+
return html`<affine-view-dropdown-menu
144+
@toggle=${onToggle}
145+
.actions=${actions.value}
146+
.context=${ctx}
147+
.viewType$=${viewType$}
148+
></affine-view-dropdown-menu>`;
151149
},
152150
} as const satisfies ToolbarActionGroup<ToolbarAction>;
153151

152+
const replaceAction = {
153+
id: 'c.replace',
154+
tooltip: 'Replace attachment',
155+
icon: ReplaceIcon(),
156+
disabled(ctx) {
157+
const block = ctx.getCurrentBlockByType(AttachmentBlockComponent);
158+
if (!block) return true;
159+
160+
const { downloading = false, uploading = false } =
161+
block.resourceController.state$.value;
162+
return downloading || uploading;
163+
},
164+
run(ctx) {
165+
const block = ctx.getCurrentBlockByType(AttachmentBlockComponent);
166+
block?.replace().catch(console.error);
167+
},
168+
} as const satisfies ToolbarAction;
169+
154170
const downloadAction = {
155-
id: 'c.download',
171+
id: 'd.download',
156172
tooltip: 'Download',
157173
icon: DownloadIcon(),
158174
run(ctx) {
159175
const block = ctx.getCurrentBlockByType(AttachmentBlockComponent);
160176
block?.download();
161177
},
162-
when: ctx => {
178+
when(ctx) {
163179
const model = ctx.getCurrentModelByType(AttachmentBlockModel);
164180
if (!model) return false;
165181
// Current citation attachment block does not support download
@@ -168,7 +184,7 @@ const downloadAction = {
168184
} as const satisfies ToolbarAction;
169185

170186
const captionAction = {
171-
id: 'd.caption',
187+
id: 'e.caption',
172188
tooltip: 'Caption',
173189
icon: CaptionIcon(),
174190
run(ctx) {
@@ -221,6 +237,7 @@ const builtinToolbarConfig = {
221237
},
222238
},
223239
attachmentViewDropdownMenu,
240+
replaceAction,
224241
downloadAction,
225242
captionAction,
226243
{
@@ -354,13 +371,17 @@ const builtinSurfaceToolbarConfig = {
354371
)}`;
355372
},
356373
} satisfies ToolbarActionGroup<ToolbarAction>,
374+
{
375+
...replaceAction,
376+
id: 'd.replace',
377+
},
357378
{
358379
...downloadAction,
359-
id: 'd.download',
380+
id: 'e.download',
360381
},
361382
{
362383
...captionAction,
363-
id: 'e.caption',
384+
id: 'f.caption',
364385
},
365386
],
366387
when: ctx => ctx.getSurfaceModelsByType(AttachmentBlockModel).length === 1,
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import {
2+
type CodeBlockModel,
3+
CodeBlockSchema,
4+
ParagraphBlockModel,
5+
} from '@blocksuite/affine-model';
6+
import { focusTextModel } from '@blocksuite/affine-rich-text';
7+
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
8+
import { matchModels } from '@blocksuite/affine-shared/utils';
9+
import type { BlockComponent } from '@blocksuite/std';
10+
import { InlineMarkdownExtension } from '@blocksuite/std/inline';
11+
12+
export const CodeBlockMarkdownExtension =
13+
InlineMarkdownExtension<AffineTextAttributes>({
14+
name: 'code-block',
15+
pattern: /^```([a-zA-Z0-9]*)\s$/,
16+
action: ({ inlineEditor, inlineRange, prefixText, pattern }) => {
17+
if (inlineEditor.yTextString.slice(0, inlineRange.index).includes('\n')) {
18+
return;
19+
}
20+
21+
const match = prefixText.match(pattern);
22+
if (!match) return;
23+
24+
const language = match[1];
25+
26+
if (!inlineEditor.rootElement) return;
27+
const blockComponent =
28+
inlineEditor.rootElement.closest<BlockComponent>('[data-block-id]');
29+
if (!blockComponent) return;
30+
31+
const { model, std, store } = blockComponent;
32+
33+
if (
34+
matchModels(model, [ParagraphBlockModel]) &&
35+
model.props.type === 'quote'
36+
) {
37+
return;
38+
}
39+
40+
const parent = store.getParent(model);
41+
if (!parent) return;
42+
const index = parent.children.indexOf(model);
43+
44+
store.captureSync();
45+
const codeId = store.addBlock<CodeBlockModel>(
46+
CodeBlockSchema.model.flavour,
47+
{ language },
48+
parent,
49+
index
50+
);
51+
52+
if (model.text && model.text.length > prefixText.length) {
53+
const text = model.text.clone();
54+
store.addBlock('affine:paragraph', { text }, parent, index + 1);
55+
text.delete(0, prefixText.length);
56+
}
57+
store.deleteBlock(model, { bringChildrenTo: parent });
58+
59+
focusTextModel(std, codeId);
60+
},
61+
});

packages/affine/blocks/code/src/styles.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ export const codeBlockStyles = css`
3333
grid-template-columns: auto minmax(0, 1fr);
3434
}
3535
36+
.affine-code-block-container.disable-line-numbers v-line {
37+
grid-template-columns: unset;
38+
}
39+
3640
.affine-code-block-container div:has(> v-line) {
3741
display: grid;
3842
}

packages/affine/blocks/code/src/view.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { CodeKeymapExtension } from './code-keymap.js';
2121
import { AFFINE_CODE_TOOLBAR_WIDGET } from './code-toolbar/index.js';
2222
import { codeSlashMenuConfig } from './configs/slash-menu.js';
2323
import { effects } from './effects.js';
24+
import { CodeBlockMarkdownExtension } from './markdown.js';
2425

2526
const codeToolbarWidget = WidgetViewExtension(
2627
'affine:code',
@@ -44,6 +45,7 @@ export class CodeBlockViewExtension extends ViewExtensionProvider {
4445
BlockViewExtension('affine:code', literal`affine-code`),
4546
SlashMenuConfigExtension('affine:code', codeSlashMenuConfig),
4647
CodeKeymapExtension,
48+
CodeBlockMarkdownExtension,
4749
...getCodeClipboardExtensions(),
4850
]);
4951
context.register([

packages/affine/blocks/database/src/properties/rich-text/cell-renderer.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,6 @@ export class RichTextCell extends BaseCellRenderer<Text, string> {
331331
this.inlineEditor$.value?.selectAll();
332332
}
333333
};
334-
this.addEventListener('keydown', selectAll);
335334
this.disposables.addFromEvent(this, 'keydown', selectAll);
336335
this.disposables.add(
337336
effect(() => {

packages/affine/blocks/database/src/properties/title/text.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,19 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
209209
}
210210
};
211211

212-
this.addEventListener('keydown', selectAll);
213212
this.disposables.addFromEvent(this, 'keydown', selectAll);
214213
}
215214

215+
private readonly _handleKeyDown = (event: KeyboardEvent) => {
216+
if (event.key !== 'Escape') {
217+
if (event.key === 'Tab') {
218+
event.preventDefault();
219+
return;
220+
}
221+
event.stopPropagation();
222+
}
223+
};
224+
216225
override firstUpdated(props: Map<string, unknown>) {
217226
super.firstUpdated(props);
218227
this.richText.value?.updateComplete
@@ -233,6 +242,12 @@ export class HeaderAreaTextCell extends BaseCellRenderer<Text, string> {
233242
'paste',
234243
this._onPaste
235244
);
245+
const inlineEditor = this.inlineEditor;
246+
if (inlineEditor) {
247+
this.disposables.add(
248+
inlineEditor.slots.keydown.subscribe(this._handleKeyDown)
249+
);
250+
}
236251
}
237252
})
238253
.catch(console.error);

packages/affine/blocks/divider/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@blocksuite/affine-components": "workspace:*",
1414
"@blocksuite/affine-ext-loader": "workspace:*",
1515
"@blocksuite/affine-model": "workspace:*",
16+
"@blocksuite/affine-rich-text": "workspace:*",
1617
"@blocksuite/affine-shared": "workspace:*",
1718
"@blocksuite/global": "workspace:*",
1819
"@blocksuite/std": "workspace:*",
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {
2+
type DividerBlockModel,
3+
DividerBlockSchema,
4+
ParagraphBlockModel,
5+
ParagraphBlockSchema,
6+
} from '@blocksuite/affine-model';
7+
import { focusTextModel } from '@blocksuite/affine-rich-text';
8+
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
9+
import { matchModels } from '@blocksuite/affine-shared/utils';
10+
import type { BlockComponent } from '@blocksuite/std';
11+
import { InlineMarkdownExtension } from '@blocksuite/std/inline';
12+
13+
export const DividerMarkdownExtension =
14+
InlineMarkdownExtension<AffineTextAttributes>({
15+
name: 'divider',
16+
pattern: /^(-{3,}|\*{3,}|_{3,})\s$/,
17+
action: ({ inlineEditor, inlineRange }) => {
18+
if (inlineEditor.yTextString.slice(0, inlineRange.index).includes('\n')) {
19+
return;
20+
}
21+
22+
if (!inlineEditor.rootElement) return;
23+
const blockComponent =
24+
inlineEditor.rootElement.closest<BlockComponent>('[data-block-id]');
25+
if (!blockComponent) return;
26+
27+
const { model, std, store } = blockComponent;
28+
29+
if (
30+
matchModels(model, [ParagraphBlockModel]) &&
31+
model.props.type !== 'quote'
32+
) {
33+
const parent = store.getParent(model);
34+
if (!parent) return;
35+
const index = parent.children.indexOf(model);
36+
37+
store.captureSync();
38+
inlineEditor.deleteText({
39+
index: 0,
40+
length: inlineRange.index,
41+
});
42+
store.addBlock<DividerBlockModel>(
43+
DividerBlockSchema.model.flavour,
44+
{
45+
children: model.children,
46+
},
47+
parent,
48+
index
49+
);
50+
51+
const nextBlock = parent.children.at(index + 1);
52+
let id = nextBlock?.id;
53+
if (!id) {
54+
id = store.addBlock<ParagraphBlockModel>(
55+
ParagraphBlockSchema.model.flavour,
56+
{},
57+
parent
58+
);
59+
}
60+
focusTextModel(std, id);
61+
}
62+
},
63+
});

0 commit comments

Comments
 (0)