@formicoidea/labre-core 0.26.1 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/dist/_pkgs/affine-block-database/data-source.d.ts +28 -40
  2. package/dist/_pkgs/affine-block-database/data-source.js +116 -178
  3. package/dist/_pkgs/affine-block-database/database-block.d.ts +10 -3
  4. package/dist/_pkgs/affine-block-database/database-block.js +110 -70
  5. package/dist/_pkgs/affine-block-database/external-data-source.d.ts +69 -0
  6. package/dist/_pkgs/affine-block-database/external-data-source.js +144 -0
  7. package/dist/_pkgs/affine-block-database/index.d.ts +1 -0
  8. package/dist/_pkgs/affine-block-database/index.js +1 -0
  9. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-linked-doc-block.js +6 -7
  10. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/components/embed-synced-doc-card.js +5 -5
  11. package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +1 -1
  12. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.d.ts +18 -0
  13. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.d.ts +18 -0
  14. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +104 -2
  15. package/dist/_pkgs/affine-block-root/index.d.ts +1 -0
  16. package/dist/_pkgs/affine-block-root/index.js +1 -0
  17. package/dist/_pkgs/affine-block-root/keyboard/keyboard-manager.js +3 -21
  18. package/dist/_pkgs/affine-block-root/keyboard/shortcuts.d.ts +8 -0
  19. package/dist/_pkgs/affine-block-root/keyboard/shortcuts.js +48 -0
  20. package/dist/_pkgs/affine-block-root/view.js +6 -1
  21. package/dist/_pkgs/affine-gfx-link/effects.js +2 -2
  22. package/dist/_pkgs/affine-gfx-link/toolbar/undo-dense-menu.d.ts +3 -0
  23. package/dist/_pkgs/affine-gfx-link/toolbar/undo-dense-menu.js +12 -0
  24. package/dist/_pkgs/affine-gfx-link/toolbar/undo-tool-button.d.ts +18 -0
  25. package/dist/_pkgs/affine-gfx-link/toolbar/undo-tool-button.js +41 -0
  26. package/dist/_pkgs/affine-gfx-link/undo-tool.d.ts +6 -0
  27. package/dist/_pkgs/affine-gfx-link/undo-tool.js +17 -0
  28. package/dist/_pkgs/affine-gfx-link/view.js +4 -2
  29. package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-tool-button.js +13 -2
  30. package/dist/_pkgs/affine-gfx-mindmap/toolbar/senior-tool.d.ts +12 -2
  31. package/dist/_pkgs/affine-gfx-mindmap/toolbar/senior-tool.js +62 -11
  32. package/dist/_pkgs/affine-gfx-mindmap/view.d.ts +13 -2
  33. package/dist/_pkgs/affine-gfx-mindmap/view.js +24 -5
  34. package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
  35. package/dist/_pkgs/affine-shared/services/index.js +2 -0
  36. package/dist/_pkgs/affine-shared/services/keymap-override-service.d.ts +15 -0
  37. package/dist/_pkgs/affine-shared/services/keymap-override-service.js +26 -0
  38. package/dist/_pkgs/affine-shared/services/linked-doc-content-service.d.ts +42 -0
  39. package/dist/_pkgs/affine-shared/services/linked-doc-content-service.js +47 -0
  40. package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.d.ts +29 -0
  41. package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.js +233 -0
  42. package/dist/_pkgs/affine-widget-edgeless-selected-rect/effects.js +2 -0
  43. package/dist/_pkgs/affine-widget-edgeless-selected-rect/spec.d.ts +1 -0
  44. package/dist/_pkgs/affine-widget-edgeless-selected-rect/spec.js +2 -0
  45. package/dist/_pkgs/affine-widget-edgeless-selected-rect/view.js +2 -1
  46. package/dist/_pkgs/data-view/core/data-source/base.d.ts +6 -0
  47. package/dist/_pkgs/data-view/core/data-source/base.js +6 -0
  48. package/dist/_pkgs/std/extension/index.d.ts +1 -0
  49. package/dist/_pkgs/std/extension/index.js +1 -0
  50. package/dist/_pkgs/std/extension/shortcut.d.ts +69 -0
  51. package/dist/_pkgs/std/extension/shortcut.js +106 -0
  52. package/dist/_pkgs/std/gfx/model/surface/element-model.d.ts +7 -0
  53. package/dist/_pkgs/std/gfx/model/surface/element-model.js +24 -1
  54. package/dist/_pkgs/std/identifier.d.ts +7 -0
  55. package/dist/_pkgs/std/identifier.js +6 -0
  56. package/dist/_pkgs/std/inline/components/v-element.js +5 -2
  57. package/dist/extensions/view.js +5 -2
  58. package/dist/flags.d.ts +1 -1
  59. package/dist/flags.js +4 -1
  60. package/dist/shortcuts.d.ts +24 -0
  61. package/dist/shortcuts.js +39 -0
  62. package/package.json +5 -1
  63. package/dist/_pkgs/affine-gfx-link/link-tool.d.ts +0 -1
  64. package/dist/_pkgs/affine-gfx-link/link-tool.js +0 -11
  65. package/dist/_pkgs/affine-gfx-link/toolbar/link-dense-menu.d.ts +0 -2
  66. package/dist/_pkgs/affine-gfx-link/toolbar/link-dense-menu.js +0 -26
  67. package/dist/_pkgs/affine-gfx-link/toolbar/link-tool-button.d.ts +0 -10
  68. package/dist/_pkgs/affine-gfx-link/toolbar/link-tool-button.js +0 -84
