@formicoidea/labre-core 0.50.0 → 0.51.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 (191) 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/interchange-headless.d.ts +29 -0
  105. package/dist/_pkgs/affine-block-surface/interchange-headless.js +32 -0
  106. package/dist/_pkgs/affine-block-surface/view.js +22 -2
  107. package/dist/_pkgs/affine-block-table/adapters/utils.js +24 -1
  108. package/dist/_pkgs/affine-gfx-note/toolbar/note-menu.js +5 -8
  109. package/dist/_pkgs/affine-gfx-template/contribute.d.ts +14 -4
  110. package/dist/_pkgs/affine-gfx-template/contribute.js +47 -6
  111. package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.d.ts +7 -2
  112. package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.js +14 -40
  113. package/dist/_pkgs/affine-gfx-template/toolbar/resolve-name.d.ts +1 -1
  114. package/dist/_pkgs/affine-gfx-template/toolbar/resolve-name.js +1 -1
  115. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.d.ts +4 -4
  116. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.js +9 -9
  117. package/dist/_pkgs/affine-gfx-template/toolbar/template-type.d.ts +2 -4
  118. package/dist/_pkgs/affine-gfx-template/translations.d.ts +5 -0
  119. package/dist/_pkgs/affine-gfx-template/translations.js +9 -0
  120. package/dist/_pkgs/affine-inline-footnote/adapters/markdown/inline-delta.js +6 -0
  121. package/dist/_pkgs/affine-inline-footnote/adapters/markdown/markdown-inline.js +7 -0
  122. package/dist/_pkgs/affine-inline-link/adapters/html/html-inline.js +4 -1
  123. package/dist/_pkgs/affine-inline-link/adapters/html/inline-delta.js +3 -1
  124. package/dist/_pkgs/affine-inline-link/adapters/markdown/inline-delta.js +4 -1
  125. package/dist/_pkgs/affine-inline-link/adapters/notion-html/html-inline.js +2 -1
  126. package/dist/_pkgs/affine-inline-link/link-node/affine-link.d.ts +7 -0
  127. package/dist/_pkgs/affine-inline-link/link-node/affine-link.js +37 -0
  128. package/dist/_pkgs/affine-inline-link/link-node/index.d.ts +1 -0
  129. package/dist/_pkgs/affine-inline-link/link-node/index.js +1 -0
  130. package/dist/_pkgs/affine-inline-link/link-node/propose-views.d.ts +22 -0
  131. package/dist/_pkgs/affine-inline-link/link-node/propose-views.js +59 -0
  132. package/dist/_pkgs/affine-model/blocks/image/image-crop.d.ts +48 -0
  133. package/dist/_pkgs/affine-model/blocks/image/image-crop.js +78 -0
  134. package/dist/_pkgs/affine-model/blocks/image/image-model.d.ts +6 -0
  135. package/dist/_pkgs/affine-model/blocks/image/image-model.js +2 -0
  136. package/dist/_pkgs/affine-model/blocks/image/index.d.ts +1 -0
  137. package/dist/_pkgs/affine-model/blocks/image/index.js +1 -0
  138. package/dist/_pkgs/affine-model/consts/shape.d.ts +2 -0
  139. package/dist/_pkgs/affine-model/consts/shape.js +5 -0
  140. package/dist/_pkgs/affine-model/elements/bpmn/consts.d.ts +31 -0
  141. package/dist/_pkgs/affine-model/elements/bpmn/consts.js +31 -0
  142. package/dist/_pkgs/affine-model/elements/bpmn/index.d.ts +1 -0
  143. package/dist/_pkgs/affine-model/elements/bpmn/index.js +1 -0
  144. package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +0 -25
  145. package/dist/_pkgs/affine-model/elements/bpmn/pool.js +1 -25
  146. package/dist/_pkgs/affine-model/elements/shape/shape.d.ts +0 -2
  147. package/dist/_pkgs/affine-model/elements/shape/shape.js +1 -2
  148. package/dist/_pkgs/affine-model/elements/uml/consts.d.ts +133 -0
  149. package/dist/_pkgs/affine-model/elements/uml/consts.js +107 -0
  150. package/dist/_pkgs/affine-model/elements/uml/diagram.d.ts +1 -79
  151. package/dist/_pkgs/affine-model/elements/uml/diagram.js +1 -53
  152. package/dist/_pkgs/affine-model/elements/uml/fragment.d.ts +0 -15
  153. package/dist/_pkgs/affine-model/elements/uml/fragment.js +1 -15
  154. package/dist/_pkgs/affine-model/elements/uml/index.d.ts +1 -0
  155. package/dist/_pkgs/affine-model/elements/uml/index.js +1 -0
  156. package/dist/_pkgs/affine-model/elements/uml/partition.d.ts +0 -20
  157. package/dist/_pkgs/affine-model/elements/uml/partition.js +1 -20
  158. package/dist/_pkgs/affine-model/elements/uml/region.d.ts +0 -11
  159. package/dist/_pkgs/affine-model/elements/uml/region.js +1 -11
  160. package/dist/_pkgs/affine-model/headless.d.ts +19 -0
  161. package/dist/_pkgs/affine-model/headless.js +19 -0
  162. package/dist/_pkgs/affine-shared/headless.d.ts +10 -0
  163. package/dist/_pkgs/affine-shared/headless.js +10 -0
  164. package/dist/_pkgs/affine-shared/services/asset-picker-service.d.ts +63 -0
  165. package/dist/_pkgs/affine-shared/services/asset-picker-service.js +20 -0
  166. package/dist/_pkgs/affine-shared/services/frame-template-service.d.ts +83 -0
  167. package/dist/_pkgs/affine-shared/services/frame-template-service.js +17 -0
  168. package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
  169. package/dist/_pkgs/affine-shared/services/index.js +2 -0
  170. package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +6 -0
  171. package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +10 -0
  172. package/dist/_pkgs/affine-widget-drag-handle/watchers/drag-event-watcher.js +8 -3
  173. package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +4 -16
  174. package/dist/_pkgs/affine-widget-linked-doc/transformers/html.d.ts +7 -0
  175. package/dist/_pkgs/affine-widget-linked-doc/transformers/html.js +25 -24
  176. package/dist/_pkgs/affine-widget-linked-doc/transformers/index.d.ts +1 -1
  177. package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.d.ts +7 -0
  178. package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.js +26 -25
  179. package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.d.ts +23 -0
  180. package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.js +14 -0
  181. package/dist/commands.js +5 -2
  182. package/dist/host-panels.d.ts +21 -1
  183. package/dist/host-panels.js +50 -10
  184. package/dist/interchange.d.ts +1 -0
  185. package/dist/interchange.js +1 -0
  186. package/dist/model/headless.d.ts +1 -0
  187. package/dist/model/headless.js +1 -0
  188. package/dist/shared/headless.d.ts +1 -0
  189. package/dist/shared/headless.js +1 -0
  190. package/dist/translations.js +8 -1
  191. package/package.json +13 -1
