@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.
- package/dist/_pkgs/affine-block-database/data-source.d.ts +28 -40
- package/dist/_pkgs/affine-block-database/data-source.js +116 -178
- package/dist/_pkgs/affine-block-database/database-block.d.ts +10 -3
- package/dist/_pkgs/affine-block-database/database-block.js +110 -70
- package/dist/_pkgs/affine-block-database/external-data-source.d.ts +69 -0
- package/dist/_pkgs/affine-block-database/external-data-source.js +144 -0
- package/dist/_pkgs/affine-block-database/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-database/index.js +1 -0
- package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-linked-doc-block.js +6 -7
- package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/components/embed-synced-doc-card.js +5 -5
- package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +1 -1
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.d.ts +18 -0
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.d.ts +18 -0
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +104 -2
- package/dist/_pkgs/affine-block-root/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-root/index.js +1 -0
- package/dist/_pkgs/affine-block-root/keyboard/keyboard-manager.js +3 -21
- package/dist/_pkgs/affine-block-root/keyboard/shortcuts.d.ts +8 -0
- package/dist/_pkgs/affine-block-root/keyboard/shortcuts.js +48 -0
- package/dist/_pkgs/affine-block-root/view.js +6 -1
- package/dist/_pkgs/affine-gfx-link/effects.js +2 -2
- package/dist/_pkgs/affine-gfx-link/toolbar/undo-dense-menu.d.ts +3 -0
- package/dist/_pkgs/affine-gfx-link/toolbar/undo-dense-menu.js +12 -0
- package/dist/_pkgs/affine-gfx-link/toolbar/undo-tool-button.d.ts +18 -0
- package/dist/_pkgs/affine-gfx-link/toolbar/undo-tool-button.js +41 -0
- package/dist/_pkgs/affine-gfx-link/undo-tool.d.ts +6 -0
- package/dist/_pkgs/affine-gfx-link/undo-tool.js +17 -0
- package/dist/_pkgs/affine-gfx-link/view.js +4 -2
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-tool-button.js +13 -2
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/senior-tool.d.ts +12 -2
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/senior-tool.js +62 -11
- package/dist/_pkgs/affine-gfx-mindmap/view.d.ts +13 -2
- package/dist/_pkgs/affine-gfx-mindmap/view.js +24 -5
- package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/services/index.js +2 -0
- package/dist/_pkgs/affine-shared/services/keymap-override-service.d.ts +15 -0
- package/dist/_pkgs/affine-shared/services/keymap-override-service.js +26 -0
- package/dist/_pkgs/affine-shared/services/linked-doc-content-service.d.ts +42 -0
- package/dist/_pkgs/affine-shared/services/linked-doc-content-service.js +47 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.d.ts +29 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.js +233 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/effects.js +2 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/spec.d.ts +1 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/spec.js +2 -0
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/view.js +2 -1
- package/dist/_pkgs/data-view/core/data-source/base.d.ts +6 -0
- package/dist/_pkgs/data-view/core/data-source/base.js +6 -0
- package/dist/_pkgs/std/extension/index.d.ts +1 -0
- package/dist/_pkgs/std/extension/index.js +1 -0
- package/dist/_pkgs/std/extension/shortcut.d.ts +69 -0
- package/dist/_pkgs/std/extension/shortcut.js +106 -0
- package/dist/_pkgs/std/gfx/model/surface/element-model.d.ts +7 -0
- package/dist/_pkgs/std/gfx/model/surface/element-model.js +24 -1
- package/dist/_pkgs/std/identifier.d.ts +7 -0
- package/dist/_pkgs/std/identifier.js +6 -0
- package/dist/_pkgs/std/inline/components/v-element.js +5 -2
- package/dist/extensions/view.js +5 -2
- package/dist/flags.d.ts +1 -1
- package/dist/flags.js +4 -1
- package/dist/shortcuts.d.ts +24 -0
- package/dist/shortcuts.js +39 -0
- package/package.json +5 -1
- package/dist/_pkgs/affine-gfx-link/link-tool.d.ts +0 -1
- package/dist/_pkgs/affine-gfx-link/link-tool.js +0 -11
- package/dist/_pkgs/affine-gfx-link/toolbar/link-dense-menu.d.ts +0 -2
- package/dist/_pkgs/affine-gfx-link/toolbar/link-dense-menu.js +0 -26
- package/dist/_pkgs/affine-gfx-link/toolbar/link-tool-button.d.ts +0 -10
- package/dist/_pkgs/affine-gfx-link/toolbar/link-tool-button.js +0 -84
|
@@ -6,10 +6,11 @@ import { ImageBlockComponent } from '../../../../affine-block-image/index.js';
|
|
|
6
6
|
import { EdgelessCRUDIdentifier, getSurfaceComponent, } from '../../../../affine-block-surface/index.js';
|
|
7
7
|
import { createGroupFromSelectedCommand } from '../../../../affine-gfx-group/index.js';
|
|
8
8
|
import { AttachmentBlockModel, BookmarkBlockModel, EmbedLinkedDocBlockSchema, EmbedLinkedDocModel, EmbedSyncedDocBlockSchema, EmbedSyncedDocModel, FrameBlockModel, ImageBlockModel, isExternalEmbedModel, NoteBlockModel, ParagraphBlockModel, } from '../../../../affine-model/index.js';
|
|
9
|
+
import { QuickSearchProvider } from '../../../../affine-shared/services/index.js';
|
|
9
10
|
import { matchModels, } from '../../../../affine-shared/utils/index.js';
|
|
10
11
|
import { Bound, getCommonBoundWithRotation } from '../../../../global/gfx/index.js';
|
|
11
|
-
import { ArrowDownBigBottomIcon, ArrowDownBigIcon, ArrowUpBigIcon, ArrowUpBigTopIcon, CopyIcon, DeleteIcon, DuplicateIcon, FrameIcon, GroupIcon, LinkedPageIcon, ResetIcon, } from '@blocksuite/icons/lit';
|
|
12
|
-
import { GfxBlockElementModel } from '../../../../std/gfx/index.js';
|
|
12
|
+
import { ArrowDownBigBottomIcon, ArrowDownBigIcon, ArrowUpBigIcon, ArrowUpBigTopIcon, CopyIcon, DeleteIcon, DuplicateIcon, FrameIcon, GroupIcon, LinkIcon, LinkedPageIcon, ResetIcon, UnlinkIcon, } from '@blocksuite/icons/lit';
|
|
13
|
+
import { GfxBlockElementModel, GfxPrimitiveElementModel, } from '../../../../std/gfx/index.js';
|
|
13
14
|
import { EdgelessClipboardController } from '../../clipboard/clipboard';
|
|
14
15
|
import { duplicate } from '../../utils/clipboard-utils';
|
|
15
16
|
import { getSortedCloneElements } from '../../utils/clone-utils';
|
|
@@ -298,6 +299,54 @@ export const moreActions = [
|
|
|
298
299
|
create().catch(console.error);
|
|
299
300
|
},
|
|
300
301
|
},
|
|
302
|
+
{
|
|
303
|
+
id: 'c.link-element',
|
|
304
|
+
// Attach a link (existing doc or external URL) to a single drawing
|
|
305
|
+
// element. A hover arrow (edgeless-element-link widget) opens it.
|
|
306
|
+
label: 'Link',
|
|
307
|
+
icon: LinkIcon(),
|
|
308
|
+
when(ctx) {
|
|
309
|
+
const el = getLinkableElement(ctx);
|
|
310
|
+
return el !== null && !hasElementLink(el) && canPickLink(ctx);
|
|
311
|
+
},
|
|
312
|
+
run(ctx) {
|
|
313
|
+
const el = getLinkableElement(ctx);
|
|
314
|
+
if (el)
|
|
315
|
+
linkElementViaQuickSearch(ctx, el);
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
id: 'c.edit-element-link',
|
|
320
|
+
label: 'Edit link',
|
|
321
|
+
icon: LinkIcon(),
|
|
322
|
+
when(ctx) {
|
|
323
|
+
const el = getLinkableElement(ctx);
|
|
324
|
+
return el !== null && hasElementLink(el) && canPickLink(ctx);
|
|
325
|
+
},
|
|
326
|
+
run(ctx) {
|
|
327
|
+
const el = getLinkableElement(ctx);
|
|
328
|
+
if (el)
|
|
329
|
+
linkElementViaQuickSearch(ctx, el);
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
id: 'c.remove-element-link',
|
|
334
|
+
label: 'Remove link',
|
|
335
|
+
icon: UnlinkIcon(),
|
|
336
|
+
when(ctx) {
|
|
337
|
+
const el = getLinkableElement(ctx);
|
|
338
|
+
return el !== null && hasElementLink(el);
|
|
339
|
+
},
|
|
340
|
+
run(ctx) {
|
|
341
|
+
const el = getLinkableElement(ctx);
|
|
342
|
+
if (!el)
|
|
343
|
+
return;
|
|
344
|
+
ctx.std.get(EdgelessCRUDIdentifier).updateElement(el.id, {
|
|
345
|
+
linkedDocId: undefined,
|
|
346
|
+
externalLink: undefined,
|
|
347
|
+
});
|
|
348
|
+
},
|
|
349
|
+
},
|
|
301
350
|
],
|
|
302
351
|
},
|
|
303
352
|
// Deleting Group
|
|
@@ -321,6 +370,59 @@ export const moreActions = [
|
|
|
321
370
|
},
|
|
322
371
|
},
|
|
323
372
|
];
|
|
373
|
+
/** The single selected drawing element a link can be attached to, or null. */
|
|
374
|
+
function getLinkableElement(ctx) {
|
|
375
|
+
const models = ctx.getSurfaceModels();
|
|
376
|
+
return models.length === 1 && models[0] instanceof GfxPrimitiveElementModel
|
|
377
|
+
? models[0]
|
|
378
|
+
: null;
|
|
379
|
+
}
|
|
380
|
+
function hasElementLink(el) {
|
|
381
|
+
return Boolean(el.linkedDocId || el.externalLink);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Picking a doc/URL needs the host-provided quick-search modal. Hide the
|
|
385
|
+
* Link / Edit link items where it is absent (they would no-op); Remove link
|
|
386
|
+
* stays available since clearing needs no picker.
|
|
387
|
+
*/
|
|
388
|
+
function canPickLink(ctx) {
|
|
389
|
+
return Boolean(ctx.std.getOptional(QuickSearchProvider));
|
|
390
|
+
}
|
|
391
|
+
/** Pick a doc or URL via the quick-search modal and store it on the element. */
|
|
392
|
+
function linkElementViaQuickSearch(ctx, el) {
|
|
393
|
+
const quickSearch = ctx.std.getOptional(QuickSearchProvider);
|
|
394
|
+
if (!quickSearch)
|
|
395
|
+
return;
|
|
396
|
+
quickSearch
|
|
397
|
+
.openQuickSearch()
|
|
398
|
+
.then(result => {
|
|
399
|
+
if (!result)
|
|
400
|
+
return;
|
|
401
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
402
|
+
if ('docId' in result) {
|
|
403
|
+
crud.updateElement(el.id, {
|
|
404
|
+
linkedDocId: result.docId,
|
|
405
|
+
externalLink: undefined,
|
|
406
|
+
});
|
|
407
|
+
ctx.track('LinkedDocCreated', {
|
|
408
|
+
control: 'link element',
|
|
409
|
+
type: 'element-ref',
|
|
410
|
+
other: 'existing doc',
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
else if ('externalUrl' in result) {
|
|
414
|
+
crud.updateElement(el.id, {
|
|
415
|
+
externalLink: result.externalUrl,
|
|
416
|
+
linkedDocId: undefined,
|
|
417
|
+
});
|
|
418
|
+
ctx.track('Link', {
|
|
419
|
+
control: 'link element',
|
|
420
|
+
type: 'external url',
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
})
|
|
424
|
+
.catch(console.error);
|
|
425
|
+
}
|
|
324
426
|
function reorderElements(ctx, models, type) {
|
|
325
427
|
if (!models.length)
|
|
326
428
|
return;
|
|
@@ -2,6 +2,7 @@ export * from './adapters';
|
|
|
2
2
|
export * from './clipboard/index.js';
|
|
3
3
|
export * from './edgeless/edgeless-root-spec.js';
|
|
4
4
|
export * from './edgeless/index.js';
|
|
5
|
+
export * from './keyboard/shortcuts.js';
|
|
5
6
|
export * from './page/page-root-block.js';
|
|
6
7
|
export * from './preview/preview-root-block.js';
|
|
7
8
|
export * from './types.js';
|
|
@@ -2,6 +2,7 @@ export * from './adapters';
|
|
|
2
2
|
export * from './clipboard/index.js';
|
|
3
3
|
export * from './edgeless/edgeless-root-spec.js';
|
|
4
4
|
export * from './edgeless/index.js';
|
|
5
|
+
export * from './keyboard/shortcuts.js';
|
|
5
6
|
export * from './page/page-root-block.js';
|
|
6
7
|
export * from './preview/preview-root-block.js';
|
|
7
8
|
export * from './types.js';
|
|
@@ -3,7 +3,7 @@ import { ParagraphBlockComponent } from '../../affine-block-paragraph/index.js';
|
|
|
3
3
|
import { NoteBlockModel, ParagraphBlockModel } from '../../affine-model/index.js';
|
|
4
4
|
import { draftSelectedModelsCommand, getSelectedModelsCommand, } from '../../affine-shared/commands/index.js';
|
|
5
5
|
import { matchModels } from '../../affine-shared/utils/index.js';
|
|
6
|
-
import { IS_MAC
|
|
6
|
+
import { IS_MAC } from '../../global/env/index.js';
|
|
7
7
|
import { BlockSelection, } from '../../std/index.js';
|
|
8
8
|
import { toDraftModel } from '../../store/index.js';
|
|
9
9
|
export class PageKeyboardManager {
|
|
@@ -55,27 +55,9 @@ export class PageKeyboardManager {
|
|
|
55
55
|
this._selection.clear(['block', 'text']);
|
|
56
56
|
});
|
|
57
57
|
};
|
|
58
|
+
// undo / redo are now rebindable shortcut descriptors (see
|
|
59
|
+
// `keyboard/shortcuts.ts`), installed via `ShortcutKeymapExtension`.
|
|
58
60
|
this.rootComponent.bindHotKey({
|
|
59
|
-
'Mod-z': ctx => {
|
|
60
|
-
ctx.get('defaultState').event.preventDefault();
|
|
61
|
-
if (this._doc.canUndo) {
|
|
62
|
-
this._doc.undo();
|
|
63
|
-
}
|
|
64
|
-
},
|
|
65
|
-
'Shift-Mod-z': ctx => {
|
|
66
|
-
ctx.get('defaultState').event.preventDefault();
|
|
67
|
-
if (this._doc.canRedo) {
|
|
68
|
-
this._doc.redo();
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
'Control-y': ctx => {
|
|
72
|
-
if (!IS_WINDOWS)
|
|
73
|
-
return;
|
|
74
|
-
ctx.get('defaultState').event.preventDefault();
|
|
75
|
-
if (this._doc.canRedo) {
|
|
76
|
-
this._doc.redo();
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
61
|
'Mod-Backspace': () => true,
|
|
80
62
|
Backspace: this._handleDelete,
|
|
81
63
|
Delete: this._handleDelete,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ShortcutDescriptor } from '../../std/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Core (always-on) keyboard shortcuts, expressed as rebindable descriptors.
|
|
4
|
+
* Migrated out of the imperative `PageKeyboardManager` bindings so the host can
|
|
5
|
+
* enumerate and override them. Installed by `ShortcutKeymapExtension` (see
|
|
6
|
+
* root `view.ts`).
|
|
7
|
+
*/
|
|
8
|
+
export declare const coreShortcuts: ShortcutDescriptor[];
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { IS_WINDOWS } from '../../global/env/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Core (always-on) keyboard shortcuts, expressed as rebindable descriptors.
|
|
4
|
+
* Migrated out of the imperative `PageKeyboardManager` bindings so the host can
|
|
5
|
+
* enumerate and override them. Installed by `ShortcutKeymapExtension` (see
|
|
6
|
+
* root `view.ts`).
|
|
7
|
+
*/
|
|
8
|
+
export const coreShortcuts = [
|
|
9
|
+
{
|
|
10
|
+
id: 'undo',
|
|
11
|
+
labelKey: 'com.affine.keyboardShortcuts.undo',
|
|
12
|
+
defaultKeys: { mac: ['Mod', 'z'], other: ['Mod', 'z'] },
|
|
13
|
+
scope: 'global',
|
|
14
|
+
owner: 'core',
|
|
15
|
+
handler: std => ctx => {
|
|
16
|
+
ctx.get('defaultState').event.preventDefault();
|
|
17
|
+
if (std.store.canUndo)
|
|
18
|
+
std.store.undo();
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'redo',
|
|
23
|
+
labelKey: 'com.affine.keyboardShortcuts.redo',
|
|
24
|
+
defaultKeys: { mac: ['Shift', 'Mod', 'z'], other: ['Shift', 'Mod', 'z'] },
|
|
25
|
+
scope: 'global',
|
|
26
|
+
owner: 'core',
|
|
27
|
+
handler: std => ctx => {
|
|
28
|
+
ctx.get('defaultState').event.preventDefault();
|
|
29
|
+
if (std.store.canRedo)
|
|
30
|
+
std.store.redo();
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
// Windows-only redo alias (Ctrl+Y); not bound on mac (empty combo).
|
|
35
|
+
id: 'redo-windows',
|
|
36
|
+
labelKey: 'com.affine.keyboardShortcuts.redo',
|
|
37
|
+
defaultKeys: { mac: [], other: ['Control', 'y'] },
|
|
38
|
+
scope: 'global',
|
|
39
|
+
owner: 'core',
|
|
40
|
+
handler: std => ctx => {
|
|
41
|
+
if (!IS_WINDOWS)
|
|
42
|
+
return;
|
|
43
|
+
ctx.get('defaultState').event.preventDefault();
|
|
44
|
+
if (std.store.canRedo)
|
|
45
|
+
std.store.redo();
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ViewExtensionProvider, } from '../affine-ext-loader/index.js';
|
|
2
2
|
import { NoteBlockSchema } from '../affine-model/index.js';
|
|
3
3
|
import { ToolbarModuleExtension, ViewportElementExtension, } from '../affine-shared/services/index.js';
|
|
4
|
-
import { BlockFlavourIdentifier, BlockViewExtension, FlavourExtension, } from '../std/index.js';
|
|
4
|
+
import { BlockFlavourIdentifier, BlockViewExtension, FlavourExtension, ShortcutExtension, ShortcutKeymapExtension, } from '../std/index.js';
|
|
5
5
|
import { literal } from 'lit/static-html.js';
|
|
6
6
|
import { PageClipboard, ReadOnlyClipboard } from './clipboard';
|
|
7
7
|
import { builtinToolbarConfig } from './configs/toolbar';
|
|
@@ -11,6 +11,7 @@ import { EdgelessLocker } from './edgeless/edgeless-root-spec';
|
|
|
11
11
|
import { AltCloneExtension } from './edgeless/interact-extensions/clone-ext';
|
|
12
12
|
import { effects } from './effects';
|
|
13
13
|
import { fallbackKeymap } from './keyboard/keymap';
|
|
14
|
+
import { coreShortcuts } from './keyboard/shortcuts';
|
|
14
15
|
export class RootViewExtension extends ViewExtensionProvider {
|
|
15
16
|
constructor() {
|
|
16
17
|
super(...arguments);
|
|
@@ -53,6 +54,10 @@ export class RootViewExtension extends ViewExtensionProvider {
|
|
|
53
54
|
context.register([
|
|
54
55
|
FlavourExtension('affine:page'),
|
|
55
56
|
fallbackKeymap,
|
|
57
|
+
// Enumerable, host-overridable shortcuts. Core undo/redo live here; the
|
|
58
|
+
// installer binds the effective keymap (see `@labre/std` shortcut.ts).
|
|
59
|
+
ShortcutExtension(coreShortcuts),
|
|
60
|
+
ShortcutKeymapExtension('global'),
|
|
56
61
|
ToolbarModuleExtension({
|
|
57
62
|
id: BlockFlavourIdentifier(NoteBlockSchema.model.flavour),
|
|
58
63
|
config: builtinToolbarConfig,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { EdgelessUndoToolButton } from './toolbar/undo-tool-button';
|
|
2
2
|
export function effects() {
|
|
3
|
-
customElements.define('edgeless-
|
|
3
|
+
customElements.define('edgeless-undo-tool-button', EdgelessUndoToolButton);
|
|
4
4
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { menu } from '../../affine-components/context-menu/index.js';
|
|
2
|
+
import { UndoIcon } from '@blocksuite/icons/lit';
|
|
3
|
+
/** Dense-mode (narrow / mobile toolbar) entry for the undo action. */
|
|
4
|
+
export const buildUndoDenseMenu = edgeless => menu.action({
|
|
5
|
+
name: 'Undo',
|
|
6
|
+
prefix: UndoIcon(),
|
|
7
|
+
select: () => {
|
|
8
|
+
const { store } = edgeless;
|
|
9
|
+
if (store.canUndo)
|
|
10
|
+
store.undo();
|
|
11
|
+
},
|
|
12
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
declare const EdgelessUndoToolButton_base: typeof LitElement & import("../../global/utils/types.js").Constructor<import("../../affine-widget-edgeless-toolbar/index.js").QuickToolMixinClass>;
|
|
3
|
+
/**
|
|
4
|
+
* Edgeless quick-tool "undo" button. It took over the slot of the former link
|
|
5
|
+
* quick tool (see `undo-tool.ts`). It is an action, not a tool mode, so `type`
|
|
6
|
+
* is empty (the button never renders as "active").
|
|
7
|
+
*
|
|
8
|
+
* ponytail: always clickable; the click is a no-op when there is nothing to
|
|
9
|
+
* undo. A reactive disabled state would need the history `canUndo$` signal,
|
|
10
|
+
* which `Store` does not expose today — not worth wiring for this button.
|
|
11
|
+
*/
|
|
12
|
+
export declare class EdgelessUndoToolButton extends EdgelessUndoToolButton_base {
|
|
13
|
+
static styles: import("lit").CSSResult;
|
|
14
|
+
type: never[];
|
|
15
|
+
private _onClick;
|
|
16
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { QuickToolMixin } from '../../affine-widget-edgeless-toolbar/index.js';
|
|
2
|
+
import { UndoIcon } from '@blocksuite/icons/lit';
|
|
3
|
+
import { css, html, LitElement } from 'lit';
|
|
4
|
+
/**
|
|
5
|
+
* Edgeless quick-tool "undo" button. It took over the slot of the former link
|
|
6
|
+
* quick tool (see `undo-tool.ts`). It is an action, not a tool mode, so `type`
|
|
7
|
+
* is empty (the button never renders as "active").
|
|
8
|
+
*
|
|
9
|
+
* ponytail: always clickable; the click is a no-op when there is nothing to
|
|
10
|
+
* undo. A reactive disabled state would need the history `canUndo$` signal,
|
|
11
|
+
* which `Store` does not expose today — not worth wiring for this button.
|
|
12
|
+
*/
|
|
13
|
+
export class EdgelessUndoToolButton extends QuickToolMixin(LitElement) {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this.type = [];
|
|
17
|
+
}
|
|
18
|
+
static { this.styles = css `
|
|
19
|
+
.undo-icon,
|
|
20
|
+
.undo-icon > svg {
|
|
21
|
+
width: 24px;
|
|
22
|
+
height: 24px;
|
|
23
|
+
}
|
|
24
|
+
`; }
|
|
25
|
+
_onClick() {
|
|
26
|
+
const { store } = this.edgeless;
|
|
27
|
+
if (store.canUndo)
|
|
28
|
+
store.undo();
|
|
29
|
+
}
|
|
30
|
+
render() {
|
|
31
|
+
return html `<edgeless-tool-icon-button
|
|
32
|
+
.iconContainerPadding="${6}"
|
|
33
|
+
.tooltip="${'Undo'}"
|
|
34
|
+
.tooltipOffset=${17}
|
|
35
|
+
class="edgeless-undo-tool-button"
|
|
36
|
+
@click=${this._onClick}
|
|
37
|
+
>
|
|
38
|
+
<span class="undo-icon">${UndoIcon()}</span>
|
|
39
|
+
</edgeless-tool-icon-button>`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Undo quick tool. It replaces the former link quick tool in the edgeless
|
|
3
|
+
* toolbar (same slot, next to `frame`). Wired to the store undo command, the
|
|
4
|
+
* same one the Ctrl/Cmd-Z shortcut uses.
|
|
5
|
+
*/
|
|
6
|
+
export declare const undoQuickTool: import("../store/index.js").ExtensionType;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { QuickToolExtension } from '../affine-widget-edgeless-toolbar/index.js';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { buildUndoDenseMenu } from './toolbar/undo-dense-menu';
|
|
4
|
+
/**
|
|
5
|
+
* Undo quick tool. It replaces the former link quick tool in the edgeless
|
|
6
|
+
* toolbar (same slot, next to `frame`). Wired to the store undo command, the
|
|
7
|
+
* same one the Ctrl/Cmd-Z shortcut uses.
|
|
8
|
+
*/
|
|
9
|
+
export const undoQuickTool = QuickToolExtension('undo', ({ block, gfx }) => {
|
|
10
|
+
return {
|
|
11
|
+
content: html `<edgeless-undo-tool-button
|
|
12
|
+
.edgeless=${block}
|
|
13
|
+
></edgeless-undo-tool-button>`,
|
|
14
|
+
menu: buildUndoDenseMenu(block, gfx),
|
|
15
|
+
enable: !block.store.readonly,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ViewExtensionProvider, } from '../affine-ext-loader/index.js';
|
|
2
2
|
import { effects } from './effects';
|
|
3
|
-
import {
|
|
3
|
+
import { undoQuickTool } from './undo-tool';
|
|
4
|
+
// This gfx package historically shipped the edgeless "link" quick tool; it now
|
|
5
|
+
// ships the "undo" quick tool, which took over that toolbar slot (see #27).
|
|
4
6
|
export class LinkViewExtension extends ViewExtensionProvider {
|
|
5
7
|
constructor() {
|
|
6
8
|
super(...arguments);
|
|
@@ -13,7 +15,7 @@ export class LinkViewExtension extends ViewExtensionProvider {
|
|
|
13
15
|
setup(context) {
|
|
14
16
|
super.setup(context);
|
|
15
17
|
if (this.isEdgeless(context.scope)) {
|
|
16
|
-
context.register(
|
|
18
|
+
context.register(undoQuickTool);
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
21
|
}
|
|
@@ -77,6 +77,9 @@ let EdgelessMindmapToolButton = (() => {
|
|
|
77
77
|
display: flex;
|
|
78
78
|
justify-content: center;
|
|
79
79
|
align-items: center;
|
|
80
|
+
/* Treat the whole button as a tap target (no double-tap zoom / tap delay
|
|
81
|
+
* on touch browsers like Firefox mobile). */
|
|
82
|
+
touch-action: manipulation;
|
|
80
83
|
}
|
|
81
84
|
.partial-clip {
|
|
82
85
|
flex-shrink: 0;
|
|
@@ -104,8 +107,10 @@ let EdgelessMindmapToolButton = (() => {
|
|
|
104
107
|
}
|
|
105
108
|
.basket-tool-item svg {
|
|
106
109
|
display: block;
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
/* Render near the icons' natural size (58x44 / 64x48) so the mindmap
|
|
111
|
+
* button reads at the same size as its neighbours (text / add-file). */
|
|
112
|
+
max-width: 64px;
|
|
113
|
+
max-height: 50px;
|
|
109
114
|
width: auto;
|
|
110
115
|
height: auto;
|
|
111
116
|
}
|
|
@@ -202,6 +207,11 @@ let EdgelessMindmapToolButton = (() => {
|
|
|
202
207
|
scopeElement: this.toolbarContainer,
|
|
203
208
|
standardWidth: 100,
|
|
204
209
|
clickToDrag: false,
|
|
210
|
+
// On touch, the drag controller calls preventDefault on touchstart, so the
|
|
211
|
+
// native click that opens the menu never fires (button is "dead" on
|
|
212
|
+
// mobile, or only its non-icon edge responds). A tap without drag fires
|
|
213
|
+
// onElementClick instead — wire it to the same toggle as the button click.
|
|
214
|
+
onElementClick: () => this._toggleMenu(),
|
|
205
215
|
onOverlayCreated: (overlay, { data }) => {
|
|
206
216
|
const tool = this.draggableTools.find(t => t.name === data.name);
|
|
207
217
|
if (!tool)
|
|
@@ -347,6 +357,7 @@ let EdgelessMindmapToolButton = (() => {
|
|
|
347
357
|
preview: tool.icon,
|
|
348
358
|
standardWidth: tool.standardWidth,
|
|
349
359
|
})}
|
|
360
|
+
@click=${(e) => e.stopPropagation()}
|
|
350
361
|
class="basket-tool-item current ${tool.name}"
|
|
351
362
|
>
|
|
352
363
|
${tool.icon}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
export declare const mindMapSeniorTool: import("../../store/index.js").ExtensionType;
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Standalone "Text" tool. Promoted out of the former "Others" submenu: a single
|
|
4
|
+
* tap inserts an editable text element at the viewport center. Reuses the
|
|
5
|
+
* submenu's `textRender` action.
|
|
6
|
+
*/
|
|
7
|
+
export declare const textSeniorTool: import("../../store/index.js").ExtensionType;
|
|
8
|
+
/**
|
|
9
|
+
* Standalone "Add file" tool. Promoted out of the former "Others" submenu: a
|
|
10
|
+
* single tap opens the file picker and inserts the image/attachment at the
|
|
11
|
+
* viewport center. Reuses the submenu's `mediaRender` action.
|
|
12
|
+
*/
|
|
13
|
+
export declare const mediaSeniorTool: import("../../store/index.js").ExtensionType;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { SeniorToolExtension } from '../../affine-widget-edgeless-toolbar/index.js';
|
|
2
|
+
import { Bound } from '../../global/gfx/index.js';
|
|
2
3
|
import { html } from 'lit';
|
|
4
|
+
import { mediaRender, textRender } from './basket-elements';
|
|
5
|
+
import { mindmapMenuMediaIcon, textIcon } from './icons';
|
|
3
6
|
export const mindMapSeniorTool = SeniorToolExtension('mindMap', ({ block, toolbarContainer }) => {
|
|
4
7
|
return {
|
|
5
8
|
name: 'Mind Map',
|
|
@@ -10,14 +13,62 @@ export const mindMapSeniorTool = SeniorToolExtension('mindMap', ({ block, toolba
|
|
|
10
13
|
></edgeless-mindmap-tool-button>`,
|
|
11
14
|
};
|
|
12
15
|
});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
16
|
+
// Reuse the (nicely drawn) icons from the former "Others" submenu, with a
|
|
17
|
+
// playful resting tilt that pops upright + scales on hover — echoing the old
|
|
18
|
+
// basket hover animation.
|
|
19
|
+
const slot = (icon, tooltip, onClick) => html `<style>
|
|
20
|
+
.promoted-tool {
|
|
21
|
+
width: 100%;
|
|
22
|
+
height: 100%;
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
}
|
|
27
|
+
.promoted-tool .promoted-tool-icon {
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
/* Scale the drawn icons up so they read at the same size as the
|
|
32
|
+
* neighbouring pen/shape icons (their natural SVG size is smaller). */
|
|
33
|
+
transform: rotate(-4deg) scale(1.4);
|
|
34
|
+
transition: transform 0.24s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
35
|
+
}
|
|
36
|
+
.promoted-tool:hover .promoted-tool-icon {
|
|
37
|
+
transform: rotate(0deg) scale(1.65);
|
|
38
|
+
}
|
|
39
|
+
.promoted-tool .promoted-tool-icon svg {
|
|
40
|
+
width: auto;
|
|
41
|
+
height: auto;
|
|
42
|
+
max-width: 52px;
|
|
43
|
+
max-height: 44px;
|
|
44
|
+
}
|
|
45
|
+
</style>
|
|
46
|
+
<div class="promoted-tool">
|
|
47
|
+
<edgeless-tool-icon-button .tooltip=${tooltip} @click=${onClick}>
|
|
48
|
+
<span class="promoted-tool-icon">${icon}</span>
|
|
49
|
+
</edgeless-tool-icon-button>
|
|
50
|
+
</div>`;
|
|
51
|
+
/**
|
|
52
|
+
* Standalone "Text" tool. Promoted out of the former "Others" submenu: a single
|
|
53
|
+
* tap inserts an editable text element at the viewport center. Reuses the
|
|
54
|
+
* submenu's `textRender` action.
|
|
55
|
+
*/
|
|
56
|
+
export const textSeniorTool = SeniorToolExtension('edgeless-text', ({ block, gfx }) => ({
|
|
57
|
+
name: 'Text',
|
|
58
|
+
content: slot(textIcon, 'Text', () => {
|
|
59
|
+
const { centerX, centerY } = gfx.viewport;
|
|
60
|
+
void textRender(new Bound(centerX - 50, centerY - 16, 100, 32), block);
|
|
61
|
+
}),
|
|
62
|
+
}));
|
|
63
|
+
/**
|
|
64
|
+
* Standalone "Add file" tool. Promoted out of the former "Others" submenu: a
|
|
65
|
+
* single tap opens the file picker and inserts the image/attachment at the
|
|
66
|
+
* viewport center. Reuses the submenu's `mediaRender` action.
|
|
67
|
+
*/
|
|
68
|
+
export const mediaSeniorTool = SeniorToolExtension('edgeless-media', ({ block, gfx }) => ({
|
|
69
|
+
name: 'Add file',
|
|
70
|
+
content: slot(mindmapMenuMediaIcon, 'Add file', () => {
|
|
71
|
+
const { centerX, centerY } = gfx.viewport;
|
|
72
|
+
void mediaRender(new Bound(centerX - 50, centerY - 50, 100, 100), block);
|
|
73
|
+
}),
|
|
74
|
+
}));
|
|
@@ -15,8 +15,19 @@ export declare class MindmapToolViewExtension extends ViewExtensionProvider {
|
|
|
15
15
|
name: string;
|
|
16
16
|
setup(context: ViewExtensionContext): void;
|
|
17
17
|
}
|
|
18
|
-
/**
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Standalone "Text" senior button (formerly inside the "Others" submenu) —
|
|
20
|
+
* flag-gated (`edgeless-text`).
|
|
21
|
+
*/
|
|
22
|
+
export declare class TextToolViewExtension extends ViewExtensionProvider {
|
|
23
|
+
name: string;
|
|
24
|
+
setup(context: ViewExtensionContext): void;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Standalone "Add file" senior button (formerly inside the "Others" submenu) —
|
|
28
|
+
* flag-gated (`edgeless-media`).
|
|
29
|
+
*/
|
|
30
|
+
export declare class MediaToolViewExtension extends ViewExtensionProvider {
|
|
20
31
|
name: string;
|
|
21
32
|
setup(context: ViewExtensionContext): void;
|
|
22
33
|
}
|
|
@@ -5,7 +5,7 @@ import { MindmapElementRendererExtension } from './element-renderer';
|
|
|
5
5
|
import { MindMapIndicatorOverlay } from './indicator-overlay';
|
|
6
6
|
import { MindMapDragExtension } from './interactivity';
|
|
7
7
|
import { mindmapToolbarExtension, shapeMindmapToolbarExtension, } from './toolbar/config';
|
|
8
|
-
import { mindMapSeniorTool,
|
|
8
|
+
import { mediaSeniorTool, mindMapSeniorTool, textSeniorTool, } from './toolbar/senior-tool';
|
|
9
9
|
import { mindmapTemplateCategory } from './templates';
|
|
10
10
|
import { MindMapInteraction, MindMapView } from './view/view';
|
|
11
11
|
/**
|
|
@@ -48,16 +48,35 @@ export class MindmapToolViewExtension extends ViewExtensionProvider {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
/**
|
|
52
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Standalone "Text" senior button (formerly inside the "Others" submenu) —
|
|
53
|
+
* flag-gated (`edgeless-text`).
|
|
54
|
+
*/
|
|
55
|
+
export class TextToolViewExtension extends ViewExtensionProvider {
|
|
56
|
+
constructor() {
|
|
57
|
+
super(...arguments);
|
|
58
|
+
this.name = 'affine-edgeless-text-tool-gfx';
|
|
59
|
+
}
|
|
60
|
+
setup(context) {
|
|
61
|
+
super.setup(context);
|
|
62
|
+
if (this.isEdgeless(context.scope)) {
|
|
63
|
+
context.register(textSeniorTool);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Standalone "Add file" senior button (formerly inside the "Others" submenu) —
|
|
69
|
+
* flag-gated (`edgeless-media`).
|
|
70
|
+
*/
|
|
71
|
+
export class MediaToolViewExtension extends ViewExtensionProvider {
|
|
53
72
|
constructor() {
|
|
54
73
|
super(...arguments);
|
|
55
|
-
this.name = 'affine-
|
|
74
|
+
this.name = 'affine-edgeless-media-tool-gfx';
|
|
56
75
|
}
|
|
57
76
|
setup(context) {
|
|
58
77
|
super.setup(context);
|
|
59
78
|
if (this.isEdgeless(context.scope)) {
|
|
60
|
-
context.register(
|
|
79
|
+
context.register(mediaSeniorTool);
|
|
61
80
|
}
|
|
62
81
|
}
|
|
63
82
|
}
|
|
@@ -13,7 +13,9 @@ export * from './feature-flag-service';
|
|
|
13
13
|
export * from './file-size-limit-service';
|
|
14
14
|
export * from './font-loader';
|
|
15
15
|
export * from './generate-url-service';
|
|
16
|
+
export * from './keymap-override-service';
|
|
16
17
|
export * from './link-preview-service';
|
|
18
|
+
export * from './linked-doc-content-service';
|
|
17
19
|
export * from './linked-doc-creation-service';
|
|
18
20
|
export * from './native-clipboard-service';
|
|
19
21
|
export * from './notification-service';
|
|
@@ -13,7 +13,9 @@ export * from './feature-flag-service';
|
|
|
13
13
|
export * from './file-size-limit-service';
|
|
14
14
|
export * from './font-loader';
|
|
15
15
|
export * from './generate-url-service';
|
|
16
|
+
export * from './keymap-override-service';
|
|
16
17
|
export * from './link-preview-service';
|
|
18
|
+
export * from './linked-doc-content-service';
|
|
17
19
|
export * from './linked-doc-creation-service';
|
|
18
20
|
export * from './native-clipboard-service';
|
|
19
21
|
export * from './notification-service';
|