@formicoidea/labre-core 0.38.2 → 0.39.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/_pkgs/affine-block-database/properties/rich-text/cell-renderer.js +14 -9
  2. package/dist/_pkgs/affine-block-frame/frame-manager.js +10 -9
  3. package/dist/_pkgs/affine-block-list/list-block.d.ts +8 -1
  4. package/dist/_pkgs/affine-block-list/list-block.js +27 -17
  5. package/dist/_pkgs/affine-block-paragraph/paragraph-block.d.ts +8 -1
  6. package/dist/_pkgs/affine-block-paragraph/paragraph-block.js +30 -15
  7. package/dist/_pkgs/affine-block-root/edgeless/clipboard/clipboard.js +3 -3
  8. package/dist/_pkgs/affine-block-root/keyboard/keymap.js +20 -0
  9. package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.d.ts +6 -4
  10. package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +15 -10
  11. package/dist/_pkgs/affine-block-surface/utils/priority-queue.js +2 -2
  12. package/dist/_pkgs/affine-components/toggle-button/toggle-button.d.ts +5 -0
  13. package/dist/_pkgs/affine-components/toggle-button/toggle-button.js +31 -18
  14. package/dist/_pkgs/affine-gfx-connector/connector-manager.d.ts +13 -1
  15. package/dist/_pkgs/affine-gfx-connector/connector-manager.js +9 -5
  16. package/dist/_pkgs/affine-gfx-connector/connector-watcher.js +48 -17
  17. package/dist/_pkgs/affine-gfx-connector/view/view.js +4 -0
  18. package/dist/_pkgs/affine-gfx-mindmap/view/view.js +9 -1
  19. package/dist/_pkgs/affine-gfx-mindmap/view.js +7 -2
  20. package/dist/_pkgs/affine-gfx-template/contribute.d.ts +32 -7
  21. package/dist/_pkgs/affine-gfx-template/contribute.js +66 -22
  22. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.d.ts +7 -0
  23. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.js +19 -4
  24. package/dist/_pkgs/affine-model/elements/mindmap/mindmap.js +10 -1
  25. package/dist/_pkgs/affine-shared/adapters/middlewares/proxy.d.ts +10 -0
  26. package/dist/_pkgs/affine-shared/adapters/middlewares/proxy.js +19 -12
  27. package/dist/_pkgs/affine-shared/adapters/utils/fetch.js +4 -0
  28. package/dist/_pkgs/affine-widget-keyboard-toolbar/widget.js +18 -3
  29. package/dist/_pkgs/affine-widget-linked-doc/mobile-linked-doc-menu.d.ts +7 -1
  30. package/dist/_pkgs/affine-widget-linked-doc/mobile-linked-doc-menu.js +8 -2
  31. package/dist/_pkgs/std/gfx/index.d.ts +1 -1
  32. package/dist/_pkgs/std/gfx/model/surface/element-model.d.ts +2 -2
  33. package/dist/_pkgs/std/gfx/model/surface/element-model.js +2 -2
  34. package/dist/_pkgs/std/gfx/model/surface/surface-model.d.ts +1 -1
  35. package/dist/_pkgs/std/gfx/model/surface/surface-model.js +1 -1
  36. package/package.json +1 -1
@@ -167,14 +167,17 @@ export class RichTextCell extends BaseCellRenderer {
167
167
  e.stopPropagation();
168
168
  };
