@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.
- package/dist/_pkgs/affine-block-attachment/asset-picker.d.ts +24 -0
- package/dist/_pkgs/affine-block-attachment/asset-picker.js +87 -0
- package/dist/_pkgs/affine-block-attachment/configs/slash-menu.js +3 -20
- package/dist/_pkgs/affine-block-attachment/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-attachment/index.js +1 -0
- package/dist/_pkgs/affine-block-attachment/utils.d.ts +9 -1
- package/dist/_pkgs/affine-block-attachment/utils.js +9 -0
- package/dist/_pkgs/affine-block-bookmark/adapters/markdown/markdown.js +14 -1
- package/dist/_pkgs/affine-block-bookmark/adapters/notion-html.js +5 -1
- package/dist/_pkgs/affine-block-database/adapters/markdown.js +5 -3
- package/dist/_pkgs/affine-block-embed/common/adapters/html.js +5 -3
- package/dist/_pkgs/affine-block-embed/common/adapters/markdown.js +11 -0
- package/dist/_pkgs/affine-block-frame/frame-template.d.ts +58 -0
- package/dist/_pkgs/affine-block-frame/frame-template.js +207 -0
- package/dist/_pkgs/affine-block-frame/frame-toolbar.js +19 -3
- package/dist/_pkgs/affine-block-frame/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-frame/index.js +1 -0
- package/dist/_pkgs/affine-block-frame/translations.d.ts +7 -0
- package/dist/_pkgs/affine-block-frame/translations.js +16 -1
- package/dist/_pkgs/affine-block-frame/view.js +6 -0
- package/dist/_pkgs/affine-block-image/asset-picker.d.ts +63 -0
- package/dist/_pkgs/affine-block-image/asset-picker.js +183 -0
- package/dist/_pkgs/affine-block-image/commands/insert-images.d.ts +5 -0
- package/dist/_pkgs/affine-block-image/commands/insert-images.js +8 -8
- package/dist/_pkgs/affine-block-image/components/page-image-block.d.ts +11 -0
- package/dist/_pkgs/affine-block-image/components/page-image-block.js +81 -12
- package/dist/_pkgs/affine-block-image/configs/replace-action.d.ts +9 -0
- package/dist/_pkgs/affine-block-image/configs/replace-action.js +27 -0
- package/dist/_pkgs/affine-block-image/configs/toolbar.js +6 -0
- package/dist/_pkgs/affine-block-image/crop/crop-controller.d.ts +46 -0
- package/dist/_pkgs/affine-block-image/crop/crop-controller.js +104 -0
- package/dist/_pkgs/affine-block-image/crop/crop-math.d.ts +70 -0
- package/dist/_pkgs/affine-block-image/crop/crop-math.js +141 -0
- package/dist/_pkgs/affine-block-image/crop/crop-overlay.d.ts +57 -0
- package/dist/_pkgs/affine-block-image/crop/crop-overlay.js +343 -0
- package/dist/_pkgs/affine-block-image/crop/crop-toolbar.d.ts +12 -0
- package/dist/_pkgs/affine-block-image/crop/crop-toolbar.js +78 -0
- package/dist/_pkgs/affine-block-image/crop/write-crop.d.ts +16 -0
- package/dist/_pkgs/affine-block-image/crop/write-crop.js +57 -0
- package/dist/_pkgs/affine-block-image/edgeless-clipboard-config.js +4 -1
- package/dist/_pkgs/affine-block-image/effects.js +2 -0
- package/dist/_pkgs/affine-block-image/image-block.d.ts +2 -0
- package/dist/_pkgs/affine-block-image/image-block.js +2 -0
- package/dist/_pkgs/affine-block-image/image-edgeless-block.d.ts +2 -0
- package/dist/_pkgs/affine-block-image/image-edgeless-block.js +36 -8
- package/dist/_pkgs/affine-block-image/image-resize-manager.js +15 -0
- package/dist/_pkgs/affine-block-image/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-image/index.js +1 -0
- package/dist/_pkgs/affine-block-image/translations.d.ts +15 -0
- package/dist/_pkgs/affine-block-image/translations.js +43 -0
- package/dist/_pkgs/affine-block-image/utils.d.ts +13 -0
- package/dist/_pkgs/affine-block-image/utils.js +10 -1
- package/dist/_pkgs/affine-block-root/clipboard/page-clipboard.js +5 -0
- package/dist/_pkgs/affine-block-root/commands/page-export-commands.d.ts +2 -0
- package/dist/_pkgs/affine-block-root/commands/page-export-commands.js +71 -0
- package/dist/_pkgs/affine-block-root/index.d.ts +1 -0
- package/dist/_pkgs/affine-block-root/index.js +1 -0
- package/dist/_pkgs/affine-block-root/view.js +8 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/capability.d.ts +30 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/capability.js +44 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/colors.d.ts +26 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/colors.js +82 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/command.d.ts +18 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/command.js +91 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/index.d.ts +3 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/index.js +3 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/writer.d.ts +50 -0
- package/dist/_pkgs/affine-block-surface/extensions/drawio-export/writer.js +732 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/draw-image-block.d.ts +12 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/draw-image-block.js +15 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +7 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.d.ts +7 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.js +60 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.d.ts +2 -2
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.js +2 -2
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/marker-version.d.ts +6 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/marker-version.js +8 -0
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.d.ts +28 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.js +66 -6
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.d.ts +2 -7
- package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.js +2 -6
- package/dist/_pkgs/affine-block-surface/extensions/index.d.ts +2 -0
- package/dist/_pkgs/affine-block-surface/extensions/index.js +2 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange-export.d.ts +84 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange-export.js +150 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +24 -2
- package/dist/_pkgs/affine-block-surface/extensions/interchange.d.ts +23 -2
- package/dist/_pkgs/affine-block-surface/extensions/interchange.js +4 -1
- package/dist/_pkgs/affine-block-surface/extensions/legend-role.d.ts +18 -0
- package/dist/_pkgs/affine-block-surface/extensions/legend-role.js +20 -0
- package/dist/_pkgs/affine-block-surface/extensions/legend.d.ts +2 -19
- package/dist/_pkgs/affine-block-surface/extensions/legend.js +2 -18
- package/dist/_pkgs/affine-block-surface/extensions/svg-sanitize.d.ts +67 -0
- package/dist/_pkgs/affine-block-surface/extensions/svg-sanitize.js +235 -0
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch-translations.d.ts +1 -0
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch-translations.js +9 -0
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.d.ts +17 -13
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.js +52 -80
- package/dist/_pkgs/affine-block-surface/extensions/xml-element.d.ts +98 -0
- package/dist/_pkgs/affine-block-surface/extensions/xml-element.js +261 -0
- package/dist/_pkgs/affine-block-surface/extensions/xml-reader.d.ts +238 -0
- package/dist/_pkgs/affine-block-surface/extensions/xml-reader.js +570 -0
- package/dist/_pkgs/affine-block-surface/framework-background/def.js +1 -1
- package/dist/_pkgs/affine-block-surface/interchange-headless.d.ts +29 -0
- package/dist/_pkgs/affine-block-surface/interchange-headless.js +32 -0
- package/dist/_pkgs/affine-block-surface/view.js +22 -2
- package/dist/_pkgs/affine-block-table/adapters/utils.js +24 -1
- package/dist/_pkgs/affine-gfx-note/toolbar/note-menu.js +5 -8
- package/dist/_pkgs/affine-gfx-template/contribute.d.ts +14 -4
- package/dist/_pkgs/affine-gfx-template/contribute.js +47 -6
- package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.d.ts +7 -2
- package/dist/_pkgs/affine-gfx-template/toolbar/builtin-templates.js +14 -40
- package/dist/_pkgs/affine-gfx-template/toolbar/resolve-name.d.ts +1 -1
- package/dist/_pkgs/affine-gfx-template/toolbar/resolve-name.js +1 -1
- package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.d.ts +4 -4
- package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.js +9 -9
- package/dist/_pkgs/affine-gfx-template/toolbar/template-type.d.ts +2 -4
- package/dist/_pkgs/affine-gfx-template/translations.d.ts +5 -0
- package/dist/_pkgs/affine-gfx-template/translations.js +9 -0
- package/dist/_pkgs/affine-inline-footnote/adapters/markdown/inline-delta.js +6 -0
- package/dist/_pkgs/affine-inline-footnote/adapters/markdown/markdown-inline.js +7 -0
- package/dist/_pkgs/affine-inline-link/adapters/html/html-inline.js +4 -1
- package/dist/_pkgs/affine-inline-link/adapters/html/inline-delta.js +3 -1
- package/dist/_pkgs/affine-inline-link/adapters/markdown/inline-delta.js +4 -1
- package/dist/_pkgs/affine-inline-link/adapters/notion-html/html-inline.js +2 -1
- package/dist/_pkgs/affine-inline-link/link-node/affine-link.d.ts +7 -0
- package/dist/_pkgs/affine-inline-link/link-node/affine-link.js +37 -0
- package/dist/_pkgs/affine-inline-link/link-node/index.d.ts +1 -0
- package/dist/_pkgs/affine-inline-link/link-node/index.js +1 -0
- package/dist/_pkgs/affine-inline-link/link-node/propose-views.d.ts +22 -0
- package/dist/_pkgs/affine-inline-link/link-node/propose-views.js +59 -0
- package/dist/_pkgs/affine-model/blocks/image/image-crop.d.ts +48 -0
- package/dist/_pkgs/affine-model/blocks/image/image-crop.js +78 -0
- package/dist/_pkgs/affine-model/blocks/image/image-model.d.ts +6 -0
- package/dist/_pkgs/affine-model/blocks/image/image-model.js +2 -0
- package/dist/_pkgs/affine-model/blocks/image/index.d.ts +1 -0
- package/dist/_pkgs/affine-model/blocks/image/index.js +1 -0
- package/dist/_pkgs/affine-model/consts/shape.d.ts +2 -0
- package/dist/_pkgs/affine-model/consts/shape.js +5 -0
- package/dist/_pkgs/affine-model/elements/bpmn/consts.d.ts +31 -0
- package/dist/_pkgs/affine-model/elements/bpmn/consts.js +31 -0
- package/dist/_pkgs/affine-model/elements/bpmn/index.d.ts +1 -0
- package/dist/_pkgs/affine-model/elements/bpmn/index.js +1 -0
- package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +0 -25
- package/dist/_pkgs/affine-model/elements/bpmn/pool.js +1 -25
- package/dist/_pkgs/affine-model/elements/shape/shape.d.ts +0 -2
- package/dist/_pkgs/affine-model/elements/shape/shape.js +1 -2
- package/dist/_pkgs/affine-model/elements/uml/consts.d.ts +133 -0
- package/dist/_pkgs/affine-model/elements/uml/consts.js +107 -0
- package/dist/_pkgs/affine-model/elements/uml/diagram.d.ts +1 -79
- package/dist/_pkgs/affine-model/elements/uml/diagram.js +1 -53
- package/dist/_pkgs/affine-model/elements/uml/fragment.d.ts +0 -15
- package/dist/_pkgs/affine-model/elements/uml/fragment.js +1 -15
- package/dist/_pkgs/affine-model/elements/uml/index.d.ts +1 -0
- package/dist/_pkgs/affine-model/elements/uml/index.js +1 -0
- package/dist/_pkgs/affine-model/elements/uml/partition.d.ts +0 -20
- package/dist/_pkgs/affine-model/elements/uml/partition.js +1 -20
- package/dist/_pkgs/affine-model/elements/uml/region.d.ts +0 -11
- package/dist/_pkgs/affine-model/elements/uml/region.js +1 -11
- package/dist/_pkgs/affine-model/headless.d.ts +19 -0
- package/dist/_pkgs/affine-model/headless.js +19 -0
- package/dist/_pkgs/affine-shared/headless.d.ts +10 -0
- package/dist/_pkgs/affine-shared/headless.js +10 -0
- package/dist/_pkgs/affine-shared/services/asset-picker-service.d.ts +63 -0
- package/dist/_pkgs/affine-shared/services/asset-picker-service.js +20 -0
- package/dist/_pkgs/affine-shared/services/frame-template-service.d.ts +83 -0
- package/dist/_pkgs/affine-shared/services/frame-template-service.js +17 -0
- package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/services/index.js +2 -0
- package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +6 -0
- package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +10 -0
- package/dist/_pkgs/affine-widget-drag-handle/watchers/drag-event-watcher.js +8 -3
- package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +4 -16
- package/dist/_pkgs/affine-widget-linked-doc/transformers/html.d.ts +7 -0
- package/dist/_pkgs/affine-widget-linked-doc/transformers/html.js +25 -24
- package/dist/_pkgs/affine-widget-linked-doc/transformers/index.d.ts +1 -1
- package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.d.ts +7 -0
- package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.js +26 -25
- package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.d.ts +23 -0
- package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.js +14 -0
- package/dist/commands.js +5 -2
- package/dist/host-panels.d.ts +21 -1
- package/dist/host-panels.js +50 -10
- package/dist/interchange.d.ts +1 -0
- package/dist/interchange.js +1 -0
- package/dist/model/headless.d.ts +1 -0
- package/dist/model/headless.js +1 -0
- package/dist/shared/headless.d.ts +1 -0
- package/dist/shared/headless.js +1 -0
- package/dist/translations.js +8 -1
- package/package.json +13 -1
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readImageCrop, } from '../../affine-model/index.js';
|
|
2
|
+
import { Bound } from '../../global/gfx/index.js';
|
|
3
|
+
import { computed, signal } from '@preact/signals-core';
|
|
4
|
+
import { html, nothing } from 'lit';
|
|
5
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
6
|
+
import { maskRatio, presetCrop, WHOLE_IMAGE, wholeImageFrame, } from './crop-math.js';
|
|
7
|
+
import { writeImageCrop } from './write-crop.js';
|
|
8
|
+
/**
|
|
9
|
+
* The « Rogner » state of one image block (ADR 0037), shared by the page and
|
|
10
|
+
* the canvas components: which preset the crop frame is open on, if any, and
|
|
11
|
+
* the gestures that write. The frame is local UI and never stored.
|
|
12
|
+
*/
|
|
13
|
+
export class ImageCropController {
|
|
14
|
+
constructor(_block, _onCanvas) {
|
|
15
|
+
this._block = _block;
|
|
16
|
+
this._onCanvas = _onCanvas;
|
|
17
|
+
/** The mask the crop frame is open on, `null` when it is closed. */
|
|
18
|
+
this.editing$ = signal(null);
|
|
19
|
+
this.crop$ = computed(() => readImageCrop(this._block.model.props.crop$.value));
|
|
20
|
+
}
|
|
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
|
+
const img = this._block.querySelector('img.drag-target');
|
|
29
|
+
if (img?.naturalWidth && img.naturalHeight) {
|
|
30
|
+
return { width: img.naturalWidth, height: img.naturalHeight };
|
|
31
|
+
}
|
|
32
|
+
const { model } = this._block;
|
|
33
|
+
const crop = this.crop$.peek() ?? WHOLE_IMAGE;
|
|
34
|
+
if (this._onCanvas) {
|
|
35
|
+
const bound = Bound.deserialize(model.xywh);
|
|
36
|
+
if (bound.w <= 0 || bound.h <= 0)
|
|
37
|
+
return null;
|
|
38
|
+
return { width: bound.w / crop.w, height: bound.h / crop.h };
|
|
39
|
+
}
|
|
40
|
+
const { width, height } = model.props;
|
|
41
|
+
return width && height ? { width, height } : null;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Open the frame on a preset. A ratio preset first writes its largest
|
|
45
|
+
* centred window — unless the crop already uses that preset, which keeps
|
|
46
|
+
* the author's adjustment. Free opens on the current crop and writes
|
|
47
|
+
* nothing until a drag.
|
|
48
|
+
*/
|
|
49
|
+
choose(mask) {
|
|
50
|
+
const { model } = this._block;
|
|
51
|
+
if (model.store.readonly)
|
|
52
|
+
return;
|
|
53
|
+
if (mask !== 'free' && this.crop$.peek()?.mask !== mask) {
|
|
54
|
+
const natural = this.natural();
|
|
55
|
+
if (!natural)
|
|
56
|
+
return;
|
|
57
|
+
this.commit(presetCrop(mask, natural.width, natural.height), mask);
|
|
58
|
+
}
|
|
59
|
+
this.editing$.value = mask;
|
|
60
|
+
}
|
|
61
|
+
commit(rect, mask = this.editing$.peek() ?? 'free') {
|
|
62
|
+
return writeImageCrop(this._block.model, rect, mask, this._onCanvas);
|
|
63
|
+
}
|
|
64
|
+
/** « Image d'origine »: removes the crop; the bytes never changed. */
|
|
65
|
+
restoreOriginal() {
|
|
66
|
+
this.editing$.value = null;
|
|
67
|
+
return writeImageCrop(this._block.model, undefined, 'free', this._onCanvas);
|
|
68
|
+
}
|
|
69
|
+
stop() {
|
|
70
|
+
this.editing$.value = null;
|
|
71
|
+
}
|
|
72
|
+
/** The frame, or nothing when it is closed or the page is readonly. */
|
|
73
|
+
renderOverlay(src, rotate = 0) {
|
|
74
|
+
const mask = this.editing$.value;
|
|
75
|
+
if (mask === null || !src || this._block.model.store.readonly) {
|
|
76
|
+
return nothing;
|
|
77
|
+
}
|
|
78
|
+
const crop = this.crop$.value ?? WHOLE_IMAGE;
|
|
79
|
+
const natural = this.natural();
|
|
80
|
+
const ratio = natural
|
|
81
|
+
? maskRatio(mask, natural.width, natural.height)
|
|
82
|
+
: null;
|
|
83
|
+
return html `<affine-image-crop-overlay
|
|
84
|
+
data-testid="image-crop-overlay"
|
|
85
|
+
style=${styleMap(wholeImageFrame(crop))}
|
|
86
|
+
.src=${src}
|
|
87
|
+
.crop=${crop}
|
|
88
|
+
.ratio=${ratio}
|
|
89
|
+
.rotate=${rotate}
|
|
90
|
+
.onCommit=${(rect) => {
|
|
91
|
+
this.commit(rect);
|
|
92
|
+
}}
|
|
93
|
+
.onExit=${() => this.stop()}
|
|
94
|
+
></affine-image-crop-overlay>`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Inline styles of an `<img>` showing `crop` of its source in its box. */
|
|
98
|
+
export function croppedImageStyle(crop) {
|
|
99
|
+
return styleMap({
|
|
100
|
+
position: 'absolute',
|
|
101
|
+
maxWidth: 'none',
|
|
102
|
+
...wholeImageFrame(crop),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { type ImageCrop, type ImageCropMask } from '../../affine-model/index.js';
|
|
2
|
+
/** A crop rectangle without its mask, normalised to the source (ADR 0037). */
|
|
3
|
+
export type CropRect = Pick<ImageCrop, 'x' | 'y' | 'w' | 'h'>;
|
|
4
|
+
export type CropCorner = 'nw' | 'ne' | 'sw' | 'se';
|
|
5
|
+
export declare const WHOLE_IMAGE: CropRect;
|
|
6
|
+
/**
|
|
7
|
+
* The normalised `w / h` a mask holds on a source of this size, or `null`
|
|
8
|
+
* when the crop is free or the size is unknown. A 16:9 window on a portrait
|
|
9
|
+
* photo is much wider in normalised units than in pixels.
|
|
10
|
+
*/
|
|
11
|
+
export declare function maskRatio(mask: ImageCropMask, naturalWidth: number, naturalHeight: number): number | null;
|
|
12
|
+
/**
|
|
13
|
+
* The largest centred rectangle of the mask's ratio: what choosing a preset
|
|
14
|
+
* writes before the author adjusts it. Free is the whole image.
|
|
15
|
+
*/
|
|
16
|
+
export declare function presetCrop(mask: ImageCropMask, naturalWidth: number, naturalHeight: number): CropRect;
|
|
17
|
+
/** The window dragged by a normalised delta, kept inside the image. */
|
|
18
|
+
export declare function moveCrop(start: CropRect, dx: number, dy: number): CropRect;
|
|
19
|
+
/**
|
|
20
|
+
* The window resized by dragging one corner, the opposite corner pinned.
|
|
21
|
+
* With a ratio `k` (normalised `w / h`), the window grows to cover the pointer
|
|
22
|
+
* and then shrinks to fit the image, so it never leaves the source.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resizeCrop(start: CropRect, corner: CropCorner, dx: number, dy: number, k: number | null): CropRect;
|
|
25
|
+
export declare function sameCropRect(a: CropRect, b: CropRect): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The canvas box once the crop changes from `from` to `to`, for a block whose
|
|
28
|
+
* box shows `from`: the picture keeps its scale and the kept part stays where
|
|
29
|
+
* it was on the canvas. The box rotates about its centre (`rotate` degrees,
|
|
30
|
+
* CSS direction), so the shift is rotated before it is applied.
|
|
31
|
+
*/
|
|
32
|
+
export declare function cropBoundFor(bound: {
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
w: number;
|
|
36
|
+
h: number;
|
|
37
|
+
}, rotate: number, from: CropRect, to: CropRect): {
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
w: number;
|
|
41
|
+
h: number;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* The stored page `width`/`height` of a cropped image whose window was
|
|
45
|
+
* resized to `box`: the WHOLE picture's size (ADR 0037), never the window's.
|
|
46
|
+
* The height follows the source's proportions when they are known; an SVG
|
|
47
|
+
* with no intrinsic size, or a picture not loaded yet, scales the window's
|
|
48
|
+
* own height instead — writing the window itself would shrink the image on
|
|
49
|
+
* every resize.
|
|
50
|
+
*/
|
|
51
|
+
export declare function wholeImageSize(box: {
|
|
52
|
+
w: number;
|
|
53
|
+
h: number;
|
|
54
|
+
}, crop: CropRect, natural: {
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
} | null): {
|
|
58
|
+
width: number;
|
|
59
|
+
height: number;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Where the WHOLE source sits relative to a box showing `crop`, as CSS
|
|
63
|
+
* percentages of that box: the cropped `<img>` and the crop frame both use it.
|
|
64
|
+
*/
|
|
65
|
+
export declare function wholeImageFrame(crop: CropRect): {
|
|
66
|
+
left: string;
|
|
67
|
+
top: string;
|
|
68
|
+
width: string;
|
|
69
|
+
height: string;
|
|
70
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { IMAGE_CROP_MIN_SIDE, } from '../../affine-model/index.js';
|
|
2
|
+
export const WHOLE_IMAGE = { x: 0, y: 0, w: 1, h: 1 };
|
|
3
|
+
/** Width over height, in source pixels, each preset keeps. */
|
|
4
|
+
const MASK_RATIOS = {
|
|
5
|
+
'16:9': 16 / 9,
|
|
6
|
+
'4:3': 4 / 3,
|
|
7
|
+
'1:1': 1,
|
|
8
|
+
};
|
|
9
|
+
const EPSILON = 1e-6;
|
|
10
|
+
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
|
|
11
|
+
/**
|
|
12
|
+
* The normalised `w / h` a mask holds on a source of this size, or `null`
|
|
13
|
+
* when the crop is free or the size is unknown. A 16:9 window on a portrait
|
|
14
|
+
* photo is much wider in normalised units than in pixels.
|
|
15
|
+
*/
|
|
16
|
+
export function maskRatio(mask, naturalWidth, naturalHeight) {
|
|
17
|
+
if (mask === 'free' || naturalWidth <= 0 || naturalHeight <= 0)
|
|
18
|
+
return null;
|
|
19
|
+
return (MASK_RATIOS[mask] * naturalHeight) / naturalWidth;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The largest centred rectangle of the mask's ratio: what choosing a preset
|
|
23
|
+
* writes before the author adjusts it. Free is the whole image.
|
|
24
|
+
*/
|
|
25
|
+
export function presetCrop(mask, naturalWidth, naturalHeight) {
|
|
26
|
+
const k = maskRatio(mask, naturalWidth, naturalHeight);
|
|
27
|
+
if (k === null)
|
|
28
|
+
return WHOLE_IMAGE;
|
|
29
|
+
let w = k;
|
|
30
|
+
let h = 1;
|
|
31
|
+
if (w > 1) {
|
|
32
|
+
w = 1;
|
|
33
|
+
h = 1 / k;
|
|
34
|
+
}
|
|
35
|
+
return { x: (1 - w) / 2, y: (1 - h) / 2, w, h };
|
|
36
|
+
}
|
|
37
|
+
/** The window dragged by a normalised delta, kept inside the image. */
|
|
38
|
+
export function moveCrop(start, dx, dy) {
|
|
39
|
+
return {
|
|
40
|
+
...start,
|
|
41
|
+
x: clamp(start.x + dx, 0, 1 - start.w),
|
|
42
|
+
y: clamp(start.y + dy, 0, 1 - start.h),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The window resized by dragging one corner, the opposite corner pinned.
|
|
47
|
+
* With a ratio `k` (normalised `w / h`), the window grows to cover the pointer
|
|
48
|
+
* and then shrinks to fit the image, so it never leaves the source.
|
|
49
|
+
*/
|
|
50
|
+
export function resizeCrop(start, corner, dx, dy, k) {
|
|
51
|
+
const west = corner === 'nw' || corner === 'sw';
|
|
52
|
+
const north = corner === 'nw' || corner === 'ne';
|
|
53
|
+
const anchorX = west ? start.x + start.w : start.x;
|
|
54
|
+
const anchorY = north ? start.y + start.h : start.y;
|
|
55
|
+
const pointerX = (west ? start.x : start.x + start.w) + dx;
|
|
56
|
+
const pointerY = (north ? start.y : start.y + start.h) + dy;
|
|
57
|
+
const maxW = west ? anchorX : 1 - anchorX;
|
|
58
|
+
const maxH = north ? anchorY : 1 - anchorY;
|
|
59
|
+
const min = IMAGE_CROP_MIN_SIDE;
|
|
60
|
+
let w = clamp(west ? anchorX - pointerX : pointerX - anchorX, min, maxW);
|
|
61
|
+
let h = clamp(north ? anchorY - pointerY : pointerY - anchorY, min, maxH);
|
|
62
|
+
if (k !== null) {
|
|
63
|
+
if (w / k > h)
|
|
64
|
+
h = w / k;
|
|
65
|
+
else
|
|
66
|
+
w = h * k;
|
|
67
|
+
if (w > maxW) {
|
|
68
|
+
w = maxW;
|
|
69
|
+
h = w / k;
|
|
70
|
+
}
|
|
71
|
+
if (h > maxH) {
|
|
72
|
+
h = maxH;
|
|
73
|
+
w = h * k;
|
|
74
|
+
}
|
|
75
|
+
if (w < min) {
|
|
76
|
+
w = min;
|
|
77
|
+
h = w / k;
|
|
78
|
+
}
|
|
79
|
+
if (h < min) {
|
|
80
|
+
h = min;
|
|
81
|
+
w = h * k;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
x: west ? anchorX - w : anchorX,
|
|
86
|
+
y: north ? anchorY - h : anchorY,
|
|
87
|
+
w,
|
|
88
|
+
h,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function sameCropRect(a, b) {
|
|
92
|
+
return (Math.abs(a.x - b.x) < EPSILON &&
|
|
93
|
+
Math.abs(a.y - b.y) < EPSILON &&
|
|
94
|
+
Math.abs(a.w - b.w) < EPSILON &&
|
|
95
|
+
Math.abs(a.h - b.h) < EPSILON);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The canvas box once the crop changes from `from` to `to`, for a block whose
|
|
99
|
+
* box shows `from`: the picture keeps its scale and the kept part stays where
|
|
100
|
+
* it was on the canvas. The box rotates about its centre (`rotate` degrees,
|
|
101
|
+
* CSS direction), so the shift is rotated before it is applied.
|
|
102
|
+
*/
|
|
103
|
+
export function cropBoundFor(bound, rotate, from, to) {
|
|
104
|
+
const scaleX = bound.w / from.w;
|
|
105
|
+
const scaleY = bound.h / from.h;
|
|
106
|
+
const w = to.w * scaleX;
|
|
107
|
+
const h = to.h * scaleY;
|
|
108
|
+
const dx = (to.x - from.x) * scaleX + w / 2 - bound.w / 2;
|
|
109
|
+
const dy = (to.y - from.y) * scaleY + h / 2 - bound.h / 2;
|
|
110
|
+
const t = (rotate * Math.PI) / 180;
|
|
111
|
+
const cx = bound.x + bound.w / 2 + dx * Math.cos(t) - dy * Math.sin(t);
|
|
112
|
+
const cy = bound.y + bound.h / 2 + dx * Math.sin(t) + dy * Math.cos(t);
|
|
113
|
+
return { x: cx - w / 2, y: cy - h / 2, w, h };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The stored page `width`/`height` of a cropped image whose window was
|
|
117
|
+
* resized to `box`: the WHOLE picture's size (ADR 0037), never the window's.
|
|
118
|
+
* The height follows the source's proportions when they are known; an SVG
|
|
119
|
+
* with no intrinsic size, or a picture not loaded yet, scales the window's
|
|
120
|
+
* own height instead — writing the window itself would shrink the image on
|
|
121
|
+
* every resize.
|
|
122
|
+
*/
|
|
123
|
+
export function wholeImageSize(box, crop, natural) {
|
|
124
|
+
const width = box.w / crop.w;
|
|
125
|
+
return {
|
|
126
|
+
width,
|
|
127
|
+
height: natural ? (width * natural.height) / natural.width : box.h / crop.h,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Where the WHOLE source sits relative to a box showing `crop`, as CSS
|
|
132
|
+
* percentages of that box: the cropped `<img>` and the crop frame both use it.
|
|
133
|
+
*/
|
|
134
|
+
export function wholeImageFrame(crop) {
|
|
135
|
+
return {
|
|
136
|
+
left: `${(-crop.x / crop.w) * 100}%`,
|
|
137
|
+
top: `${(-crop.y / crop.h) * 100}%`,
|
|
138
|
+
width: `${100 / crop.w}%`,
|
|
139
|
+
height: `${100 / crop.h}%`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { LitElement, type PropertyValues } from 'lit';
|
|
2
|
+
import { type CropRect } from './crop-math.js';
|
|
3
|
+
declare const ImageCropOverlay_base: typeof LitElement & import("../../global/utils/types.js").Constructor<import("../../global/lit/with-disposable.js").DisposableClass>;
|
|
4
|
+
/**
|
|
5
|
+
* The crop frame (ADR 0037): the WHOLE source, laid over the block at the
|
|
6
|
+
* place it would occupy uncropped (the parent sizes this element with
|
|
7
|
+
* `wholeImageFrame`), dimmed outside the window being edited.
|
|
8
|
+
*
|
|
9
|
+
* Dragging the window moves it, dragging a corner resizes it, the ratio held
|
|
10
|
+
* when `ratio` is set. The draft lives here while the pointer is down; the
|
|
11
|
+
* release hands it to `onCommit` once — one gesture, one write, one undo
|
|
12
|
+
* step, decided by the caller (`writeImageCrop`).
|
|
13
|
+
*
|
|
14
|
+
* Pointer events never reach the editor while the frame is open: a press on
|
|
15
|
+
* the frame would otherwise start a canvas drag or a page selection. Escape,
|
|
16
|
+
* Enter or a press anywhere else calls `onExit`.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ImageCropOverlay extends ImageCropOverlay_base {
|
|
19
|
+
static styles: import("lit").CSSResult;
|
|
20
|
+
accessor src: string;
|
|
21
|
+
/** The stored crop the frame opens on; the whole image when uncropped. */
|
|
22
|
+
accessor crop: CropRect;
|
|
23
|
+
/** Normalised `w / h` to hold while resizing, `null` for a free crop. */
|
|
24
|
+
accessor ratio: number | null;
|
|
25
|
+
/** The block's rotation in degrees (canvas only), for pointer deltas. */
|
|
26
|
+
accessor rotate: number;
|
|
27
|
+
accessor onCommit: (rect: CropRect) => void;
|
|
28
|
+
accessor onExit: () => void;
|
|
29
|
+
accessor draft: CropRect;
|
|
30
|
+
private _dragging;
|
|
31
|
+
/** Removes the listeners of the gesture in progress, if any. */
|
|
32
|
+
private _releaseGesture;
|
|
33
|
+
private readonly _stop;
|
|
34
|
+
private readonly _onOutsidePointer;
|
|
35
|
+
private readonly _onKeydown;
|
|
36
|
+
/**
|
|
37
|
+
* The document-wide listeners (a press elsewhere, Escape, Enter) live only
|
|
38
|
+
* as long as this element: the frame is rendered while it is open and
|
|
39
|
+
* removed when it closes, which disposes them.
|
|
40
|
+
*/
|
|
41
|
+
connectedCallback(): void;
|
|
42
|
+
willUpdate(changed: PropertyValues<this>): void;
|
|
43
|
+
/**
|
|
44
|
+
* Client pixels per layout pixel, read from the rendered box: the canvas
|
|
45
|
+
* zoom and any CSS scale, without asking the viewport. The bounding box of
|
|
46
|
+
* a rotated element is wider than the element, hence the trigonometry.
|
|
47
|
+
*/
|
|
48
|
+
private _geometry;
|
|
49
|
+
private _startGesture;
|
|
50
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
51
|
+
}
|
|
52
|
+
declare global {
|
|
53
|
+
interface HTMLElementTagNameMap {
|
|
54
|
+
'affine-image-crop-overlay': ImageCropOverlay;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export {};
|