@formicoidea/labre-core 0.50.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) hide show
  1. package/dist/_pkgs/affine-block-attachment/asset-picker.d.ts +24 -0
  2. package/dist/_pkgs/affine-block-attachment/asset-picker.js +87 -0
  3. package/dist/_pkgs/affine-block-attachment/configs/slash-menu.js +3 -20
  4. package/dist/_pkgs/affine-block-attachment/index.d.ts +1 -0
  5. package/dist/_pkgs/affine-block-attachment/index.js +1 -0
  6. package/dist/_pkgs/affine-block-attachment/utils.d.ts +9 -1
  7. package/dist/_pkgs/affine-block-attachment/utils.js +9 -0
  8. package/dist/_pkgs/affine-block-bookmark/adapters/markdown/markdown.js +14 -1
  9. package/dist/_pkgs/affine-block-bookmark/adapters/notion-html.js +5 -1
  10. package/dist/_pkgs/affine-block-database/adapters/markdown.js +5 -3
  11. package/dist/_pkgs/affine-block-embed/common/adapters/html.js +5 -3
  12. package/dist/_pkgs/affine-block-embed/common/adapters/markdown.js +11 -0
  13. package/dist/_pkgs/affine-block-frame/frame-template.d.ts +58 -0
  14. package/dist/_pkgs/affine-block-frame/frame-template.js +207 -0
  15. package/dist/_pkgs/affine-block-frame/frame-toolbar.js +19 -3
  16. package/dist/_pkgs/affine-block-frame/index.d.ts +1 -0
  17. package/dist/_pkgs/affine-block-frame/index.js +1 -0
  18. package/dist/_pkgs/affine-block-frame/translations.d.ts +7 -0
  19. package/dist/_pkgs/affine-block-frame/translations.js +16 -1
  20. package/dist/_pkgs/affine-block-frame/view.js +6 -0
  21. package/dist/_pkgs/affine-block-image/asset-picker.d.ts +63 -0
  22. package/dist/_pkgs/affine-block-image/asset-picker.js +183 -0
  23. package/dist/_pkgs/affine-block-image/commands/insert-images.d.ts +5 -0
  24. package/dist/_pkgs/affine-block-image/commands/insert-images.js +8 -8
  25. package/dist/_pkgs/affine-block-image/components/page-image-block.d.ts +11 -0
  26. package/dist/_pkgs/affine-block-image/components/page-image-block.js +81 -12
  27. package/dist/_pkgs/affine-block-image/configs/replace-action.d.ts +9 -0
  28. package/dist/_pkgs/affine-block-image/configs/replace-action.js +27 -0
  29. package/dist/_pkgs/affine-block-image/configs/toolbar.js +6 -0
  30. package/dist/_pkgs/affine-block-image/crop/crop-controller.d.ts +46 -0
  31. package/dist/_pkgs/affine-block-image/crop/crop-controller.js +104 -0
  32. package/dist/_pkgs/affine-block-image/crop/crop-math.d.ts +70 -0
  33. package/dist/_pkgs/affine-block-image/crop/crop-math.js +141 -0
  34. package/dist/_pkgs/affine-block-image/crop/crop-overlay.d.ts +57 -0
  35. package/dist/_pkgs/affine-block-image/crop/crop-overlay.js +343 -0
  36. package/dist/_pkgs/affine-block-image/crop/crop-toolbar.d.ts +12 -0
  37. package/dist/_pkgs/affine-block-image/crop/crop-toolbar.js +78 -0
  38. package/dist/_pkgs/affine-block-image/crop/write-crop.d.ts +16 -0
  39. package/dist/_pkgs/affine-block-image/crop/write-crop.js +57 -0
  40. package/dist/_pkgs/affine-block-image/edgeless-clipboard-config.js +4 -1
  41. package/dist/_pkgs/affine-block-image/effects.js +2 -0
  42. package/dist/_pkgs/affine-block-image/image-block.d.ts +2 -0
  43. package/dist/_pkgs/affine-block-image/image-block.js +2 -0
  44. package/dist/_pkgs/affine-block-image/image-edgeless-block.d.ts +2 -0
  45. package/dist/_pkgs/affine-block-image/image-edgeless-block.js +36 -8
  46. package/dist/_pkgs/affine-block-image/image-resize-manager.js +15 -0
  47. package/dist/_pkgs/affine-block-image/index.d.ts +1 -0
  48. package/dist/_pkgs/affine-block-image/index.js +1 -0
  49. package/dist/_pkgs/affine-block-image/translations.d.ts +15 -0
  50. package/dist/_pkgs/affine-block-image/translations.js +43 -0
  51. package/dist/_pkgs/affine-block-image/utils.d.ts +13 -0
  52. package/dist/_pkgs/affine-block-image/utils.js +10 -1
  53. package/dist/_pkgs/affine-block-root/clipboard/page-clipboard.js +5 -0
  54. package/dist/_pkgs/affine-block-root/commands/page-export-commands.d.ts +2 -0
  55. package/dist/_pkgs/affine-block-root/commands/page-export-commands.js +71 -0
  56. package/dist/_pkgs/affine-block-root/index.d.ts +1 -0
  57. package/dist/_pkgs/affine-block-root/index.js +1 -0
  58. package/dist/_pkgs/affine-block-root/view.js +8 -0
  59. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/capability.d.ts +30 -0
  60. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/capability.js +44 -0
  61. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/colors.d.ts +26 -0
  62. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/colors.js +82 -0
  63. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/command.d.ts +18 -0
  64. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/command.js +91 -0
  65. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/index.d.ts +3 -0
  66. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/index.js +3 -0
  67. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/writer.d.ts +50 -0
  68. package/dist/_pkgs/affine-block-surface/extensions/drawio-export/writer.js +732 -0
  69. package/dist/_pkgs/affine-block-surface/extensions/export-manager/draw-image-block.d.ts +12 -0
  70. package/dist/_pkgs/affine-block-surface/extensions/export-manager/draw-image-block.js +15 -0
  71. package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +7 -1
  72. package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.d.ts +7 -1
  73. package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.js +60 -1
  74. package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.d.ts +2 -2
  75. package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.js +2 -2
  76. package/dist/_pkgs/affine-block-surface/extensions/export-svg/marker-version.d.ts +6 -0
  77. package/dist/_pkgs/affine-block-surface/extensions/export-svg/marker-version.js +8 -0
  78. package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.d.ts +28 -1
  79. package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.js +66 -6
  80. package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.d.ts +2 -7
  81. package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.js +2 -6
  82. package/dist/_pkgs/affine-block-surface/extensions/index.d.ts +2 -0
  83. package/dist/_pkgs/affine-block-surface/extensions/index.js +2 -0
  84. package/dist/_pkgs/affine-block-surface/extensions/interchange-export.d.ts +84 -0
  85. package/dist/_pkgs/affine-block-surface/extensions/interchange-export.js +150 -0
  86. package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +24 -2
  87. package/dist/_pkgs/affine-block-surface/extensions/interchange.d.ts +23 -2
  88. package/dist/_pkgs/affine-block-surface/extensions/interchange.js +4 -1
  89. package/dist/_pkgs/affine-block-surface/extensions/legend-role.d.ts +18 -0
  90. package/dist/_pkgs/affine-block-surface/extensions/legend-role.js +20 -0
  91. package/dist/_pkgs/affine-block-surface/extensions/legend.d.ts +2 -19
  92. package/dist/_pkgs/affine-block-surface/extensions/legend.js +2 -18
  93. package/dist/_pkgs/affine-block-surface/extensions/svg-sanitize.d.ts +67 -0
  94. package/dist/_pkgs/affine-block-surface/extensions/svg-sanitize.js +235 -0
  95. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch-translations.d.ts +1 -0
  96. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch-translations.js +9 -0
  97. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.d.ts +17 -13
  98. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.js +52 -80
  99. package/dist/_pkgs/affine-block-surface/extensions/xml-element.d.ts +98 -0
  100. package/dist/_pkgs/affine-block-surface/extensions/xml-element.js +261 -0
  101. package/dist/_pkgs/affine-block-surface/extensions/xml-reader.d.ts +238 -0
  102. package/dist/_pkgs/affine-block-surface/extensions/xml-reader.js +570 -0
  103. package/dist/_pkgs/affine-block-surface/framework-background/def.js +1 -1
  104. package/dist/_pkgs/affine-block-surface/index.d.ts +1 -0
  105. package/dist/_pkgs/affine-block-surface/index.js +1 -0
  106. package/dist/_pkgs/affine-block-surface/interchange-headless.d.ts +29 -0
  107. package/dist/_pkgs/affine-block-surface/interchange-headless.js +32 -0
  108. package/dist/_pkgs/affine-block-surface/view.js +22 -2
  109. package/dist/_pkgs/affine-block-table/adapters/utils.js +24 -1
  110. package/dist/_pkgs/affine-gfx-note/toolbar/note-menu.js +5 -8
  111. package/dist/_pkgs/affine-gfx-template/contribute.d.ts +14 -4
  112. package/dist/_pkgs/affine-gfx-template/contribute.js +47 -6
  113. package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.d.ts +7 -2
  114. package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.js +14 -40
  115. package/dist/_pkgs/affine-gfx-template/toolbar/resolve-name.d.ts +1 -1
  116. package/dist/_pkgs/affine-gfx-template/toolbar/resolve-name.js +1 -1
  117. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.d.ts +4 -4
  118. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.js +9 -9
  119. package/dist/_pkgs/affine-gfx-template/toolbar/template-type.d.ts +2 -4
  120. package/dist/_pkgs/affine-gfx-template/translations.d.ts +5 -0
  121. package/dist/_pkgs/affine-gfx-template/translations.js +9 -0
  122. package/dist/_pkgs/affine-inline-footnote/adapters/markdown/inline-delta.js +6 -0
  123. package/dist/_pkgs/affine-inline-footnote/adapters/markdown/markdown-inline.js +7 -0
  124. package/dist/_pkgs/affine-inline-link/adapters/html/html-inline.js +4 -1
  125. package/dist/_pkgs/affine-inline-link/adapters/html/inline-delta.js +3 -1
  126. package/dist/_pkgs/affine-inline-link/adapters/markdown/inline-delta.js +4 -1
  127. package/dist/_pkgs/affine-inline-link/adapters/notion-html/html-inline.js +2 -1
  128. package/dist/_pkgs/affine-inline-link/link-node/affine-link.d.ts +7 -0
  129. package/dist/_pkgs/affine-inline-link/link-node/affine-link.js +37 -0
  130. package/dist/_pkgs/affine-inline-link/link-node/index.d.ts +1 -0
  131. package/dist/_pkgs/affine-inline-link/link-node/index.js +1 -0
  132. package/dist/_pkgs/affine-inline-link/link-node/propose-views.d.ts +22 -0
  133. package/dist/_pkgs/affine-inline-link/link-node/propose-views.js +59 -0
  134. package/dist/_pkgs/affine-model/blocks/image/image-crop.d.ts +48 -0
  135. package/dist/_pkgs/affine-model/blocks/image/image-crop.js +78 -0
  136. package/dist/_pkgs/affine-model/blocks/image/image-model.d.ts +6 -0
  137. package/dist/_pkgs/affine-model/blocks/image/image-model.js +2 -0
  138. package/dist/_pkgs/affine-model/blocks/image/index.d.ts +1 -0
  139. package/dist/_pkgs/affine-model/blocks/image/index.js +1 -0
  140. package/dist/_pkgs/affine-model/consts/shape.d.ts +2 -0
  141. package/dist/_pkgs/affine-model/consts/shape.js +5 -0
  142. package/dist/_pkgs/affine-model/elements/bpmn/consts.d.ts +31 -0
  143. package/dist/_pkgs/affine-model/elements/bpmn/consts.js +31 -0
  144. package/dist/_pkgs/affine-model/elements/bpmn/index.d.ts +1 -0
  145. package/dist/_pkgs/affine-model/elements/bpmn/index.js +1 -0
  146. package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +0 -25
  147. package/dist/_pkgs/affine-model/elements/bpmn/pool.js +1 -25
  148. package/dist/_pkgs/affine-model/elements/shape/shape.d.ts +0 -2
  149. package/dist/_pkgs/affine-model/elements/shape/shape.js +1 -2
  150. package/dist/_pkgs/affine-model/elements/uml/consts.d.ts +133 -0
  151. package/dist/_pkgs/affine-model/elements/uml/consts.js +107 -0
  152. package/dist/_pkgs/affine-model/elements/uml/diagram.d.ts +1 -79
  153. package/dist/_pkgs/affine-model/elements/uml/diagram.js +1 -53
  154. package/dist/_pkgs/affine-model/elements/uml/fragment.d.ts +0 -15
  155. package/dist/_pkgs/affine-model/elements/uml/fragment.js +1 -15
  156. package/dist/_pkgs/affine-model/elements/uml/index.d.ts +1 -0
  157. package/dist/_pkgs/affine-model/elements/uml/index.js +1 -0
  158. package/dist/_pkgs/affine-model/elements/uml/partition.d.ts +0 -20
  159. package/dist/_pkgs/affine-model/elements/uml/partition.js +1 -20
  160. package/dist/_pkgs/affine-model/elements/uml/region.d.ts +0 -11
  161. package/dist/_pkgs/affine-model/elements/uml/region.js +1 -11
  162. package/dist/_pkgs/affine-model/headless.d.ts +19 -0
  163. package/dist/_pkgs/affine-model/headless.js +19 -0
  164. package/dist/_pkgs/affine-shared/headless.d.ts +10 -0
  165. package/dist/_pkgs/affine-shared/headless.js +10 -0
  166. package/dist/_pkgs/affine-shared/services/asset-picker-service.d.ts +63 -0
  167. package/dist/_pkgs/affine-shared/services/asset-picker-service.js +20 -0
  168. package/dist/_pkgs/affine-shared/services/frame-template-service.d.ts +83 -0
  169. package/dist/_pkgs/affine-shared/services/frame-template-service.js +17 -0
  170. package/dist/_pkgs/affine-shared/services/index.d.ts +3 -0
  171. package/dist/_pkgs/affine-shared/services/index.js +3 -0
  172. package/dist/_pkgs/affine-shared/services/thread-marks-service.d.ts +69 -0
  173. package/dist/_pkgs/affine-shared/services/thread-marks-service.js +20 -0
  174. package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +6 -0
  175. package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +10 -0
  176. package/dist/_pkgs/affine-widget-drag-handle/watchers/drag-event-watcher.js +8 -3
  177. package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +4 -16
  178. package/dist/_pkgs/affine-widget-linked-doc/transformers/html.d.ts +7 -0
  179. package/dist/_pkgs/affine-widget-linked-doc/transformers/html.js +25 -24
  180. package/dist/_pkgs/affine-widget-linked-doc/transformers/index.d.ts +1 -1
  181. package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.d.ts +7 -0
  182. package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.js +26 -25
  183. package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.d.ts +23 -0
  184. package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.js +14 -0
  185. package/dist/_pkgs/affine-widget-thread-marks/effects.d.ts +1 -0
  186. package/dist/_pkgs/affine-widget-thread-marks/effects.js +4 -0
  187. package/dist/_pkgs/affine-widget-thread-marks/excerpt.d.ts +19 -0
  188. package/dist/_pkgs/affine-widget-thread-marks/excerpt.js +68 -0
  189. package/dist/_pkgs/affine-widget-thread-marks/index.d.ts +5 -0
  190. package/dist/_pkgs/affine-widget-thread-marks/index.js +8 -0
  191. package/dist/_pkgs/affine-widget-thread-marks/toolbar.d.ts +49 -0
  192. package/dist/_pkgs/affine-widget-thread-marks/toolbar.js +108 -0
  193. package/dist/_pkgs/affine-widget-thread-marks/translations.d.ts +9 -0
  194. package/dist/_pkgs/affine-widget-thread-marks/translations.js +11 -0
  195. package/dist/_pkgs/affine-widget-thread-marks/view.d.ts +12 -0
  196. package/dist/_pkgs/affine-widget-thread-marks/view.js +25 -0
  197. package/dist/_pkgs/affine-widget-thread-marks/widget.d.ts +51 -0
  198. package/dist/_pkgs/affine-widget-thread-marks/widget.js +214 -0
  199. package/dist/commands.js +5 -2
  200. package/dist/extensions/view.js +2 -0
  201. package/dist/host-panels.d.ts +24 -1
  202. package/dist/host-panels.js +61 -10
  203. package/dist/interchange.d.ts +1 -0
  204. package/dist/interchange.js +1 -0
  205. package/dist/model/headless.d.ts +1 -0
  206. package/dist/model/headless.js +1 -0
  207. package/dist/shared/headless.d.ts +1 -0
  208. package/dist/shared/headless.js +1 -0
  209. package/dist/translations.js +10 -1
  210. package/package.json +13 -1
