@streamscloud/kit 0.63.0 → 0.64.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 (36) hide show
  1. package/dist/core/utils/color-helper.d.ts +4 -0
  2. package/dist/core/utils/color-helper.js +10 -0
  3. package/dist/ui/cropper/image-editor-dialog/cmp.image-editor-dialog.svelte +17 -51
  4. package/dist/ui/cropper/image-editor-dialog/crop-and-resize.d.ts +4 -16
  5. package/dist/ui/cropper/image-editor-dialog/crop-and-resize.js +1 -14
  6. package/dist/ui/cropper/image-editor-dialog/index.d.ts +8 -7
  7. package/dist/ui/cropper/image-editor-dialog/index.js +8 -7
  8. package/dist/ui/cropper/image-editor-dialog/types.d.ts +1 -1
  9. package/dist/ui/cropper/img-cropper/cmp.img-cropper-toolbar.svelte +149 -181
  10. package/dist/ui/cropper/img-cropper/cmp.img-cropper-toolbar.svelte.d.ts +4 -13
  11. package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte +389 -147
  12. package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte.d.ts +13 -39
  13. package/dist/ui/cropper/img-cropper/frame-geometry.d.ts +82 -0
  14. package/dist/ui/cropper/img-cropper/frame-geometry.js +165 -0
  15. package/dist/ui/cropper/img-cropper/img-cropper-localization.d.ts +2 -3
  16. package/dist/ui/cropper/img-cropper/img-cropper-localization.js +12 -19
  17. package/dist/ui/cropper/img-cropper/img-cropper-toolbar-localization.d.ts +14 -5
  18. package/dist/ui/cropper/img-cropper/img-cropper-toolbar-localization.js +127 -33
  19. package/dist/ui/cropper/img-cropper/img-cropper-utils.d.ts +4 -51
  20. package/dist/ui/cropper/img-cropper/img-cropper-utils.js +35 -190
  21. package/dist/ui/cropper/img-cropper/img-cropper.svelte.d.ts +99 -34
  22. package/dist/ui/cropper/img-cropper/img-cropper.svelte.js +405 -104
  23. package/dist/ui/cropper/img-cropper/index.d.ts +1 -2
  24. package/dist/ui/cropper/img-cropper/index.js +0 -1
  25. package/package.json +1 -3
  26. package/dist/ui/cropper/img-cropper/cmp.img-cropper-controls.svelte +0 -68
  27. package/dist/ui/cropper/img-cropper/cmp.img-cropper-controls.svelte.d.ts +0 -21
  28. package/dist/ui/cropper/img-cropper/cropperjs-elements.d.ts +0 -35
  29. package/dist/ui/cropper/img-cropper/img-cropper-base-worker.svelte.d.ts +0 -40
  30. package/dist/ui/cropper/img-cropper/img-cropper-base-worker.svelte.js +0 -193
  31. package/dist/ui/cropper/img-cropper/img-cropper-contain-worker.svelte.d.ts +0 -7
  32. package/dist/ui/cropper/img-cropper/img-cropper-contain-worker.svelte.js +0 -51
  33. package/dist/ui/cropper/img-cropper/img-cropper-cover-worker.svelte.d.ts +0 -7
  34. package/dist/ui/cropper/img-cropper/img-cropper-cover-worker.svelte.js +0 -89
  35. package/dist/ui/cropper/img-cropper/img-cropper-worker.svelte.d.ts +0 -42
  36. package/dist/ui/cropper/img-cropper/img-cropper-worker.svelte.js +0 -1
