@formicoidea/labre-core 0.42.0 → 0.43.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-data-view/configs/tooltips.js +1 -1
- package/dist/_pkgs/affine-block-database/configs/tooltips.js +1 -1
- package/dist/_pkgs/affine-block-edgeless-text/edgeless-text-block.js +4 -2
- package/dist/_pkgs/affine-block-embed/common/iframe-sandbox.d.ts +39 -0
- package/dist/_pkgs/affine-block-embed/common/iframe-sandbox.js +39 -0
- package/dist/_pkgs/affine-block-embed/embed-figma-block/embed-figma-block.js +2 -0
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-error-card.d.ts +14 -1
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-error-card.js +21 -2
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/consts.d.ts +0 -2
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/consts.js +2 -5
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-iframe-block.d.ts +1 -1
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-iframe-block.js +14 -6
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/types.d.ts +16 -0
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/types.js +17 -1
- package/dist/_pkgs/affine-block-embed/embed-loom-block/embed-loom-block.js +2 -0
- package/dist/_pkgs/affine-block-embed/embed-youtube-block/embed-youtube-block.js +2 -0
- package/dist/_pkgs/affine-block-embed/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-embed/index.js +1 -0
- package/dist/_pkgs/affine-block-embed/translations.d.ts +8 -0
- package/dist/_pkgs/affine-block-embed/translations.js +28 -0
- package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-linked-doc-block.js +4 -2
- package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-synced-doc-block.js +2 -2
- package/dist/_pkgs/affine-block-frame/frame-manager.js +5 -2
- package/dist/_pkgs/affine-block-list/utils/get-list-icon.js +3 -1
- package/dist/_pkgs/affine-block-note/configs/tooltips.js +1 -1
- package/dist/_pkgs/affine-block-root/adapters/html.js +2 -2
- package/dist/_pkgs/affine-block-root/commands/copy-link-commands.d.ts +2 -0
- package/dist/_pkgs/affine-block-root/commands/copy-link-commands.js +155 -0
- 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/view.js +2 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +13 -1
- package/dist/_pkgs/affine-block-surface/extensions/legend.d.ts +8 -0
- package/dist/_pkgs/affine-block-surface/extensions/legend.js +59 -3
- package/dist/_pkgs/affine-block-surface/extensions/morph-toolbar.js +4 -0
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch-translations.d.ts +3 -0
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch-translations.js +23 -0
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.d.ts +1 -1
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.js +12 -4
- package/dist/_pkgs/affine-block-surface/framework-background/background-view.js +1 -1
- package/dist/_pkgs/affine-block-surface/framework-background/fit.d.ts +36 -0
- package/dist/_pkgs/affine-block-surface/framework-background/fit.js +30 -0
- package/dist/_pkgs/affine-block-surface/framework-background/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-surface/framework-background/index.js +1 -0
- package/dist/_pkgs/affine-block-surface/renderer/element-highlight-overlay.js +2 -7
- package/dist/_pkgs/affine-block-surface-ref/configs/tooltips.js +2 -2
- package/dist/_pkgs/affine-components/color-picker/palette-carousel.js +5 -0
- package/dist/_pkgs/affine-components/color-picker/styles.js +1 -1
- package/dist/_pkgs/affine-components/context-menu/button.js +3 -1
- package/dist/_pkgs/affine-components/icons/ai.js +3 -3
- package/dist/_pkgs/affine-components/icons/list.js +7 -1
- package/dist/_pkgs/affine-components/size-dropdown-menu/dropdown-menu.js +6 -1
- package/dist/_pkgs/affine-components/toolbar/index.d.ts +1 -1
- package/dist/_pkgs/affine-components/toolbar/index.js +1 -1
- package/dist/_pkgs/affine-components/toolbar/menu-button.d.ts +7 -0
- package/dist/_pkgs/affine-components/toolbar/menu-button.js +30 -0
- package/dist/_pkgs/affine-components/toolbar/picker-menu.d.ts +16 -1
- package/dist/_pkgs/affine-components/toolbar/picker-menu.js +2 -2
- package/dist/_pkgs/affine-components/toolbar/toolbar.d.ts +1 -0
- package/dist/_pkgs/affine-components/toolbar/toolbar.js +67 -0
- package/dist/_pkgs/affine-fragment-frame-panel/card/frame-card-title-editor.js +1 -1
- package/dist/_pkgs/affine-fragment-frame-panel/card/frame-card.js +1 -1
- package/dist/_pkgs/affine-gfx-brush/toolbar/components/eraser/icons.js +1 -1
- package/dist/_pkgs/affine-gfx-connector/direction/direction-reveal.d.ts +11 -2
- package/dist/_pkgs/affine-gfx-connector/direction/direction-reveal.js +11 -2
- package/dist/_pkgs/affine-gfx-connector/text/edgeless-connector-label-editor.js +3 -2
- package/dist/_pkgs/affine-gfx-connector/toolbar/config.js +23 -7
- package/dist/_pkgs/affine-gfx-connector/translations.d.ts +3 -0
- package/dist/_pkgs/affine-gfx-connector/translations.js +23 -0
- package/dist/_pkgs/affine-gfx-mindmap/indicator-overlay/index.js +6 -2
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/config.js +4 -2
- package/dist/_pkgs/affine-gfx-mindmap/translations.d.ts +6 -0
- package/dist/_pkgs/affine-gfx-mindmap/translations.js +10 -0
- package/dist/_pkgs/affine-gfx-shape/toolbar/config.js +4 -2
- package/dist/_pkgs/affine-gfx-shape/translations.d.ts +7 -0
- package/dist/_pkgs/affine-gfx-shape/translations.js +11 -0
- package/dist/_pkgs/affine-gfx-template/builtin/other.js +13 -7
- package/dist/_pkgs/affine-gfx-template/toolbar/template-loading.js +1 -1
- package/dist/_pkgs/affine-gfx-template/translations.d.ts +5 -0
- package/dist/_pkgs/affine-gfx-template/translations.js +38 -0
- package/dist/_pkgs/affine-gfx-text/edgeless-text-editor.js +3 -2
- package/dist/_pkgs/affine-gfx-text/toolbar/actions.js +3 -1
- package/dist/_pkgs/affine-gfx-text/translations.d.ts +8 -0
- package/dist/_pkgs/affine-gfx-text/translations.js +12 -0
- package/dist/_pkgs/affine-inline-latex/latex-node/latex-editor-menu.js +2 -1
- package/dist/_pkgs/affine-model/consts/text.d.ts +15 -2
- package/dist/_pkgs/affine-model/consts/text.js +25 -1
- package/dist/_pkgs/affine-shared/services/font-loader/config.d.ts +13 -2
- package/dist/_pkgs/affine-shared/services/font-loader/config.js +43 -54
- package/dist/_pkgs/affine-shared/services/theme-service.d.ts +19 -0
- package/dist/_pkgs/affine-shared/services/theme-service.js +24 -0
- package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +20 -0
- package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +81 -0
- package/dist/_pkgs/affine-widget-edgeless-dragging-area/edgeless-dragging-area-rect.js +2 -2
- package/dist/_pkgs/affine-widget-edgeless-selected-rect/utils.js +5 -1
- package/dist/_pkgs/affine-widget-edgeless-toolbar/config/types.d.ts +15 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/config/utils.d.ts +1 -1
- package/dist/_pkgs/affine-widget-edgeless-toolbar/config/utils.js +7 -2
- package/dist/_pkgs/affine-widget-edgeless-toolbar/panel/font-family-panel.d.ts +18 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/panel/font-family-panel.js +52 -6
- package/dist/_pkgs/affine-widget-edgeless-toolbar/translations.d.ts +6 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/translations.js +10 -0
- package/dist/_pkgs/affine-widget-frame-title/edgeless-frame-title-editor.js +2 -1
- package/dist/_pkgs/affine-widget-slash-menu/translations.d.ts +16 -0
- package/dist/_pkgs/affine-widget-slash-menu/translations.js +23 -1
- package/dist/_pkgs/affine-widget-toolbar/utils.js +57 -14
- package/dist/_pkgs/data-view/core/detail/field.js +2 -1
- package/dist/_pkgs/data-view/core/filter/literal/define.js +3 -1
- package/dist/_pkgs/data-view/core/group-by/renderer/boolean-group.js +1 -1
- package/dist/_pkgs/data-view/property-presets/checkbox/cell-renderer.js +8 -2
- package/dist/_pkgs/data-view/view-presets/kanban/pc/cell.js +1 -1
- package/dist/_pkgs/data-view/view-presets/kanban/pc/controller/drag.js +2 -1
- package/dist/_pkgs/data-view/view-presets/table/pc/controller/drag.js +2 -1
- package/dist/_pkgs/data-view/view-presets/table/pc/controller/selection.js +1 -1
- package/dist/_pkgs/data-view/view-presets/table/pc/header/vertical-indicator.js +2 -1
- package/dist/_pkgs/data-view/view-presets/table/pc/row/row-select-checkbox.js +3 -1
- package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/drag.js +2 -1
- package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/selection.js +1 -1
- package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/vertical-indicator.js +2 -1
- package/dist/_pkgs/data-view/view-presets/table/pc-virtual/row/row-header.js +3 -1
- package/dist/_pkgs/std/gfx/model/surface/role.d.ts +10 -5
- package/dist/commands.js +2 -1
- package/dist/translations.js +5 -0
- package/package.json +5 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `doc.copyLink` — put the current document's URL on the clipboard, anchored on
|
|
3
|
+
* whatever is selected.
|
|
4
|
+
*
|
|
5
|
+
* The library does not know what a document's address looks like: routing is
|
|
6
|
+
* the host's, and the seam for it — `GenerateDocUrlProvider` — has shipped
|
|
7
|
+
* since AFFiNE with **no consumer at all** on either side. This command is the
|
|
8
|
+
* consumer, and the counterpart of the paste path that already turns a host URL
|
|
9
|
+
* back into an inline reference (`ParseDocUrlProvider`, read by
|
|
10
|
+
* `adapters/middlewares/paste.ts`). Copy here, paste there, and the round trip
|
|
11
|
+
* closes without the library ever spelling a URL.
|
|
12
|
+
*
|
|
13
|
+
* It lives in the registry rather than in the host chrome because ADR 0008
|
|
14
|
+
* makes a command the single source for its keystroke, its palette entry and
|
|
15
|
+
* its Settings › Shortcuts row. A host-side button would be reachable from none
|
|
16
|
+
* of the three, and would not fire while the focus is inside the editor.
|
|
17
|
+
*
|
|
18
|
+
* Default chord `Mod-Alt-l` (product decision, PR #401 — the gesture users
|
|
19
|
+
* already know from Notion). NOT `Mod-l`: `Ctrl+L` / `Cmd+L` is the browser's
|
|
20
|
+
* address bar and never reaches the page, so a default on it would silently do
|
|
21
|
+
* nothing on the web. `scope: 'global'` so it fires in page mode and on the
|
|
22
|
+
* canvas alike.
|
|
23
|
+
*
|
|
24
|
+
* **`navigator.clipboard` can be absent, not merely refusing.** The Clipboard
|
|
25
|
+
* API is a secure-context feature: over plain `http://` on anything but
|
|
26
|
+
* `localhost`, and in a cross-origin iframe with no `clipboard-write`
|
|
27
|
+
* permission, the `clipboard` property is `undefined` altogether — so reaching
|
|
28
|
+
* for `writeText` throws a synchronous `TypeError` out of `run` rather than
|
|
29
|
+
* returning a promise to reject. That is checked before anything is announced:
|
|
30
|
+
* no copy, no toast, no telemetry, one `console.error` saying why. Announcing a
|
|
31
|
+
* copy that never happened is the one failure mode worse than doing nothing.
|
|
32
|
+
*
|
|
33
|
+
* A clipboard that EXISTS and then rejects (permission denied at the moment of
|
|
34
|
+
* writing) is a different case and is left as it is: the promise is caught, and
|
|
35
|
+
* the toast has already been shown — the same optimistic behaviour the inline
|
|
36
|
+
* link toolbar has always had, kept deliberately so the two copy-a-link
|
|
37
|
+
* gestures do not answer differently.
|
|
38
|
+
*/
|
|
39
|
+
import { DocModeProvider, GenerateDocUrlProvider, NotificationProvider, TOAST_LINK_COPIED, TelemetryProvider, translateKey, } from '../../affine-shared/services/index.js';
|
|
40
|
+
import { BlockSelection, SurfaceSelection, } from '../../std/index.js';
|
|
41
|
+
/**
|
|
42
|
+
* Where the link points INSIDE the document, read off the live selection.
|
|
43
|
+
*
|
|
44
|
+
* `mode` is not decoration: the reader of these params
|
|
45
|
+
* (`extractSearchParams`, `adapters/middlewares/paste.ts`) only looks at
|
|
46
|
+
* `blockIds` / `elementIds` once it has found a valid `mode` in the query
|
|
47
|
+
* string, so an anchor emitted without one is dropped on the way back in. When
|
|
48
|
+
* no `DocModeProvider` is registered the anchor is therefore left out entirely
|
|
49
|
+
* rather than written in a form nothing can read — the link still opens the
|
|
50
|
+
* document, which is the honest degradation.
|
|
51
|
+
*
|
|
52
|
+
* Canvas before prose: a surface selection and a block selection can coexist in
|
|
53
|
+
* the store (selecting a note on the canvas leaves the page's own selection
|
|
54
|
+
* behind), and on the canvas the element IS what the user pointed at.
|
|
55
|
+
*
|
|
56
|
+
* A caret is deliberately NOT an anchor. `TextSelection` exists the whole time
|
|
57
|
+
* somebody is typing, so reading it would mean every copy silently points at a
|
|
58
|
+
* paragraph instead of the document — the opposite of what an unsolicited
|
|
59
|
+
* keystroke should do.
|
|
60
|
+
*/
|
|
61
|
+
function anchorOf(std) {
|
|
62
|
+
const mode = std.getOptional(DocModeProvider)?.getEditorMode();
|
|
63
|
+
if (!mode)
|
|
64
|
+
return undefined;
|
|
65
|
+
const elementIds = std.selection
|
|
66
|
+
.filter(SurfaceSelection)
|
|
67
|
+
.flatMap(selection => selection.elements);
|
|
68
|
+
if (elementIds.length)
|
|
69
|
+
return { mode, elementIds };
|
|
70
|
+
const blockIds = std.selection
|
|
71
|
+
.filter(BlockSelection)
|
|
72
|
+
.map(selection => selection.blockId);
|
|
73
|
+
if (blockIds.length)
|
|
74
|
+
return { mode, blockIds };
|
|
75
|
+
return { mode };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The seam, or nothing. Read through `getOptional` like every other host seam,
|
|
79
|
+
* and reported as UNAVAILABLE rather than as a silent no-op: a palette entry
|
|
80
|
+
* that copies nothing and a shortcut that does nothing are both worse than an
|
|
81
|
+
* entry that is not there (`docs/integrate/04-host-seams.md`, step 2 of "How to
|
|
82
|
+
* add a seam").
|
|
83
|
+
*/
|
|
84
|
+
const canCopyDocLink = (std) => !!std.getOptional(GenerateDocUrlProvider);
|
|
85
|
+
const copyDocLink = {
|
|
86
|
+
id: 'doc.copyLink',
|
|
87
|
+
owner: 'core',
|
|
88
|
+
kind: 'action',
|
|
89
|
+
// `com.labre.command.*` like the other keyless-by-intent core commands, not
|
|
90
|
+
// `com.labre.keyboardShortcuts.*`: this one is a capability that happens to
|
|
91
|
+
// carry a default chord, and the shortcut namespace would file it as a
|
|
92
|
+
// keymap row for a translator.
|
|
93
|
+
labelKey: 'com.labre.command.doc.copy-link',
|
|
94
|
+
labelFallback: 'Copy document link',
|
|
95
|
+
descriptionKey: 'com.labre.command.doc.copy-link.description',
|
|
96
|
+
descriptionFallback: 'Copy a link to this document, anchored on the selection, so it can be ' +
|
|
97
|
+
'pasted into a text as a reference.',
|
|
98
|
+
keywords: ['link', 'url', 'share', 'reference'],
|
|
99
|
+
surfaces: ['palette', 'agent'],
|
|
100
|
+
scope: 'global',
|
|
101
|
+
defaultKeys: { mac: ['Mod-Alt-l'], other: ['Mod-Alt-l'] },
|
|
102
|
+
// Copying an address is legitimate in a read-only document, so nothing here
|
|
103
|
+
// is gated on `editable`; the only precondition is the seam, which is not
|
|
104
|
+
// expressible in the serializable union and therefore rides on `when`.
|
|
105
|
+
availability: 'always',
|
|
106
|
+
when: canCopyDocLink,
|
|
107
|
+
run: (std, invocation) => {
|
|
108
|
+
// The load-bearing half of the gate: `runCommand` consults neither
|
|
109
|
+
// `availability` nor `when`, so the palette and the agent reach `run`
|
|
110
|
+
// directly — the same arbitration `tag.set` records.
|
|
111
|
+
const generator = std.getOptional(GenerateDocUrlProvider);
|
|
112
|
+
if (!generator)
|
|
113
|
+
return;
|
|
114
|
+
const params = anchorOf(std);
|
|
115
|
+
const url = generator.generateDocUrl(std.store.id, params);
|
|
116
|
+
// A host may decline — an unsaved document, one it does not route. Nothing
|
|
117
|
+
// is copied and nothing is announced.
|
|
118
|
+
if (!url)
|
|
119
|
+
return;
|
|
120
|
+
// Not a promise rejection: outside a secure context the property itself is
|
|
121
|
+
// `undefined`, so this would be a synchronous `TypeError` thrown out of
|
|
122
|
+
// `run` — and `runCommand` only catches what a returned promise rejects
|
|
123
|
+
// with, so it would surface as an unhandled error taking the keystroke
|
|
124
|
+
// down with it. Nothing announced, one line saying why.
|
|
125
|
+
if (!navigator.clipboard) {
|
|
126
|
+
console.error('doc.copyLink: no clipboard API — a secure context (https, or ' +
|
|
127
|
+
'localhost) is required, and an iframe needs `clipboard-write`.');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
navigator.clipboard.writeText(url).catch(console.error);
|
|
131
|
+
std
|
|
132
|
+
.getOptional(NotificationProvider)
|
|
133
|
+
?.toast(translateKey(std, ...TOAST_LINK_COPIED));
|
|
134
|
+
// Self-emitted rather than declared through `CommandDescriptor.telemetry`:
|
|
135
|
+
// that field routes the bottleneck's reporter onto the three framework
|
|
136
|
+
// CREATION events and requires a `FrameworkId`, and this command creates
|
|
137
|
+
// nothing and belongs to no framework. `CopiedLink` is the event the two
|
|
138
|
+
// other copy-a-link sites already emit (the inline link toolbar, the
|
|
139
|
+
// iframe embed toolbar); reusing it keeps "links copied" one series.
|
|
140
|
+
// Ids never cross: the document id and the anchored ids stay here.
|
|
141
|
+
std.getOptional(TelemetryProvider)?.track('CopiedLink', {
|
|
142
|
+
page: params?.mode === 'edgeless' ? 'whiteboard editor' : 'doc editor',
|
|
143
|
+
segment: 'editor',
|
|
144
|
+
module: invocation.surface,
|
|
145
|
+
control: invocation.source,
|
|
146
|
+
type: params?.elementIds?.length
|
|
147
|
+
? 'element'
|
|
148
|
+
: params?.blockIds?.length
|
|
149
|
+
? 'block'
|
|
150
|
+
: 'document',
|
|
151
|
+
result: 'success',
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
export const copyLinkCommands = [copyDocLink];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './adapters/index.js';
|
|
2
2
|
export * from './clipboard/index.js';
|
|
3
|
+
export * from './commands/copy-link-commands.js';
|
|
3
4
|
export * from './commands/pivot-commands.js';
|
|
4
5
|
export * from './commands/reading-commands.js';
|
|
5
6
|
export * from './commands/tag-commands.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './adapters/index.js';
|
|
2
2
|
export * from './clipboard/index.js';
|
|
3
|
+
export * from './commands/copy-link-commands.js';
|
|
3
4
|
export * from './commands/pivot-commands.js';
|
|
4
5
|
export * from './commands/reading-commands.js';
|
|
5
6
|
export * from './commands/tag-commands.js';
|
|
@@ -4,6 +4,7 @@ import { ToolbarModuleExtension, ViewportElementExtension, } from '../affine-sha
|
|
|
4
4
|
import { BlockFlavourIdentifier, BlockViewExtension, FlavourExtension, CommandExtension, ShortcutKeymapExtension, } from '../std/index.js';
|
|
5
5
|
import { literal } from 'lit/static-html.js';
|
|
6
6
|
import { PageClipboard, ReadOnlyClipboard } from './clipboard/index.js';
|
|
7
|
+
import { copyLinkCommands } from './commands/copy-link-commands.js';
|
|
7
8
|
import { pivotCommands } from './commands/pivot-commands.js';
|
|
8
9
|
import { readingCommands } from './commands/reading-commands.js';
|
|
9
10
|
import { tagCommands } from './commands/tag-commands.js';
|
|
@@ -70,6 +71,7 @@ export class RootViewExtension extends ViewExtensionProvider {
|
|
|
70
71
|
...pivotCommands,
|
|
71
72
|
...tagCommands,
|
|
72
73
|
...readingCommands,
|
|
74
|
+
...copyLinkCommands,
|
|
73
75
|
]),
|
|
74
76
|
ShortcutKeymapExtension('global'),
|
|
75
77
|
ToolbarModuleExtension({
|
|
@@ -276,6 +276,15 @@ const IMPORT_DONE_KEY = 'com.labre.interchange.import.done';
|
|
|
276
276
|
const IMPORT_DONE_FALLBACK = 'file imported';
|
|
277
277
|
const IMPORT_FAILED_KEY = 'com.labre.interchange.import.failed';
|
|
278
278
|
const IMPORT_FAILED_FALLBACK = 'This file could not be imported';
|
|
279
|
+
/**
|
|
280
|
+
* What an imported sheet is called when neither the file nor the source
|
|
281
|
+
* document names it — resolved HERE, at the creation site, because that is
|
|
282
|
+
* where `std` is (ADR 0023 §3: a seed is translated by whoever writes it, and
|
|
283
|
+
* a pure reader never can). The readers keep the same English text as their
|
|
284
|
+
* own last-resort default, so a headless call still produces a named sheet.
|
|
285
|
+
*/
|
|
286
|
+
const IMPORT_DEFAULT_NAME_KEY = 'com.labre.interchange.import.default-name';
|
|
287
|
+
const IMPORT_DEFAULT_NAME_FALLBACK = 'Imported diagram';
|
|
279
288
|
/**
|
|
280
289
|
* The document went read-only between the moment the file was accepted and the
|
|
281
290
|
* moment its board was about to be written — see the second guard in
|
|
@@ -531,7 +540,10 @@ export async function importInterchangeFile(std, capability, file, options = {})
|
|
|
531
540
|
// inflated" and "this is not a BPMN document" are the same event to the
|
|
532
541
|
// person who picked the file, and they take the same notification.
|
|
533
542
|
const source = options.decode ? await options.decode(text, file) : text;
|
|
534
|
-
result = capability.run(source, {
|
|
543
|
+
result = capability.run(source, {
|
|
544
|
+
name: file.name.trim() ||
|
|
545
|
+
translateKey(std, IMPORT_DEFAULT_NAME_KEY, IMPORT_DEFAULT_NAME_FALLBACK),
|
|
546
|
+
});
|
|
535
547
|
}
|
|
536
548
|
catch (error) {
|
|
537
549
|
// `InterchangeImportError` checked FIRST: a reader that throws it declared
|
|
@@ -128,6 +128,14 @@ export declare function legendFromCommands(std: BlockStdScope, owner: CommandOwn
|
|
|
128
128
|
* Build the legend of what is drawn inside `board` and drop it bottom-left of
|
|
129
129
|
* it, grouped and selected. THE gesture the shared toolbar button runs.
|
|
130
130
|
*
|
|
131
|
+
* REPLACES the board's own legend if it already has one, rather than stacking a
|
|
132
|
+
* second box on the first (issue #391): the placement is derived from the board
|
|
133
|
+
* alone, so every press landed at the same pixel and the copies were invisible
|
|
134
|
+
* — one box to the eye, N groups and N × ~15 elements in the document. The
|
|
135
|
+
* second press is therefore a REFRESH, which is what a user who has just added
|
|
136
|
+
* three artefacts is asking for; the cost is that hand retouching of the box is
|
|
137
|
+
* lost, and one undo brings it back (see {@link legendsOn} and ADR 0026).
|
|
138
|
+
*
|
|
131
139
|
* Returns the group id, or `undefined` when there is no surface to draw on.
|
|
132
140
|
*/
|
|
133
141
|
export declare function createBoardLegend(std: BlockStdScope, board: {
|
|
@@ -276,13 +276,42 @@ const INSET_BOTTOM = 56;
|
|
|
276
276
|
* It is also why a legend never lists itself: nothing it draws carries a role.
|
|
277
277
|
*/
|
|
278
278
|
export function rolesInBound(gfx, bound) {
|
|
279
|
+
return rolesOf(gfx.getElementsByBound(bound, { type: 'canvas' }));
|
|
280
|
+
}
|
|
281
|
+
/** {@link rolesInBound} over a perimeter already scanned. */
|
|
282
|
+
function rolesOf(elements) {
|
|
279
283
|
const present = new Set();
|
|
280
|
-
for (const el of
|
|
284
|
+
for (const el of elements) {
|
|
281
285
|
if (el.role !== undefined)
|
|
282
286
|
present.add(el.role);
|
|
283
287
|
}
|
|
284
288
|
return present;
|
|
285
289
|
}
|
|
290
|
+
/**
|
|
291
|
+
* The legends ALREADY drawn on this board — what turns the button's second
|
|
292
|
+
* press into "regenerate" rather than a second stacked box (issue #391).
|
|
293
|
+
*
|
|
294
|
+
* The scan already sees them: a generated legend's wrapper carries
|
|
295
|
+
* {@link LEGEND_ROLE}, so recognising one costs a filter over the elements the
|
|
296
|
+
* perimeter pass returned, and nothing in the document format changes.
|
|
297
|
+
*
|
|
298
|
+
* CONTAINMENT rather than intersection, which is what `getElementsByBound`
|
|
299
|
+
* answers with: two boards drawn edge to edge each overlap the other's corner,
|
|
300
|
+
* and the press on one would eat the other's box. A legend is inset well inside
|
|
301
|
+
* the board it documents, so asking for the whole box costs nothing here and
|
|
302
|
+
* keeps the neighbour's.
|
|
303
|
+
*
|
|
304
|
+
* Two boxes it deliberately does not find: one the author dragged off the board
|
|
305
|
+
* (it is no longer this board's legend, and a fresh one is drawn), and one
|
|
306
|
+
* generated before {@link LEGEND_ROLE} shipped in 0.42 — that box carries no
|
|
307
|
+
* role, so the old stacking survives on those documents. Nothing is backfilled,
|
|
308
|
+
* as ever (ADR 0026).
|
|
309
|
+
*/
|
|
310
|
+
function legendsOn(elements, bound) {
|
|
311
|
+
return elements.filter(el => el.type === 'group' &&
|
|
312
|
+
el.role === LEGEND_ROLE &&
|
|
313
|
+
bound.contains(el.elementBound));
|
|
314
|
+
}
|
|
286
315
|
/** Every registered vocabulary, merged — a role id is namespaced, so it is flat. */
|
|
287
316
|
function vocabularyOf(std) {
|
|
288
317
|
const merged = {};
|
|
@@ -410,15 +439,32 @@ function boardLegendBox(std, owner) {
|
|
|
410
439
|
* Build the legend of what is drawn inside `board` and drop it bottom-left of
|
|
411
440
|
* it, grouped and selected. THE gesture the shared toolbar button runs.
|
|
412
441
|
*
|
|
442
|
+
* REPLACES the board's own legend if it already has one, rather than stacking a
|
|
443
|
+
* second box on the first (issue #391): the placement is derived from the board
|
|
444
|
+
* alone, so every press landed at the same pixel and the copies were invisible
|
|
445
|
+
* — one box to the eye, N groups and N × ~15 elements in the document. The
|
|
446
|
+
* second press is therefore a REFRESH, which is what a user who has just added
|
|
447
|
+
* three artefacts is asking for; the cost is that hand retouching of the box is
|
|
448
|
+
* lost, and one undo brings it back (see {@link legendsOn} and ADR 0026).
|
|
449
|
+
*
|
|
413
450
|
* Returns the group id, or `undefined` when there is no surface to draw on.
|
|
414
451
|
*/
|
|
415
452
|
export function createBoardLegend(std, board, owner) {
|
|
416
453
|
const gfx = std.get(GfxControllerIdentifier);
|
|
417
454
|
const surface = gfx.surface;
|
|
418
|
-
|
|
455
|
+
// The button already refuses on a readonly document; said again here because
|
|
456
|
+
// this gesture now DELETES before it draws, and a refused delete followed by
|
|
457
|
+
// a throwing draw is the one order that could lose a box.
|
|
458
|
+
if (!surface || std.store.readonly)
|
|
419
459
|
return undefined;
|
|
420
460
|
const bound = Bound.deserialize(board.xywh);
|
|
421
|
-
const
|
|
461
|
+
const inside = gfx.getElementsByBound(bound, { type: 'canvas' });
|
|
462
|
+
const previous = legendsOn(inside, bound);
|
|
463
|
+
// The box about to be drawn describes the DRAWING, never the box it replaces:
|
|
464
|
+
// the outgoing wrapper is dropped from the scan so `core:legend` is not among
|
|
465
|
+
// the roles the rows are derived from. Its glyphs carry no role at all.
|
|
466
|
+
const outgoing = new Set(previous);
|
|
467
|
+
const present = rolesOf(inside.filter(el => !outgoing.has(el)));
|
|
422
468
|
const sections = legendFromCommands(std, owner, present);
|
|
423
469
|
const box = boardLegendBox(std, owner);
|
|
424
470
|
const layout = {
|
|
@@ -432,7 +478,17 @@ export function createBoardLegend(std, board, owner) {
|
|
|
432
478
|
const extras = box?.extras?.({ std, board, present }) ?? [];
|
|
433
479
|
const title = translateKey(std, ...(box?.titleWording ?? BOARD_LEGEND_TITLE));
|
|
434
480
|
const { height } = measureLegend(sections, layout, extras);
|
|
481
|
+
// ONE checkpoint, in front of BOTH halves of the replacement: a single
|
|
482
|
+
// Ctrl+Z after a regenerate puts the previous box back whole, rather than
|
|
483
|
+
// leaving the board with no legend at all.
|
|
435
484
|
std.store.captureSync();
|
|
485
|
+
if (previous.length > 0) {
|
|
486
|
+
const crud = std.get(EdgelessCRUDIdentifier);
|
|
487
|
+
// Cascades into the group's children (`crud-extension.ts`), so one call
|
|
488
|
+
// per legend takes the whole box away.
|
|
489
|
+
for (const legend of previous)
|
|
490
|
+
crud.removeElement(legend.id);
|
|
491
|
+
}
|
|
436
492
|
const id = addLegend(surface, std, bound.x + INSET_X, bound.y + bound.h - INSET_BOTTOM - height, { title, sections, extras, ...layout });
|
|
437
493
|
gfx.selection.set({ elements: [id], editing: false });
|
|
438
494
|
return id;
|
|
@@ -229,6 +229,10 @@ export function morphToolbarConfig(spec) {
|
|
|
229
229
|
return renderPickerMenu({
|
|
230
230
|
testId: 'element-morph',
|
|
231
231
|
label: wording(ctx, spec.label),
|
|
232
|
+
// The shell's accessible name stays the stable ENGLISH
|
|
233
|
+
// identifier: composing it from the translated label produced
|
|
234
|
+
// half a sentence per language (#390).
|
|
235
|
+
menuAria: `${spec.label.fallback.toLowerCase()}-menu`,
|
|
232
236
|
// The current kind's own icon on the closed button, exactly as the
|
|
233
237
|
// shape picker shows the current shape: the dropdown reads as a
|
|
234
238
|
// statement about the selection before it is opened.
|
|
@@ -39,6 +39,9 @@ export declare const SVG_SKETCH_SKIPPED: ChromeWording;
|
|
|
39
39
|
export declare const SVG_SKETCH_REMOVED: ChromeWording;
|
|
40
40
|
export declare const SVG_SKETCH_REMOVED_ATTRIBUTE: ChromeWording;
|
|
41
41
|
export declare const SVG_SKETCH_EMPTY: ChromeWording;
|
|
42
|
+
export declare const SVG_SKETCH_ERROR_MALFORMED_XML: ChromeWording;
|
|
43
|
+
export declare const SVG_SKETCH_ERROR_NOT_SVG: ChromeWording;
|
|
44
|
+
export declare const SVG_SKETCH_ERROR_SANITIZED_AWAY: ChromeWording;
|
|
42
45
|
/**
|
|
43
46
|
* Every wording declared above, in declaration order — joined into
|
|
44
47
|
* `PACKAGE_WORDINGS` (`packages/affine/all/src/translations.ts`) alongside
|
|
@@ -92,6 +92,26 @@ export const SVG_SKETCH_EMPTY = [
|
|
|
92
92
|
'com.labre.interchange.svg.empty',
|
|
93
93
|
'No shape or text was recognised in this SVG, so nothing was drawn.',
|
|
94
94
|
];
|
|
95
|
+
/* ── The reader's own refusals (#390) ─────────────────────────────────────
|
|
96
|
+
*
|
|
97
|
+
* Thrown via `InterchangeImportError` and rendered by `runInterchangeImport`
|
|
98
|
+
* in the failure notification — the same shape as `BPMN_IMPORT_ERRORS`
|
|
99
|
+
* (`gfx/bpmn/src/import.ts`), and keyed for the same reason: these three
|
|
100
|
+
* sentences were written for a user, not for a console, and were reaching a
|
|
101
|
+
* French host in English because a bare `new Error(…)` carries no key.
|
|
102
|
+
*/
|
|
103
|
+
export const SVG_SKETCH_ERROR_MALFORMED_XML = [
|
|
104
|
+
'com.labre.interchange.svg.error.malformed-xml',
|
|
105
|
+
'This file is not well-formed XML, so no drawing can be read out of it: {{detail}}',
|
|
106
|
+
];
|
|
107
|
+
export const SVG_SKETCH_ERROR_NOT_SVG = [
|
|
108
|
+
'com.labre.interchange.svg.error.not-svg',
|
|
109
|
+
'An SVG opens on <svg>; this one opens on <{{tag}}>.',
|
|
110
|
+
];
|
|
111
|
+
export const SVG_SKETCH_ERROR_SANITIZED_AWAY = [
|
|
112
|
+
'com.labre.interchange.svg.error.sanitized-away',
|
|
113
|
+
'Nothing survived sanitizing this SVG, so there is no drawing to read.',
|
|
114
|
+
];
|
|
95
115
|
/**
|
|
96
116
|
* Every wording declared above, in declaration order — joined into
|
|
97
117
|
* `PACKAGE_WORDINGS` (`packages/affine/all/src/translations.ts`) alongside
|
|
@@ -117,4 +137,7 @@ export const SVG_SKETCH_WORDINGS = [
|
|
|
117
137
|
SVG_SKETCH_REMOVED,
|
|
118
138
|
SVG_SKETCH_REMOVED_ATTRIBUTE,
|
|
119
139
|
SVG_SKETCH_EMPTY,
|
|
140
|
+
SVG_SKETCH_ERROR_MALFORMED_XML,
|
|
141
|
+
SVG_SKETCH_ERROR_NOT_SVG,
|
|
142
|
+
SVG_SKETCH_ERROR_SANITIZED_AWAY,
|
|
120
143
|
];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type InterchangeImportContext, type InterchangeImportResult } from './interchange.js';
|
|
2
2
|
/**
|
|
3
3
|
* **SVG → a canvas sketch.** The platform's VISUAL-tier importer, shared by
|
|
4
4
|
* every framework that declares it (`docs/adr/0012`, P2).
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ShapeStyle, ShapeType, StrokeStyle, TextAlign, } from '../../affine-model/index.js';
|
|
2
2
|
import DOMPurify from 'dompurify';
|
|
3
|
-
import {
|
|
3
|
+
import { InterchangeImportError, } from './interchange.js';
|
|
4
|
+
import { SVG_SKETCH_CURRENT_COLOR, SVG_SKETCH_CURVE, SVG_SKETCH_EMPTY, SVG_SKETCH_EMPTY_BOX, SVG_SKETCH_EMPTY_TEXT, SVG_SKETCH_ERROR_MALFORMED_XML, SVG_SKETCH_ERROR_NOT_SVG, SVG_SKETCH_ERROR_SANITIZED_AWAY, SVG_SKETCH_FLAT_POLYGON, SVG_SKETCH_FONT_UNIT, SVG_SKETCH_HIDDEN_DISPLAY, SVG_SKETCH_HIDDEN_VISIBILITY, SVG_SKETCH_LONE_POINT, SVG_SKETCH_OPACITY, SVG_SKETCH_PAINT_SERVER, SVG_SKETCH_PERCENT_RADIUS, SVG_SKETCH_REMOVED, SVG_SKETCH_REMOVED_ATTRIBUTE, SVG_SKETCH_SKIPPED, SVG_SKETCH_SWITCH, SVG_SKETCH_TRANSFORM, } from './svg-sketch-translations.js';
|
|
4
5
|
/**
|
|
5
6
|
* **SVG → a canvas sketch.** The platform's VISUAL-tier importer, shared by
|
|
6
7
|
* every framework that declares it (`docs/adr/0012`, P2).
|
|
@@ -879,10 +880,15 @@ function parseSvgRoot(source, notes) {
|
|
|
879
880
|
.split('\n')
|
|
880
881
|
.map(line => line.trim())
|
|
881
882
|
.filter(line => line.length > 0);
|
|
882
|
-
|
|
883
|
+
const detail = summary.slice(0, 200);
|
|
884
|
+
throw new InterchangeImportError(`This file is not well-formed XML, so no drawing can be read out of it: ${detail}`, {
|
|
885
|
+
messageKey: SVG_SKETCH_ERROR_MALFORMED_XML[0],
|
|
886
|
+
messageParams: { detail },
|
|
887
|
+
});
|
|
883
888
|
}
|
|
884
889
|
if (nameOf(doc.documentElement) !== 'svg') {
|
|
885
|
-
|
|
890
|
+
const tag = doc.documentElement?.localName ?? 'nothing';
|
|
891
|
+
throw new InterchangeImportError(`An SVG opens on <svg>; this one opens on <${tag}>.`, { messageKey: SVG_SKETCH_ERROR_NOT_SVG[0], messageParams: { tag } });
|
|
886
892
|
}
|
|
887
893
|
const clean = DOMPurify.sanitize(source, {
|
|
888
894
|
USE_PROFILES: { svg: true },
|
|
@@ -923,7 +929,9 @@ function parseSvgRoot(source, notes) {
|
|
|
923
929
|
// overload, which answers an `HTMLElement` — the sanitized body wrapper.
|
|
924
930
|
const root = childElements(clean).find(child => nameOf(child) === 'svg');
|
|
925
931
|
if (!root) {
|
|
926
|
-
throw new
|
|
932
|
+
throw new InterchangeImportError(SVG_SKETCH_ERROR_SANITIZED_AWAY[1], {
|
|
933
|
+
messageKey: SVG_SKETCH_ERROR_SANITIZED_AWAY[0],
|
|
934
|
+
});
|
|
927
935
|
}
|
|
928
936
|
return root;
|
|
929
937
|
}
|
|
@@ -105,7 +105,7 @@ export class FrameworkBackgroundView extends GfxElementModelView {
|
|
|
105
105
|
font: '14px Inter, sans-serif',
|
|
106
106
|
color: 'var(--affine-text-primary-color, #1f2328)',
|
|
107
107
|
background: 'var(--affine-background-overlay-panel-color, #ffffff)',
|
|
108
|
-
border: '1px solid var(--affine-primary-color
|
|
108
|
+
border: '1px solid var(--affine-primary-color)',
|
|
109
109
|
borderRadius: '6px',
|
|
110
110
|
boxShadow: 'var(--affine-shadow-2, 0 2px 8px rgba(0,0,0,0.18))',
|
|
111
111
|
outline: 'none',
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a framework that draws a FIXED reference artwork puts it in its element.
|
|
3
|
+
*
|
|
4
|
+
* A declared background (`FrameworkBackgroundDef`) never needs this: it is made
|
|
5
|
+
* of ratios of a plot and stretches with the element by construction. The
|
|
6
|
+
* frameworks that reproduce an authored drawing — Cynefin, Estuarine, EDGY, the
|
|
7
|
+
* DDD stencils — cannot stretch: circles would turn into ellipses and a
|
|
8
|
+
* hand-drawn boundary into a smear. They fit the artwork uniformly instead and
|
|
9
|
+
* centre what is left over.
|
|
10
|
+
*
|
|
11
|
+
* This was written three times over — `gfx/cynefin-estuarine/src/utils.ts`,
|
|
12
|
+
* `gfx/ddd-shared/src/shared/utils.ts` and `gfx/edgy/src/consts.ts` — with the
|
|
13
|
+
* same body each time. One source of truth (R37): the three call it, and a
|
|
14
|
+
* board that crops itself onto its drawing
|
|
15
|
+
* (`gfx/cynefin-estuarine/src/cynefin/crop.ts`) reads the very function the
|
|
16
|
+
* renderer painted through, so the two can never disagree about where the
|
|
17
|
+
* drawing is.
|
|
18
|
+
*/
|
|
19
|
+
/** A uniform fit: the scale, and the offsets that centre the letterbox. */
|
|
20
|
+
export interface RefFit {
|
|
21
|
+
/** Scale from reference units to element units. */
|
|
22
|
+
s: number;
|
|
23
|
+
/** Left offset of the fitted artwork inside the element, in element units. */
|
|
24
|
+
ox: number;
|
|
25
|
+
/** Top offset of the fitted artwork inside the element, in element units. */
|
|
26
|
+
oy: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Uniform fit of a `refW × refH` reference design into a `w × h` box: the
|
|
30
|
+
* scale factor plus the centring offsets (letterboxed), undistorted.
|
|
31
|
+
*
|
|
32
|
+
* The fitted artwork is `refW * s` by `refH * s` and shares the box's CENTRE —
|
|
33
|
+
* `ox + refW * s / 2 === w / 2` — which is what lets a board crop itself onto
|
|
34
|
+
* its drawing without the drawing moving, rotation included.
|
|
35
|
+
*/
|
|
36
|
+
export declare function refScale(w: number, h: number, refW: number, refH: number): RefFit;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a framework that draws a FIXED reference artwork puts it in its element.
|
|
3
|
+
*
|
|
4
|
+
* A declared background (`FrameworkBackgroundDef`) never needs this: it is made
|
|
5
|
+
* of ratios of a plot and stretches with the element by construction. The
|
|
6
|
+
* frameworks that reproduce an authored drawing — Cynefin, Estuarine, EDGY, the
|
|
7
|
+
* DDD stencils — cannot stretch: circles would turn into ellipses and a
|
|
8
|
+
* hand-drawn boundary into a smear. They fit the artwork uniformly instead and
|
|
9
|
+
* centre what is left over.
|
|
10
|
+
*
|
|
11
|
+
* This was written three times over — `gfx/cynefin-estuarine/src/utils.ts`,
|
|
12
|
+
* `gfx/ddd-shared/src/shared/utils.ts` and `gfx/edgy/src/consts.ts` — with the
|
|
13
|
+
* same body each time. One source of truth (R37): the three call it, and a
|
|
14
|
+
* board that crops itself onto its drawing
|
|
15
|
+
* (`gfx/cynefin-estuarine/src/cynefin/crop.ts`) reads the very function the
|
|
16
|
+
* renderer painted through, so the two can never disagree about where the
|
|
17
|
+
* drawing is.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Uniform fit of a `refW × refH` reference design into a `w × h` box: the
|
|
21
|
+
* scale factor plus the centring offsets (letterboxed), undistorted.
|
|
22
|
+
*
|
|
23
|
+
* The fitted artwork is `refW * s` by `refH * s` and shares the box's CENTRE —
|
|
24
|
+
* `ox + refW * s / 2 === w / 2` — which is what lets a board crop itself onto
|
|
25
|
+
* its drawing without the drawing moving, rotation included.
|
|
26
|
+
*/
|
|
27
|
+
export function refScale(w, h, refW, refH) {
|
|
28
|
+
const s = Math.min(w / refW, h / refH);
|
|
29
|
+
return { s, ox: (w - refW * s) / 2, oy: (h - refH * s) / 2 };
|
|
30
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getChromeAccentColor } from '../../affine-shared/services/index.js';
|
|
2
2
|
import { DisposableGroup } from '../../global/disposable/index.js';
|
|
3
3
|
import { effect } from '@preact/signals-core';
|
|
4
4
|
import { Overlay } from './overlay.js';
|
|
@@ -8,8 +8,6 @@ const HIGHLIGHT_STROKE_WIDTH = 2;
|
|
|
8
8
|
const HIGHLIGHT_GAP = 4;
|
|
9
9
|
/** Ring corner radius, in screen pixels. */
|
|
10
10
|
const HIGHLIGHT_RADIUS = 4;
|
|
11
|
-
/** Used when no theme is provided, matches the legacy frame highlight. */
|
|
12
|
-
const FALLBACK_HIGHLIGHT_COLOR = '#1E96EB';
|
|
13
11
|
/**
|
|
14
12
|
* Draws the transient emphasis ring around the elements held by
|
|
15
13
|
* `gfx.highlight` (`ElementHighlightManager`).
|
|
@@ -20,10 +18,7 @@ const FALLBACK_HIGHLIGHT_COLOR = '#1E96EB';
|
|
|
20
18
|
export class ElementHighlightOverlay extends Overlay {
|
|
21
19
|
static { this.overlayName = 'element-highlight'; }
|
|
22
20
|
get _strokeColor() {
|
|
23
|
-
return (this.gfx.std
|
|
24
|
-
.getOptional(ThemeProvider)
|
|
25
|
-
?.getCssVariableColor('--affine-primary-color') ??
|
|
26
|
-
FALLBACK_HIGHLIGHT_COLOR);
|
|
21
|
+
return getChromeAccentColor(this.gfx.std);
|
|
27
22
|
}
|
|
28
23
|
constructor(gfx) {
|
|
29
24
|
super(gfx);
|
|
@@ -6,8 +6,8 @@ export const EdgelessTooltip = html `<svg width="170" height="106" viewBox="0 0
|
|
|
6
6
|
<rect width="170" height="106" rx="2" fill="white"/>
|
|
7
7
|
</mask>
|
|
8
8
|
<g mask="url(#mask0_16460_1252)">
|
|
9
|
-
<rect x="100.5" y="42.6565" width="141" height="51" stroke="
|
|
10
|
-
<circle cx="101.5" cy="43.5" r="6" fill="white" stroke="
|
|
9
|
+
<rect x="100.5" y="42.6565" width="141" height="51" stroke="var(--affine-primary-color)" stroke-width="3" stroke-dasharray="5 5"/>
|
|
10
|
+
<circle cx="101.5" cy="43.5" r="6" fill="white" stroke="var(--affine-primary-color)" stroke-width="3"/>
|
|
11
11
|
<rect x="105" y="8" width="59" height="26" rx="10" fill="black" fill-opacity="0.1"/>
|
|
12
12
|
<text fill="#121212" xml:space="preserve" style="white-space: pre" font-family="Inter" font-size="12" letter-spacing="0em"><tspan x="117" y="25.3636">Group</tspan></text>
|
|
13
13
|
<mask id="mask1_16460_1252" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="98" height="106">
|
|
@@ -125,6 +125,11 @@ export const paletteCarouselStyles = css `
|
|
|
125
125
|
/* Nine pages fit without scrolling; the cap is a seatbelt for a tenth. */
|
|
126
126
|
max-height: 280px;
|
|
127
127
|
overflow-y: auto;
|
|
128
|
+
/* Same trap as the popup box that holds this list (issue #392, see
|
|
129
|
+
* toolbar/menu-button.ts): an overflow-y of its own makes the other axis
|
|
130
|
+
* scrollable too, and this list is one long page name — a translation, or
|
|
131
|
+
* a tenth page — away from overflowing it. */
|
|
132
|
+
overflow-x: hidden;
|
|
128
133
|
overscroll-behavior: contain;
|
|
129
134
|
animation: palette-carousel-settle ${springDuration} ${springEasing};
|
|
130
135
|
}
|
|
@@ -264,7 +264,9 @@ export const menuButtonItems = {
|
|
|
264
264
|
const data = {
|
|
265
265
|
content: () => html `
|
|
266
266
|
${config.checked.value
|
|
267
|
-
? CheckBoxCheckSolidIcon({
|
|
267
|
+
? CheckBoxCheckSolidIcon({
|
|
268
|
+
style: `color:var(--affine-primary-color)`,
|
|
269
|
+
})
|
|
268
270
|
: CheckBoxUnIcon()}
|
|
269
271
|
<div class="affine-menu-action-text">
|
|
270
272
|
${config.label?.() ?? config.name}
|
|
@@ -9,11 +9,11 @@ export const AIStarIcon = html `<svg
|
|
|
9
9
|
<g clip-path="url(#clip0_7444_26654)">
|
|
10
10
|
<path
|
|
11
11
|
d="M9.40033 4.57585C9.36627 4.27519 9.11207 4.04792 8.80948 4.04761C8.50689 4.0473 8.25223 4.27405 8.21756 4.57465C7.99211 6.52928 7.40771 7.90373 6.46492 8.84651C5.52213 9.7893 4.14769 10.3737 2.19305 10.5991C1.89246 10.6338 1.66571 10.8885 1.66602 11.1911C1.66633 11.4937 1.8936 11.7479 2.19426 11.7819C4.11659 11.9997 5.52025 12.5838 6.48542 13.5315C7.44696 14.4757 8.04356 15.8498 8.21597 17.7908C8.24327 18.0981 8.50096 18.3337 8.80954 18.3333C9.11813 18.333 9.37528 18.0969 9.40189 17.7894C9.56714 15.8804 10.1632 14.4774 11.1295 13.5111C12.0958 12.5448 13.4988 11.9487 15.4078 11.7835C15.7153 11.7569 15.9514 11.4997 15.9517 11.1911C15.9521 10.8825 15.7165 10.6249 15.4092 10.5976C13.4683 10.4252 12.0941 9.82855 11.15 8.86701C10.2022 7.90184 9.61807 6.49818 9.40033 4.57585Z"
|
|
12
|
-
fill="
|
|
12
|
+
fill="var(--affine-primary-color)"
|
|
13
13
|
/>
|
|
14
14
|
<path
|
|
15
15
|
d="M15.7849 1.87208C15.7717 1.75516 15.6728 1.66678 15.5551 1.66666C15.4375 1.66654 15.3384 1.75472 15.3249 1.87162C15.2373 2.63175 15.01 3.16626 14.6434 3.5329C14.2767 3.89954 13.7422 4.1268 12.9821 4.21448C12.8652 4.22796 12.777 4.327 12.7771 4.44467C12.7772 4.56234 12.8656 4.6612 12.9826 4.67445C13.7301 4.75912 14.276 4.9863 14.6513 5.35485C15.0253 5.72203 15.2573 6.25642 15.3243 7.01121C15.335 7.13075 15.4352 7.22235 15.5552 7.22221C15.6752 7.22208 15.7752 7.13025 15.7855 7.01069C15.8498 6.26831 16.0816 5.72268 16.4574 5.3469C16.8332 4.97111 17.3788 4.73932 18.1212 4.67505C18.2407 4.6647 18.3325 4.5647 18.3327 4.44469C18.3328 4.32469 18.2412 4.22448 18.1217 4.21386C17.3669 4.14681 16.8325 3.9148 16.4653 3.54087C16.0968 3.16552 15.8696 2.61966 15.7849 1.87208Z"
|
|
16
|
-
fill="
|
|
16
|
+
fill="var(--affine-primary-color)"
|
|
17
17
|
/>
|
|
18
18
|
</g>
|
|
19
19
|
<defs>
|
|
@@ -46,7 +46,7 @@ export const AIStopIcon = html `<svg
|
|
|
46
46
|
fill-rule="evenodd"
|
|
47
47
|
clip-rule="evenodd"
|
|
48
48
|
d="M17.0827 10C17.0827 13.912 13.9114 17.0834 9.99935 17.0834C6.08733 17.0834 2.91602 13.912 2.91602 10C2.91602 6.088 6.08733 2.91669 9.99935 2.91669C13.9114 2.91669 17.0827 6.088 17.0827 10ZM18.3327 10C18.3327 14.6024 14.6017 18.3334 9.99935 18.3334C5.39698 18.3334 1.66602 14.6024 1.66602 10C1.66602 5.39765 5.39698 1.66669 9.99935 1.66669C14.6017 1.66669 18.3327 5.39765 18.3327 10ZM8.33268 6.66669C7.41221 6.66669 6.66602 7.41288 6.66602 8.33335V11.6667C6.66602 12.5872 7.41221 13.3334 8.33268 13.3334H11.666C12.5865 13.3334 13.3327 12.5872 13.3327 11.6667V8.33335C13.3327 7.41288 12.5865 6.66669 11.666 6.66669H8.33268Z"
|
|
49
|
-
fill="
|
|
49
|
+
fill="var(--affine-primary-color)"
|
|
50
50
|
/>
|
|
51
51
|
</g>
|
|
52
52
|
<defs>
|