@formicoidea/labre-core 0.28.0 → 0.29.1
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-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/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/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/identifier.d.ts +7 -0
- package/dist/_pkgs/std/identifier.js +6 -0
- 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
|
@@ -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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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.
|
package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/components/embed-synced-doc-card.js
CHANGED
|
@@ -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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
}, {
|
|
@@ -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';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ShortcutConflictReporter, type ShortcutOverrides } from '../../std/index.js';
|
|
2
|
+
import type { ExtensionType } from '../../store/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Host-injected shortcut rebinding table. Each entry maps a shortcut id to a
|
|
5
|
+
* combo (array of keys, e.g. `['Ctrl', 'Shift', 'Z']`) or `'disabled'`. The
|
|
6
|
+
* effective keymap is `override ?? default`; `'disabled'` removes the binding.
|
|
7
|
+
*
|
|
8
|
+
* Mirrors `TelemetryExtension` / `EditorSettingExtension`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function KeymapOverrideExtension(overrides: ShortcutOverrides): ExtensionType;
|
|
11
|
+
/**
|
|
12
|
+
* Host-injected sink for combo conflicts detected within a scope. Without it,
|
|
13
|
+
* conflicts are logged to the console.
|
|
14
|
+
*/
|
|
15
|
+
export declare function ShortcutConflictReporterExtension(reporter: ShortcutConflictReporter): ExtensionType;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ShortcutConflictReporterIdentifier, ShortcutOverrideIdentifier, } from '../../std/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Host-injected shortcut rebinding table. Each entry maps a shortcut id to a
|
|
4
|
+
* combo (array of keys, e.g. `['Ctrl', 'Shift', 'Z']`) or `'disabled'`. The
|
|
5
|
+
* effective keymap is `override ?? default`; `'disabled'` removes the binding.
|
|
6
|
+
*
|
|
7
|
+
* Mirrors `TelemetryExtension` / `EditorSettingExtension`.
|
|
8
|
+
*/
|
|
9
|
+
export function KeymapOverrideExtension(overrides) {
|
|
10
|
+
return {
|
|
11
|
+
setup: di => {
|
|
12
|
+
di.override(ShortcutOverrideIdentifier, () => overrides);
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Host-injected sink for combo conflicts detected within a scope. Without it,
|
|
18
|
+
* conflicts are logged to the console.
|
|
19
|
+
*/
|
|
20
|
+
export function ShortcutConflictReporterExtension(reporter) {
|
|
21
|
+
return {
|
|
22
|
+
setup: di => {
|
|
23
|
+
di.override(ShortcutConflictReporterIdentifier, () => reporter);
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|