@@ -1,4 +1,4 @@
1
- import { TOOLBAR_INSERT_INTO_PAGE, TOOLBAR_UNGROUP, } from '../affine-shared/services/index.js';
1
+ import { SAVE_AS_TEMPLATE, TOOLBAR_INSERT_INTO_PAGE, TOOLBAR_UNGROUP, } from '../affine-shared/services/index.js';
2
2
  /**
3
3
  * The frame's own seed: the default title a newly created frame is stamped
4
4
  * with (`Frame 3`), written into the document at creation
@@ -48,6 +48,19 @@ export const FRAME_TOAST_INSERTED_MESSAGE = [
48
48
  'Frame has been inserted into page',
49
49
  ];
50
50
  export const FRAME_TOOLBAR_UNGROUP = TOOLBAR_UNGROUP;
51
+ /**
52
+ * "Save as template" (the `FrameTemplateProvider` seam): the button, then
53
+ * the notification that says how the host's save went.
54
+ */
55
+ export const FRAME_TOOLBAR_SAVE_AS_TEMPLATE = SAVE_AS_TEMPLATE;
56
+ export const FRAME_TOAST_TEMPLATE_SAVED = [
57
+ 'com.labre.frame.toast.template-saved',
58
+ 'Frame saved as a template',
59
+ ];
60
+ export const FRAME_TOAST_TEMPLATE_SAVE_FAILED = [
61
+ 'com.labre.frame.toast.template-save-failed',
62
+ 'The frame could not be saved as a template',
63
+ ];
51
64
  export const FRAME_TOOLBAR_BACKGROUND = [
52
65
  'com.labre.frame.toolbar.background',
53
66
  'Background',
@@ -117,6 +130,8 @@ export const FRAME_CHROME_WORDINGS = [
117
130
  FRAME_DENSE_MENU_SLIDE,
118
131
  FRAME_TOAST_INSERTED_MESSAGE,
119
132
  FRAME_TOOLBAR_BACKGROUND,
133
+ FRAME_TOAST_TEMPLATE_SAVED,
134
+ FRAME_TOAST_TEMPLATE_SAVE_FAILED,
120
135
  FRAME_PRESENT_TOOL_TOOLTIP,
121
136
  FRAME_PRESENT_PREVIOUS,
122
137
  FRAME_PRESENT_NEXT,
@@ -8,6 +8,7 @@ import { effects } from './effects.js';
8
8
  import { FrameBlockInteraction } from './frame-block.js';
9
9
  import { FrameHighlightManager } from './frame-highlight-manager.js';
10
10
  import { FrameBlockSpec } from './frame-spec.js';
11
+ import { FrameTemplateWatcher } from './frame-template.js';
11
12
  import { FrameTool } from './frame-tool.js';
12
13
  import { frameToolbarExtension } from './frame-toolbar.js';
13
14
  import { edgelessNavigatorBgWidget } from './present/navigator-bg-widget.js';
@@ -33,6 +34,11 @@ export class FrameViewExtension extends ViewExtensionProvider {
33
34
  context.register(edgelessNavigatorBgWidget);
34
35
  context.register(EdgelessClipboardFrameConfig);
35
36
  context.register(FrameBlockInteraction);
37
+ // One watcher per editor a viewer edits in: a preview mounted on the
38
+ // same store would report the same deletion a second time.
39
+ if (!this.isPreview(context.scope)) {
40
+ context.register(FrameTemplateWatcher);
41
+ }
36
42
  // The presentation order's one write (ADR 0034). The frame panel also
37
43
  // mounts in page mode: it runs the imported descriptor, not this
38
44
  // registration, so its reorder works in both modes. `canvas.present`
@@ -0,0 +1,63 @@
1
+ import { type ImageBlockModel, type ImageBlockProps } from '../affine-model/index.js';
2
+ import { type AssetRef } from '../affine-shared/services/index.js';
3
+ import { type IVec } from '../global/gfx/index.js';
4
+ import type { BlockStdScope } from '../std/index.js';
5
+ import type { BlockModel } from '../store/index.js';
6
+ /**
7
+ * Images from the host's asset library (`AssetPickerProvider`, labre P4): the
8
+ * block references a blob the host already stores, by its `sourceId`. Nothing
9
+ * here calls `BlobSource.set`; the bytes are read once, through the store's
10
+ * blob sources, only to measure an image whose ref carries no size.
11
+ *
12
+ * Without a host every entry point keeps the system file chooser and the
13
+ * upload path of `utils.ts`. Pasting and dropping never come here.
14
+ */
15
+ type Size = {
16
+ width: number;
17
+ height: number;
18
+ };
19
+ /**
20
+ * Adds one image block per image ref next to `targetModel`, as one undo
21
+ * step. Refs that are not `image/*`, or whose size cannot be read, are
22
+ * skipped. Nothing is written on a read-only document.
23
+ */
24
+ export declare function addSiblingImageBlocksFromAssets(std: BlockStdScope, assets: readonly AssetRef[], targetModel: BlockModel, placement?: 'after' | 'before'): Promise<string[]>;
25
+ /**
26
+ * The canvas counterpart of {@link addSiblingImageBlocksFromAssets}: places
27
+ * the images like `addImages` does (around `point`, active layer, selected).
28
+ */
29
+ export declare function addImagesFromAssets(std: BlockStdScope, assets: readonly AssetRef[], options: {
30
+ point?: IVec;
31
+ maxWidth?: number;
32
+ shouldTransformPoint?: boolean;
33
+ }): Promise<string[]>;
34
+ /**
35
+ * The props that swap `model`'s picture for one of `natural` size while it
36
+ * keeps its displayed width and follows the new ratio. On the canvas the
37
+ * frame keeps its top-left corner and width; in a page the block keeps its
38
+ * `width` (an image still at its natural size, `width` unset, stays so).
39
+ */
40
+ export declare function replacementProps(model: ImageBlockModel, asset: Pick<AssetRef, 'sourceId' | 'size'>, natural: Size): Partial<ImageBlockProps>;
41
+ /**
42
+ * Swaps the picture of one image block for `asset`, keeping its displayed
43
+ * width and its caption, in one undo step. `false` when nothing was written:
44
+ * a read-only document, the same blob, a ref that is not an image, a size
45
+ * that cannot be read, or a block deleted meanwhile.
46
+ */
47
+ export declare function replaceImage(std: BlockStdScope, model: ImageBlockModel, asset: AssetRef): Promise<boolean>;
48
+ /**
49
+ * The image entries of the slash menu and of the mobile keyboard toolbar:
50
+ * the host's asset dialog when there is one, the system file chooser
51
+ * otherwise (the upload path, unchanged).
52
+ */
53
+ export declare function pickAndAddSiblingImageBlocks(std: BlockStdScope, targetModel: BlockModel, placement?: 'after' | 'before'): Promise<string[]>;
54
+ /** The canvas's image button: the host's asset dialog, or the file chooser. */
55
+ export declare function pickAndAddImages(std: BlockStdScope, options: {
56
+ maxWidth?: number;
57
+ }): Promise<string[]>;
58
+ /**
59
+ * The image toolbar's "Replace": only with a host (`false` otherwise, and
60
+ * the toolbar does not offer it), one ref, `mode: 'replace'`.
61
+ */
62
+ export declare function pickAndReplaceImage(std: BlockStdScope, model: ImageBlockModel): Promise<boolean>;
63
+ export {};
@@ -0,0 +1,183 @@
1
+ import { toast } from '../affine-components/toast/index.js';
2
+ import { ImageBlockSchema, } from '../affine-model/index.js';
3
+ import { AssetPickerProvider, translateKey, } from '../affine-shared/services/index.js';
4
+ import { getImageFilesFromLocal, readImageSize, } from '../affine-shared/utils/index.js';
5
+ import { Bound } from '../global/gfx/index.js';
6
+ import { IMAGE_TOAST_READ_SIZE_FAILED } from './translations.js';
7
+ import { addImages, addSiblingImageBlocks, placeImagesOnCanvas, } from './utils.js';
8
+ function isSize(width = 0, height = 0) {
9
+ return width > 0 && height > 0;
10
+ }
11
+ async function naturalSize(store, asset) {
12
+ const { width = 0, height = 0 } = asset;
13
+ if (isSize(width, height))
14
+ return { width, height };
15
+ const blob = await store.blobSync.get(asset.sourceId);
16
+ if (!blob)
17
+ return null;
18
+ // `readImageSize` trusts the MIME type; a blob source may not keep it.
19
+ const typed = blob.type ? blob : new Blob([blob], { type: asset.type });
20
+ const size = await readImageSize(typed);
21
+ return isSize(size.width, size.height) ? size : null;
22
+ }
23
+ async function imagePropsFrom(std, assets) {
24
+ const images = assets.filter(asset => asset.type.startsWith('image/'));
25
+ const sized = await Promise.all(images.map(async (asset) => {
26
+ const size = await naturalSize(std.store, asset);
27
+ return size
28
+ ? { sourceId: asset.sourceId, size: asset.size, ...size }
29
+ : null;
30
+ }));
31
+ const props = sized.filter((p) => p !== null);
32
+ if (props.length < images.length) {
33
+ toast(std.host, translateKey(std, ...IMAGE_TOAST_READ_SIZE_FAILED));
34
+ }
35
+ return props;
36
+ }
37
+ /**
38
+ * Adds one image block per image ref next to `targetModel`, as one undo
39
+ * step. Refs that are not `image/*`, or whose size cannot be read, are
40
+ * skipped. Nothing is written on a read-only document.
41
+ */
42
+ export async function addSiblingImageBlocksFromAssets(std, assets, targetModel, placement = 'after') {
43
+ if (std.store.readonly)
44
+ return [];
45
+ const props = await imagePropsFrom(std, assets);
46
+ // Re-checked: the document may have turned read-only while measuring.
47
+ if (!props.length || std.store.readonly)
48
+ return [];
49
+ const flavour = ImageBlockSchema.model.flavour;
50
+ std.store.captureSync();
51
+ return std.store.addSiblingBlocks(targetModel, props.map(p => ({ ...p, flavour })), placement);
52
+ }
53
+ /**
54
+ * The canvas counterpart of {@link addSiblingImageBlocksFromAssets}: places
55
+ * the images like `addImages` does (around `point`, active layer, selected).
56
+ */
57
+ export async function addImagesFromAssets(std, assets, options) {
58
+ if (std.store.readonly)
59
+ return [];
60
+ const props = await imagePropsFrom(std, assets);
61
+ if (!props.length || std.store.readonly)
62
+ return [];
63
+ std.store.captureSync();
64
+ return placeImagesOnCanvas(std, props, options);
65
+ }
66
+ /**
67
+ * The props that swap `model`'s picture for one of `natural` size while it
68
+ * keeps its displayed width and follows the new ratio. On the canvas the
69
+ * frame keeps its top-left corner and width; in a page the block keeps its
70
+ * `width` (an image still at its natural size, `width` unset, stays so).
71
+ */
72
+ export function replacementProps(model, asset, natural) {
73
+ const ratio = natural.height / natural.width;
74
+ const props = {
75
+ sourceId: asset.sourceId,
76
+ size: asset.size,
77
+ };
78
+ if (model.parent?.flavour === 'affine:surface') {
79
+ const bound = Bound.deserialize(model.props.xywh);
80
+ bound.h = bound.w * ratio;
81
+ props.xywh = bound.serialize();
82
+ props.width = natural.width;
83
+ props.height = natural.height;
84
+ return props;
85
+ }
86
+ const width = model.props.width ?? 0;
87
+ if (width > 0) {
88
+ props.width = width;
89
+ props.height = width * ratio;
90
+ }
91
+ return props;
92
+ }
93
+ /**
94
+ * Swaps the picture of one image block for `asset`, keeping its displayed
95
+ * width and its caption, in one undo step. `false` when nothing was written:
96
+ * a read-only document, the same blob, a ref that is not an image, a size
97
+ * that cannot be read, or a block deleted meanwhile.
98
+ */
99
+ export async function replaceImage(std, model, asset) {
100
+ const { store } = model;
101
+ if (store.readonly)
102
+ return false;
103
+ if (!asset.type.startsWith('image/'))
104
+ return false;
105
+ if (asset.sourceId === model.props.sourceId$.peek())
106
+ return false;
107
+ const natural = await naturalSize(store, asset);
108
+ if (!natural) {
109
+ toast(std.host, translateKey(std, ...IMAGE_TOAST_READ_SIZE_FAILED));
110
+ return false;
111
+ }
112
+ if (store.readonly || !store.getBlock(model.id))
113
+ return false;
114
+ store.captureSync();
115
+ store.transact(() => {
116
+ store.updateBlock(model, replacementProps(model, asset, natural));
117
+ // A crop is a window on the picture it was drawn on (ADR 0037).
118
+ if (model.props.crop)
119
+ delete model.props.crop;
120
+ });
121
+ return true;
122
+ }
123
+ function pickRequest(std, request) {
124
+ const picker = std.getOptional(AssetPickerProvider);
125
+ if (!picker)
126
+ return null;
127
+ return picker.pick({ ...request, docId: std.store.id });
128
+ }
129
+ /**
130
+ * The image entries of the slash menu and of the mobile keyboard toolbar:
131
+ * the host's asset dialog when there is one, the system file chooser
132
+ * otherwise (the upload path, unchanged).
133
+ */
134
+ export async function pickAndAddSiblingImageBlocks(std, targetModel, placement = 'after') {
135
+ const picked = pickRequest(std, {
136
+ kind: 'image',
137
+ mode: 'insert',
138
+ multiple: true,
139
+ });
140
+ if (!picked) {
141
+ const files = await getImageFilesFromLocal();
142
+ return addSiblingImageBlocks(std, files, targetModel, placement);
143
+ }
144
+ const assets = await picked;
145
+ if (!assets?.length)
146
+ return [];
147
+ return addSiblingImageBlocksFromAssets(std, assets, targetModel, placement);
148
+ }
149
+ /** The canvas's image button: the host's asset dialog, or the file chooser. */
150
+ export async function pickAndAddImages(std, options) {
151
+ const picked = pickRequest(std, {
152
+ kind: 'image',
153
+ mode: 'insert',
154
+ multiple: true,
155
+ });
156
+ if (!picked) {
157
+ const files = await getImageFilesFromLocal();
158
+ return addImages(std, files, options);
159
+ }
160
+ const assets = await picked;
161
+ if (!assets?.length)
162
+ return [];
163
+ return addImagesFromAssets(std, assets, options);
164
+ }
165
+ /**
166
+ * The image toolbar's "Replace": only with a host (`false` otherwise, and
167
+ * the toolbar does not offer it), one ref, `mode: 'replace'`.
168
+ */
169
+ export async function pickAndReplaceImage(std, model) {
170
+ if (model.store.readonly)
171
+ return false;
172
+ const picked = pickRequest(std, {
173
+ kind: 'image',
174
+ mode: 'replace',
175
+ multiple: false,
176
+ });
177
+ if (!picked)
178
+ return false;
179
+ const [asset] = (await picked) ?? [];
180
+ if (!asset)
181
+ return false;
182
+ return replaceImage(std, model, asset);
183
+ }
@@ -1,5 +1,10 @@
1
1
  import type { Command } from '../../std/index.js';
2
2
  import type { BlockModel } from '../../store/index.js';
3
+ /**
4
+ * Inserts images next to the selection: from the host's asset dialog
5
+ * (`AssetPickerProvider`) when there is one, from the system file chooser
6
+ * otherwise.
7
+ */
3
8
  export declare const insertImagesCommand: Command<{
4
9
  selectedModels?: BlockModel[];
5
10
  removeEmptyLine?: boolean;
@@ -1,5 +1,9 @@
1
- import { getImageFilesFromLocal } from '../../affine-shared/utils/index.js';
2
- import { addSiblingImageBlocks } from '../utils.js';
1
+ import { pickAndAddSiblingImageBlocks } from '../asset-picker.js';
2
+ /**
3
+ * Inserts images next to the selection: from the host's asset dialog
4
+ * (`AssetPickerProvider`) when there is one, from the system file chooser
5
+ * otherwise.
6
+ */
3
7
  export const insertImagesCommand = (ctx, next) => {
4
8
  const { selectedModels, placement, removeEmptyLine, std } = ctx;
5
9
  if (!selectedModels?.length)
@@ -8,12 +12,8 @@ export const insertImagesCommand = (ctx, next) => {
8
12
  ? selectedModels[0]
9
13
  : selectedModels[selectedModels.length - 1];
10
14
  return next({
11
- insertedImageIds: getImageFilesFromLocal()
12
- .then(files => addSiblingImageBlocks(std, files, targetModel, placement))
13
- .then(result => {
14
- if (result.length &&
15
- removeEmptyLine &&
16
- targetModel.text?.length === 0) {
15
+ insertedImageIds: pickAndAddSiblingImageBlocks(std, targetModel, placement).then(result => {
16
+ if (result.length && removeEmptyLine && targetModel.text?.length === 0) {
17
17
  std.store.deleteBlock(targetModel);
18
18
  }
19
19
  return result;
@@ -7,6 +7,17 @@ export declare class ImageBlockPageComponent extends ImageBlockPageComponent_bas
7
7
  static styles: import("lit").CSSResult;
8
8
  resizeable$: import("@preact/signals-core").ReadonlySignal<boolean>;
9
9
  private _isDragging;
10
+ /** The source's pixel size once loaded: the crop's aspect needs it. */
11
+ private accessor _loadedNatural;
12
+ private readonly _onLoad;
13
+ /**
14
+ * The box of a cropped page image (ADR 0037). `width`/`height` stay the
15
+ * displayed size of the WHOLE picture, so the box is the crop's share of it
16
+ * and cropping keeps the picture's scale. While the size is unknown (no
17
+ * stored size, image not loaded yet) the image is drawn whole: an empty
18
+ * box would never load a lazy image.
19
+ */
20
+ private _croppedBox;
10
21
  private get _doc();
11
22
  private get _host();
12
23
  private get _model();
@@ -40,16 +40,20 @@ import { SignalWatcher, WithDisposable } from '../../global/lit/index.js';
40
40
  import { BlockSelection, ShadowlessElement, TextSelection } from '../../std/index.js';
41
41
  import { computed } from '@preact/signals-core';
42
42
  import { css, html } from 'lit';
43
- import { property, query } from 'lit/decorators.js';
43
+ import { property, query, state } from 'lit/decorators.js';
44
44
  import { classMap } from 'lit/directives/class-map.js';
45
45
  import { styleMap } from 'lit/directives/style-map.js';
46
46
  import { when } from 'lit/directives/when.js';
47
+ import { croppedImageStyle } from '../crop/crop-controller.js';
47
48
  import { ImageResizeManager } from '../image-resize-manager.js';
48
49
  import { IMAGE_LABEL, IMAGE_TOAST_DOWNLOAD_FAILED } from '../translations.js';
49
50
  import { shouldResizeImage } from '../utils.js';
50
51
  import { ImageSelectedRect } from './image-selected-rect.js';
51
52
  let ImageBlockPageComponent = (() => {
52
53
  let _classSuper = SignalWatcher(WithDisposable(ShadowlessElement));
54
+ let __loadedNatural_decorators;
55
+ let __loadedNatural_initializers = [];
56
+ let __loadedNatural_extraInitializers = [];
53
57
  let _block_decorators;
54
58
  let _block_initializers = [];
55
59
  let _block_extraInitializers = [];
@@ -62,9 +66,11 @@ let ImageBlockPageComponent = (() => {
62
66
  return class ImageBlockPageComponent extends _classSuper {
63
67
  static {
64
68
  const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
69
+ __loadedNatural_decorators = [state()];
65
70
  _block_decorators = [property({ attribute: false })];
66
71
  _state_decorators = [property({ attribute: false })];
67
72
  _resizeImg_decorators = [query('.resizable-img')];
73
+ __esDecorate(this, null, __loadedNatural_decorators, { kind: "accessor", name: "_loadedNatural", static: false, private: false, access: { has: obj => "_loadedNatural" in obj, get: obj => obj._loadedNatural, set: (obj, value) => { obj._loadedNatural = value; } }, metadata: _metadata }, __loadedNatural_initializers, __loadedNatural_extraInitializers);
68
74
  __esDecorate(this, null, _block_decorators, { kind: "accessor", name: "block", static: false, private: false, access: { has: obj => "block" in obj, get: obj => obj.block, set: (obj, value) => { obj.block = value; } }, metadata: _metadata }, _block_initializers, _block_extraInitializers);
69
75
  __esDecorate(this, null, _state_decorators, { kind: "accessor", name: "state", static: false, private: false, access: { has: obj => "state" in obj, get: obj => obj.state, set: (obj, value) => { obj.state = value; } }, metadata: _metadata }, _state_initializers, _state_extraInitializers);
70
76
  __esDecorate(this, null, _resizeImg_decorators, { kind: "accessor", name: "resizeImg", static: false, private: false, access: { has: obj => "resizeImg" in obj, get: obj => obj.resizeImg, set: (obj, value) => { obj.resizeImg = value; } }, metadata: _metadata }, _resizeImg_initializers, _resizeImg_extraInitializers);
@@ -115,10 +121,44 @@ let ImageBlockPageComponent = (() => {
115
121
  height: 100%;
116
122
  }
117
123
 
124
+ affine-page-image .labre-image-crop-window {
125
+ position: absolute;
126
+ inset: 0;
127
+ overflow: hidden;
128
+ }
129
+
118
130
  affine-page-image .comment-highlighted {
119
131
  outline: 2px solid ${unsafeCSSVarV2('block/comment/highlightUnderline')};
120
132
  }
121
133
  `; }
134
+ #_loadedNatural_accessor_storage;
135
+ /** The source's pixel size once loaded: the crop's aspect needs it. */
136
+ get _loadedNatural() { return this.#_loadedNatural_accessor_storage; }
137
+ set _loadedNatural(value) { this.#_loadedNatural_accessor_storage = value; }
138
+ /**
139
+ * The box of a cropped page image (ADR 0037). `width`/`height` stay the
140
+ * displayed size of the WHOLE picture, so the box is the crop's share of it
141
+ * and cropping keeps the picture's scale. While the size is unknown (no
142
+ * stored size, image not loaded yet) the image is drawn whole: an empty
143
+ * box would never load a lazy image.
144
+ */
145
+ _croppedBox(imageSize) {
146
+ const crop = this.block.cropController.crop$.value;
147
+ if (!crop)
148
+ return null;
149
+ const { width, height } = this._model.props;
150
+ const natural = width && height ? { width, height } : this._loadedNatural;
151
+ if (!natural)
152
+ return null;
153
+ const aspect = (crop.w * natural.width) / (crop.h * natural.height);
154
+ const boxWidth = this._isDragging
155
+ ? imageSize.width
156
+ : `${natural.width * crop.w}px`;
157
+ return {
158
+ crop,
159
+ style: { width: boxWidth, aspectRatio: `${aspect}` },
160
+ };
161
+ }
122
162
  get _doc() {
123
163
  return this.block.store;
124
164
  }
@@ -326,6 +366,7 @@ let ImageBlockPageComponent = (() => {
326
366
  }
327
367
  render() {
328
368
  const imageSize = this._normalizeImageSize();
369
+ const cropped = this._croppedBox(imageSize);
329
370
  const imageSelectedRect = this.resizeable$.value
330
371
  ? ImageSelectedRect(this._doc.readonly)
331
372
  : null;
@@ -339,18 +380,31 @@ let ImageBlockPageComponent = (() => {
339
380
  'resizable-img': true,
340
381
  'comment-highlighted': this.block.isCommentHighlighted,
341
382
  })}
342
- style=${styleMap(imageSize)}
383
+ style=${styleMap(cropped ? cropped.style : imageSize)}
343
384
  >
344
- <img
345
- class="drag-target"
346
- draggable="false"
347
- loading="lazy"
348
- src=${blobUrl}
349
- alt=${caption}
350
- @error=${this._handleError}
351
- />
352
-
353
- ${imageSelectedRect}
385
+ ${cropped
386
+ ? html `<div class="labre-image-crop-window">
387
+ <img
388
+ class="drag-target"
389
+ draggable="false"
390
+ loading="lazy"
391
+ src=${blobUrl}
392
+ alt=${caption}
393
+ style=${croppedImageStyle(cropped.crop)}
394
+ @load=${this._onLoad}
395
+ @error=${this._handleError}
396
+ />
397
+ </div>`
398
+ : html `<img
399
+ class="drag-target"
400
+ draggable="false"
401
+ loading="lazy"
402
+ src=${blobUrl}
403
+ alt=${caption}
404
+ @load=${this._onLoad}
405
+ @error=${this._handleError}
406
+ />`}
407
+ ${imageSelectedRect} ${this.block.cropController.renderOverlay(blobUrl)}
354
408
  </div>
355
409
 
356
410
  ${when(loading, () => html `<div class="loading">${icon}</div>`)}
@@ -377,6 +431,21 @@ let ImageBlockPageComponent = (() => {
377
431
  super(...arguments);
378
432
  this.resizeable$ = computed(() => this.block.resizeable$.value);
379
433
  this._isDragging = false;
434
+ this.#_loadedNatural_accessor_storage = __runInitializers(this, __loadedNatural_initializers, null);
435
+ this._onLoad = (__runInitializers(this, __loadedNatural_extraInitializers), (event) => {
436
+ const img = event.currentTarget;
437
+ if (!img.naturalWidth || !img.naturalHeight)
438
+ return;
439
+ const loaded = this._loadedNatural;
440
+ if (loaded?.width === img.naturalWidth &&
441
+ loaded.height === img.naturalHeight) {
442
+ return;
443
+ }
444
+ this._loadedNatural = {
445
+ width: img.naturalWidth,
446
+ height: img.naturalHeight,
447
+ };
448
+ });
380
449
  this.#block_accessor_storage = __runInitializers(this, _block_initializers, void 0);
381
450
  this.#state_accessor_storage = (__runInitializers(this, _block_extraInitializers), __runInitializers(this, _state_initializers, void 0));
382
451
  this.#resizeImg_accessor_storage = (__runInitializers(this, _state_extraInitializers), __runInitializers(this, _resizeImg_initializers, void 0));
@@ -0,0 +1,9 @@
1
+ import type { ImageBlockModel } from '../../affine-model/index.js';
2
+ import { type ToolbarAction, type ToolbarContext } from '../../affine-shared/services/index.js';
3
+ /**
4
+ * "Replace" on the image toolbar, page and canvas alike: opens the host's
5
+ * asset dialog in `replace` mode and swaps the picture in place
6
+ * (`replaceImage`). It exists only with an `AssetPickerProvider` and on a
7
+ * writable document: without a host there is no library to pick from.
8
+ */
9
+ export declare function imageReplaceToolbarAction(getModel: (ctx: ToolbarContext) => ImageBlockModel | null): ToolbarAction;
@@ -0,0 +1,27 @@
1
+ import { AssetPickerProvider, } from '../../affine-shared/services/index.js';
2
+ import { ReplaceIcon } from '@blocksuite/icons/lit';
3
+ import { pickAndReplaceImage } from '../asset-picker.js';
4
+ import { IMAGE_TOOLBAR_REPLACE } from '../translations.js';
5
+ /**
6
+ * "Replace" on the image toolbar, page and canvas alike: opens the host's
7
+ * asset dialog in `replace` mode and swaps the picture in place
8
+ * (`replaceImage`). It exists only with an `AssetPickerProvider` and on a
9
+ * writable document: without a host there is no library to pick from.
10
+ */
11
+ export function imageReplaceToolbarAction(getModel) {
12
+ return {
13
+ id: 'a.replace',
14
+ tooltip: 'Replace',
15
+ tooltipWording: IMAGE_TOOLBAR_REPLACE,
16
+ icon: ReplaceIcon(),
17
+ when: ctx => !ctx.readonly &&
18
+ !!ctx.std.getOptional(AssetPickerProvider) &&
19
+ getModel(ctx) !== null,
20
+ run(ctx) {
21
+ const model = getModel(ctx);
22
+ if (!model)
23
+ return;
24
+ pickAndReplaceImage(ctx.std, model).catch(console.error);
25
+ },
26
+ };
27
+ }
@@ -2,10 +2,12 @@ import { ImageBlockModel } from '../../affine-model/index.js';
2
2
  import { ActionPlacement, blockCommentToolbarButton, TOOLBAR_CAPTION, TOOLBAR_COPY, TOOLBAR_DELETE, TOOLBAR_DOWNLOAD, TOOLBAR_DUPLICATE, ToolbarModuleExtension, } from '../../affine-shared/services/index.js';
3
3
  import { BookmarkIcon, CaptionIcon, CopyIcon, DeleteIcon, DownloadIcon, DuplicateIcon, } from '@blocksuite/icons/lit';
4
4
  import { BlockFlavourIdentifier } from '../../std/index.js';
5
+ import { imageCropToolbarAction } from '../crop/crop-toolbar.js';
5
6
  import { ImageBlockComponent } from '../image-block.js';
6
7
  import { ImageEdgelessBlockComponent } from '../image-edgeless-block.js';
7
8
  import { IMAGE_TOOLBAR_TURN_INTO_CARD_VIEW } from '../translations.js';
8
9
  import { duplicate } from '../utils.js';
10
+ import { imageReplaceToolbarAction } from './replace-action.js';
9
11
  const trackBaseProps = {
10
12
  category: 'image',
11
13
  type: 'card view',
@@ -22,6 +24,7 @@ const builtinToolbarConfig = {
22
24
  block?.download();
23
25
  },
24
26
  },
27
+ imageReplaceToolbarAction(ctx => ctx.getCurrentBlockByType(ImageBlockComponent)?.model ?? null),
25
28
  {
26
29
  id: 'b.caption',
27
30
  tooltip: 'Caption',
@@ -36,6 +39,7 @@ const builtinToolbarConfig = {
36
39
  });
37
40
  },
38
41
  },
42
+ imageCropToolbarAction(ctx => ctx.getCurrentBlockByType(ImageBlockComponent)),
39
43
  {
40
44
  id: 'c.comment',
41
45
  ...blockCommentToolbarButton,
@@ -117,6 +121,7 @@ const builtinSurfaceToolbarConfig = {
117
121
  block?.download();
118
122
  },
119
123
  },
124
+ imageReplaceToolbarAction(ctx => ctx.getCurrentBlockByType(ImageEdgelessBlockComponent)?.model ?? null),
120
125
  {
121
126
  id: 'b.caption',
122
127
  tooltip: 'Caption',
@@ -131,6 +136,7 @@ const builtinSurfaceToolbarConfig = {
131
136
  });
132
137
  },
133
138
  },
139
+ imageCropToolbarAction(ctx => ctx.getCurrentBlockByType(ImageEdgelessBlockComponent)),
134
140
  {
135
141
  id: 'c.comment',
136
142
  ...blockCommentToolbarButton,
@@ -0,0 +1,46 @@
1
+ import { type ImageBlockModel, type ImageCrop, type ImageCropMask } from '../../affine-model/index.js';
2
+ import { nothing } from 'lit';
3
+ import { type CropRect } from './crop-math.js';
4
+ /** What the controller needs from the block it serves, page or canvas. */
5
+ export interface CroppableImageBlock {
6
+ readonly model: ImageBlockModel;
7
+ querySelector<E extends Element = Element>(selectors: string): E | null;
8
+ }
9
+ /**
10
+ * The « Rogner » state of one image block (ADR 0037), shared by the page and
11
+ * the canvas components: which preset the crop frame is open on, if any, and
12
+ * the gestures that write. The frame is local UI and never stored.
13
+ */
14
+ export declare class ImageCropController {
15
+ private readonly _block;
16
+ private readonly _onCanvas;
17
+ /** The mask the crop frame is open on, `null` when it is closed. */
18
+ readonly editing$: import("@preact/signals-core").Signal<"free" | "16:9" | "4:3" | "1:1" | null>;
19
+ readonly crop$: import("@preact/signals-core").ReadonlySignal<ImageCrop | undefined>;
20
+ constructor(_block: CroppableImageBlock, _onCanvas: boolean);
21
+ /**
22
+ * The source's proportions: the loaded `<img>`'s pixel size, else what the
23
+ * block stores. Only the ratio matters to the crop, and an SVG without an
24
+ * intrinsic size loads with none: a canvas box is the cropped share of the
25
+ * whole picture, a page `width`/`height` is the whole picture.
26
+ */
27
+ natural(): {
28
+ width: number;
29
+ height: number;
30
+ } | null;
31
+ /**
32
+ * Open the frame on a preset. A ratio preset first writes its largest
33
+ * centred window — unless the crop already uses that preset, which keeps
34
+ * the author's adjustment. Free opens on the current crop and writes
35
+ * nothing until a drag.
36
+ */
37
+ choose(mask: ImageCropMask): void;
38
+ commit(rect: CropRect, mask?: "free" | "16:9" | "4:3" | "1:1"): boolean;
39
+ /** « Image d'origine »: removes the crop; the bytes never changed. */
40
+ restoreOriginal(): boolean;
41
+ stop(): void;
42
+ /** The frame, or nothing when it is closed or the page is readonly. */
43
+ renderOverlay(src: string | null | undefined, rotate?: number): import("lit-html").TemplateResult<1> | typeof nothing;
44
+ }
45
+ /** Inline styles of an `<img>` showing `crop` of its source in its box. */
46
+ export declare function croppedImageStyle(crop: ImageCrop): import("lit-html/directive.js").DirectiveResult<typeof import("lit-html/directives/style-map.js").StyleMapDirective>;