@streamscloud/kit 0.1.10 → 0.1.12-1772032209109

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 (57) hide show
  1. package/dist/core/toastr/index.d.ts +1 -1
  2. package/dist/core/toastr/toastr.scss +38 -0
  3. package/dist/core/toastr/toastr.svelte.d.ts +1 -1
  4. package/dist/core/toastr/toastr.svelte.js +13 -6
  5. package/dist/core/toastr/types.d.ts +2 -0
  6. package/dist/ui/cropper/image-editor-dialog/cmp.image-editor-dialog.svelte +9 -8
  7. package/dist/ui/cropper/image-editor-dialog/index.d.ts +4 -4
  8. package/dist/ui/cropper/image-editor-dialog/index.js +3 -3
  9. package/dist/ui/cropper/image-editor-dialog/types.d.ts +12 -12
  10. package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte +28 -38
  11. package/dist/ui/cropper/img-cropper/cmp.img-cropper.svelte.d.ts +8 -10
  12. package/dist/ui/cropper/img-cropper/img-cropper-base-worker.svelte.d.ts +40 -0
  13. package/dist/ui/cropper/img-cropper/img-cropper-base-worker.svelte.js +175 -0
  14. package/dist/ui/cropper/img-cropper/img-cropper-contain-worker.svelte.d.ts +5 -38
  15. package/dist/ui/cropper/img-cropper/img-cropper-contain-worker.svelte.js +29 -149
  16. package/dist/ui/cropper/img-cropper/img-cropper-cover-worker.svelte.d.ts +5 -38
  17. package/dist/ui/cropper/img-cropper/img-cropper-cover-worker.svelte.js +37 -135
  18. package/dist/ui/cropper/img-cropper/img-cropper-utils.d.ts +11 -1
  19. package/dist/ui/cropper/img-cropper/img-cropper-utils.js +30 -0
  20. package/dist/ui/cropper/img-cropper/img-cropper-worker.svelte.d.ts +17 -14
  21. package/dist/ui/cropper/img-cropper/img-cropper.svelte.d.ts +13 -31
  22. package/dist/ui/cropper/img-cropper/img-cropper.svelte.js +29 -28
  23. package/dist/ui/cropper/img-cropper/index.d.ts +1 -1
  24. package/dist/ui/dialog/cmp.dialog-container.svelte +12 -12
  25. package/dist/ui/dialog/dialog-data.d.ts +2 -0
  26. package/dist/ui/dialog/dialog-mount.d.ts +1 -1
  27. package/dist/ui/dialog/dialog-mount.js +2 -2
  28. package/dist/ui/dialog/dialogs.svelte.d.ts +3 -0
  29. package/dist/ui/dialog/dialogs.svelte.js +21 -2
  30. package/dist/ui/dialog/index.d.ts +1 -1
  31. package/dist/ui/dialog/index.js +1 -1
  32. package/dist/ui/dialog/types.svelte.d.ts +3 -14
  33. package/dist/ui/dialog/types.svelte.js +3 -18
  34. package/dist/ui/form-group/cmp.form-group-label.svelte +25 -0
  35. package/dist/ui/form-group/cmp.form-group-label.svelte.d.ts +8 -0
  36. package/dist/ui/form-group/cmp.form-group-note.svelte +16 -0
  37. package/dist/ui/form-group/cmp.form-group-note.svelte.d.ts +7 -0
  38. package/dist/ui/form-group/cmp.form-group.svelte +16 -0
  39. package/dist/ui/form-group/cmp.form-group.svelte.d.ts +8 -0
  40. package/dist/ui/form-group/index.d.ts +3 -0
  41. package/dist/ui/form-group/index.js +3 -0
  42. package/dist/ui/html-block/cmp.html-block.svelte +112 -0
  43. package/dist/ui/html-block/cmp.html-block.svelte.d.ts +7 -0
  44. package/dist/ui/html-block/index.d.ts +1 -0
  45. package/dist/ui/html-block/index.js +1 -0
  46. package/dist/ui/media-viewer-dialog/cmp.media-viewer-dialog.svelte +50 -0
  47. package/dist/ui/media-viewer-dialog/cmp.media-viewer-dialog.svelte.d.ts +9 -0
  48. package/dist/ui/media-viewer-dialog/index.d.ts +13 -0
  49. package/dist/ui/media-viewer-dialog/index.js +17 -0
  50. package/dist/ui/media-viewer-dialog/media-viewer-item.svelte +61 -0
  51. package/dist/ui/media-viewer-dialog/media-viewer-item.svelte.d.ts +7 -0
  52. package/dist/ui/media-viewer-dialog/types.d.ts +13 -0
  53. package/dist/ui/media-viewer-dialog/types.js +1 -0
  54. package/dist/ui/player/carousel/cmp.carousel.svelte +2 -2
  55. package/dist/ui/player/carousel/cmp.carousel.svelte.d.ts +1 -1
  56. package/dist/ui/video/cmp.video.svelte +16 -7
  57. package/package.json +14 -1