@@ -0,0 +1,82 @@
1
+ export type Size = {
2
+ w: number;
3
+ h: number;
4
+ };
5
+ export type Point = {
6
+ x: number;
7
+ y: number;
8
+ };
9
+ export type Rect = Point & Size;
10
+ export type FrameFitMode = 'cover' | 'contain';
11
+ /** The frame the rect is being fitted into, the aspect it must keep, and the mode's placement rule. */
12
+ export type FrameContext = {
13
+ frame: Size;
14
+ aspect: number;
15
+ mode: FrameFitMode;
16
+ free?: boolean;
17
+ };
18
+ export declare const MAX_FRAME_ZOOM = 5;
19
+ export declare const MIN_FREE_ZOOM = 0.1;
20
+ /** Smallest `aspect`-shaped rect that fully covers `frame`, centered. */
21
+ export declare const coverBox: (frame: Size, aspect: number) => Rect;
22
+ /** Largest `aspect`-shaped rect that fits inside `frame`, centered. */
23
+ export declare const containBox: (frame: Size, aspect: number) => Rect;
24
+ /** Zoom relative to the cover size: 1 means the rect exactly covers the frame. */
25
+ export declare const zoomOf: (rect: Size, frame: Size, aspect: number) => number;
26
+ /** The zoom at which a rect exactly fits inside the frame — at or below 1. */
27
+ export declare const containZoom: (frame: Size, aspect: number) => number;
28
+ /**
29
+ * Constrains a rect's size to the mode's floor/ceiling and its position so it never violates the mode's placement
30
+ * rule (always covering the frame in `cover`, always inside it in `contain`) unless `free` lifts the position rule.
31
+ */
32
+ export declare const clampBox: (rect: Rect, ctx: FrameContext) => Rect;
33
+ /**
34
+ * Scales a rect by `factor` about a frame-local anchor point, capping the factor to the floor/ceiling first.
35
+ * When the anchor is the rect's own corner (handle drag) and the scale hits a position limit, `clampPosition` still
36
+ * slides the rect and the opposite corner drifts — deliberate: a hard stop there felt worse.
37
+ */
38
+ export declare const scaleBox: (rect: Rect, factor: number, anchor: Point, ctx: FrameContext) => Rect;
39
+ /**
40
+ * The box after the frame and the picture turn a quarter together about the frame's center, so whatever was inside
41
+ * the frame stays inside, just turned. `from` is the frame before the turn (its width and height swap in the turn);
42
+ * `ctx.frame` is the frame after it, laid out anew, so everything is scaled by how much the turned frame resized.
43
+ * Only meaningful when the frame follows the picture's own aspect — a fixed-aspect frame can't turn.
44
+ *
45
+ * Screen y grows downward, so a clockwise turn maps an offset `(dx, dy)` from the center to `(-dy, dx)`.
46
+ */
47
+ export declare const boxTurnedWithFrame: ({ box, from, clockwise, ctx }: {
48
+ box: Rect;
49
+ from: Size;
50
+ clockwise: boolean;
51
+ ctx: FrameContext;
52
+ }) => Rect;
53
+ /** Rescales a rect to an absolute `zoom` (relative to cover), about the frame's center. */
54
+ export declare const boxAtZoom: (rect: Rect, zoom: number, ctx: FrameContext) => Rect;
55
+ /** Moves a rect by a frame-local delta, then re-clamps it. */
56
+ export declare const moveBox: (rect: Rect, delta: Point, ctx: FrameContext) => Rect;
57
+ /**
58
+ * Snaps a rect so its `fx`/`fy` fraction (0, 0.5 or 1 along each axis) lines up with the same fraction of the frame.
59
+ * Works for both regimes: for a bigger rect `frame - size` is negative, so 0 aligns starts, 1 aligns ends.
60
+ */
61
+ export declare const alignBox: (rect: Rect, fx: number, fy: number, ctx: FrameContext) => Rect;
62
+ /** Largest `aspect`-shaped rect that fits `available`, centered — how the frame itself is laid out on the stage. */
63
+ export declare const fitFrame: (available: Size, aspect: number) => Rect;
64
+ /** Moves a selection rect by a frame-local delta, keeping it fully inside `frame`. */
65
+ export declare const shiftSelection: ({ rect, delta, frame }: {
66
+ rect: Rect;
67
+ delta: Point;
68
+ frame: Size;
69
+ }) => Rect;
70
+ /**
71
+ * A drag-constructed selection inside `frame`, anchored at `anchor` and growing toward `point` — locked to `aspect` when
72
+ * given, free-form when `aspect` is null. `anchor` never moves; growth stops at the frame edge instead of wrapping the
73
+ * anchor past it, and at `minSize` on the shorter side instead of shrinking further. The caller passes `minSize` once the
74
+ * selection is valid, so it can't become invalid again; one still growing from zero passes none.
75
+ */
76
+ export declare const selectionFromDrag: ({ anchor, point, frame, aspect, minSize }: {
77
+ anchor: Point;
78
+ point: Point;
79
+ frame: Size;
80
+ aspect: number | null;
81
+ minSize?: number;
82
+ }) => Rect;
@@ -0,0 +1,165 @@
1
+ /*
2
+ * Pure geometry behind `ImgCropper`. All rects are frame-local: (0, 0) is the frame's top-left corner. The picture's
3
+ * box routinely extends past `[0, frame.w] × [0, frame.h]` (that spill is the dimmed part); a selection never does.
4
+ * Zoom is measured against `coverBox`: 1 = the picture exactly covers the frame, contain-fit is ≤ 1.
5
+ * Sizes are driven by width, height is derived (`h = w / aspect`), so the aspect can't drift through rounding.
6
+ */
7
+ export const MAX_FRAME_ZOOM = 5;
8
+ export const MIN_FREE_ZOOM = 0.1;
9
+ const MIN_FREE_OVERLAP_PX = 24;
10
+ const round2 = (value) => Math.round(value * 100) / 100;
11
+ /** Smallest `aspect`-shaped rect that fully covers `frame`, centered. */
12
+ export const coverBox = (frame, aspect) => {
13
+ const byWidth = { w: frame.w, h: frame.w / aspect };
14
+ const byHeight = { w: frame.h * aspect, h: frame.h };
15
+ const { w, h } = byWidth.h >= frame.h ? byWidth : byHeight;
16
+ return { x: (frame.w - w) / 2, y: (frame.h - h) / 2, w, h };
17
+ };
18
+ /** Largest `aspect`-shaped rect that fits inside `frame`, centered. */
19
+ export const containBox = (frame, aspect) => {
20
+ const byWidth = { w: frame.w, h: frame.w / aspect };
21
+ const byHeight = { w: frame.h * aspect, h: frame.h };
22
+ const { w, h } = byWidth.h <= frame.h ? byWidth : byHeight;
23
+ return { x: (frame.w - w) / 2, y: (frame.h - h) / 2, w, h };
24
+ };
25
+ /** Zoom relative to the cover size: 1 means the rect exactly covers the frame. */
26
+ export const zoomOf = (rect, frame, aspect) => rect.w / coverBox(frame, aspect).w;
27
+ /** The zoom at which a rect exactly fits inside the frame — at or below 1. */
28
+ export const containZoom = (frame, aspect) => containBox(frame, aspect).w / coverBox(frame, aspect).w;
29
+ /** Size floor: `free` allows almost anything; otherwise `contain` stops at fitting inside (gaps allowed), `cover` at exactly covering. */
30
+ const minWidth = (ctx) => {
31
+ const { frame, aspect, mode, free } = ctx;
32
+ const cover = coverBox(frame, aspect);
33
+ if (free) {
34
+ return cover.w * MIN_FREE_ZOOM;
35
+ }
36
+ return mode === 'contain' ? containBox(frame, aspect).w : cover.w;
37
+ };
38
+ /**
39
+ * Constrained position on one axis so the rect never opens a gap past the frame's edge on that side.
40
+ * One formula for both regimes — `frameSize - size` flips sign between them:
41
+ * - rect bigger than the frame: range `[frameSize - size, 0]`, so it always covers the frame;
42
+ * - rect smaller than the frame: range `[0, frameSize - size]`, so it stays fully inside.
43
+ */
44
+ const clampAxisBounded = (position, size, frameSize) => {
45
+ const minPosition = Math.min(0, frameSize - size);
46
+ const maxPosition = Math.max(0, frameSize - size);
47
+ return Math.min(Math.max(position, minPosition), maxPosition);
48
+ };
49
+ /** Free mode: the rect may leave the frame, but at least `overlap` of it stays over the frame on either side. */
50
+ const clampAxisFree = (position, size, frameSize) => {
51
+ const overlap = Math.min(MIN_FREE_OVERLAP_PX, size, frameSize);
52
+ return Math.min(Math.max(position, overlap - size), frameSize - overlap);
53
+ };
54
+ const clampSize = (rect, ctx) => {
55
+ const w = round2(Math.min(Math.max(rect.w, minWidth(ctx)), coverBox(ctx.frame, ctx.aspect).w * MAX_FRAME_ZOOM));
56
+ return { ...rect, w, h: round2(w / ctx.aspect) };
57
+ };
58
+ const clampPosition = (rect, ctx) => {
59
+ const clampAxis = ctx.free ? clampAxisFree : clampAxisBounded;
60
+ return { ...rect, x: round2(clampAxis(rect.x, rect.w, ctx.frame.w)), y: round2(clampAxis(rect.y, rect.h, ctx.frame.h)) };
61
+ };
62
+ /**
63
+ * Constrains a rect's size to the mode's floor/ceiling and its position so it never violates the mode's placement
64
+ * rule (always covering the frame in `cover`, always inside it in `contain`) unless `free` lifts the position rule.
65
+ */
66
+ export const clampBox = (rect, ctx) => clampPosition(clampSize(rect, ctx), ctx);
67
+ /**
68
+ * Scales a rect by `factor` about a frame-local anchor point, capping the factor to the floor/ceiling first.
69
+ * When the anchor is the rect's own corner (handle drag) and the scale hits a position limit, `clampPosition` still
70
+ * slides the rect and the opposite corner drifts — deliberate: a hard stop there felt worse.
71
+ */
72
+ // Capping size after applying the raw factor to position (instead of capping the factor itself) drifts the anchor
73
+ // a little further every repeated call sitting at the floor/ceiling — position and size go out of sync.
74
+ export const scaleBox = (rect, factor, anchor, ctx) => {
75
+ const targetWidth = clampSize({ ...rect, w: rect.w * factor }, ctx).w;
76
+ const appliedFactor = targetWidth / rect.w;
77
+ const scaled = {
78
+ x: anchor.x + (rect.x - anchor.x) * appliedFactor,
79
+ y: anchor.y + (rect.y - anchor.y) * appliedFactor,
80
+ w: targetWidth,
81
+ h: targetWidth / ctx.aspect
82
+ };
83
+ return clampPosition(scaled, ctx);
84
+ };
85
+ /**
86
+ * The box after the frame and the picture turn a quarter together about the frame's center, so whatever was inside
87
+ * the frame stays inside, just turned. `from` is the frame before the turn (its width and height swap in the turn);
88
+ * `ctx.frame` is the frame after it, laid out anew, so everything is scaled by how much the turned frame resized.
89
+ * Only meaningful when the frame follows the picture's own aspect — a fixed-aspect frame can't turn.
90
+ *
91
+ * Screen y grows downward, so a clockwise turn maps an offset `(dx, dy)` from the center to `(-dy, dx)`.
92
+ */
93
+ export const boxTurnedWithFrame = ({ box, from, clockwise, ctx }) => {
94
+ const dx = box.x + box.w / 2 - from.w / 2;
95
+ const dy = box.y + box.h / 2 - from.h / 2;
96
+ const turned = clockwise ? { x: -dy, y: dx } : { x: dy, y: -dx };
97
+ const scale = ctx.frame.w / from.h;
98
+ const w = box.h * scale;
99
+ const h = box.w * scale;
100
+ const center = { x: ctx.frame.w / 2 + turned.x * scale, y: ctx.frame.h / 2 + turned.y * scale };
101
+ return clampBox({ x: center.x - w / 2, y: center.y - h / 2, w, h }, ctx);
102
+ };
103
+ /** Rescales a rect to an absolute `zoom` (relative to cover), about the frame's center. */
104
+ export const boxAtZoom = (rect, zoom, ctx) => {
105
+ const targetWidth = coverBox(ctx.frame, ctx.aspect).w * zoom;
106
+ const center = { x: ctx.frame.w / 2, y: ctx.frame.h / 2 };
107
+ return scaleBox(rect, targetWidth / rect.w, center, ctx);
108
+ };
109
+ /** Moves a rect by a frame-local delta, then re-clamps it. */
110
+ export const moveBox = (rect, delta, ctx) => clampBox({ ...rect, x: rect.x + delta.x, y: rect.y + delta.y }, ctx);
111
+ /**
112
+ * Snaps a rect so its `fx`/`fy` fraction (0, 0.5 or 1 along each axis) lines up with the same fraction of the frame.
113
+ * Works for both regimes: for a bigger rect `frame - size` is negative, so 0 aligns starts, 1 aligns ends.
114
+ */
115
+ export const alignBox = (rect, fx, fy, ctx) => clampBox({ ...rect, x: fx * (ctx.frame.w - rect.w), y: fy * (ctx.frame.h - rect.h) }, ctx);
116
+ /** Largest `aspect`-shaped rect that fits `available`, centered — how the frame itself is laid out on the stage. */
117
+ export const fitFrame = (available, aspect) => containBox(available, aspect);
118
+ /** Moves a selection rect by a frame-local delta, keeping it fully inside `frame`. */
119
+ export const shiftSelection = ({ rect, delta, frame }) => ({
120
+ ...rect,
121
+ x: round2(clampAxisBounded(rect.x + delta.x, rect.w, frame.w)),
122
+ y: round2(clampAxisBounded(rect.y + delta.y, rect.h, frame.h))
123
+ });
124
+ /**
125
+ * Grows toward the pointer unless `minSize` doesn't fit that side of `anchor` — then stays on the other side.
126
+ * Only bites on a resize dragged across the anchor toward a near edge; the original side always has room, since the
127
+ * selection already fit there at the minimum.
128
+ */
129
+ const growsPositive = (towardPositive, anchor, frameSize, minSize) => towardPositive ? frameSize - anchor >= minSize : anchor < minSize;
130
+ /**
131
+ * A drag-constructed selection inside `frame`, anchored at `anchor` and growing toward `point` — locked to `aspect` when
132
+ * given, free-form when `aspect` is null. `anchor` never moves; growth stops at the frame edge instead of wrapping the
133
+ * anchor past it, and at `minSize` on the shorter side instead of shrinking further. The caller passes `minSize` once the
134
+ * selection is valid, so it can't become invalid again; one still growing from zero passes none.
135
+ */
136
+ export const selectionFromDrag = ({ anchor, point, frame, aspect, minSize = 0 }) => {
137
+ // The minimum is for the shorter side: a wide aspect has the shorter height, so its width floor is min × aspect.
138
+ const minW = aspect === null ? minSize : minSize * Math.max(1, aspect);
139
+ const minH = aspect === null ? minSize : minW / aspect;
140
+ const growRight = growsPositive(point.x >= anchor.x, anchor.x, frame.w, minW);
141
+ const growDown = growsPositive(point.y >= anchor.y, anchor.y, frame.h, minH);
142
+ const roomX = growRight ? frame.w - anchor.x : anchor.x;
143
+ const roomY = growDown ? frame.h - anchor.y : anchor.y;
144
+ const dx = Math.max(Math.min(Math.abs(point.x - anchor.x), roomX), minW);
145
+ const dy = Math.max(Math.min(Math.abs(point.y - anchor.y), roomY), minH);
146
+ let w;
147
+ let h;
148
+ if (aspect === null) {
149
+ w = dx;
150
+ h = dy;
151
+ }
152
+ else {
153
+ // The two axes imply different sizes; take the larger so the selection keeps up with the drag (deliberate), then
154
+ // cap by both rooms — the larger candidate can overshoot the other axis.
155
+ const candidateW = dx / aspect < dy ? dy * aspect : dx;
156
+ w = Math.min(candidateW, roomX, roomY * aspect);
157
+ h = round2(w / aspect);
158
+ }
159
+ return {
160
+ x: round2(growRight ? anchor.x : anchor.x - w),
161
+ y: round2(growDown ? anchor.y : anchor.y - h),
162
+ w: round2(w),
163
+ h
164
+ };
165
+ };
@@ -1,6 +1,5 @@
1
1
  export declare class ImgCropperLocalization {
2
- get apply(): string;
3
- get cancel(): string;
4
2
  get imageLoadError(): string;
5
- get initCropperError(): string;
3
+ get resizeHandle(): string;
4
+ get resizeSelection(): string;
6
5
  }