169
169
  this._onPaste = (e) => {
170
- e.preventDefault();
171
- e.stopPropagation();
172
170
  const inlineEditor = this.inlineEditor$.value;
173
- if (!inlineEditor)
174
- return;
175
- const inlineRange = inlineEditor.getInlineRange();
176
- if (!inlineRange)
171
+ const inlineRange = inlineEditor?.getInlineRange();
172
+ // Bail out before cancelling the event: with no resolvable inline range
173
+ // there is nothing to insert into, and swallowing the paste here would
174
+ // drop the clipboard silently instead of letting the default path act.
175
+ if (!inlineEditor || !inlineRange)
177
176
  return;
177
+ // The cell owns the paste: stop it before the document clipboard sees it,
178
+ // whichever branch below ends up handling the payload.
179
+ e.preventDefault();
180
+ e.stopPropagation();
178
181
  if (e.clipboardData) {
179
182
  try {
180
183
  const getDeltas = (snapshot) => {
@@ -290,9 +293,11 @@ export class RichTextCell extends BaseCellRenderer {
290
293
  richText.addEventListener('cut', this._onCut, true);
291
294
  richText.addEventListener('paste', this._onPaste, true);
292
295
  return () => {
293
- richText.removeEventListener('copy', this._onCopy);
294
- richText.removeEventListener('cut', this._onCut);
295
- richText.removeEventListener('paste', this._onPaste);
296
+ // the capture flag is part of the listener identity: without it
297
+ // these three would never be removed.
298
+ richText.removeEventListener('copy', this._onCopy, true);
299
+ richText.removeEventListener('cut', this._onCut, true);
300
+ richText.removeEventListener('paste', this._onPaste, true);
296
301
  };
297
302
  }
298
303
  return;
@@ -259,6 +259,15 @@ export class EdgelessFrameManager extends GfxExtension {
259
259
  }
260
260
  }));
261
261
  this._disposable.add(doc.slots.blockUpdated.subscribe(payload => {
262
+ // Local adds only, exactly like the canvas-element half above. A block
263
+ // added by a remote peer was already adopted THERE: the frame's
264
+ // `childElementIds` arrives with the same sync. Adopting it again here
265
+ // is a duplicate write on every receiving peer — and a readonly viewer
266
+ // cannot write at all, which is how this surfaced (#251). See #242 for
267
+ // the rule: a cascade reacts to local transactions, the receiving peer
268
+ // trusts the sync.
269
+ if (!payload.isLocal)
270
+ return;
262
271
  if (payload.type === 'add' &&
263
272
  payload.model instanceof GfxBlockElementModel &&
264
273
  renderableInEdgeless(doc, surfaceModel, payload.model)) {
@@ -275,15 +284,7 @@ export class EdgelessFrameManager extends GfxExtension {
275
284
  if (this._enclosesFrame(payload.model, frame)) {
276
285
  return;
277
286
  }
278
- if (payload.isLocal) {
279
- this._adoptNewlyCreatedElement(frame, payload.model);
280
- }
281
- else {
282
- // Remote adds: adopt only. The originating client reassigns the
283
- // index and syncs it — reindexing here too would produce
284
- // competing concurrent writes for the same element.
285
- this.addElementsToFrame(frame, [payload.model]);
286
- }
287
+ this._adoptNewlyCreatedElement(frame, payload.model);
287
288
  }
288
289
  }));
289
290
  }
@@ -6,6 +6,14 @@ import { type TemplateResult } from 'lit';
6
6
  export declare class ListBlockComponent extends CaptionedBlockComponent<ListBlockModel> {
7
7
  static styles: import("lit").CSSResult;
8
8
  private _inlineRangeProvider;
9
+ /**
10
+ * The collapse state a READER holds locally. Same contract as
11
+ * `affine-paragraph`: a readonly document is never written, so the toggle
12
+ * carries the reader's own expansion here instead of in the model. A signal
13
+ * rather than a Lit `@state()`, so BlockSuite effects can depend on it.
14
+ */
15
+ private readonly _readonlyCollapsed;
16
+ private _setReadonlyCollapsed;
9
17
  private readonly _onClickIcon;
10
18
  get attributeRenderer(): import("../std/inline/types.js").AttributeRenderer<import("../affine-shared/types/index.js").AffineTextAttributes>;
11
19
  get attributesSchema(): import("zod").ZodType<any, import("zod").ZodTypeDef, any>;
@@ -16,7 +24,6 @@ export declare class ListBlockComponent extends CaptionedBlockComponent<ListBloc
16
24
  connectedCallback(): void;
17
25
  getUpdateComplete(): Promise<boolean>;
18
26
  renderBlock(): TemplateResult<1>;
19
- private accessor _readonlyCollapsed;
20
27
  private accessor _richTextElement;
21
28
  accessor blockContainerStyles: {
22
29
  margin: string;
@@ -42,9 +42,9 @@ import { DocModeProvider } from '../affine-shared/services/index.js';
42
42
  import { getViewportElement } from '../affine-shared/utils/index.js';
43
43
  import { BlockSelection, TextSelection } from '../std/index.js';
44
44
  import { getInlineRangeProvider, } from '../std/inline/index.js';
45
- import { effect } from '@preact/signals-core';
45
+ import { effect, signal } from '@preact/signals-core';
46
46
  import { html, nothing } from 'lit';
47
- import { query, state } from 'lit/decorators.js';
47
+ import { query } from 'lit/decorators.js';
48
48
  import { classMap } from 'lit/directives/class-map.js';
49
49
  import { styleMap } from 'lit/directives/style-map.js';
50
50
  import { correctNumberedListsOrderToPrev } from './commands/utils.js';
@@ -52,9 +52,6 @@ import { listBlockStyles } from './styles.js';
52
52
  import { getListIcon } from './utils/get-list-icon.js';
53
53
  let ListBlockComponent = (() => {
54
54
  let _classSuper = CaptionedBlockComponent;
55
- let __readonlyCollapsed_decorators;
56
- let __readonlyCollapsed_initializers = [];
57
- let __readonlyCollapsed_extraInitializers = [];
58
55
  let __richTextElement_decorators;
59
56
  let __richTextElement_initializers = [];
60
57
  let __richTextElement_extraInitializers = [];
@@ -62,12 +59,19 @@ let ListBlockComponent = (() => {
62
59
  constructor() {
63
60
  super(...arguments);
64
61
  this._inlineRangeProvider = null;
62
+ /**
63
+ * The collapse state a READER holds locally. Same contract as
64
+ * `affine-paragraph`: a readonly document is never written, so the toggle
65
+ * carries the reader's own expansion here instead of in the model. A signal
66
+ * rather than a Lit `@state()`, so BlockSuite effects can depend on it.
67
+ */
68
+ this._readonlyCollapsed = signal(false);
65
69
  this._onClickIcon = (e) => {
66
70
  e.stopPropagation();
67
71
  e.preventDefault();
68
72
  if (this.model.props.type === 'toggle') {
69
73
  if (this.store.readonly) {
70
- this._readonlyCollapsed = !this._readonlyCollapsed;
74
+ this._setReadonlyCollapsed(!this._readonlyCollapsed.value);
71
75
  }
72
76
  else {
73
77
  this.store.captureSync();
@@ -93,21 +97,22 @@ let ListBlockComponent = (() => {
93
97
  }
94
98
  this._select();
95
99
  };
96
- this.#_readonlyCollapsed_accessor_storage = __runInitializers(this, __readonlyCollapsed_initializers, false);
97
- this.#_richTextElement_accessor_storage = (__runInitializers(this, __readonlyCollapsed_extraInitializers), __runInitializers(this, __richTextElement_initializers, null));
100
+ this.#_richTextElement_accessor_storage = __runInitializers(this, __richTextElement_initializers, null);
98
101
  this.#blockContainerStyles_accessor_storage = (__runInitializers(this, __richTextElement_extraInitializers), {
99
102
  margin: 'var(--affine-list-margin, 10px 0)',
100
103
  });
101
104
  }
102
105
  static {
103
106
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
104
- __readonlyCollapsed_decorators = [state()];
105
107
  __richTextElement_decorators = [query('rich-text')];
106
- __esDecorate(this, null, __readonlyCollapsed_decorators, { kind: "accessor", name: "_readonlyCollapsed", static: false, private: false, access: { has: obj => "_readonlyCollapsed" in obj, get: obj => obj._readonlyCollapsed, set: (obj, value) => { obj._readonlyCollapsed = value; } }, metadata: _metadata }, __readonlyCollapsed_initializers, __readonlyCollapsed_extraInitializers);
107
108
  __esDecorate(this, null, __richTextElement_decorators, { kind: "accessor", name: "_richTextElement", static: false, private: false, access: { has: obj => "_richTextElement" in obj, get: obj => obj._richTextElement, set: (obj, value) => { obj._richTextElement = value; } }, metadata: _metadata }, __richTextElement_initializers, __richTextElement_extraInitializers);
108
109
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
109
110
  }
110
111
  static { this.styles = listBlockStyles; }
112
+ _setReadonlyCollapsed(collapsed) {
113
+ this._readonlyCollapsed.value = collapsed;
114
+ this.requestUpdate();
115
+ }
111
116
  get attributeRenderer() {
112
117
  return this.inlineManager.getRenderer();
113
118
  }
@@ -137,9 +142,14 @@ let ListBlockComponent = (() => {
137
142
  connectedCallback() {
138
143
  super.connectedCallback();
139
144
  this._inlineRangeProvider = getInlineRangeProvider(this);
145
+ // Only ENTERING readonly mode (or a later change of the persisted value)
146
+ // seeds the reader's local state; while readonly holds, the reader's own
147
+ // expansion is never overwritten. Aligned on `affine-paragraph`, where the
148
+ // unguarded version was a real defect (#262).
140
149
  this.disposables.add(effect(() => {
141
- const collapsed = this.model.props.collapsed$.value;
142
- this._readonlyCollapsed = collapsed;
150
+ if (this.store.readonly$.value) {
151
+ this._setReadonlyCollapsed(this.model.props.collapsed$.value);
152
+ }
143
153
  }));
144
154
  this.disposables.add(effect(() => {
145
155
  const type = this.model.props.type$.value;
@@ -162,10 +172,12 @@ let ListBlockComponent = (() => {
162
172
  renderBlock() {
163
173
  const { model, _onClickIcon } = this;
164
174
  const collapsed = this.store.readonly
165
- ? this._readonlyCollapsed
175
+ ? this._readonlyCollapsed.value
166
176
  : model.props.collapsed;
167
177
  const listIcon = getListIcon(model, !collapsed, _onClickIcon);
178
+ const childrenId = `list-children-${this.model.id}`;
168
179
  const children = html `<div
180
+ id=${childrenId}
169
181
  class="affine-block-children-container"
170
182
  style=${styleMap({
171
183
  paddingLeft: `${BLOCK_CHILDREN_CONTAINER_PADDING_LEFT}px`,
@@ -187,9 +199,10 @@ let ListBlockComponent = (() => {
187
199
  ? html `
188
200
  <blocksuite-toggle-button
189
201
  .collapsed=${collapsed}
202
+ .controls=${childrenId}
190
203
  .updateCollapsed=${(value) => {
191
204
  if (this.store.readonly) {
192
- this._readonlyCollapsed = value;
205
+ this._setReadonlyCollapsed(value);
193
206
  }
194
207
  else {
195
208
  this.store.captureSync();
@@ -222,9 +235,6 @@ let ListBlockComponent = (() => {
222
235
  </div>
223
236
  `;
224
237
  }
225
- #_readonlyCollapsed_accessor_storage;
226
- get _readonlyCollapsed() { return this.#_readonlyCollapsed_accessor_storage; }
227
- set _readonlyCollapsed(value) { this.#_readonlyCollapsed_accessor_storage = value; }
228
238
  #_richTextElement_accessor_storage;
229
239
  get _richTextElement() { return this.#_richTextElement_accessor_storage; }
230
240
  set _richTextElement(value) { this.#_richTextElement_accessor_storage = value; }
@@ -7,9 +7,17 @@ export declare class ParagraphBlockComponent extends CaptionedBlockComponent<Par
7
7
  focused$: import("@preact/signals-core").ReadonlySignal<boolean>;
8
8
  private readonly _composing;
9
9
  private readonly _displayPlaceholder;
10
+ /**
11
+ * The collapse state a READER holds locally. A readonly document must never
12
+ * be written, so the chevron cannot touch `model.props.collapsed`; this
13
+ * signal carries the reader's own expansion instead. It is a signal rather
14
+ * than a Lit `@state()` so BlockSuite effects can depend on it as well.
15
+ */
16
+ private readonly _readonlyCollapsed;
10
17
  private _inlineRangeProvider;
11
18
  private readonly _isInDatabase;
12
19
  private get _placeholder();
20
+ private _setReadonlyCollapsed;
13
21
  get citationService(): import("../affine-shared/services/index.js").CitationViewService;
14
22
  get attributeRenderer(): import("../std/inline/types.js").AttributeRenderer<import("../affine-shared/types/index.js").AffineTextAttributes>;
15
23
  get attributesSchema(): import("zod").ZodType<any, import("zod").ZodTypeDef, any>;
@@ -24,7 +32,6 @@ export declare class ParagraphBlockComponent extends CaptionedBlockComponent<Par
24
32
  connectedCallback(): void;
25
33
  getUpdateComplete(): Promise<boolean>;
26
34
  renderBlock(): TemplateResult<1>;
27
- private accessor _readonlyCollapsed;
28
35
  private accessor _richTextElement;
29
36
  accessor blockContainerStyles: {
30
37
  margin: string;
@@ -42,7 +42,7 @@ import { TextSelection } from '../std/index.js';
42
42
  import { getInlineRangeProvider, } from '../std/inline/index.js';
43
43
  import { computed, effect, signal } from '@preact/signals-core';
44
44
  import { html, nothing } from 'lit';
45
- import { query, state } from 'lit/decorators.js';
45
+ import { query } from 'lit/decorators.js';
46
46
  import { classMap } from 'lit/directives/class-map.js';
47
47
  import { styleMap } from 'lit/directives/style-map.js';
48
48
  import { unsafeHTML } from 'lit/directives/unsafe-html.js';
@@ -50,9 +50,6 @@ import { ParagraphBlockConfigExtension } from './paragraph-block-config.js';
50
50
  import { paragraphBlockStyles } from './styles.js';
51
51
  let ParagraphBlockComponent = (() => {
52
52
  let _classSuper = CaptionedBlockComponent;
53
- let __readonlyCollapsed_decorators;
54
- let __readonlyCollapsed_initializers = [];
55
- let __readonlyCollapsed_extraInitializers = [];
56
53
  let __richTextElement_decorators;
57
54
  let __richTextElement_initializers = [];
58
55
  let __richTextElement_extraInitializers = [];
@@ -67,6 +64,13 @@ let ParagraphBlockComponent = (() => {
67
64
  });
68
65
  this._composing = signal(false);
69
66
  this._displayPlaceholder = signal(false);
67
+ /**
68
+ * The collapse state a READER holds locally. A readonly document must never
69
+ * be written, so the chevron cannot touch `model.props.collapsed`; this
70
+ * signal carries the reader's own expansion instead. It is a signal rather
71
+ * than a Lit `@state()` so BlockSuite effects can depend on it as well.
72
+ */
73
+ this._readonlyCollapsed = signal(false);
70
74
  this._inlineRangeProvider = null;
71
75
  this._isInDatabase = () => {
72
76
  let parent = this.parentElement;
@@ -78,17 +82,14 @@ let ParagraphBlockComponent = (() => {
78
82
  }
79
83
  return false;
80
84
  };
81
- this.#_readonlyCollapsed_accessor_storage = __runInitializers(this, __readonlyCollapsed_initializers, false);
82
- this.#_richTextElement_accessor_storage = (__runInitializers(this, __readonlyCollapsed_extraInitializers), __runInitializers(this, __richTextElement_initializers, null));
85
+ this.#_richTextElement_accessor_storage = __runInitializers(this, __richTextElement_initializers, null);
83
86
  this.#blockContainerStyles_accessor_storage = (__runInitializers(this, __richTextElement_extraInitializers), {
84
87
  margin: 'var(--affine-paragraph-margin, 10px 0)',
85
88
  });
86
89
  }
87
90
  static {
88
91
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
89
- __readonlyCollapsed_decorators = [state()];
90
92
  __richTextElement_decorators = [query('rich-text')];
91
- __esDecorate(this, null, __readonlyCollapsed_decorators, { kind: "accessor", name: "_readonlyCollapsed", static: false, private: false, access: { has: obj => "_readonlyCollapsed" in obj, get: obj => obj._readonlyCollapsed, set: (obj, value) => { obj._readonlyCollapsed = value; } }, metadata: _metadata }, __readonlyCollapsed_initializers, __readonlyCollapsed_extraInitializers);
92
93
  __esDecorate(this, null, __richTextElement_decorators, { kind: "accessor", name: "_richTextElement", static: false, private: false, access: { has: obj => "_richTextElement" in obj, get: obj => obj._richTextElement, set: (obj, value) => { obj._richTextElement = value; } }, metadata: _metadata }, __richTextElement_initializers, __richTextElement_extraInitializers);
93
94
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
94
95
  }
@@ -98,6 +99,10 @@ let ParagraphBlockComponent = (() => {
98
99
  .get(ParagraphBlockConfigExtension.identifier)
99
100
  ?.getPlaceholder(this.model);
100
101
  }
102
+ _setReadonlyCollapsed(collapsed) {
103
+ this._readonlyCollapsed.value = collapsed;
104
+ this.requestUpdate();
105
+ }
101
106
  get citationService() {
102
107
  return this.std.get(CitationProvider);
103
108
  }
@@ -176,9 +181,19 @@ let ParagraphBlockComponent = (() => {
176
181
  this.model.props.collapsed = false;
177
182
  }
178
183
  }));
184
+ // Entering readonly mode (and any later change of the persisted value)
185
+ // seeds the reader's local state. Deliberately NOT re-run on every
186
+ // selection change: this effect must not depend on the selection, or the
187
+ // reader's own expansion would be overwritten mid-drag.
188
+ this.disposables.add(effect(() => {
189
+ if (this.store.readonly$.value) {
190
+ this._setReadonlyCollapsed(this.model.props.collapsed$.value);
191
+ }
192
+ }));
179
193
  this.disposables.add(effect(() => {
180
- const collapsed = this.model.props.collapsed$.value;
181
- this._readonlyCollapsed = collapsed;
194
+ const collapsed = this.store.readonly
195
+ ? this._readonlyCollapsed.value
196
+ : this.model.props.collapsed$.value;
182
197
  // reset text selection when selected block is collapsed
183
198
  if (this.model.props.type$.value.startsWith('h') && collapsed) {
184
199
  const collapsedSiblings = this.collapsedSiblings;
@@ -216,7 +231,7 @@ let ParagraphBlockComponent = (() => {
216
231
  renderBlock() {
217
232
  const { type$ } = this.model.props;
218
233
  const collapsed = this.store.readonly
219
- ? this._readonlyCollapsed
234
+ ? this._readonlyCollapsed.value
220
235
  : this.model.props.collapsed;
221
236
  const collapsedSiblings = this.collapsedSiblings;
222
237
  let style = html ``;
@@ -231,7 +246,9 @@ let ParagraphBlockComponent = (() => {
231
246
  </style>
232
247
  `;
233
248
  }
249
+ const childrenId = `heading-children-${this.model.id}`;
234
250
  const children = html `<div
251
+ id=${childrenId}
235
252
  class="affine-block-children-container"
236
253
  style=${styleMap({
237
254
  paddingLeft: `${BLOCK_CHILDREN_CONTAINER_PADDING_LEFT}px`,
@@ -275,9 +292,10 @@ let ParagraphBlockComponent = (() => {
275
292
  ? html `
276
293
  <blocksuite-toggle-button
277
294
  .collapsed=${collapsed}
295
+ .controls=${childrenId}
278
296
  .updateCollapsed=${(value) => {
279
297
  if (this.store.readonly) {
280
- this._readonlyCollapsed = value;
298
+ this._setReadonlyCollapsed(value);
281
299
  }
282
300
  else {
283
301
  this.store.captureSync();
@@ -328,9 +346,6 @@ let ParagraphBlockComponent = (() => {
328
346
  </div>
329
347
  `;
330
348
  }
331
- #_readonlyCollapsed_accessor_storage;
332
- get _readonlyCollapsed() { return this.#_readonlyCollapsed_accessor_storage; }
333
- set _readonlyCollapsed(value) { this.#_readonlyCollapsed_accessor_storage = value; }
334
349
  #_richTextElement_accessor_storage;
335
350
  get _richTextElement() { return this.#_richTextElement_accessor_storage; }
336
351
  set _richTextElement(value) { this.#_richTextElement_accessor_storage = value; }
@@ -4,7 +4,7 @@ import { addImages } from '../../../affine-block-image/index.js';
4
4
  import { CanvasRenderer, DefaultTool, EdgelessCRUDIdentifier, ExportManager, getSurfaceComponent, } from '../../../affine-block-surface/index.js';
5
5
  import { splitIntoLines } from '../../../affine-gfx-text/index.js';
6
6
  import { BookmarkStyles, DEFAULT_NOTE_HEIGHT, DEFAULT_NOTE_WIDTH, FrameBlockModel, MAX_IMAGE_WIDTH, } from '../../../affine-model/index.js';
7
- import { ClipboardAdapter, decodeClipboardBlobs, } from '../../../affine-shared/adapters/index.js';
7
+ import { ClipboardAdapter, decodeClipboardBlobs, ImageProxyService, } from '../../../affine-shared/adapters/index.js';
8
8
  import { CANVAS_EXPORT_IGNORE_TAGS, EMBED_CARD_HEIGHT, EMBED_CARD_WIDTH, } from '../../../affine-shared/consts/index.js';
9
9
  import { EmbedOptionProvider, ParseDocUrlProvider, TelemetryProvider, } from '../../../affine-shared/services/index.js';
10
10
  import { convertToPng, isInsidePageEditor, isTopLevelBlock, isUrlInClipboard, matchModels, referenceToNode, } from '../../../affine-shared/utils/index.js';
@@ -324,7 +324,7 @@ export class EdgelessClipboardController extends PageClipboard {
324
324
  }
325
325
  ctx.scale(dpr, dpr);
326
326
  const replaceImgSrcWithSvg = this._exportManager?.replaceImgSrcWithSvg;
327
- const imageProxy = host.std.clipboard.configs.get('imageProxy');
327
+ const imageProxy = host.std.get(ImageProxyService).imageProxyURL;
328
328
  const html2canvasOption = {
329
329
  ignoreElements: function (element) {
330
330
  if (CANVAS_EXPORT_IGNORE_TAGS.includes(element.tagName) ||
@@ -351,7 +351,7 @@ export class EdgelessClipboardController extends PageClipboard {
351
351
  await replaceImgSrcWithSvg?.(element);
352
352
  },
353
353
  backgroundColor: 'transparent',
354
- useCORS: imageProxy ? false : true,
354
+ useCORS: !imageProxy,
355
355
  proxy: imageProxy,
356
356
  };
357
357
  const _drawTopLevelBlock = async (block, isInFrame = false) => {
@@ -1,13 +1,33 @@
1
1
  import { KeymapExtension } from '../../std/index.js';
2
+ const NATIVE_FOCUSABLE_SELECTOR = 'button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])';
3
+ /**
4
+ * The editor host swallows Tab and Shift-Tab so that indentation stays under
5
+ * the control of the block-scoped handlers. That would however trap the focus
6
+ * on the native controls the editor renders (the collapse button of headings
7
+ * and lists, for instance), which then could neither be reached nor left with
8
+ * the keyboard. When the event originates from such a control — and not from
9
+ * editable text — the native tabbing behaviour is let through.
10
+ */
11
+ const isNativeFocusableTarget = (target) => {
12
+ if (!(target instanceof Element))
13
+ return false;
14
+ if (target instanceof HTMLElement && target.isContentEditable)
15
+ return false;
16
+ return !!target.closest(NATIVE_FOCUSABLE_SELECTOR);
17
+ };
2
18
  export const fallbackKeymap = KeymapExtension(() => {
3
19
  return {
4
20
  Tab: ctx => {
5
21
  const event = ctx.get('defaultState').event;
22
+ if (isNativeFocusableTarget(event.target))
23
+ return;
6
24
  event.stopPropagation();
7
25
  event.preventDefault();
8
26
  },
9
27
  'Shift-Tab': ctx => {
10
28
  const event = ctx.get('defaultState').event;
29
+ if (isNativeFocusableTarget(event.target))
30
+ return;
11
31
  event.stopPropagation();
12
32
  event.preventDefault();
13
33
  },
@@ -3,12 +3,14 @@ import { type BlockStdScope, type EditorHost } from '../../../std/index.js';
3
3
  import { GfxBlockElementModel, type GfxController, GfxPrimitiveElementModel } from '../../../std/gfx/index.js';
4
4
  import type { ExtensionType, Store } from '../../../store/index.js';
5
5
  import { CanvasRenderer } from '../../renderer/canvas-renderer.js';
6
- export type ExportOptions = {
7
- imageProxyEndpoint: string;
8
- };
9
6
  export declare class ExportManager {
10
7
  readonly std: BlockStdScope;
11
- private readonly _exportOptions;
8
+ /**
9
+ * The image proxy every remote-image fetch of the export goes through —
10
+ * resolved from {@link ImageProxyService}, the one host seam for it, never
11
+ * from a local constant. Empty means « fetch direct » (issue #258).
12
+ */
13
+ private get _imageProxyURL();
12
14
  replaceImgSrcWithSvg: (element: HTMLElement) => Promise<void>;
13
15
  get doc(): Store;
14
16
  get editorHost(): EditorHost;
@@ -1,6 +1,6 @@
1
1
  import { ImageBlockModel } from '../../../affine-model/index.js';
2
- import { FetchUtils } from '../../../affine-shared/adapters/index.js';
3
- import { CANVAS_EXPORT_IGNORE_TAGS, DEFAULT_IMAGE_PROXY_ENDPOINT, } from '../../../affine-shared/consts/index.js';
2
+ import { FetchUtils, ImageProxyService } from '../../../affine-shared/adapters/index.js';
3
+ import { CANVAS_EXPORT_IGNORE_TAGS } from '../../../affine-shared/consts/index.js';
4
4
  import { isInsidePageEditor, matchModels } from '../../../affine-shared/utils/index.js';
5
5
  import { BlockSuiteError, ErrorCode } from '../../../global/exceptions/index.js';
6
6
  import { deserializeXYWH } from '../../../global/gfx/index.js';
@@ -10,6 +10,14 @@ import { CanvasRenderer } from '../../renderer/canvas-renderer.js';
10
10
  import { getBgGridGap } from '../../utils/get-bg-grip-gap.js';
11
11
  import { FileExporter } from './file-exporter.js';
12
12
  export class ExportManager {
13
+ /**
14
+ * The image proxy every remote-image fetch of the export goes through —
15
+ * resolved from {@link ImageProxyService}, the one host seam for it, never
16
+ * from a local constant. Empty means « fetch direct » (issue #258).
17
+ */
18
+ get _imageProxyURL() {
19
+ return this.std.get(ImageProxyService).imageProxyURL;
20
+ }
13
21
  get doc() {
14
22
  return this.std.store;
15
23
  }
@@ -18,14 +26,11 @@ export class ExportManager {
18
26
  }
19
27
  constructor(std) {
20
28
  this.std = std;
21
- this._exportOptions = {
22
- imageProxyEndpoint: DEFAULT_IMAGE_PROXY_ENDPOINT,
23
- };
24
29
  this.replaceImgSrcWithSvg = async (element) => {
25
30
  const imgList = Array.from(element.querySelectorAll('img'));
26
31
  // Create an array of promises
27
32
  const promises = imgList.map(img => {
28
- return FetchUtils.fetchImage(img.src, undefined, this._exportOptions.imageProxyEndpoint)
33
+ return FetchUtils.fetchImage(img.src, undefined, this._imageProxyURL)
29
34
  .then(response => response && response.blob())
30
35
  .then(async (blob) => {
31
36
  if (!blob)
@@ -159,8 +164,8 @@ export class ExportManager {
159
164
  x: pageLeft - viewport.left,
160
165
  width: pageWidth,
161
166
  height: viewportHeight,
162
- useCORS: this._exportOptions.imageProxyEndpoint ? false : true,
163
- proxy: this._exportOptions.imageProxyEndpoint,
167
+ useCORS: !this._imageProxyURL,
168
+ proxy: this._imageProxyURL,
164
169
  };
165
170
  let data;
166
171
  try {
@@ -228,8 +233,8 @@ export class ExportManager {
228
233
  });
229
234
  await this.replaceImgSrcWithSvg(element);
230
235
  },
231
- useCORS: this._exportOptions.imageProxyEndpoint ? false : true,
232
- proxy: this._exportOptions.imageProxyEndpoint,
236
+ useCORS: !this._imageProxyURL,
237
+ proxy: this._imageProxyURL,
233
238
  };
234
239
  let data;
235
240
  try {
@@ -21,9 +21,9 @@ export class PriorityQueue {
21
21
  if (rightChildIndex < length) {
22
22
  leftChild = this.heap[leftChildIndex];
23
23
  rightChild = this.heap[rightChildIndex];
24
- if ((swap === null &&
24
+ if ((swap === -1 &&
25
25
  this._compare(rightChild.priority, element.priority) < 0) ||
26
- (swap !== null &&
26
+ (swap !== -1 &&
27
27
  this._compare(rightChild.priority, leftChild.priority) < 0)) {
28
28
  swap = rightChildIndex;
29
29
  }
@@ -6,6 +6,11 @@ export declare class ToggleButton extends ToggleButton_base {
6
6
  render(): import("lit-html").TemplateResult<1>;
7
7
  accessor collapsed: boolean;
8
8
  accessor updateCollapsed: (collapsed: boolean) => void;
9
+ /**
10
+ * Id of the children container this button expands and collapses,
11
+ * exposed to assistive technologies through `aria-controls`.
12
+ */
13
+ accessor controls: string;
9
14
  }
10
15
  declare global {
11
16
  interface HTMLElementTagNameMap {
@@ -48,13 +48,18 @@ let ToggleButton = (() => {
48
48
  let _updateCollapsed_decorators;
49
49
  let _updateCollapsed_initializers = [];
50
50
  let _updateCollapsed_extraInitializers = [];
51
+ let _controls_decorators;
52
+ let _controls_initializers = [];
53
+ let _controls_extraInitializers = [];
51
54
  return class ToggleButton extends _classSuper {
52
55
  static {
53
56
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
54
57
  _collapsed_decorators = [property({ attribute: false })];
55
58
  _updateCollapsed_decorators = [property({ attribute: false })];
59
+ _controls_decorators = [property({ attribute: false })];
56
60
  __esDecorate(this, null, _collapsed_decorators, { kind: "accessor", name: "collapsed", static: false, private: false, access: { has: obj => "collapsed" in obj, get: obj => obj.collapsed, set: (obj, value) => { obj.collapsed = value; } }, metadata: _metadata }, _collapsed_initializers, _collapsed_extraInitializers);
57
61
  __esDecorate(this, null, _updateCollapsed_decorators, { kind: "accessor", name: "updateCollapsed", static: false, private: false, access: { has: obj => "updateCollapsed" in obj, get: obj => obj.updateCollapsed, set: (obj, value) => { obj.updateCollapsed = value; } }, metadata: _metadata }, _updateCollapsed_initializers, _updateCollapsed_extraInitializers);
62
+ __esDecorate(this, null, _controls_decorators, { kind: "accessor", name: "controls", static: false, private: false, access: { has: obj => "controls" in obj, get: obj => obj.controls, set: (obj, value) => { obj.controls = value; } }, metadata: _metadata }, _controls_initializers, _controls_extraInitializers);
58
63
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
59
64
  }
60
65
  static { this.styles = css `
@@ -63,6 +68,11 @@ let ToggleButton = (() => {
63
68
  align-items: start;
64
69
  justify-content: start;
65
70
  position: absolute;
71
+ padding: 0;
72
+ border: 0;
73
+ color: inherit;
74
+ background: transparent;
75
+ font: inherit;
66
76
  width: 16px;
67
77
  height: 16px;
68
78
  top: calc((1em - 16px) / 2 + 5px);
@@ -78,6 +88,11 @@ let ToggleButton = (() => {
78
88
  background: var(--affine-hover-color);
79
89
  }
80
90
 
91
+ .toggle-icon:focus-visible {
92
+ opacity: 1;
93
+ outline: 1px solid var(--affine-primary-color);
94
+ }
95
+
81
96
  .toggle-icon[data-collapsed='true'] {
82
97
  opacity: 1;
83
98
  }
@@ -100,32 +115,23 @@ let ToggleButton = (() => {
100
115
  }
101
116
  `; }
102
117
  render() {
103
- const toggleDownTemplate = html `
104
- <div
105
- contenteditable="false"
106
- class="toggle-icon"
107
- @click=${() => this.updateCollapsed(!this.collapsed)}
108
- >
109
- ${ToggleDownIcon({
110
- width: '16px',
111
- height: '16px',
112
- })}
113
- </div>
114
- `;
115
- const toggleRightTemplate = html `
116
- <div
118
+ return html `
119
+ <button
120
+ type="button"
117
121
  contenteditable="false"
118
122
  class="toggle-icon"
119
123
  data-collapsed=${this.collapsed}
124
+ aria-label=${this.collapsed ? 'Expand content' : 'Collapse content'}
125
+ aria-expanded=${!this.collapsed}
126
+ aria-controls=${this.controls}
120
127
  @click=${() => this.updateCollapsed(!this.collapsed)}
121
128
  >
122
- ${ToggleRightIcon({
129
+ ${(this.collapsed ? ToggleRightIcon : ToggleDownIcon)({
123
130
  width: '16px',
124
131
  height: '16px',
125
132
  })}
126
- </div>
133
+ </button>
127
134
  `;
128
- return this.collapsed ? toggleRightTemplate : toggleDownTemplate;
129
135
  }
130
136
  #collapsed_accessor_storage = __runInitializers(this, _collapsed_initializers, void 0);
131
137
  get collapsed() { return this.#collapsed_accessor_storage; }
@@ -133,9 +139,16 @@ let ToggleButton = (() => {
133
139
  #updateCollapsed_accessor_storage = (__runInitializers(this, _collapsed_extraInitializers), __runInitializers(this, _updateCollapsed_initializers, void 0));
134
140
  get updateCollapsed() { return this.#updateCollapsed_accessor_storage; }
135
141
  set updateCollapsed(value) { this.#updateCollapsed_accessor_storage = value; }
142
+ #controls_accessor_storage = (__runInitializers(this, _updateCollapsed_extraInitializers), __runInitializers(this, _controls_initializers, void 0));
143
+ /**
144
+ * Id of the children container this button expands and collapses,
145
+ * exposed to assistive technologies through `aria-controls`.
146
+ */
147
+ get controls() { return this.#controls_accessor_storage; }
148
+ set controls(value) { this.#controls_accessor_storage = value; }
136
149
  constructor() {
137
150
  super(...arguments);
138
- __runInitializers(this, _updateCollapsed_extraInitializers);
151
+ __runInitializers(this, _controls_extraInitializers);
139
152
  }
140
153
  };
141
154
  })();