@@ -1,159 +1,39 @@
1
- import { ColorHelper } from '../../../core/utils';
2
- import { computeImageScale, computeNaturalOutputSize, handleSelectionBoundary } from './img-cropper-utils';
3
- import { tick } from 'svelte';
4
- export class ImgCropperContainWorker {
5
- cropBoxVisible = $state(false);
6
- dragMode = $state('move');
7
- naturalWidth = $state(0);
8
- naturalHeight = $state(0);
9
- canvasRatio = $state(null);
10
- destroy;
11
- _image;
12
- _selection;
13
- _canvas;
14
- _selectHandle;
15
- _originalSrc;
1
+ import { ImgCropperBaseWorker } from './img-cropper-base-worker.svelte';
2
+ import { fitInContainer } from './img-cropper-utils';
3
+ export class ImgCropperContainWorker extends ImgCropperBaseWorker {
4
+ _centerMode = 'contain';
16
5
  constructor(params) {
17
- this._originalSrc = params.originalSrc;
18
- this._image = params.image;
19
- this._selection = params.selection;
20
- this._canvas = params.canvas;
21
- this._selectHandle = params.selectHandle;
22
- const onSelectionChange = (e) => {
23
- const { width, height } = e.detail;
24
- this.cropBoxVisible = width > 0 && height > 0;
25
- };
26
- const onBoundaryCheck = (e) => {
27
- handleSelectionBoundary({ event: e, selection: this._selection, canvas: this._canvas });
28
- };
29
- this._selection.addEventListener('change', onSelectionChange);
30
- this._selection.addEventListener('change', onBoundaryCheck);
31
- this.destroy = () => {
32
- this._selection.removeEventListener('change', onSelectionChange);
33
- this._selection.removeEventListener('change', onBoundaryCheck);
34
- };
35
- this._applyMoveMode();
6
+ super(params);
36
7
  }
37
- ready = async () => {
38
- this._image.src = this._originalSrc;
39
- await this._fitImage();
40
- this._selection.$clear();
41
- };
42
- rotate = async () => {
43
- if (this.canvasRatio !== null) {
44
- this.canvasRatio = 1 / this.canvasRatio;
45
- const prevW = this.naturalWidth;
46
- this.naturalWidth = this.naturalHeight;
47
- this.naturalHeight = prevW;
48
- await tick();
8
+ _computeCanvasSize = () => {
9
+ const container = this._canvas.parentElement;
10
+ if (!container) {
11
+ return;
49
12
  }
50
- this._image.$rotate('90deg');
51
- this._centerForCurrentRotation();
52
- };
53
- zoomIn = () => {
54
- this._image.$zoom(0.1);
55
- };
56
- zoomOut = () => {
57
- this._image.$zoom(-0.1);
58
- };
59
- reset = async () => {
60
- this._selection.$clear();
61
- this._applyMoveMode();
62
- this.canvasRatio = null;
63
- this._image.src = this._originalSrc;
64
- await this._fitImage();
65
- };
66
- crop = async (options) => {
67
- const fillColor = options?.fillColor;
68
- const selectionRatio = this._selection.width / this._selection.height;
69
- const imageScale = computeImageScale(this._image.$getTransform());
70
- const outputSize = computeNaturalOutputSize({ displayWidth: this._selection.width, displayHeight: this._selection.height, imageScale });
71
- const canvas = await this._selection.$toCanvas({
72
- width: outputSize.width,
73
- height: outputSize.height,
74
- beforeDraw: (ctx, cvs) => {
75
- if (fillColor && !ColorHelper.isTransparent(fillColor)) {
76
- ctx.fillStyle = fillColor;
77
- ctx.fillRect(0, 0, cvs.width, cvs.height);
78
- }
79
- }
80
- });
81
- const dataUrl = canvas.toDataURL('image/png', 0.9);
82
- this._applyMoveMode();
83
- if (options?.freeRatio) {
84
- this.canvasRatio = selectionRatio;
13
+ const containerWidth = container.clientWidth;
14
+ const containerHeight = container.clientHeight;
15
+ const visualWidth = this._visualWidth;
16
+ const visualHeight = this._visualHeight;
17
+ const aspectRatio = this.canvasGeometry.aspectRatio;
18
+ if (containerWidth <= 0 || containerHeight <= 0 || visualWidth <= 0 || visualHeight <= 0) {
19
+ this.canvasGeometry = { aspectRatio, width: 0, height: 0 };
20
+ return;
85
21
  }
86
- this._image.src = dataUrl;
87
- await this._fitImage();
88
- this._selection.$clear();
89
- return { dataUrl, width: canvas.width, height: canvas.height };
90
- };
91
- save = async (options) => {
92
- const fillColor = options?.fillColor;
93
- const beforeDraw = (ctx, cvs) => {
94
- if (fillColor && !ColorHelper.isTransparent(fillColor)) {
95
- ctx.fillStyle = fillColor;
96
- ctx.fillRect(0, 0, cvs.width, cvs.height);
97
- }
98
- };
99
- const imageScale = computeImageScale(this._image.$getTransform());
100
- const displayWidth = this.cropBoxVisible ? this._selection.width : this._canvas.offsetWidth;
101
- const displayHeight = this.cropBoxVisible ? this._selection.height : this._canvas.offsetHeight;
102
- const outputSize = computeNaturalOutputSize({ displayWidth, displayHeight, imageScale });
103
- const toCanvasOptions = { width: outputSize.width, height: outputSize.height, beforeDraw };
104
- const canvas = this.cropBoxVisible ? await this._selection.$toCanvas(toCanvasOptions) : await this._canvas.$toCanvas(toCanvasOptions);
105
- const dataUrl = canvas.toDataURL('image/png', 0.9);
106
- return { dataUrl, width: canvas.width, height: canvas.height };
107
- };
108
- clearSelection = () => {
109
- this._selection.$clear();
110
- this._applyMoveMode();
111
- };
112
- enableCropMode = () => {
113
- this._image.translatable = false;
114
- if (this._selectHandle) {
115
- this._selectHandle.setAttribute('action', 'select');
116
- }
117
- this.dragMode = 'crop';
118
- };
119
- _applyMoveMode = () => {
120
- this._image.translatable = true;
121
- if (this._selectHandle) {
122
- this._selectHandle.setAttribute('action', 'move');
123
- }
124
- this.dragMode = 'move';
125
- this.cropBoxVisible = false;
126
- };
127
- _fitImage = async () => {
128
- const img = await this._image.$ready();
129
- this.naturalWidth = img.naturalWidth;
130
- this.naturalHeight = img.naturalHeight;
131
- await tick();
132
- this._image.$resetTransform();
133
- this._centerForCurrentRotation();
134
- };
135
- // Choose $center() (natural size) or $center('contain') (scale-to-fit) based on
136
- // whether the image's visual bounds fit inside the canvas at the current rotation.
137
- // When canvasRatio is active, naturalWidth/Height are already swapped to match
138
- // the visual orientation. Otherwise, derive visual dims from the rotation angle.
139
- _centerForCurrentRotation = () => {
140
- let visualW = this.naturalWidth;
141
- let visualH = this.naturalHeight;
142
- if (this.canvasRatio === null) {
143
- const matrix = this._image.$getTransform();
144
- const isSwapped = Math.abs(Math.sin(Math.atan2(matrix[1], matrix[0]))) > 0.5;
145
- if (isSwapped) {
146
- visualW = this.naturalHeight;
147
- visualH = this.naturalWidth;
148
- }
22
+ if (aspectRatio !== null) {
23
+ // Fixed ratio: fit in container, cap at natural image bounds
24
+ const fitted = fitInContainer({ ratio: aspectRatio, containerWidth, containerHeight });
25
+ const maxWidth = Math.max(visualWidth, visualHeight * aspectRatio);
26
+ const width = Math.min(fitted.width, maxWidth);
27
+ this.canvasGeometry = { aspectRatio, width, height: width / aspectRatio };
149
28
  }
150
- const canvasRect = this._canvas.getBoundingClientRect();
151
- const fitsNaturally = visualW <= canvasRect.width && visualH <= canvasRect.height;
152
- if (fitsNaturally) {
153
- this._image.$center();
29
+ else if (visualWidth <= containerWidth && visualHeight <= containerHeight) {
30
+ // Free ratio, image fits naturally no upscale
31
+ this.canvasGeometry = { aspectRatio, width: visualWidth, height: visualHeight };
154
32
  }
155
33
  else {
156
- this._image.$center('contain');
34
+ // Free ratio, image too large — scale down to fit
35
+ const fitted = fitInContainer({ ratio: visualWidth / visualHeight, containerWidth, containerHeight });
36
+ this.canvasGeometry = { aspectRatio, width: fitted.width, height: fitted.height };
157
37
  }
158
38
  };
159
39
  }
@@ -1,40 +1,7 @@
1
- import type { DragMode, ImgCropperWorker, ImgCropperWorkerParams } from './img-cropper-worker.svelte';
2
- export declare class ImgCropperCoverWorker implements ImgCropperWorker {
3
- cropBoxVisible: boolean;
4
- dragMode: DragMode;
5
- naturalWidth: number;
6
- naturalHeight: number;
7
- canvasRatio: number | null;
8
- destroy: () => void;
9
- private _image;
10
- private _selection;
11
- private _canvas;
12
- private _selectHandle;
13
- private _originalSrc;
14
- private _withCoverCheckSuspended;
1
+ import { ImgCropperBaseWorker } from './img-cropper-base-worker.svelte';
2
+ import type { ImgCropperWorkerParams } from './img-cropper-worker.svelte';
3
+ export declare class ImgCropperCoverWorker extends ImgCropperBaseWorker {
4
+ protected readonly _centerMode: "cover";
15
5
  constructor(params: ImgCropperWorkerParams);
16
- ready: () => Promise<void>;
17
- rotate: () => void;
18
- zoomIn: () => void;
19
- zoomOut: () => void;
20
- reset: () => Promise<void>;
21
- crop: (options?: {
22
- fillColor?: string;
23
- freeRatio?: boolean;
24
- }) => Promise<{
25
- dataUrl: string;
26
- width: number;
27
- height: number;
28
- }>;
29
- save: (options?: {
30
- fillColor?: string;
31
- }) => Promise<{
32
- dataUrl: string;
33
- width: number;
34
- height: number;
35
- }>;
36
- clearSelection: () => void;
37
- enableCropMode: () => void;
38
- private _applyMoveMode;
39
- private _fitImage;
6
+ protected _computeCanvasSize: () => void;
40
7
  }
@@ -1,32 +1,9 @@
1
- import { ColorHelper } from '../../../core/utils';
2
- import { computeImageScale, computeNaturalOutputSize, handleCoverCheck, handleSelectionBoundary } from './img-cropper-utils';
3
- import { tick } from 'svelte';
4
- export class ImgCropperCoverWorker {
5
- cropBoxVisible = $state(false);
6
- dragMode = $state('move');
7
- naturalWidth = $state(0);
8
- naturalHeight = $state(0);
9
- canvasRatio = $state(null);
10
- destroy;
11
- _image;
12
- _selection;
13
- _canvas;
14
- _selectHandle;
15
- _originalSrc;
16
- _withCoverCheckSuspended;
1
+ import { ImgCropperBaseWorker } from './img-cropper-base-worker.svelte';
2
+ import { fitInContainer, handleCoverCheck } from './img-cropper-utils';
3
+ export class ImgCropperCoverWorker extends ImgCropperBaseWorker {
4
+ _centerMode = 'cover';
17
5
  constructor(params) {
18
- this._originalSrc = params.originalSrc;
19
- this._image = params.image;
20
- this._selection = params.selection;
21
- this._canvas = params.canvas;
22
- this._selectHandle = params.selectHandle;
23
- const onSelectionChange = (e) => {
24
- const { width, height } = e.detail;
25
- this.cropBoxVisible = width > 0 && height > 0;
26
- };
27
- const onBoundaryCheck = (e) => {
28
- handleSelectionBoundary({ event: e, selection: this._selection, canvas: this._canvas });
29
- };
6
+ super(params);
30
7
  // Dedup: handleCoverCheck calls $setTransform(clamped) which synchronously
31
8
  // fires another transform event with a matrix that differs by ~0.0001px
32
9
  // due to floating point. Without this guard CropperJS re-fires the same
@@ -41,123 +18,48 @@ export class ImgCropperCoverWorker {
41
18
  handleCoverCheck({ event: e, image: this._image, canvas: this._canvas });
42
19
  prevMatrix = null;
43
20
  };
44
- this._selection.addEventListener('change', onSelectionChange);
45
- this._selection.addEventListener('change', onBoundaryCheck);
46
21
  this._image.addEventListener('transform', onCoverCheck);
47
- this._withCoverCheckSuspended = (fn) => {
22
+ this._wrapTransformOp = (fn) => {
48
23
  this._image.removeEventListener('transform', onCoverCheck);
49
- fn();
50
- this._image.addEventListener('transform', onCoverCheck);
24
+ try {
25
+ fn();
26
+ }
27
+ finally {
28
+ this._image.addEventListener('transform', onCoverCheck);
29
+ }
51
30
  };
31
+ const baseDestroy = this.destroy;
52
32
  this.destroy = () => {
53
- this._selection.removeEventListener('change', onSelectionChange);
54
- this._selection.removeEventListener('change', onBoundaryCheck);
33
+ baseDestroy();
55
34
  this._image.removeEventListener('transform', onCoverCheck);
56
35
  };
57
- this._applyMoveMode();
58
36
  }
59
- ready = async () => {
60
- this._image.src = this._originalSrc;
61
- await this._fitImage();
62
- this._selection.$clear();
63
- };
64
- rotate = () => {
65
- this._withCoverCheckSuspended(() => {
66
- this._image.$rotate('90deg');
67
- this._image.$center('cover');
68
- });
69
- };
70
- zoomIn = () => {
71
- this._image.$zoom(0.1);
72
- };
73
- zoomOut = () => {
74
- this._image.$zoom(-0.1);
75
- };
76
- reset = async () => {
77
- this._selection.$clear();
78
- this._applyMoveMode();
79
- this._image.src = this._originalSrc;
80
- await this._fitImage();
81
- };
82
- crop = async (options) => {
83
- const fillColor = options?.fillColor;
84
- const imageScale = computeImageScale(this._image.$getTransform());
85
- const outputSize = computeNaturalOutputSize({ displayWidth: this._selection.width, displayHeight: this._selection.height, imageScale });
86
- const canvas = await this._selection.$toCanvas({
87
- width: outputSize.width,
88
- height: outputSize.height,
89
- beforeDraw: (ctx, cvs) => {
90
- if (fillColor && !ColorHelper.isTransparent(fillColor)) {
91
- ctx.fillStyle = fillColor;
92
- ctx.fillRect(0, 0, cvs.width, cvs.height);
93
- }
94
- }
95
- });
96
- const dataUrl = canvas.toDataURL('image/png', 0.9);
97
- this._applyMoveMode();
98
- this._image.src = dataUrl;
99
- await this._fitImage();
100
- this._selection.$clear();
101
- return { dataUrl, width: canvas.width, height: canvas.height };
102
- };
103
- save = async (options) => {
104
- const fillColor = options?.fillColor;
105
- const beforeDraw = (ctx, cvs) => {
106
- if (fillColor && !ColorHelper.isTransparent(fillColor)) {
107
- ctx.fillStyle = fillColor;
108
- ctx.fillRect(0, 0, cvs.width, cvs.height);
109
- }
110
- };
111
- // Compute natural output dimensions before any micro-zoom so the
112
- // output matches the source pixel density, not the display size.
113
- const imageScale = computeImageScale(this._image.$getTransform());
114
- const displayWidth = this.cropBoxVisible ? this._selection.width : this._canvas.offsetWidth;
115
- const displayHeight = this.cropBoxVisible ? this._selection.height : this._canvas.offsetHeight;
116
- const outputSize = computeNaturalOutputSize({ displayWidth, displayHeight, imageScale });
117
- const toCanvasOptions = { width: outputSize.width, height: outputSize.height, beforeDraw };
118
- // CropperJS $toCanvas exports only the visible portion of the canvas.
119
- // In cover mode the image exactly covers the canvas, so sub-pixel rounding
120
- // can leave a 1px transparent edge. A micro-zoom nudges the image slightly
121
- // past the canvas boundary so the export captures the full area; the zoom
122
- // is reversed immediately after.
123
- const needsCoverZoom = !this.cropBoxVisible;
124
- if (needsCoverZoom) {
125
- this._image.$zoom(0.003);
37
+ _computeCanvasSize = () => {
38
+ const container = this._canvas.parentElement;
39
+ if (!container) {
40
+ return;
126
41
  }
127
- const canvas = this.cropBoxVisible ? await this._selection.$toCanvas(toCanvasOptions) : await this._canvas.$toCanvas(toCanvasOptions);
128
- if (needsCoverZoom) {
129
- this._withCoverCheckSuspended(() => this._image.$zoom(-0.003));
42
+ const containerWidth = container.clientWidth;
43
+ const containerHeight = container.clientHeight;
44
+ const visualWidth = this._visualWidth;
45
+ const visualHeight = this._visualHeight;
46
+ const aspectRatio = this.canvasGeometry.aspectRatio;
47
+ if (containerWidth <= 0 || containerHeight <= 0) {
48
+ this.canvasGeometry = { aspectRatio, width: 0, height: 0 };
49
+ return;
130
50
  }
131
- const dataUrl = canvas.toDataURL('image/png', 0.9);
132
- return { dataUrl, width: canvas.width, height: canvas.height };
133
- };
134
- clearSelection = () => {
135
- this._selection.$clear();
136
- this._applyMoveMode();
137
- };
138
- enableCropMode = () => {
139
- this._image.translatable = false;
140
- if (this._selectHandle) {
141
- this._selectHandle.setAttribute('action', 'select');
51
+ if (aspectRatio !== null) {
52
+ const fitted = fitInContainer({ ratio: aspectRatio, containerWidth, containerHeight });
53
+ // Cap so the image can cover the canvas without upscaling
54
+ const maxWidth = visualWidth > 0 && visualHeight > 0 ? Math.min(visualWidth, visualHeight * aspectRatio) : fitted.width;
55
+ const width = Math.min(fitted.width, maxWidth);
56
+ this.canvasGeometry = { aspectRatio, width, height: width / aspectRatio };
142
57
  }
143
- this.dragMode = 'crop';
144
- };
145
- _applyMoveMode = () => {
146
- this._image.translatable = true;
147
- if (this._selectHandle) {
148
- this._selectHandle.setAttribute('action', 'move');
58
+ else {
59
+ // Cap each dimension at visual size to avoid upscaling small images
60
+ const width = visualWidth > 0 ? Math.min(containerWidth, visualWidth) : containerWidth;
61
+ const height = visualHeight > 0 ? Math.min(containerHeight, visualHeight) : containerHeight;
62
+ this.canvasGeometry = { aspectRatio, width, height };
149
63
  }
150
- this.dragMode = 'move';
151
- this.cropBoxVisible = false;
152
- };
153
- _fitImage = async () => {
154
- const img = await this._image.$ready();
155
- this.naturalWidth = img.naturalWidth;
156
- this.naturalHeight = img.naturalHeight;
157
- await tick();
158
- this._withCoverCheckSuspended(() => {
159
- this._image.$resetTransform();
160
- this._image.$center('cover');
161
- });
162
64
  };
163
65
  }
@@ -1,10 +1,11 @@
1
1
  import type { CropperCanvas, CropperImage, CropperSelection } from 'cropperjs';
2
- export type Rect = {
2
+ type Rect = {
3
3
  left: number;
4
4
  top: number;
5
5
  right: number;
6
6
  bottom: number;
7
7
  };
8
+ export declare const detectMimeFromUrl: (url: string) => string | null;
8
9
  export declare const computeImageScale: (transform: number[]) => number;
9
10
  export declare const computeNaturalOutputSize: (params: {
10
11
  displayWidth: number;
@@ -25,8 +26,17 @@ export declare const handleCoverCheck: (params: {
25
26
  image: CropperImage;
26
27
  canvas: CropperCanvas;
27
28
  }) => void;
29
+ export declare const fitInContainer: (params: {
30
+ ratio: number;
31
+ containerWidth: number;
32
+ containerHeight: number;
33
+ }) => {
34
+ width: number;
35
+ height: number;
36
+ };
28
37
  export declare const handleSelectionBoundary: (params: {
29
38
  event: Event;
30
39
  selection: CropperSelection;
31
40
  canvas: CropperCanvas;
32
41
  }) => void;
42
+ export {};
@@ -1,3 +1,26 @@
1
+ import { Base64Helper } from '../../../core/files/base64-helper';
2
+ import { default as mime } from 'mime';
3
+ // Detect MIME type from a URL without fetching the resource.
4
+ // Returns a MIME string for data: URLs, extension-based URLs, or null for blob: / unknown URLs.
5
+ export const detectMimeFromUrl = (url) => {
6
+ if (url.startsWith('data:')) {
7
+ return Base64Helper.getMimeType(url);
8
+ }
9
+ if (url.startsWith('blob:')) {
10
+ return null;
11
+ }
12
+ try {
13
+ const pathname = url.includes('://') ? new URL(url).pathname : url.split('?')[0].split('#')[0];
14
+ const lastDot = pathname.lastIndexOf('.');
15
+ if (lastDot === -1) {
16
+ return null;
17
+ }
18
+ return mime.getType(pathname.slice(lastDot + 1).toLowerCase()) ?? null;
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ };
1
24
  // Extract the uniform scale factor from a CSS transform matrix [a, b, c, d, e, f].
2
25
  // Works for any combination of scale + rotation: scale = sqrt(a² + b²).
3
26
  export const computeImageScale = (transform) => {
@@ -91,6 +114,13 @@ export const handleCoverCheck = (params) => {
91
114
  }
92
115
  image.$setTransform(matrix[0], matrix[1], matrix[2], matrix[3], clampedTx, clampedTy);
93
116
  };
117
+ export const fitInContainer = (params) => {
118
+ const { ratio, containerWidth, containerHeight } = params;
119
+ if (ratio > containerWidth / containerHeight) {
120
+ return { width: containerWidth, height: containerWidth / ratio };
121
+ }
122
+ return { width: containerHeight * ratio, height: containerHeight };
123
+ };
94
124
  export const handleSelectionBoundary = (params) => {
95
125
  const { event, selection, canvas } = params;
96
126
  const { x, y, width, height } = event.detail;
@@ -1,18 +1,29 @@
1
1
  import type { CropperCanvas, CropperImage, CropperSelection } from 'cropperjs';
2
2
  export type DragMode = 'move' | 'crop';
3
+ export type CanvasGeometry = {
4
+ aspectRatio: number | null;
5
+ width: number;
6
+ height: number;
7
+ };
3
8
  export type ImgCropperWorkerParams = {
4
9
  originalSrc: string;
10
+ sourceMime: string | null;
5
11
  image: CropperImage;
6
12
  selection: CropperSelection;
7
13
  canvas: CropperCanvas;
8
14
  selectHandle: HTMLElement | null;
15
+ aspectRatio: number | null;
16
+ };
17
+ export type ImgCropperResult = {
18
+ dataUrl: string;
19
+ width: number;
20
+ height: number;
21
+ mimeType: string;
9
22
  };
10
23
  export interface ImgCropperWorker {
11
24
  cropBoxVisible: boolean;
12
25
  dragMode: DragMode;
13
- naturalWidth: number;
14
- naturalHeight: number;
15
- canvasRatio: number | null;
26
+ canvasGeometry: CanvasGeometry;
16
27
  destroy: () => void;
17
28
  ready: () => Promise<void>;
18
29
  rotate: () => void | Promise<void>;
@@ -21,19 +32,11 @@ export interface ImgCropperWorker {
21
32
  reset: () => Promise<void>;
22
33
  crop: (options?: {
23
34
  fillColor?: string;
24
- freeRatio?: boolean;
25
- }) => Promise<{
26
- dataUrl: string;
27
- width: number;
28
- height: number;
29
- }>;
35
+ }) => Promise<ImgCropperResult>;
30
36
  save: (options?: {
31
37
  fillColor?: string;
32
- }) => Promise<{
33
- dataUrl: string;
34
- width: number;
35
- height: number;
36
- }>;
38
+ }) => Promise<ImgCropperResult>;
37
39
  clearSelection: () => void;
38
40
  enableCropMode: () => void;
41
+ refit: () => void | Promise<void>;
39
42
  }
@@ -1,17 +1,13 @@
1
- import type { DragMode } from './img-cropper-worker.svelte';
1
+ import type { CanvasGeometry, DragMode, ImgCropperResult } from './img-cropper-worker.svelte';
2
2
  import type { CropperCanvas, CropperImage, CropperSelection } from 'cropperjs';
3
+ export type { CanvasGeometry };
3
4
  export type CropperMode = 'contain' | 'cover';
4
- export type CorsMode = 'native' | 'fetch';
5
5
  export type { DragMode };
6
6
  export type ImgCropperRatioOption = {
7
7
  label: string;
8
8
  value: number | null;
9
9
  };
10
- export type ImgCropperCoverAspectRatio = {
11
- initial: number;
12
- supported: number[];
13
- };
14
- export type ImgCropperContainAspectRatio = {
10
+ export type ImgCropperDynamicAspectRatio = {
15
11
  initial: number;
16
12
  supported: number[];
17
13
  allowFreeCrop?: false;
@@ -20,40 +16,29 @@ export type ImgCropperContainAspectRatio = {
20
16
  supported: number[];
21
17
  allowFreeCrop: true;
22
18
  };
23
- export type ImgCropperSaveResult = {
24
- dataUrl: string;
25
- width: number;
26
- height: number;
27
- };
28
- type ImgCropperOptionsBase = {
29
- corsMode?: CorsMode;
19
+ export type { ImgCropperResult };
20
+ export type ImgCropperSaveResult = ImgCropperResult;
21
+ export type ImgCropperOptions = {
22
+ mode: CropperMode;
23
+ aspectRatio?: number | ImgCropperDynamicAspectRatio;
30
24
  fillColor?: string;
31
25
  showImageShadow?: boolean;
32
26
  };
33
- export type ImgCropperOptions = (ImgCropperOptionsBase & {
34
- mode: 'cover';
35
- aspectRatio?: number | ImgCropperCoverAspectRatio;
36
- }) | (ImgCropperOptionsBase & {
37
- mode: 'contain';
38
- aspectRatio?: number | ImgCropperContainAspectRatio;
39
- });
40
27
  export declare class ImgCropper {
41
28
  loading: boolean;
42
29
  ready: boolean;
43
30
  fillColor: string;
44
- aspectRatio: number | null;
45
31
  readonly mode: CropperMode;
46
- readonly corsMode: CorsMode;
47
32
  readonly ratioOptions: ImgCropperRatioOption[] | null;
48
33
  readonly showImageShadow: boolean;
49
34
  private _worker;
50
35
  private _originalSrc;
36
+ private _defaultGeometry;
51
37
  constructor(options: ImgCropperOptions);
52
38
  get showFillColor(): boolean;
53
39
  get isTransparentFill(): boolean;
54
- get effectiveCanvasRatio(): number | null;
55
- get naturalWidth(): number;
56
- get naturalHeight(): number;
40
+ get canvasGeometry(): CanvasGeometry;
41
+ get aspectRatio(): number | null;
57
42
  get cropBoxVisible(): boolean;
58
43
  get dragMode(): DragMode;
59
44
  init: (params: {
@@ -69,13 +54,10 @@ export declare class ImgCropper {
69
54
  zoomOut: () => void;
70
55
  reset: () => Promise<void>;
71
56
  changeAspectRatio: (ratio: number | null) => Promise<void>;
72
- crop: () => Promise<{
73
- dataUrl: string;
74
- width: number;
75
- height: number;
76
- } | null>;
57
+ crop: () => Promise<ImgCropperSaveResult | null>;
77
58
  save: () => Promise<ImgCropperSaveResult | null>;
78
59
  clearSelection: () => void;
79
60
  enableCropMode: () => void;
80
61
  enableMoveMode: () => void;
62
+ refit: () => void;
81
63
  }