@@ -0,0 +1,343 @@
1
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
+ var _, done = false;
7
+ for (var i = decorators.length - 1; i >= 0; i--) {
8
+ var context = {};
9
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
+ if (kind === "accessor") {
14
+ if (result === void 0) continue;
15
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
+ if (_ = accept(result.get)) descriptor.get = _;
17
+ if (_ = accept(result.set)) descriptor.set = _;
18
+ if (_ = accept(result.init)) initializers.unshift(_);
19
+ }
20
+ else if (_ = accept(result)) {
21
+ if (kind === "field") initializers.unshift(_);
22
+ else descriptor[key] = _;
23
+ }
24
+ }
25
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
+ done = true;
27
+ };
28
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
+ var useValue = arguments.length > 2;
30
+ for (var i = 0; i < initializers.length; i++) {
31
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
+ }
33
+ return useValue ? value : void 0;
34
+ };
35
+ import { unsafeCSSVarV2 } from '../../affine-shared/theme/index.js';
36
+ import { WithDisposable } from '../../global/lit/index.js';
37
+ import { css, html, LitElement } from 'lit';
38
+ import { property, state } from 'lit/decorators.js';
39
+ import { repeat } from 'lit/directives/repeat.js';
40
+ import { styleMap } from 'lit/directives/style-map.js';
41
+ import { moveCrop, resizeCrop, WHOLE_IMAGE, } from './crop-math.js';
42
+ const CORNERS = ['nw', 'ne', 'sw', 'se'];
43
+ const rectStyle = (rect) => styleMap({
44
+ left: `${rect.x * 100}%`,
45
+ top: `${rect.y * 100}%`,
46
+ width: `${rect.w * 100}%`,
47
+ height: `${rect.h * 100}%`,
48
+ });
49
+ /**
50
+ * The crop frame (ADR 0037): the WHOLE source, laid over the block at the
51
+ * place it would occupy uncropped (the parent sizes this element with
52
+ * `wholeImageFrame`), dimmed outside the window being edited.
53
+ *
54
+ * Dragging the window moves it, dragging a corner resizes it, the ratio held
55
+ * when `ratio` is set. The draft lives here while the pointer is down; the
56
+ * release hands it to `onCommit` once — one gesture, one write, one undo
57
+ * step, decided by the caller (`writeImageCrop`).
58
+ *
59
+ * Pointer events never reach the editor while the frame is open: a press on
60
+ * the frame would otherwise start a canvas drag or a page selection. Escape,
61
+ * Enter or a press anywhere else calls `onExit`.
62
+ */
63
+ let ImageCropOverlay = (() => {
64
+ let _classSuper = WithDisposable(LitElement);
65
+ let _src_decorators;
66
+ let _src_initializers = [];
67
+ let _src_extraInitializers = [];
68
+ let _crop_decorators;
69
+ let _crop_initializers = [];
70
+ let _crop_extraInitializers = [];
71
+ let _ratio_decorators;
72
+ let _ratio_initializers = [];
73
+ let _ratio_extraInitializers = [];
74
+ let _rotate_decorators;
75
+ let _rotate_initializers = [];
76
+ let _rotate_extraInitializers = [];
77
+ let _onCommit_decorators;
78
+ let _onCommit_initializers = [];
79
+ let _onCommit_extraInitializers = [];
80
+ let _onExit_decorators;
81
+ let _onExit_initializers = [];
82
+ let _onExit_extraInitializers = [];
83
+ let _draft_decorators;
84
+ let _draft_initializers = [];
85
+ let _draft_extraInitializers = [];
86
+ return class ImageCropOverlay extends _classSuper {
87
+ constructor() {
88
+ super(...arguments);
89
+ this.#src_accessor_storage = __runInitializers(this, _src_initializers, '');
90
+ this.#crop_accessor_storage = (__runInitializers(this, _src_extraInitializers), __runInitializers(this, _crop_initializers, WHOLE_IMAGE));
91
+ this.#ratio_accessor_storage = (__runInitializers(this, _crop_extraInitializers), __runInitializers(this, _ratio_initializers, null));
92
+ this.#rotate_accessor_storage = (__runInitializers(this, _ratio_extraInitializers), __runInitializers(this, _rotate_initializers, 0));
93
+ this.#onCommit_accessor_storage = (__runInitializers(this, _rotate_extraInitializers), __runInitializers(this, _onCommit_initializers, () => { }));
94
+ this.#onExit_accessor_storage = (__runInitializers(this, _onCommit_extraInitializers), __runInitializers(this, _onExit_initializers, () => { }));
95
+ this.#draft_accessor_storage = (__runInitializers(this, _onExit_extraInitializers), __runInitializers(this, _draft_initializers, WHOLE_IMAGE));
96
+ this._dragging = (__runInitializers(this, _draft_extraInitializers), false);
97
+ /** Removes the listeners of the gesture in progress, if any. */
98
+ this._releaseGesture = null;
99
+ this._stop = (event) => event.stopPropagation();
100
+ this._onOutsidePointer = (event) => {
101
+ if (event.composedPath().includes(this))
102
+ return;
103
+ this.onExit();
104
+ };
105
+ this._onKeydown = (event) => {
106
+ if (event.key !== 'Escape' && event.key !== 'Enter')
107
+ return;
108
+ event.preventDefault();
109
+ event.stopPropagation();
110
+ this.onExit();
111
+ };
112
+ }
113
+ static {
114
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
115
+ _src_decorators = [property({ attribute: false })];
116
+ _crop_decorators = [property({ attribute: false })];
117
+ _ratio_decorators = [property({ attribute: false })];
118
+ _rotate_decorators = [property({ attribute: false })];
119
+ _onCommit_decorators = [property({ attribute: false })];
120
+ _onExit_decorators = [property({ attribute: false })];
121
+ _draft_decorators = [state()];
122
+ __esDecorate(this, null, _src_decorators, { kind: "accessor", name: "src", static: false, private: false, access: { has: obj => "src" in obj, get: obj => obj.src, set: (obj, value) => { obj.src = value; } }, metadata: _metadata }, _src_initializers, _src_extraInitializers);
123
+ __esDecorate(this, null, _crop_decorators, { kind: "accessor", name: "crop", static: false, private: false, access: { has: obj => "crop" in obj, get: obj => obj.crop, set: (obj, value) => { obj.crop = value; } }, metadata: _metadata }, _crop_initializers, _crop_extraInitializers);
124
+ __esDecorate(this, null, _ratio_decorators, { kind: "accessor", name: "ratio", static: false, private: false, access: { has: obj => "ratio" in obj, get: obj => obj.ratio, set: (obj, value) => { obj.ratio = value; } }, metadata: _metadata }, _ratio_initializers, _ratio_extraInitializers);
125
+ __esDecorate(this, null, _rotate_decorators, { kind: "accessor", name: "rotate", static: false, private: false, access: { has: obj => "rotate" in obj, get: obj => obj.rotate, set: (obj, value) => { obj.rotate = value; } }, metadata: _metadata }, _rotate_initializers, _rotate_extraInitializers);
126
+ __esDecorate(this, null, _onCommit_decorators, { kind: "accessor", name: "onCommit", static: false, private: false, access: { has: obj => "onCommit" in obj, get: obj => obj.onCommit, set: (obj, value) => { obj.onCommit = value; } }, metadata: _metadata }, _onCommit_initializers, _onCommit_extraInitializers);
127
+ __esDecorate(this, null, _onExit_decorators, { kind: "accessor", name: "onExit", static: false, private: false, access: { has: obj => "onExit" in obj, get: obj => obj.onExit, set: (obj, value) => { obj.onExit = value; } }, metadata: _metadata }, _onExit_initializers, _onExit_extraInitializers);
128
+ __esDecorate(this, null, _draft_decorators, { kind: "accessor", name: "draft", static: false, private: false, access: { has: obj => "draft" in obj, get: obj => obj.draft, set: (obj, value) => { obj.draft = value; } }, metadata: _metadata }, _draft_initializers, _draft_extraInitializers);
129
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
130
+ }
131
+ static { this.styles = css `
132
+ :host {
133
+ position: absolute;
134
+ z-index: 1;
135
+ display: block;
136
+ touch-action: none;
137
+ user-select: none;
138
+ }
139
+
140
+ .clip {
141
+ position: absolute;
142
+ inset: 0;
143
+ overflow: hidden;
144
+ }
145
+
146
+ img {
147
+ display: block;
148
+ width: 100%;
149
+ height: 100%;
150
+ pointer-events: none;
151
+ }
152
+
153
+ .shade {
154
+ position: absolute;
155
+ box-shadow: 0 0 0 100000px
156
+ ${unsafeCSSVarV2('layer/background/modal', 'rgba(0, 0, 0, 0.7)')};
157
+ pointer-events: none;
158
+ }
159
+
160
+ .window {
161
+ position: absolute;
162
+ box-sizing: border-box;
163
+ border: 1px solid ${unsafeCSSVarV2('button/primary')};
164
+ cursor: move;
165
+ }
166
+
167
+ .handle {
168
+ position: absolute;
169
+ width: 12px;
170
+ height: 12px;
171
+ box-sizing: border-box;
172
+ border: 1px solid ${unsafeCSSVarV2('button/primary')};
173
+ border-radius: 2px;
174
+ background: ${unsafeCSSVarV2('layer/pureWhite')};
175
+ }
176
+
177
+ /* A wider grip than the square drawn, for a finger. */
178
+ .handle::before {
179
+ content: '';
180
+ position: absolute;
181
+ inset: -10px;
182
+ }
183
+
184
+ .handle[data-corner='nw'] {
185
+ left: -6px;
186
+ top: -6px;
187
+ cursor: nwse-resize;
188
+ }
189
+
190
+ .handle[data-corner='ne'] {
191
+ right: -6px;
192
+ top: -6px;
193
+ cursor: nesw-resize;
194
+ }
195
+
196
+ .handle[data-corner='sw'] {
197
+ left: -6px;
198
+ bottom: -6px;
199
+ cursor: nesw-resize;
200
+ }
201
+
202
+ .handle[data-corner='se'] {
203
+ right: -6px;
204
+ bottom: -6px;
205
+ cursor: nwse-resize;
206
+ }
207
+ `; }
208
+ #src_accessor_storage;
209
+ get src() { return this.#src_accessor_storage; }
210
+ set src(value) { this.#src_accessor_storage = value; }
211
+ #crop_accessor_storage;
212
+ /** The stored crop the frame opens on; the whole image when uncropped. */
213
+ get crop() { return this.#crop_accessor_storage; }
214
+ set crop(value) { this.#crop_accessor_storage = value; }
215
+ #ratio_accessor_storage;
216
+ /** Normalised `w / h` to hold while resizing, `null` for a free crop. */
217
+ get ratio() { return this.#ratio_accessor_storage; }
218
+ set ratio(value) { this.#ratio_accessor_storage = value; }
219
+ #rotate_accessor_storage;
220
+ /** The block's rotation in degrees (canvas only), for pointer deltas. */
221
+ get rotate() { return this.#rotate_accessor_storage; }
222
+ set rotate(value) { this.#rotate_accessor_storage = value; }
223
+ #onCommit_accessor_storage;
224
+ get onCommit() { return this.#onCommit_accessor_storage; }
225
+ set onCommit(value) { this.#onCommit_accessor_storage = value; }
226
+ #onExit_accessor_storage;
227
+ get onExit() { return this.#onExit_accessor_storage; }
228
+ set onExit(value) { this.#onExit_accessor_storage = value; }
229
+ #draft_accessor_storage;
230
+ get draft() { return this.#draft_accessor_storage; }
231
+ set draft(value) { this.#draft_accessor_storage = value; }
232
+ /**
233
+ * The document-wide listeners (a press elsewhere, Escape, Enter) live only
234
+ * as long as this element: the frame is rendered while it is open and
235
+ * removed when it closes, which disposes them.
236
+ */
237
+ connectedCallback() {
238
+ super.connectedCallback();
239
+ this.disposables.add(() => this._releaseGesture?.());
240
+ this.disposables.addFromEvent(this, 'pointerdown', this._stop);
241
+ this.disposables.addFromEvent(this, 'click', this._stop);
242
+ this.disposables.addFromEvent(this, 'dblclick', this._stop);
243
+ this.disposables.addFromEvent(document, 'pointerdown', this._onOutsidePointer, true);
244
+ this.disposables.addFromEvent(document, 'keydown', this._onKeydown, true);
245
+ }
246
+ willUpdate(changed) {
247
+ if (changed.has('crop') && !this._dragging)
248
+ this.draft = this.crop;
249
+ }
250
+ /**
251
+ * Client pixels per layout pixel, read from the rendered box: the canvas
252
+ * zoom and any CSS scale, without asking the viewport. The bounding box of
253
+ * a rotated element is wider than the element, hence the trigonometry.
254
+ */
255
+ _geometry() {
256
+ const width = this.offsetWidth;
257
+ const height = this.offsetHeight;
258
+ const t = (this.rotate * Math.PI) / 180;
259
+ const cos = Math.cos(t);
260
+ const sin = Math.sin(t);
261
+ const span = width * Math.abs(cos) + height * Math.abs(sin);
262
+ const scale = span > 0 ? this.getBoundingClientRect().width / span : 1;
263
+ return { width, height, cos, sin, scale: scale || 1 };
264
+ }
265
+ _startGesture(event, grip) {
266
+ if (event.button !== 0)
267
+ return;
268
+ event.preventDefault();
269
+ event.stopPropagation();
270
+ const start = this.draft;
271
+ const originX = event.clientX;
272
+ const originY = event.clientY;
273
+ const { width, height, cos, sin, scale } = this._geometry();
274
+ if (width <= 0 || height <= 0)
275
+ return;
276
+ this._dragging = true;
277
+ const onMove = (move) => {
278
+ move.preventDefault();
279
+ move.stopPropagation();
280
+ const cdx = move.clientX - originX;
281
+ const cdy = move.clientY - originY;
282
+ const dx = (cdx * cos + cdy * sin) / scale / width;
283
+ const dy = (-cdx * sin + cdy * cos) / scale / height;
284
+ this.draft =
285
+ grip === 'move'
286
+ ? moveCrop(start, dx, dy)
287
+ : resizeCrop(start, grip, dx, dy, this.ratio);
288
+ };
289
+ const finish = (end, commit) => {
290
+ end.preventDefault();
291
+ end.stopPropagation();
292
+ this._releaseGesture?.();
293
+ this._dragging = false;
294
+ if (commit) {
295
+ this.onCommit(this.draft);
296
+ }
297
+ else {
298
+ this.draft = this.crop;
299
+ }
300
+ };
301
+ const onUp = (end) => finish(end, true);
302
+ const onCancel = (end) => finish(end, false);
303
+ this._releaseGesture?.();
304
+ window.addEventListener('pointermove', onMove, true);
305
+ window.addEventListener('pointerup', onUp, true);
306
+ window.addEventListener('pointercancel', onCancel, true);
307
+ this._releaseGesture = () => {
308
+ window.removeEventListener('pointermove', onMove, true);
309
+ window.removeEventListener('pointerup', onUp, true);
310
+ window.removeEventListener('pointercancel', onCancel, true);
311
+ this._releaseGesture = null;
312
+ };
313
+ }
314
+ render() {
315
+ const draft = this.draft;
316
+ return html `
317
+ <div class="clip">
318
+ <img src=${this.src} draggable="false" alt="" />
319
+ <div class="shade" style=${rectStyle(draft)}></div>
320
+ </div>
321
+ <div
322
+ class="window"
323
+ data-testid="image-crop-window"
324
+ style=${rectStyle(draft)}
325
+ @pointerdown=${(event) => this._startGesture(event, 'move')}
326
+ >
327
+ ${repeat(CORNERS, corner => corner, corner => html `
328
+ <div
329
+ class="handle"
330
+ data-corner=${corner}
331
+ data-testid=${`image-crop-handle-${corner}`}
332
+ @pointerdown=${(event) => {
333
+ event.stopPropagation();
334
+ this._startGesture(event, corner);
335
+ }}
336
+ ></div>
337
+ `)}
338
+ </div>
339
+ `;
340
+ }
341
+ };
342
+ })();
343
+ export { ImageCropOverlay };
@@ -0,0 +1,12 @@
1
+ import { type ToolbarAction, type ToolbarContext } from '../../affine-shared/services/index.js';
2
+ import type { ImageCropController } from './crop-controller.js';
3
+ /**
4
+ * « Rogner » in the image toolbar (ADR 0037), page and canvas alike: the
5
+ * toolbar's own `editor-menu-button` of `editor-menu-action`s, nothing drawn
6
+ * anew. Hidden on a readonly page and until the picture has loaded — a preset
7
+ * needs the source's size.
8
+ */
9
+ export declare function imageCropToolbarAction(blockOf: (ctx: ToolbarContext) => {
10
+ cropController: ImageCropController;
11
+ blobUrl?: string | null;
12
+ } | null): ToolbarAction;
@@ -0,0 +1,78 @@
1
+ import { EditorChevronDown } from '../../affine-components/toolbar/index.js';
2
+ import { translateKey, } from '../../affine-shared/services/index.js';
3
+ import { CropIcon } from '@blocksuite/icons/lit';
4
+ import { html, nothing } from 'lit';
5
+ import { repeat } from 'lit/directives/repeat.js';
6
+ import { IMAGE_TOOLBAR_CROP, IMAGE_TOOLBAR_CROP_1_1, IMAGE_TOOLBAR_CROP_4_3, IMAGE_TOOLBAR_CROP_16_9, IMAGE_TOOLBAR_CROP_FREE, IMAGE_TOOLBAR_CROP_ORIGINAL, } from '../translations.js';
7
+ const MASK_ENTRIES = [
8
+ ['free', IMAGE_TOOLBAR_CROP_FREE],
9
+ ['16:9', IMAGE_TOOLBAR_CROP_16_9],
10
+ ['4:3', IMAGE_TOOLBAR_CROP_4_3],
11
+ ['1:1', IMAGE_TOOLBAR_CROP_1_1],
12
+ ];
13
+ const testIdOf = (mask) => `image-crop-${mask.replace(':', '-')}`;
14
+ /**
15
+ * « Rogner » in the image toolbar (ADR 0037), page and canvas alike: the
16
+ * toolbar's own `editor-menu-button` of `editor-menu-action`s, nothing drawn
17
+ * anew. Hidden on a readonly page and until the picture has loaded — a preset
18
+ * needs the source's size.
19
+ */
20
+ export function imageCropToolbarAction(blockOf) {
21
+ return {
22
+ id: 'b.crop',
23
+ when: ctx => !ctx.readonly,
24
+ content(ctx) {
25
+ const block = blockOf(ctx);
26
+ if (!block?.blobUrl)
27
+ return null;
28
+ const controller = block.cropController;
29
+ const crop = controller.crop$.value;
30
+ const current = crop ? (crop.mask ?? 'free') : null;
31
+ const label = translateKey(ctx.std, ...IMAGE_TOOLBAR_CROP);
32
+ const original = translateKey(ctx.std, ...IMAGE_TOOLBAR_CROP_ORIGINAL);
33
+ return html `
34
+ <editor-menu-button
35
+ .contentPadding="${'8px'}"
36
+ .button=${html `
37
+ <editor-icon-button
38
+ aria-label=${label}
39
+ data-testid="image-crop-button"
40
+ .tooltip=${label}
41
+ >
42
+ ${CropIcon()} ${EditorChevronDown}
43
+ </editor-icon-button>
44
+ `}
45
+ >
46
+ <div data-size="small" data-orientation="vertical">
47
+ ${repeat(MASK_ENTRIES, ([mask]) => mask, ([mask, wording]) => {
48
+ const word = translateKey(ctx.std, ...wording);
49
+ return html `
50
+ <editor-menu-action
51
+ aria-label=${word}
52
+ data-testid=${testIdOf(mask)}
53
+ ?data-selected=${current === mask}
54
+ @click=${() => controller.choose(mask)}
55
+ >
56
+ <span class="label">${word}</span>
57
+ </editor-menu-action>
58
+ `;
59
+ })}
60
+ ${crop
61
+ ? html `
62
+ <editor-menu-action
63
+ aria-label=${original}
64
+ data-testid="image-crop-original"
65
+ @click=${() => {
66
+ controller.restoreOriginal();
67
+ }}
68
+ >
69
+ <span class="label">${original}</span>
70
+ </editor-menu-action>
71
+ `
72
+ : nothing}
73
+ </div>
74
+ </editor-menu-button>
75
+ `;
76
+ },
77
+ };
78
+ }
@@ -0,0 +1,16 @@
1
+ import { type ImageBlockModel, type ImageCropMask } from '../../affine-model/index.js';
2
+ import { type CropRect } from './crop-math.js';
3
+ /**
4
+ * Record one crop gesture (ADR 0037). Answers whether anything was written.
5
+ *
6
+ * - Nothing on a readonly store, nothing when the crop (mask included) is
7
+ * already the one asked for: an unchanged write is an empty undo step.
8
+ * - One gesture, one undo step: `captureSync` then a single transaction.
9
+ * - The whole image, or `undefined`, REMOVES the key; a full rectangle is
10
+ * never stored.
11
+ * - On the canvas the box is the visible part, so `xywh` moves in the same
12
+ * transaction: the picture keeps its scale and position. On a page,
13
+ * `width`/`height` stay those of the whole picture and nothing else moves.
14
+ * - `sourceId` is never touched: the bytes do not change.
15
+ */
16
+ export declare function writeImageCrop(model: ImageBlockModel, next: CropRect | undefined, mask: ImageCropMask, onCanvas: boolean): boolean;
@@ -0,0 +1,57 @@
1
+ import { isWholeImage, readImageCrop, } from '../../affine-model/index.js';
2
+ import { Bound } from '../../global/gfx/index.js';
3
+ import { cropBoundFor, sameCropRect, WHOLE_IMAGE, } from './crop-math.js';
4
+ /**
5
+ * Record one crop gesture (ADR 0037). Answers whether anything was written.
6
+ *
7
+ * - Nothing on a readonly store, nothing when the crop (mask included) is
8
+ * already the one asked for: an unchanged write is an empty undo step.
9
+ * - One gesture, one undo step: `captureSync` then a single transaction.
10
+ * - The whole image, or `undefined`, REMOVES the key; a full rectangle is
11
+ * never stored.
12
+ * - On the canvas the box is the visible part, so `xywh` moves in the same
13
+ * transaction: the picture keeps its scale and position. On a page,
14
+ * `width`/`height` stay those of the whole picture and nothing else moves.
15
+ * - `sourceId` is never touched: the bytes do not change.
16
+ */
17
+ export function writeImageCrop(model, next, mask, onCanvas) {
18
+ const store = model.store;
19
+ if (store.readonly)
20
+ return false;
21
+ const current = readImageCrop(model.props.crop);
22
+ const target = next && !isWholeImage(next)
23
+ ? {
24
+ x: next.x,
25
+ y: next.y,
26
+ w: next.w,
27
+ h: next.h,
28
+ ...(mask === 'free' ? {} : { mask }),
29
+ }
30
+ : undefined;
31
+ if (!current && !target)
32
+ return false;
33
+ if (current &&
34
+ target &&
35
+ sameCropRect(current, target) &&
36
+ (current.mask ?? 'free') === (target.mask ?? 'free')) {
37
+ return false;
38
+ }
39
+ const props = {};
40
+ if (onCanvas) {
41
+ const bound = Bound.deserialize(model.xywh);
42
+ if (bound.w > 0 && bound.h > 0) {
43
+ const { x, y, w, h } = cropBoundFor(bound, model.props.rotate ?? 0, current ?? WHOLE_IMAGE, target ?? WHOLE_IMAGE);
44
+ props.xywh = new Bound(x, y, w, h).serialize();
45
+ }
46
+ }
47
+ if (target)
48
+ props.crop = target;
49
+ store.captureSync();
50
+ store.transact(() => {
51
+ if (Object.keys(props).length > 0)
52
+ store.updateBlock(model, props);
53
+ if (!target)
54
+ delete model.props.crop;
55
+ });
56
+ return true;
57
+ }
@@ -3,7 +3,7 @@ import {} from '../store/index.js';
3
3
  export class EdgelessClipboardImageConfig extends EdgelessClipboardConfig {
4
4
  static { this.key = 'affine:image'; }
5
5
  async createBlock(image) {
6
- const { xywh, rotate, sourceId, size, width, height, caption } = image.props;
6
+ const { xywh, rotate, sourceId, size, width, height, caption, crop } = image.props;
7
7
  if (!this.surface)
8
8
  return null;
9
9
  if (!(await this.std.workspace.blobSync.get(sourceId))) {
@@ -17,6 +17,9 @@ export class EdgelessClipboardImageConfig extends EdgelessClipboardConfig {
17
17
  size,
18
18
  width,
19
19
  height,
20
+ // The box above is the cropped one: without its crop the copy would
21
+ // stretch the whole picture into it (ADR 0037).
22
+ crop,
20
23
  }, this.surface.model.id);
21
24
  }
22
25
  }
@@ -1,5 +1,6 @@
1
1
  import { ImageBlockFallbackCard } from './components/image-block-fallback.js';
2
2
  import { ImageBlockPageComponent } from './components/page-image-block.js';
3
+ import { ImageCropOverlay } from './crop/crop-overlay.js';
3
4
  import { ImageBlockComponent } from './image-block.js';
4
5
  import { ImageEdgelessBlockComponent } from './image-edgeless-block.js';
5
6
  import { ImageEdgelessPlaceholderBlockComponent } from './preview-image/edgeless.js';
@@ -9,6 +10,7 @@ export function effects() {
9
10
  customElements.define('affine-edgeless-image', ImageEdgelessBlockComponent);
10
11
  customElements.define('affine-page-image', ImageBlockPageComponent);
11
12
  customElements.define('affine-image-fallback-card', ImageBlockFallbackCard);
13
+ customElements.define('affine-image-crop-overlay', ImageCropOverlay);
12
14
  customElements.define('affine-placeholder-preview-image', ImagePlaceholderBlockComponent);
13
15
  customElements.define('affine-edgeless-placeholder-preview-image', ImageEdgelessPlaceholderBlockComponent);
14
16
  }
@@ -1,9 +1,11 @@
1
1
  import { CaptionedBlockComponent } from '../affine-components/caption/index.js';
2
2
  import { ResourceController } from '../affine-components/resource/index.js';
3
3
  import type { ImageBlockModel } from '../affine-model/index.js';
4
+ import { ImageCropController } from './crop/crop-controller.js';
4
5
  export declare class ImageBlockComponent extends CaptionedBlockComponent<ImageBlockModel> {
5
6
  resizeable$: import("@preact/signals-core").ReadonlySignal<boolean>;
6
7
  resourceController: ResourceController;
8
+ readonly cropController: ImageCropController;
7
9
  get blobUrl(): string | null;
8
10
  convertToCardView: () => void;
9
11
  copy: () => void;
@@ -49,6 +49,7 @@ import { html } from 'lit';
49
49
  import { query } from 'lit/decorators.js';
50
50
  import { styleMap } from 'lit/directives/style-map.js';
51
51
  import { when } from 'lit/directives/when.js';
52
+ import { ImageCropController } from './crop/crop-controller.js';
52
53
  import { IMAGE_LABEL } from './translations.js';
53
54
  import { copyImageBlob, downloadImageBlob, refreshData, turnImageIntoCardView, } from './utils.js';
54
55
  let ImageBlockComponent = (() => {
@@ -71,6 +72,7 @@ let ImageBlockComponent = (() => {
71
72
  super(...arguments);
72
73
  this.resizeable$ = computed(() => this.std.selection.value.some(selection => selection.is(ImageSelection) && selection.blockId === this.blockId));
73
74
  this.resourceController = new ResourceController(computed(() => this.model.props.sourceId$.value), 'Image');
75
+ this.cropController = new ImageCropController(this, false);
74
76
  this.convertToCardView = () => {
75
77
  turnImageIntoCardView(this).catch(console.error);
76
78
  };
@@ -2,9 +2,11 @@ import type { BlockCaptionEditor } from '../affine-components/caption/index.js';
2
2
  import { ResourceController } from '../affine-components/resource/index.js';
3
3
  import { type ImageBlockModel } from '../affine-model/index.js';
4
4
  import { GfxBlockComponent } from '../std/index.js';
5
+ import { ImageCropController } from './crop/crop-controller.js';
5
6
  export declare class ImageEdgelessBlockComponent extends GfxBlockComponent<ImageBlockModel> {
6
7
  static styles: import("lit").CSSResult;
7
8
  resourceController: ResourceController;
9
+ readonly cropController: ImageCropController;
8
10
  get blobUrl(): string | null;
9
11
  convertToCardView: () => void;
10
12
  copy: () => void;
@@ -47,6 +47,7 @@ import { css, html } from 'lit';
47
47
  import { query } from 'lit/decorators.js';
48
48
  import { styleMap } from 'lit/directives/style-map.js';
49
49
  import { when } from 'lit/directives/when.js';
50
+ import { croppedImageStyle, ImageCropController, } from './crop/crop-controller.js';
50
51
  import { IMAGE_LABEL, IMAGE_TOAST_DOWNLOAD_FAILED } from './translations.js';
51
52
  import { copyImageBlob, downloadImageBlob, refreshData, turnImageIntoCardView, } from './utils.js';
52
53
  let ImageEdgelessBlockComponent = (() => {
@@ -107,6 +108,16 @@ let ImageEdgelessBlockComponent = (() => {
107
108
  width: 100%;
108
109
  height: 100%;
109
110
  }
111
+
112
+ affine-edgeless-image .resizable-img {
113
+ position: relative;
114
+ }
115
+
116
+ affine-edgeless-image .labre-image-crop-window {
117
+ position: absolute;
118
+ inset: 0;
119
+ overflow: hidden;
120
+ }
110
121
  `; }
111
122
  get blobUrl() {
112
123
  return this.resourceController.blobUrl$.value;
@@ -130,6 +141,9 @@ let ImageEdgelessBlockComponent = (() => {
130
141
  const blobUrl = this.blobUrl;
131
142
  const imageLabel = translateKey(this.std, ...IMAGE_LABEL);
132
143
  const { rotate = 0, size = 0, caption = imageLabel } = this.model.props;
144
+ // The box is the visible part (ADR 0037): the source is drawn scaled
145
+ // and offset inside it, through a window that clips the rest.
146
+ const crop = this.cropController.crop$.value;
133
147
  const containerStyleMap = styleMap({
134
148
  display: 'flex',
135
149
  position: 'relative',
@@ -153,14 +167,27 @@ let ImageEdgelessBlockComponent = (() => {
153
167
  <div class="affine-image-container" style=${containerStyleMap}>
154
168
  ${when(blobUrl, () => html `
155
169
  <div class="resizable-img">
156
- <img
157
- class="drag-target"
158
- draggable="false"
159
- loading="lazy"
160
- src=${blobUrl}
161
- alt=${caption}
162
- @error=${this._handleError}
163
- />
170
+ ${crop
171
+ ? html `<div class="labre-image-crop-window">
172
+ <img
173
+ class="drag-target"
174
+ draggable="false"
175
+ loading="lazy"
176
+ src=${blobUrl}
177
+ alt=${caption}
178
+ style=${croppedImageStyle(crop)}
179
+ @error=${this._handleError}
180
+ />
181
+ </div>`
182
+ : html `<img
183
+ class="drag-target"
184
+ draggable="false"
185
+ loading="lazy"
186
+ src=${blobUrl}
187
+ alt=${caption}
188
+ @error=${this._handleError}
189
+ />`}
190
+ ${this.cropController.renderOverlay(blobUrl, rotate)}
164
191
  </div>
165
192
  ${when(loading, () => html `<div class="loading">${icon}</div>`)}
166
193
  ${when(Boolean(error && description), () => html `<affine-resource-status
@@ -190,6 +217,7 @@ let ImageEdgelessBlockComponent = (() => {
190
217
  constructor() {
191
218
  super(...arguments);
192
219
  this.resourceController = new ResourceController(computed(() => this.model.props.sourceId$.value), 'Image');
220
+ this.cropController = new ImageCropController(this, true);
193
221
  this.convertToCardView = () => {
194
222
  turnImageIntoCardView(this).catch(console.error);
195
223
  };