@@ -6,7 +6,7 @@ import { toast } from '../affine-components/toast/index.js';
6
6
  import { EDGELESS_TOP_CONTENTEDITABLE_SELECTOR } from '../affine-shared/consts/index.js';
7
7
  import { BlockCommentManager, CommentProviderIdentifier, DocModeProvider, NotificationProvider, TelemetryProvider, } from '../affine-shared/services/index.js';
8
8
  import { getDropResult } from '../affine-widget-drag-handle/index.js';
9
- import { createRecordDetail, createUniComponentFromWebComponent, DataViewRootUILogic, defineUniComponent, ExternalGroupByConfigProvider, lazy, renderUniLit, uniMap, } from '../data-view/index.js';
9
+ import { createRecordDetail, createUniComponentFromWebComponent, DataViewRootUILogic, defineUniComponent, ExternalGroupByConfigProvider, renderUniLit, uniMap, } from '../data-view/index.js';
10
10
  import { widgetPresets } from '../data-view/widget-presets/index.js';
11
11
  import { Rect } from '../global/gfx/index.js';
12
12
  import { CommentIcon, CopyIcon, DeleteIcon, MoreHorizontalIcon, } from '@blocksuite/icons/lit';
@@ -16,6 +16,7 @@ import { Slice } from '../store/index.js';
16
16
  import { autoUpdate } from '@floating-ui/dom';
17
17
  import { computed, signal } from '@preact/signals-core';
18
18
  import { html, nothing } from 'lit';
19
+ import { keyed } from 'lit/directives/keyed.js';
19
20
  import { popSideDetail } from './components/layout.js';
20
21
  import { DatabaseConfigExtension } from './config.js';
21
22
  import { EditorHostKey } from './context/host-context.js';
@@ -88,25 +89,10 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
88
89
  options,
89
90
  });
90
91
  };
