@formicoidea/labre-core 1.0.0 → 1.1.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.
- package/architecture/code.c4 +13 -12
- package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +1 -1
- package/dist/_pkgs/affine-block-root/configs/toolbar.js +4 -2
- package/dist/_pkgs/affine-components/context-menu/menu-all.d.ts +1 -0
- package/dist/_pkgs/affine-components/context-menu/sub-menu.d.ts +3 -0
- package/dist/_pkgs/affine-components/context-menu/sub-menu.js +14 -2
- package/dist/_pkgs/affine-database-block/configs/slash-menu.d.ts +24 -0
- package/dist/_pkgs/affine-database-block/configs/slash-menu.js +38 -31
- package/dist/_pkgs/affine-database-block/creation-menu.d.ts +6 -0
- package/dist/_pkgs/affine-database-block/data-source.d.ts +6 -0
- package/dist/_pkgs/affine-database-block/data-source.js +16 -0
- package/dist/_pkgs/affine-database-block/database-block.d.ts +23 -0
- package/dist/_pkgs/affine-database-block/database-block.js +125 -3
- package/dist/_pkgs/affine-database-block/external-data-source.d.ts +7 -0
- package/dist/_pkgs/affine-database-block/external-data-source.js +30 -4
- package/dist/_pkgs/affine-database-block/index.d.ts +3 -0
- package/dist/_pkgs/affine-database-block/index.js +5 -0
- package/dist/_pkgs/affine-database-block/linked-view.d.ts +59 -0
- package/dist/_pkgs/affine-database-block/linked-view.js +131 -1
- package/dist/_pkgs/affine-database-block/translations.d.ts +18 -0
- package/dist/_pkgs/affine-database-block/translations.js +47 -0
- package/dist/_pkgs/affine-database-block/turn-into-source.d.ts +38 -0
- package/dist/_pkgs/affine-database-block/turn-into-source.js +187 -0
- package/dist/_pkgs/affine-database-block/view-deletion.d.ts +15 -0
- package/dist/_pkgs/affine-database-block/view-deletion.js +113 -0
- package/dist/_pkgs/affine-shared/services/data-source-picker-service.d.ts +99 -1
- package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +10 -21
- package/package.json +1 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A host decides what a deleted view takes with it (ADR 0035 amendment of
|
|
3
|
+
* 2026-10-29). Labre sends a source to its trash with the last view reading
|
|
4
|
+
* it, and must confirm that BEFORE the view goes, then restore it when the
|
|
5
|
+
* removal is undone. So the delete gesture of a view of a host database
|
|
6
|
+
* becomes a request (`DataSourcePickerService.deleteView`) whose `remove()`
|
|
7
|
+
* is the 1.0 removal, and the undo step it makes is tagged so an undo or a
|
|
8
|
+
* redo of it reaches `viewHistory`.
|
|
9
|
+
*
|
|
10
|
+
* The tag rides in the undo step's `meta`. Yjs builds a NEW step for the
|
|
11
|
+
* opposite stack while it pops one ('stack-item-added' with the undo manager
|
|
12
|
+
* as origin, fired inside the pop's transaction, before 'stack-item-popped'),
|
|
13
|
+
* so the tag is carried over to that step, and an undo, redo, undo… chain
|
|
14
|
+
* keeps reporting. Remote updates never pop a step: they report nothing.
|
|
15
|
+
*/
|
|
16
|
+
const VIEW_REMOVED = 'labre:database-view-removed';
|
|
17
|
+
// One listener pair per undo manager, whatever the number of blocks: the
|
|
18
|
+
// step outlives the block's data source (a block remounted after a mode
|
|
19
|
+
// switch, or unmounted, still has its removals undone).
|
|
20
|
+
const watches = new WeakMap();
|
|
21
|
+
function isViewRef(value) {
|
|
22
|
+
if (typeof value !== 'object' || value === null)
|
|
23
|
+
return false;
|
|
24
|
+
const ref = value;
|
|
25
|
+
return (typeof ref.databaseId === 'string' &&
|
|
26
|
+
typeof ref.viewId === 'string' &&
|
|
27
|
+
typeof ref.docId === 'string' &&
|
|
28
|
+
typeof ref.blockId === 'string');
|
|
29
|
+
}
|
|
30
|
+
function watchHistory(undoManager, picker) {
|
|
31
|
+
const known = watches.get(undoManager);
|
|
32
|
+
if (known) {
|
|
33
|
+
known.picker = picker;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const watch = { picker };
|
|
37
|
+
watches.set(undoManager, watch);
|
|
38
|
+
let replay = null;
|
|
39
|
+
undoManager.on('stack-item-added', event => {
|
|
40
|
+
if (event.origin === undoManager)
|
|
41
|
+
replay = event.stackItem;
|
|
42
|
+
});
|
|
43
|
+
undoManager.on('stack-item-popped', event => {
|
|
44
|
+
const opposite = replay;
|
|
45
|
+
replay = null;
|
|
46
|
+
const ref = event.stackItem.meta.get(VIEW_REMOVED);
|
|
47
|
+
if (!isViewRef(ref))
|
|
48
|
+
return;
|
|
49
|
+
opposite?.meta.set(VIEW_REMOVED, ref);
|
|
50
|
+
try {
|
|
51
|
+
watch.picker.viewHistory?.(ref, event.type);
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
// A host failure must not break the undo itself.
|
|
55
|
+
console.error(error);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Deletes view `viewId` of `model`, through the host's `deleteView` when it
|
|
61
|
+
* has one and the block shows one of its databases; at once otherwise.
|
|
62
|
+
* `removeNow` is the 1.0 removal (one undo step); `afterRemove` runs after a
|
|
63
|
+
* removal the host asked for (the view to show next).
|
|
64
|
+
*/
|
|
65
|
+
export function deleteViewThroughHost(options) {
|
|
66
|
+
const { model, viewId, picker, removeNow, afterRemove } = options;
|
|
67
|
+
const store = model.store;
|
|
68
|
+
const databaseId = model.props.externalSourceId;
|
|
69
|
+
const deleteView = picker?.deleteView;
|
|
70
|
+
if (!picker || !deleteView || !databaseId || store.readonly) {
|
|
71
|
+
removeNow();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const view = model.props.views.find(v => v.id === viewId);
|
|
75
|
+
if (!view)
|
|
76
|
+
return;
|
|
77
|
+
const ref = {
|
|
78
|
+
databaseId,
|
|
79
|
+
viewId,
|
|
80
|
+
docId: store.id,
|
|
81
|
+
blockId: model.id,
|
|
82
|
+
...(view.sourceId === undefined ? {} : { sourceId: view.sourceId }),
|
|
83
|
+
};
|
|
84
|
+
let used = false;
|
|
85
|
+
const request = {
|
|
86
|
+
...ref,
|
|
87
|
+
viewName: typeof view.name === 'string' ? view.name : '',
|
|
88
|
+
remove: () => {
|
|
89
|
+
if (used || store.readonly)
|
|
90
|
+
return false;
|
|
91
|
+
if (!model.props.views.some(v => v.id === viewId))
|
|
92
|
+
return false;
|
|
93
|
+
used = true;
|
|
94
|
+
const undoManager = store.history.undoManager;
|
|
95
|
+
watchHistory(undoManager, picker);
|
|
96
|
+
let added = null;
|
|
97
|
+
const grab = (event) => {
|
|
98
|
+
added = event.stackItem;
|
|
99
|
+
};
|
|
100
|
+
undoManager.on('stack-item-added', grab);
|
|
101
|
+
try {
|
|
102
|
+
removeNow();
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
undoManager.off('stack-item-added', grab);
|
|
106
|
+
}
|
|
107
|
+
added?.meta.set(VIEW_REMOVED, ref);
|
|
108
|
+
afterRemove();
|
|
109
|
+
return true;
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
deleteView.call(picker, request);
|
|
113
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ExtensionType } from '../../store/index.js';
|
|
1
|
+
import type { BlockSnapshot, DeltaInsert, ExtensionType } from '../../store/index.js';
|
|
2
2
|
import type { ReadonlySignal } from '@preact/signals-core';
|
|
3
3
|
/** One source of a host database, as the picker lists it. */
|
|
4
4
|
export interface DataSourcePickerSource {
|
|
@@ -83,6 +83,62 @@ export interface DataSourcePickerViewTemplate {
|
|
|
83
83
|
view: Readonly<Record<string, unknown>>;
|
|
84
84
|
properties: readonly DataSourcePickerProperty[];
|
|
85
85
|
}
|
|
86
|
+
/** One row of a table turned into a source (`createDatabaseFrom`). */
|
|
87
|
+
export interface DataSourcePickerRow {
|
|
88
|
+
/**
|
|
89
|
+
* The row's title as a rich-text delta: links, mentions (`reference`),
|
|
90
|
+
* bold… Keep what your title property allows.
|
|
91
|
+
*/
|
|
92
|
+
title: readonly DeltaInsert[];
|
|
93
|
+
/**
|
|
94
|
+
* Property id (of `DataSourcePickerContent.properties`) → the value as the
|
|
95
|
+
* library stores it: a number, a boolean, a date's epoch milliseconds, a
|
|
96
|
+
* select's option id (or ids), a rich text as a delta. A property left
|
|
97
|
+
* out has no value.
|
|
98
|
+
*/
|
|
99
|
+
values: Readonly<Record<string, unknown>>;
|
|
100
|
+
/** The row's nested blocks, for its page body; empty when it has none. */
|
|
101
|
+
body: readonly BlockSnapshot[];
|
|
102
|
+
}
|
|
103
|
+
/** What `createDatabaseFrom` turns into a database. */
|
|
104
|
+
export interface DataSourcePickerContent {
|
|
105
|
+
/** The database's name: the table's title, or empty for you to name it. */
|
|
106
|
+
title: string;
|
|
107
|
+
/**
|
|
108
|
+
* Its properties in order, the title first (type `title`). Their types are
|
|
109
|
+
* the library's (`title`, `rich-text`, `number`, `select`,
|
|
110
|
+
* `multi-select`, `checkbox`, `date`, `link`, `progress`…): map each to
|
|
111
|
+
* one of yours, or drop it.
|
|
112
|
+
*/
|
|
113
|
+
properties: readonly DataSourcePickerProperty[];
|
|
114
|
+
/** The rows, in order. */
|
|
115
|
+
rows: readonly DataSourcePickerRow[];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A view of a host database, where it is stored (ADR 0035 amendment of
|
|
119
|
+
* 2026-10-29, "a host decides what a deleted view takes with it").
|
|
120
|
+
*/
|
|
121
|
+
export interface DataSourcePickerViewRef {
|
|
122
|
+
/** The block's `externalSourceId`, as `list()` names the database. */
|
|
123
|
+
databaseId: string;
|
|
124
|
+
/** The view's stored `sourceId`; absent, the host's implicit source. */
|
|
125
|
+
sourceId?: string;
|
|
126
|
+
viewId: string;
|
|
127
|
+
/** The document (store id) and the `affine:database` block holding it. */
|
|
128
|
+
docId: string;
|
|
129
|
+
blockId: string;
|
|
130
|
+
}
|
|
131
|
+
/** What `deleteView` hands the host: the view, and the verb that removes it. */
|
|
132
|
+
export interface DataSourcePickerViewDeletion extends DataSourcePickerViewRef {
|
|
133
|
+
/** The view's name, as stored ('' when it has none). */
|
|
134
|
+
viewName: string;
|
|
135
|
+
/**
|
|
136
|
+
* Remove the view from its block, as one undo step, and show the next
|
|
137
|
+
* one. `false` when nothing was removed: the page turned read-only, the
|
|
138
|
+
* view is already gone, or `remove` was already called.
|
|
139
|
+
*/
|
|
140
|
+
remove(): boolean;
|
|
141
|
+
}
|
|
86
142
|
/**
|
|
87
143
|
* The data-source picker (ADR 0035 §9c, issue B12): the host lists the
|
|
88
144
|
* databases and sources a view may read, and creates them. The library draws
|
|
@@ -107,6 +163,33 @@ export interface DataSourcePickerService {
|
|
|
107
163
|
* leave the menu.
|
|
108
164
|
*/
|
|
109
165
|
createDatabase?(): Promise<DataSourcePickerDatabase | null>;
|
|
166
|
+
/**
|
|
167
|
+
* No native table beside your databases (ADR 0035 amendment of
|
|
168
|
+
* 2026-10-29). The source decides, never the row count: a database block
|
|
169
|
+
* with no source (`externalSourceId`) and no row, or whose source you no
|
|
170
|
+
* longer resolve, draws an empty view with "Connect a source" (it
|
|
171
|
+
* reopens the creation popover); one with no source but native rows shows
|
|
172
|
+
* them read-only; a connected one with no row is an ordinary table.
|
|
173
|
+
* Closing the popover writes nothing. Absent or `false`: as in 1.0.
|
|
174
|
+
*/
|
|
175
|
+
requireSource?: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Create a database holding `content` and resolve to it, with at least one
|
|
178
|
+
* source, or `null` when nothing was created (ADR 0035 amendment of
|
|
179
|
+
* 2026-10-29, "turn into a source"). One call, so the rows land together
|
|
180
|
+
* or not at all. With `requireSource`, "Create Table" on selected
|
|
181
|
+
* paragraphs and "Move into a database" on a database with native rows
|
|
182
|
+
* call it, then connect the block to the database's first source in one
|
|
183
|
+
* undo step; an undo leaves your database in place. Absent, both are
|
|
184
|
+
* hidden when a source is required.
|
|
185
|
+
*/
|
|
186
|
+
createDatabaseFrom?(content: DataSourcePickerContent): Promise<DataSourcePickerDatabase | null>;
|
|
187
|
+
/**
|
|
188
|
+
* With `createDatabaseFrom`: you keep a row's nested blocks as its page
|
|
189
|
+
* body (`DataSourcePickerRow.body`). Absent, a conversion whose rows have
|
|
190
|
+
* nested blocks is refused with a notice and writes nothing.
|
|
191
|
+
*/
|
|
192
|
+
rowBodies?: boolean;
|
|
110
193
|
/**
|
|
111
194
|
* Create a source in `databaseId`; it resolves to the new source, or
|
|
112
195
|
* `null`. Absent, the picker offers no "New source".
|
|
@@ -126,6 +209,21 @@ export interface DataSourcePickerService {
|
|
|
126
209
|
* offer it. Resolves `true` once saved. Absent, no "Save as template".
|
|
127
210
|
*/
|
|
128
211
|
saveViewTemplate?(template: DataSourcePickerViewTemplate): Promise<boolean>;
|
|
212
|
+
/**
|
|
213
|
+
* A reader asked to delete a view of one of your databases (a block with
|
|
214
|
+
* an `externalSourceId`, on a writable document): decide what goes with
|
|
215
|
+
* it, ask what you must, then call `request.remove()`; not calling it
|
|
216
|
+
* keeps the view. Called once per gesture ("Delete" in the view's tab
|
|
217
|
+
* menu or its settings), never for a deleted block, an undo, a redo or a
|
|
218
|
+
* remote change. Absent, the view is removed at once, as in 1.0.
|
|
219
|
+
*/
|
|
220
|
+
deleteView?(request: DataSourcePickerViewDeletion): void;
|
|
221
|
+
/**
|
|
222
|
+
* An undo (`'undo'`: the view is back) or a redo (`'redo'`: it is gone
|
|
223
|
+
* again) of a removal made by `request.remove()`, on this client. Never
|
|
224
|
+
* for a remote change. Nothing to confirm: it already happened.
|
|
225
|
+
*/
|
|
226
|
+
viewHistory?(view: DataSourcePickerViewRef, step: 'undo' | 'redo'): void;
|
|
129
227
|
}
|
|
130
228
|
export declare const DataSourcePickerProvider: import("../../global/di/types.js").ServiceIdentifier<DataSourcePickerService> & (<U extends DataSourcePickerService = DataSourcePickerService>(variant: import("../../global/di/types.js").ServiceVariant) => import("../../global/di/types.js").ServiceIdentifier<U>);
|
|
131
229
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { pickAndAddSiblingAttachmentBlocks } from '../affine-block-attachment/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { canCreateDatabase, createDatabaseByGesture, } from '../affine-database-block/index.js';
|
|
3
3
|
import { insertEmptyEmbedIframeCommand } from '../affine-block-embed/index.js';
|
|
4
4
|
import { insertImagesCommand } from '../affine-block-image/index.js';
|
|
5
5
|
import { insertLatexBlockCommand } from '../affine-block-latex/index.js';
|
|
@@ -500,33 +500,22 @@ const databaseToolGroup = {
|
|
|
500
500
|
{
|
|
501
501
|
name: 'Table view',
|
|
502
502
|
icon: DatabaseTableViewIcon(),
|
|
503
|
-
showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:database')
|
|
503
|
+
showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:database') &&
|
|
504
|
+
canCreateDatabase(std),
|
|
505
|
+
// The slash entries' gesture (ADR 0035 amendment of 2026-10-29): with
|
|
506
|
+
// a host picker, the popover over a rowless database, never 3 native
|
|
507
|
+
// rows the host would not see.
|
|
504
508
|
action: ({ std }) => {
|
|
505
|
-
std.
|
|
506
|
-
.chain()
|
|
507
|
-
.pipe(getSelectedModelsCommand)
|
|
508
|
-
.pipe(insertDatabaseBlockCommand, {
|
|
509
|
-
viewType: viewPresets.tableViewMeta.type,
|
|
510
|
-
place: 'after',
|
|
511
|
-
removeEmptyLine: true,
|
|
512
|
-
})
|
|
513
|
-
.run();
|
|
509
|
+
createDatabaseByGesture(std, viewPresets.tableViewMeta.type);
|
|
514
510
|
},
|
|
515
511
|
},
|
|
516
512
|
{
|
|
517
513
|
name: 'Kanban view',
|
|
518
514
|
icon: DatabaseKanbanViewIcon(),
|
|
519
|
-
showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:database')
|
|
515
|
+
showWhen: ({ std }) => std.store.schema.flavourSchemaMap.has('affine:database') &&
|
|
516
|
+
canCreateDatabase(std),
|
|
520
517
|
action: ({ std }) => {
|
|
521
|
-
std.
|
|
522
|
-
.chain()
|
|
523
|
-
.pipe(getSelectedModelsCommand)
|
|
524
|
-
.pipe(insertDatabaseBlockCommand, {
|
|
525
|
-
viewType: viewPresets.kanbanViewMeta.type,
|
|
526
|
-
place: 'after',
|
|
527
|
-
removeEmptyLine: true,
|
|
528
|
-
})
|
|
529
|
-
.run();
|
|
518
|
+
createDatabaseByGesture(std, viewPresets.kanbanViewMeta.type);
|
|
530
519
|
},
|
|
531
520
|
},
|
|
532
521
|
],
|