@@ -1,33 +1,26 @@
1
1
  import { AppLocale } from '../../../core/locale';
2
2
  const loc = {
3
- apply: {
4
- en: 'Apply',
5
- no: 'Bruk'
6
- },
7
- cancel: {
8
- en: 'Cancel',
9
- no: 'Avbryt'
10
- },
11
3
  imageLoadError: {
12
4
  en: 'Failed to load image',
13
5
  no: 'Kunne ikke laste bildet'
14
6
  },
15
- initCropperError: {
16
- en: 'Failed to initialize cropper',
17
- no: 'Kunne ikke initialisere cropper'
7
+ resizeHandle: {
8
+ en: 'Resize picture',
9
+ no: 'Endre størrelse på bildet'
10
+ },
11
+ resizeSelection: {
12
+ en: 'Resize crop selection',
13
+ no: 'Endre størrelse på utvalget'
18
14
  }
19
15
  };
20
16
  export class ImgCropperLocalization {
21
- get apply() {
22
- return loc.apply[AppLocale.current];
23
- }
24
- get cancel() {
25
- return loc.cancel[AppLocale.current];
26
- }
27
17
  get imageLoadError() {
28
18
  return loc.imageLoadError[AppLocale.current];
29
19
  }
30
- get initCropperError() {
31
- return loc.initCropperError[AppLocale.current];
20
+ get resizeHandle() {
21
+ return loc.resizeHandle[AppLocale.current];
22
+ }
23
+ get resizeSelection() {
24
+ return loc.resizeSelection[AppLocale.current];
32
25
  }
33
26
  }
@@ -1,11 +1,20 @@
1
1
  export declare class ImgCropperToolbarLocalization {
2
- get move(): string;
3
- get crop(): string;
2
+ get toolbar(): string;
3
+ get ratio(): string;
4
+ get original(): string;
5
+ get zoom(): string;
4
6
  get zoomIn(): string;
5
7
  get zoomOut(): string;
6
- get rotate(): string;
8
+ get fillFrame(): string;
9
+ get fitFrame(): string;
10
+ get freePosition(): string;
11
+ get cropMode(): string;
12
+ get applyCrop(): string;
13
+ get align(): string;
14
+ get rotateLeft(): string;
15
+ get rotateRight(): string;
7
16
  get reset(): string;
8
- get free(): string;
9
- get ratio(): string;
10
17
  get fill(): string;
18
+ /** The label of one alignment cell; `fx` / `fy` are 0, 0.5 or 1 along each axis. */
19
+ alignPosition: (fx: number, fy: number) => string;
11
20
  }
@@ -1,48 +1,123 @@
1
1
  import { AppLocale } from '../../../core/locale';
2
2
  const loc = {
3
- move: {
4
- en: 'Move',
5
- no: 'Flytt'
3
+ toolbar: {
4
+ en: 'Picture tools',
5
+ no: 'Bildeverktøy'
6
6
  },
7
- crop: {
8
- en: 'Crop',
9
- no: 'Beskjær'
7
+ ratio: {
8
+ en: 'Aspect ratio',
9
+ no: 'Sideforhold'
10
+ },
11
+ original: {
12
+ en: 'Original',
13
+ no: 'Original'
14
+ },
15
+ zoom: {
16
+ en: 'Zoom',
17
+ no: 'Zoom'
10
18
  },
11
19
  zoomIn: {
12
- en: 'Zoom In',
20
+ en: 'Zoom in',
13
21
  no: 'Zoom inn'
14
22
  },
15
23
  zoomOut: {
16
- en: 'Zoom Out',
24
+ en: 'Zoom out',
17
25
  no: 'Zoom ut'
18
26
  },
19
- rotate: {
20
- en: 'Rotate',
21
- no: 'Roter'
27
+ fillFrame: {
28
+ en: 'Fill frame',
29
+ no: 'Fyll rammen'
30
+ },
31
+ fitFrame: {
32
+ en: 'Fit inside frame',
33
+ no: 'Tilpass i rammen'
34
+ },
35
+ freePosition: {
36
+ en: 'Free position',
37
+ no: 'Fri plassering'
38
+ },
39
+ cropMode: {
40
+ en: 'Crop',
41
+ no: 'Beskjær'
42
+ },
43
+ applyCrop: {
44
+ en: 'Apply crop',
45
+ no: 'Bekreft beskjæring'
46
+ },
47
+ align: {
48
+ en: 'Align picture',
49
+ no: 'Juster bildet'
50
+ },
51
+ alignTopLeft: {
52
+ en: 'Top left',
53
+ no: 'Øverst til venstre'
54
+ },
55
+ alignTop: {
56
+ en: 'Top',
57
+ no: 'Øverst'
58
+ },
59
+ alignTopRight: {
60
+ en: 'Top right',
61
+ no: 'Øverst til høyre'
62
+ },
63
+ alignLeft: {
64
+ en: 'Left',
65
+ no: 'Venstre'
66
+ },
67
+ alignCenter: {
68
+ en: 'Center',
69
+ no: 'Midten'
70
+ },
71
+ alignRight: {
72
+ en: 'Right',
73
+ no: 'Høyre'
74
+ },
75
+ alignBottomLeft: {
76
+ en: 'Bottom left',
77
+ no: 'Nederst til venstre'
78
+ },
79
+ alignBottom: {
80
+ en: 'Bottom',
81
+ no: 'Nederst'
82
+ },
83
+ alignBottomRight: {
84
+ en: 'Bottom right',
85
+ no: 'Nederst til høyre'
86
+ },
87
+ rotateLeft: {
88
+ en: 'Rotate left',
89
+ no: 'Roter venstre'
90
+ },
91
+ rotateRight: {
92
+ en: 'Rotate right',
93
+ no: 'Roter høyre'
22
94
  },
23
95
  reset: {
24
96
  en: 'Reset',
25
97
  no: 'Tilbakestill'
26
98
  },
27
- free: {
28
- en: 'Free',
29
- no: 'Fri'
30
- },
31
- ratio: {
32
- en: 'Ratio',
33
- no: 'Forhold'
34
- },
35
99
  fill: {
36
- en: 'Fill',
37
- no: 'Fyll'
100
+ en: 'Fill color',
101
+ no: 'Fyllfarge'
38
102
  }
39
103
  };
104
+ const ALIGN_KEYS = [
105
+ ['alignTopLeft', 'alignTop', 'alignTopRight'],
106
+ ['alignLeft', 'alignCenter', 'alignRight'],
107
+ ['alignBottomLeft', 'alignBottom', 'alignBottomRight']
108
+ ];
40
109
  export class ImgCropperToolbarLocalization {
41
- get move() {
42
- return loc.move[AppLocale.current];
110
+ get toolbar() {
111
+ return loc.toolbar[AppLocale.current];
43
112
  }
44
- get crop() {
45
- return loc.crop[AppLocale.current];
113
+ get ratio() {
114
+ return loc.ratio[AppLocale.current];
115
+ }
116
+ get original() {
117
+ return loc.original[AppLocale.current];
118
+ }
119
+ get zoom() {
120
+ return loc.zoom[AppLocale.current];
46
121
  }
47
122
  get zoomIn() {
48
123
  return loc.zoomIn[AppLocale.current];
@@ -50,19 +125,38 @@ export class ImgCropperToolbarLocalization {
50
125
  get zoomOut() {
51
126
  return loc.zoomOut[AppLocale.current];
52
127
  }
53
- get rotate() {
54
- return loc.rotate[AppLocale.current];
128
+ get fillFrame() {
129
+ return loc.fillFrame[AppLocale.current];
55
130
  }
56
- get reset() {
57
- return loc.reset[AppLocale.current];
131
+ get fitFrame() {
132
+ return loc.fitFrame[AppLocale.current];
58
133
  }
59
- get free() {
60
- return loc.free[AppLocale.current];
134
+ get freePosition() {
135
+ return loc.freePosition[AppLocale.current];
61
136
  }
62
- get ratio() {
63
- return loc.ratio[AppLocale.current];
137
+ get cropMode() {
138
+ return loc.cropMode[AppLocale.current];
139
+ }
140
+ get applyCrop() {
141
+ return loc.applyCrop[AppLocale.current];
142
+ }
143
+ get align() {
144
+ return loc.align[AppLocale.current];
145
+ }
146
+ get rotateLeft() {
147
+ return loc.rotateLeft[AppLocale.current];
148
+ }
149
+ get rotateRight() {
150
+ return loc.rotateRight[AppLocale.current];
151
+ }
152
+ get reset() {
153
+ return loc.reset[AppLocale.current];
64
154
  }
65
155
  get fill() {
66
156
  return loc.fill[AppLocale.current];
67
157
  }
158
+ /** The label of one alignment cell; `fx` / `fy` are 0, 0.5 or 1 along each axis. */
159
+ alignPosition = (fx, fy) => {
160
+ return loc[ALIGN_KEYS[Math.round(fy * 2)][Math.round(fx * 2)]][AppLocale.current];
161
+ };
68
162
  }
@@ -1,54 +1,7 @@
1
- import type { CanvasGeometry } from './img-cropper-worker.svelte';
2
- import type { CropperCanvas, CropperImage, CropperSelection } from 'cropperjs';
3
- type Rect = {
4
- left: number;
5
- top: number;
6
- right: number;
7
- bottom: number;
8
- };
9
1
  export declare const detectMimeFromUrl: (url: string) => string | null;
10
- export declare const computeImageScale: (transform: number[]) => number;
11
- export declare const computeNaturalOutputSize: (params: {
12
- displayWidth: number;
13
- displayHeight: number;
14
- imageScale: number;
15
- }) => {
16
- width: number;
17
- height: number;
18
- };
19
- export declare const computeLocalAABB: (params: {
20
- matrix: number[];
21
- width: number;
22
- height: number;
23
- }) => Rect;
24
- export declare const rectCoversRect: (inner: Rect, outer: Rect, tolerance?: number) => boolean;
25
- export declare const handleCoverCheck: (params: {
26
- event: Event;
27
- image: CropperImage;
28
- canvas: CropperCanvas;
29
- }) => void;
30
- export declare const fitInContainer: (params: {
31
- ratio: number;
32
- containerWidth: number;
33
- containerHeight: number;
34
- }) => {
35
- width: number;
36
- height: number;
37
- };
38
2
  /**
39
- * Applies the minimum canvas size by scaling the whole rect, bounded by `maxWidth` / `maxHeight`, and rounds to whole pixels.
40
- * The minimums are soft: a container smaller than them wins, and the returned canvas is smaller than `MIN_CANVAS_WIDTH` / `MIN_CANVAS_HEIGHT`.
3
+ * Whether the picture has any non-opaque pixel. Probed on a copy downscaled to `ALPHA_PROBE_PX` — downscaling averages
4
+ * alpha, so even thin transparent details still leave a sub-255 pixel, at a fraction of a full-resolution read.
41
5
  */
42
- export declare const resolveCanvasGeometry: (params: {
43
- aspectRatio: number | null;
44
- width: number;
45
- height: number;
46
- maxWidth: number;
47
- maxHeight: number;
48
- }) => CanvasGeometry;
49
- export declare const handleSelectionBoundary: (params: {
50
- event: Event;
51
- selection: CropperSelection;
52
- canvas: CropperCanvas;
53
- }) => void;
54
- export {};
6
+ export declare const detectAlpha: (image: HTMLImageElement, mimeType: string | null) => boolean;
7
+ export declare const loadImage: (src: string) => Promise<HTMLImageElement>;