91
- this.dataSource = lazy(() => {
92
- const initFn = (dataSource) => {
93
- dataSource.serviceSet(EditorHostKey, this.host);
94
- this.std.provider
95
- .getAll(ExternalGroupByConfigProvider)
96
- .forEach(config => {
97
- dataSource.serviceSet(ExternalGroupByConfigProvider(config.name), config);
98
- });
99
- };
100
- const provider = this.std.getOptional(DatabaseDataSourceProvider);
101
- const dataSource = provider && this.model.props.externalSourceId
102
- ? provider.createDataSource(this.model, initFn)
103
- : new DatabaseBlockDataSource(this.model, initFn);
104
- const id = currentViewStorage.getCurrentView(this.model.id);
105
- if (id && dataSource.viewManager.viewGet(id)) {
106
- dataSource.viewManager.setCurrentView(id);
107
- }
108
- return dataSource;
109
- });
92
+ // Bumps on every rebuild so `renderBlock` can `keyed()` the renderer element,
93
+ // forcing Lit to tear the old one down (disposing its listeners) and mount a
94
+ // fresh one bound to the new source.
95
+ this._sourceVersion = 0;
110
96
  this.renderTitle = (dataViewLogic) => {
111
97
  return html ` <affine-database-title
112
98
  class="${databaseTitleStyles}"
@@ -219,7 +205,107 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
219
205
  return databaseSelection?.viewSelection;
220
206
  });
221
207
  this.virtualPadding$ = signal(0);
222
- this.dataViewRootLogic = lazy(() => new DataViewRootUILogic({
208
+ this.#useZeroWidth_accessor_storage = true;
209
+ }
210
+ _buildDataSource() {
211
+ const initFn = (dataSource) => {
212
+ dataSource.serviceSet(EditorHostKey, this.host);
213
+ this.std.provider
214
+ .getAll(ExternalGroupByConfigProvider)
215
+ .forEach(config => {
216
+ dataSource.serviceSet(ExternalGroupByConfigProvider(config.name), config);
217
+ });
218
+ };
219
+ const provider = this.std.getOptional(DatabaseDataSourceProvider);
220
+ const dataSource = provider && this.model.props.externalSourceId
221
+ ? provider.createDataSource(this.model, initFn)
222
+ : new DatabaseBlockDataSource(this.model, initFn);
223
+ const id = currentViewStorage.getCurrentView(this.model.id);
224
+ if (id && dataSource.viewManager.viewGet(id)) {
225
+ dataSource.viewManager.setCurrentView(id);
226
+ }
227
+ return dataSource;
228
+ }
229
+ get dataSource() {
230
+ if (!this._dataSource) {
231
+ this._dataSource = this._buildDataSource();
232
+ }
233
+ return this._dataSource;
234
+ }
235
+ rebuildDataSource() {
236
+ this._dataSource?.dispose();
237
+ this._dataSource = undefined;
238
+ this._dataViewRootLogic = undefined;
239
+ this._sourceVersion++;
240
+ this.requestUpdate();
241
+ }
242
+ get optionsConfig() {
243
+ return {
244
+ configure: (_model, options) => options,
245
+ ...this.std.getOptional(DatabaseConfigExtension.identifier),
246
+ };
247
+ }
248
+ get isCommentHighlighted() {
249
+ return (this.std
250
+ .getOptional(BlockCommentManager)
251
+ ?.isBlockCommentHighlighted(this.model) ?? false);
252
+ }
253
+ get topContenteditableElement() {
254
+ if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
255
+ return this.closest(EDGELESS_TOP_CONTENTEDITABLE_SELECTOR);
256
+ }
257
+ return this.rootComponent;
258
+ }
259
+ renderDatabaseOps() {
260
+ if (this.dataSource.readonly$.value) {
261
+ return nothing;
262
+ }
263
+ return html ` <div
264
+ data-testid="database-ops"
265
+ class="${databaseOpsStyles}"
266
+ @click="${this.clickDatabaseOps}"
267
+ >
268
+ ${MoreHorizontalIcon()}
269
+ </div>`;
270
+ }
271
+ connectedCallback() {
272
+ super.connectedCallback();
273
+ this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
274
+ this.classList.add(databaseBlockStyles);
275
+ this.listenFullWidthChange();
276
+ // Rebuild the source whenever the block is re-pointed at (or detached from)
277
+ // an external collection. The first (synchronous) emission is skipped: the
278
+ // source is still lazy at this point, so there is nothing to rebuild.
279
+ this.disposables.add(this.model.props.externalSourceId$.subscribe(() => {
280
+ if (this._dataSource) {
281
+ this.rebuildDataSource();
282
+ }
283
+ }));
284
+ }
285
+ disconnectedCallback() {
286
+ this._dataSource?.dispose();
287
+ this._dataSource = undefined;
288
+ this._dataViewRootLogic = undefined;
289
+ super.disconnectedCallback();
290
+ }
291
+ listenFullWidthChange() {
292
+ if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
293
+ return;
294
+ }
295
+ this.disposables.add(autoUpdate(this.host, this, () => {
296
+ const padding = this.getBoundingClientRect().left -
297
+ this.host.getBoundingClientRect().left;
298
+ this.virtualPadding$.value = Math.max(0, padding - 72);
299
+ }));
300
+ }
301
+ get dataViewRootLogic() {
302
+ if (!this._dataViewRootLogic) {
303
+ this._dataViewRootLogic = this._buildDataViewRootLogic();
304
+ }
305
+ return this._dataViewRootLogic;
306
+ }
307
+ _buildDataViewRootLogic() {
308
+ return new DataViewRootUILogic({
223
309
  virtualPadding$: this.virtualPadding$,
224
310
  bindHotkey: hotkeys => {
225
311
  return {
@@ -237,7 +323,7 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
237
323
  },
238
324
  selection$: this.viewSelection$,
239
325
  setSelection: this.setSelection,
240
- dataSource: this.dataSource.value,
326
+ dataSource: this.dataSource,
241
327
  headerWidget: this.headerWidget,
242
328
  onDrag: this.onDrag,
243
329
  clipboard: this.std.clipboard,
@@ -297,58 +383,12 @@ export class DatabaseBlockComponent extends CaptionedBlockComponent {
297
383
  }
298
384
  },
299
385
  },
300
- }));
301
- this.#useZeroWidth_accessor_storage = true;
302
- }
303
- get optionsConfig() {
304
- return {
305
- configure: (_model, options) => options,
306
- ...this.std.getOptional(DatabaseConfigExtension.identifier),
307
- };
308
- }
309
- get isCommentHighlighted() {
310
- return (this.std
311
- .getOptional(BlockCommentManager)
312
- ?.isBlockCommentHighlighted(this.model) ?? false);
313
- }
314
- get topContenteditableElement() {
315
- if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
316
- return this.closest(EDGELESS_TOP_CONTENTEDITABLE_SELECTOR);
317
- }
318
- return this.rootComponent;
319
- }
320
- renderDatabaseOps() {
321
- if (this.dataSource.value.readonly$.value) {
322
- return nothing;
323
- }
324
- return html ` <div
325
- data-testid="database-ops"
326
- class="${databaseOpsStyles}"
327
- @click="${this.clickDatabaseOps}"
328
- >
329
- ${MoreHorizontalIcon()}
330
- </div>`;
331
- }
332
- connectedCallback() {
333
- super.connectedCallback();
334
- this.setAttribute(RANGE_SYNC_EXCLUDE_ATTR, 'true');
335
- this.classList.add(databaseBlockStyles);
336
- this.listenFullWidthChange();
337
- }
338
- listenFullWidthChange() {
339
- if (this.std.get(DocModeProvider).getEditorMode() === 'edgeless') {
340
- return;
341
- }
342
- this.disposables.add(autoUpdate(this.host, this, () => {
343
- const padding = this.getBoundingClientRect().left -
344
- this.host.getBoundingClientRect().left;
345
- this.virtualPadding$.value = Math.max(0, padding - 72);
346
- }));
386
+ });
347
387
  }
348
388
  renderBlock() {
349
389
  return html `
350
390
  <div contenteditable="false" class="${databaseContentStyles}">
351
- ${this.dataViewRootLogic.value.render()}
391
+ ${keyed(this._sourceVersion, this.dataViewRootLogic.render())}
352
392
  </div>
353
393
  `;
354
394
  }
@@ -0,0 +1,69 @@
1
+ import type { DatabaseBlockModel } from '../affine-model/index.js';
2
+ import type { InsertToPosition } from '../affine-shared/utils/index.js';
3
+ import { type DatabaseFlags, DataSourceBase, type DataViewDataType, type PropertyMetaConfig, type TypeInstance, type ViewManager, type ViewMeta } from '../data-view/index.js';
4
+ import { DisposableGroup } from '../global/disposable/index.js';
5
+ import { type ReadonlySignal } from '@preact/signals-core';
6
+ /**
7
+ * Intermediate base for `affine:database` data sources whose **view config**
8
+ * keeps living in the block model blob (`model.props.views`) while the **data
9
+ * layer** (rows / cells / columns) is redirected elsewhere — the inline blob
10
+ * source, or a host-provided external collection.
11
+ *
12
+ * It implements everything generic — the view manager / metas / converts, the
13
+ * view-data CRUD, feature flags, readonly, and the reactive plumbing — so a
14
+ * concrete source only provides a small synchronous data contract (`getRows`,
15
+ * `getCellValue`, …) plus the mutations, and calls {@link invalidate} from its
16
+ * own change observers.
17
+ *
18
+ * Reactivity: the data-layer reads ({@link rows$}, {@link properties$} and the
19
+ * `*Get` getters that `DataSourceBase` wraps in `computed()`) all depend on
20
+ * {@link revision}. A source backed by reactive blocksuite signals (the blob
21
+ * source) tracks those naturally and never bumps `revision` — cost is nil. A
22
+ * source reading raw `Y.Map`s bumps `revision` via {@link invalidate} so the
23
+ * computeds re-run. Defining `rows$`/`properties$` here (not as instance fields
24
+ * reassigned in a subclass constructor) also avoids the "reassign after super"
25
+ * dance.
26
+ */
27
+ export declare abstract class ExternalDataSourceBase extends DataSourceBase {
28
+ protected readonly _model: DatabaseBlockModel;
29
+ /** Bumped by {@link invalidate} to re-run the data-layer computeds. */
30
+ protected readonly revision: import("@preact/signals-core").Signal<number>;
31
+ protected readonly disposables: DisposableGroup;
32
+ constructor(model: DatabaseBlockModel);
33
+ /** Notify the view layer that the underlying data changed. */
34
+ protected invalidate(): void;
35
+ dispose(): void;
36
+ get doc(): import("../store/index.js").Store;
37
+ get parentProvider(): import("../global/di/provider.js").ServiceProvider;
38
+ protected abstract getRows(): string[];
39
+ protected abstract getProperties(): string[];
40
+ protected abstract getCellValue(rowId: string, propertyId: string): unknown;
41
+ protected abstract getPropertyType(propertyId: string): string | undefined;
42
+ protected abstract getPropertyName(propertyId: string): string;
43
+ protected abstract getPropertyData(propertyId: string): Record<string, unknown>;
44
+ protected abstract getPropertyDataType(propertyId: string): TypeInstance | undefined;
45
+ protected abstract getPropertyMetas(): PropertyMetaConfig[];
46
+ rows$: ReadonlySignal<string[]>;
47
+ properties$: ReadonlySignal<string[]>;
48
+ allPropertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
49
+ propertyMetas$: ReadonlySignal<PropertyMetaConfig[]>;
50
+ cellValueGet(rowId: string, propertyId: string): unknown;
51
+ propertyTypeGet(propertyId: string): string | undefined;
52
+ propertyNameGet(propertyId: string): string;
53
+ propertyDataGet(propertyId: string): Record<string, unknown>;
54
+ propertyDataTypeGet(propertyId: string): TypeInstance | undefined;
55
+ featureFlags$: ReadonlySignal<DatabaseFlags>;
56
+ readonly$: ReadonlySignal<boolean>;
57
+ viewConverts: import("./index.js").ViewConvertConfig[];
58
+ viewMetas: ViewMeta[];
59
+ viewManager: ViewManager;
60
+ viewDataList$: ReadonlySignal<DataViewDataType[]>;
61
+ viewDataGet(viewId: string): DataViewDataType | undefined;
62
+ viewDataAdd(viewData: DataViewDataType): string;
63
+ viewDataDelete(viewId: string): void;
64
+ viewDataDuplicate(id: string): string;
65
+ viewDataMoveTo(id: string, position: InsertToPosition): void;
66
+ viewDataUpdate<ViewData extends DataViewDataType>(id: string, updater: (data: ViewData) => Partial<ViewData>): void;
67
+ viewMetaGet(type: string): ViewMeta;
68
+ viewMetaGetById(viewId: string): ViewMeta | undefined;
69
+ }
@@ -0,0 +1,144 @@
1
+ import { FeatureFlagService } from '../affine-shared/services/index.js';
2
+ import { DataSourceBase, ViewManagerBase, } from '../data-view/index.js';
3
+ import { DisposableGroup } from '../global/disposable/index.js';
4
+ import { IS_MOBILE } from '../global/env/index.js';
5
+ import { BlockSuiteError, ErrorCode } from '../global/exceptions/index.js';
6
+ import { computed, signal, } from '@preact/signals-core';
7
+ import { deleteView, duplicateView, moveViewTo, updateView, } from './utils/block-utils.js';
8
+ import { databaseBlockViewConverts, databaseBlockViewMap, databaseBlockViews, } from './views/index.js';
9
+ /**
10
+ * Intermediate base for `affine:database` data sources whose **view config**
11
+ * keeps living in the block model blob (`model.props.views`) while the **data
12
+ * layer** (rows / cells / columns) is redirected elsewhere — the inline blob
13
+ * source, or a host-provided external collection.
14
+ *
15
+ * It implements everything generic — the view manager / metas / converts, the
16
+ * view-data CRUD, feature flags, readonly, and the reactive plumbing — so a
17
+ * concrete source only provides a small synchronous data contract (`getRows`,
18
+ * `getCellValue`, …) plus the mutations, and calls {@link invalidate} from its
19
+ * own change observers.
20
+ *
21
+ * Reactivity: the data-layer reads ({@link rows$}, {@link properties$} and the
22
+ * `*Get` getters that `DataSourceBase` wraps in `computed()`) all depend on
23
+ * {@link revision}. A source backed by reactive blocksuite signals (the blob
24
+ * source) tracks those naturally and never bumps `revision` — cost is nil. A
25
+ * source reading raw `Y.Map`s bumps `revision` via {@link invalidate} so the
26
+ * computeds re-run. Defining `rows$`/`properties$` here (not as instance fields
27
+ * reassigned in a subclass constructor) also avoids the "reassign after super"
28
+ * dance.
29
+ */
30
+ export class ExternalDataSourceBase extends DataSourceBase {
31
+ constructor(model) {
32
+ super();
33
+ /** Bumped by {@link invalidate} to re-run the data-layer computeds. */
34
+ this.revision = signal(0);
35
+ this.disposables = new DisposableGroup();
36
+ // --- reactive bridge over the data contract -----------------------------
37
+ this.rows$ = computed(() => {
38
+ void this.revision.value;
39
+ return this.getRows();
40
+ });
41
+ this.properties$ = computed(() => {
42
+ void this.revision.value;
43
+ return this.getProperties();
44
+ });
45
+ this.allPropertyMetas$ = computed(() => {
46
+ void this.revision.value;
47
+ return this.getPropertyMetas();
48
+ });
49
+ this.propertyMetas$ = computed(() => {
50
+ return this.allPropertyMetas$.value.filter(v => !v.config.fixed && !v.config.hide);
51
+ });
52
+ // --- view surface (config stays in the model blob) ----------------------
53
+ this.featureFlags$ = computed(() => {
54
+ const featureFlagService = this.doc.get(FeatureFlagService);
55
+ return {
56
+ enable_number_formatting: featureFlagService.getFlag('enable_database_number_formatting') ??
57
+ false,
58
+ enable_table_virtual_scroll: featureFlagService.getFlag('enable_table_virtual_scroll') ?? false,
59
+ };
60
+ });
61
+ this.readonly$ = computed(() => {
62
+ return (this._model.store.readonly ||
63
+ // TODO(@L-Sun): use block level readonly
64
+ IS_MOBILE);
65
+ });
66
+ this.viewConverts = databaseBlockViewConverts;
67
+ this.viewMetas = databaseBlockViews;
68
+ this.viewManager = new ViewManagerBase(this);
69
+ this.viewDataList$ = computed(() => {
70
+ return this._model.props.views$.value;
71
+ });
72
+ this._model = model;
73
+ }
74
+ /** Notify the view layer that the underlying data changed. */
75
+ invalidate() {
76
+ this.revision.value++;
77
+ }
78
+ dispose() {
79
+ this.disposables.dispose();
80
+ }
81
+ get doc() {
82
+ return this._model.store;
83
+ }
84
+ get parentProvider() {
85
+ return this._model.store.provider;
86
+ }
87
+ cellValueGet(rowId, propertyId) {
88
+ void this.revision.value;
89
+ return this.getCellValue(rowId, propertyId);
90
+ }
91
+ propertyTypeGet(propertyId) {
92
+ void this.revision.value;
93
+ return this.getPropertyType(propertyId);
94
+ }
95
+ propertyNameGet(propertyId) {
96
+ void this.revision.value;
97
+ return this.getPropertyName(propertyId);
98
+ }
99
+ propertyDataGet(propertyId) {
100
+ void this.revision.value;
101
+ return this.getPropertyData(propertyId);
102
+ }
103
+ propertyDataTypeGet(propertyId) {
104
+ void this.revision.value;
105
+ return this.getPropertyDataType(propertyId);
106
+ }
107
+ viewDataGet(viewId) {
108
+ return this.viewDataList$.value.find(data => data.id === viewId);
109
+ }
110
+ viewDataAdd(viewData) {
111
+ this._model.store.captureSync();
112
+ this._model.store.transact(() => {
113
+ this._model.props.views = [...this._model.props.views, viewData];
114
+ });
115
+ return viewData.id;
116
+ }
117
+ viewDataDelete(viewId) {
118
+ this._model.store.captureSync();
119
+ deleteView(this._model, viewId);
120
+ }
121
+ viewDataDuplicate(id) {
122
+ return duplicateView(this._model, id);
123
+ }
124
+ viewDataMoveTo(id, position) {
125
+ moveViewTo(this._model, id, position);
126
+ }
127
+ viewDataUpdate(id, updater) {
128
+ updateView(this._model, id, updater);
129
+ }
130
+ viewMetaGet(type) {
131
+ const view = databaseBlockViewMap[type];
132
+ if (!view) {
133
+ throw new BlockSuiteError(ErrorCode.DatabaseBlockError, `Unknown view type: ${type}`);
134
+ }
135
+ return view;
136
+ }
137
+ viewMetaGetById(viewId) {
138
+ const view = this.viewDataGet(viewId);
139
+ if (!view) {
140
+ return;
141
+ }
142
+ return this.viewMetaGet(view.mode);
143
+ }
144
+ }
@@ -4,6 +4,7 @@ export * from './config';
4
4
  export * from './context';
5
5
  export * from './data-source';
6
6
  export * from './database-block';
7
+ export * from './external-data-source';
7
8
  export * from './detail-panel/block-renderer';
8
9
  export * from './detail-panel/note-renderer';
9
10
  export * from './properties';
@@ -4,6 +4,7 @@ export * from './config';
4
4
  export * from './context';
5
5
  export * from './data-source';
6
6
  export * from './database-block';
7
+ export * from './external-data-source';
7
8
  export * from './detail-panel/block-renderer';
8
9
  export * from './detail-panel/note-renderer';
9
10
  export * from './properties';
@@ -38,7 +38,7 @@ import { LoadingIcon } from '../../affine-components/icons/index.js';
38
38
  import { isPeekable, Peekable } from '../../affine-components/peek/index.js';
39
39
  import { RefNodeSlotsProvider } from '../../affine-inline-reference/index.js';
40
40
  import { EMBED_CARD_HEIGHT, EMBED_CARD_WIDTH, REFERENCE_NODE, } from '../../affine-shared/consts/index.js';
41
- import { CitationProvider, DocDisplayMetaProvider, DocModeProvider, OpenDocExtensionIdentifier, ThemeProvider, } from '../../affine-shared/services/index.js';
41
+ import { CitationProvider, DocDisplayMetaProvider, DocModeProvider, OpenDocExtensionIdentifier, ThemeProvider, whenLinkedDocContentReady, } from '../../affine-shared/services/index.js';
42
42
  import { cloneReferenceInfo, cloneReferenceInfoWithoutAliases, isNewTabTrigger, isNewViewTrigger, matchModels, referenceToNode, } from '../../affine-shared/utils/index.js';
43
43
  import { Bound } from '../../global/gfx/index.js';
44
44
  import { ResetIcon } from '@blocksuite/icons/lit';
@@ -286,12 +286,11 @@ let EmbedLinkedDocBlockComponent = (() => {
286
286
  }
287
287
  }
288
288
  if (!this.isError && !linkedDoc.root) {
289
- await new Promise(resolve => {
290
- const subscription = linkedDoc.slots.rootAdded.subscribe(() => {
291
- subscription.unsubscribe();
292
- resolve();
293
- });
294
- });
289
+ // Bounded wait: if the host hasn't preloaded the referenced doc, ask the
290
+ // optional content resolver to hydrate it and wait at most `timeoutMs`.
291
+ // On timeout we stop loading and fall through to a degraded card (title
292
+ // only) instead of spinning forever.
293
+ await whenLinkedDocContentReady(this.std, linkedDoc, this.model.props.pageId);
295
294
  }
296
295
  this._loading = false;
297
296
  // If it is a link to a block or element, the content will not be rendered.
@@ -34,7 +34,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
34
34
  };
35
35
  import { RENDER_CARD_THROTTLE_MS } from '../../../affine-block-embed/index.js';
36
36
  import { LoadingIcon } from '../../../affine-components/icons/index.js';
37
- import { ThemeProvider } from '../../../affine-shared/services/index.js';
37
+ import { ThemeProvider, whenLinkedDocContentReady, } from '../../../affine-shared/services/index.js';
38
38
  import { WithDisposable } from '../../../global/lit/index.js';
39
39
  import { ResetIcon } from '@blocksuite/icons/lit';
40
40
  import { BlockSelection, isGfxBlockComponent, ShadowlessElement, } from '../../../std/index.js';
@@ -139,10 +139,10 @@ let EmbedSyncedDocCard = (() => {
139
139
  renderLinkedDocInCard(this);
140
140
  }
141
141
  else {
142
- const subscription = syncedDoc.slots.rootAdded.subscribe(() => {
143
- subscription.unsubscribe();
144
- renderLinkedDocInCard(this);
145
- });
142
+ // Bounded: ask the host to hydrate the doc, wait at most timeoutMs, then
143
+ // render regardless (degraded if still empty) rather than waiting for a
144
+ // `rootAdded` that may never fire when the doc isn't preloaded.
145
+ void whenLinkedDocContentReady(this.std, syncedDoc, this.model.props.pageId).then(() => renderLinkedDocInCard(this));
146
146
  }
147
147
  this.disposables.add(syncedDoc.workspace.slots.docListUpdated.subscribe(() => {
148
148
  renderLinkedDocInCard(this);
@@ -39,12 +39,12 @@ export declare const builtinToolbarConfig: {
39
39
  readonly label?: string;
40
40
  readonly icon?: import("lit-html").TemplateResult;
41
41
  readonly active?: ((cx: import("../../affine-shared/services/index.js").ToolbarContext) => boolean) | boolean;
42
+ readonly disabled?: ((cx: import("../../affine-shared/services/index.js").ToolbarContext) => boolean) | boolean;
42
43
  readonly placement?: ActionPlacement;
43
44
  readonly tooltip?: string | import("lit-html").TemplateResult;
44
45
  readonly score?: number;
45
46
  readonly when?: ((cx: import("../../affine-shared/services/index.js").ToolbarContext) => boolean) | boolean;
46
47
  readonly showLabel?: boolean;
47
- readonly disabled?: ((cx: import("../../affine-shared/services/index.js").ToolbarContext) => boolean) | boolean;
48
48
  readonly run?: (cx: import("../../affine-shared/services/index.js").ToolbarContext) => void;
49
49
  readonly id: "g.comment";
50
50
  }, {
@@ -118,6 +118,24 @@ export declare const builtinMiscToolbarConfig: {
118
118
  readonly icon: import("lit-html").TemplateResult<1>;
119
119
  readonly when: (ctx: ToolbarContext) => boolean;
120
120
  readonly run: (ctx: ToolbarContext) => void;
121
+ }, {
122
+ readonly id: "c.link-element";
123
+ readonly label: "Link";
124
+ readonly icon: import("lit-html").TemplateResult<1>;
125
+ readonly when: (ctx: ToolbarContext) => boolean;
126
+ readonly run: (ctx: ToolbarContext) => void;
127
+ }, {
128
+ readonly id: "c.edit-element-link";
129
+ readonly label: "Edit link";
130
+ readonly icon: import("lit-html").TemplateResult<1>;
131
+ readonly when: (ctx: ToolbarContext) => boolean;
132
+ readonly run: (ctx: ToolbarContext) => void;
133
+ }, {
134
+ readonly id: "c.remove-element-link";
135
+ readonly label: "Remove link";
136
+ readonly icon: import("lit-html").TemplateResult<1>;
137
+ readonly when: (ctx: ToolbarContext) => boolean;
138
+ readonly run: (ctx: ToolbarContext) => void;
121
139
  }];
122
140
  } | {
123
141
  placement: ActionPlacement;
@@ -69,6 +69,24 @@ export declare const moreActions: [{
69
69
  readonly icon: import("lit-html").TemplateResult<1>;
70
70
  readonly when: (ctx: ToolbarContext) => boolean;
71
71
  readonly run: (ctx: ToolbarContext) => void;
72
+ }, {
73
+ readonly id: "c.link-element";
74
+ readonly label: "Link";
75
+ readonly icon: import("lit-html").TemplateResult<1>;
76
+ readonly when: (ctx: ToolbarContext) => boolean;
77
+ readonly run: (ctx: ToolbarContext) => void;
78
+ }, {
79
+ readonly id: "c.edit-element-link";
80
+ readonly label: "Edit link";
81
+ readonly icon: import("lit-html").TemplateResult<1>;
82
+ readonly when: (ctx: ToolbarContext) => boolean;
83
+ readonly run: (ctx: ToolbarContext) => void;
84
+ }, {
85
+ readonly id: "c.remove-element-link";
86
+ readonly label: "Remove link";
87
+ readonly icon: import("lit-html").TemplateResult<1>;
88
+ readonly when: (ctx: ToolbarContext) => boolean;
89
+ readonly run: (ctx: ToolbarContext) => void;
72
90
  }];
73
91
  }, {
74
92
  readonly id: "e.delete";