@djangocfg/ui-tools 2.1.389 → 2.1.393

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 (183) hide show
  1. package/README.md +7 -10
  2. package/dist/chunk-PAWJFY3S.mjs +6 -0
  3. package/dist/{chunk-N2XQF2OL.mjs.map → chunk-PAWJFY3S.mjs.map} +1 -1
  4. package/dist/chunk-PK6SKIKE.cjs +8 -0
  5. package/dist/{chunk-OLISEQHS.cjs.map → chunk-PK6SKIKE.cjs.map} +1 -1
  6. package/dist/file-icon/index.cjs +6 -6
  7. package/dist/file-icon/index.d.cts +1 -1
  8. package/dist/file-icon/index.d.ts +1 -1
  9. package/dist/file-icon/index.mjs +1 -1
  10. package/dist/tree/index.cjs +1372 -143
  11. package/dist/tree/index.cjs.map +1 -1
  12. package/dist/tree/index.d.cts +2 -2
  13. package/dist/tree/index.d.ts +2 -2
  14. package/dist/tree/index.mjs +1322 -3
  15. package/dist/tree/index.mjs.map +1 -1
  16. package/dist/{types-CevSbyfD.d.cts → types-B_zhyAqR.d.cts} +1 -1
  17. package/dist/{types-CevSbyfD.d.ts → types-B_zhyAqR.d.ts} +1 -1
  18. package/package.json +9 -17
  19. package/src/tools/AudioPlayer/README.md +4 -4
  20. package/src/tools/Chat/README.md +6 -6
  21. package/src/tools/CronScheduler/index.tsx +1 -1
  22. package/src/tools/CronScheduler/lazy.tsx +1 -1
  23. package/src/tools/ImageViewer/README.md +1 -1
  24. package/src/tools/JsonForm/README.md +2 -2
  25. package/src/tools/MarkdownEditor/README.md +3 -3
  26. package/src/tools/MarkdownMessage/README.md +2 -2
  27. package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +5 -1
  28. package/src/tools/SpeechRecognition/README.md +1 -1
  29. package/dist/ChatRoot-EFNXQXXN.cjs +0 -15
  30. package/dist/ChatRoot-EFNXQXXN.cjs.map +0 -1
  31. package/dist/ChatRoot-FITF5RVP.mjs +0 -6
  32. package/dist/ChatRoot-FITF5RVP.mjs.map +0 -1
  33. package/dist/ChatRoot-PNNGQCYF.css +0 -7
  34. package/dist/ChatRoot-PNNGQCYF.css.map +0 -1
  35. package/dist/CronScheduler.client-DLMXCPAJ.mjs +0 -67
  36. package/dist/CronScheduler.client-DLMXCPAJ.mjs.map +0 -1
  37. package/dist/CronScheduler.client-WEJF4PWQ.cjs +0 -72
  38. package/dist/CronScheduler.client-WEJF4PWQ.cjs.map +0 -1
  39. package/dist/DictationField-AS2F33WI.cjs +0 -13
  40. package/dist/DictationField-AS2F33WI.cjs.map +0 -1
  41. package/dist/DictationField-WPONUCYE.mjs +0 -4
  42. package/dist/DictationField-WPONUCYE.mjs.map +0 -1
  43. package/dist/DocsLayout-EKASBSP7.mjs +0 -3448
  44. package/dist/DocsLayout-EKASBSP7.mjs.map +0 -1
  45. package/dist/DocsLayout-MBFIB4NO.css +0 -7
  46. package/dist/DocsLayout-MBFIB4NO.css.map +0 -1
  47. package/dist/DocsLayout-OURFYWQE.cjs +0 -3455
  48. package/dist/DocsLayout-OURFYWQE.cjs.map +0 -1
  49. package/dist/JsonSchemaForm-DD7CLRIG.cjs +0 -13
  50. package/dist/JsonSchemaForm-DD7CLRIG.cjs.map +0 -1
  51. package/dist/JsonSchemaForm-XKUIVELK.mjs +0 -4
  52. package/dist/JsonSchemaForm-XKUIVELK.mjs.map +0 -1
  53. package/dist/JsonTree-43PQAJKY.mjs +0 -5
  54. package/dist/JsonTree-43PQAJKY.mjs.map +0 -1
  55. package/dist/JsonTree-MLET23ZA.css +0 -7
  56. package/dist/JsonTree-MLET23ZA.css.map +0 -1
  57. package/dist/JsonTree-X6W5YEVY.cjs +0 -11
  58. package/dist/JsonTree-X6W5YEVY.cjs.map +0 -1
  59. package/dist/LottiePlayer.client-2S7ISJ2S.cjs +0 -168
  60. package/dist/LottiePlayer.client-2S7ISJ2S.cjs.map +0 -1
  61. package/dist/LottiePlayer.client-5LDSSJWS.mjs +0 -161
  62. package/dist/LottiePlayer.client-5LDSSJWS.mjs.map +0 -1
  63. package/dist/MapContainer-AKIPABJK.mjs +0 -4
  64. package/dist/MapContainer-AKIPABJK.mjs.map +0 -1
  65. package/dist/MapContainer-STVDMC36.cjs +0 -17
  66. package/dist/MapContainer-STVDMC36.cjs.map +0 -1
  67. package/dist/Mermaid.client-DDXWXZXY.css +0 -7
  68. package/dist/Mermaid.client-DDXWXZXY.css.map +0 -1
  69. package/dist/Mermaid.client-NL4SVR7F.mjs +0 -481
  70. package/dist/Mermaid.client-NL4SVR7F.mjs.map +0 -1
  71. package/dist/Mermaid.client-NNTI6DFX.cjs +0 -487
  72. package/dist/Mermaid.client-NNTI6DFX.cjs.map +0 -1
  73. package/dist/Player-BRV7XTWR.mjs +0 -4
  74. package/dist/Player-BRV7XTWR.mjs.map +0 -1
  75. package/dist/Player-PM7F7DD7.cjs +0 -13
  76. package/dist/Player-PM7F7DD7.cjs.map +0 -1
  77. package/dist/Player-ZGQKKOWI.css +0 -66
  78. package/dist/Player-ZGQKKOWI.css.map +0 -1
  79. package/dist/PrettyCode.client-GWFAIVFN.css +0 -7
  80. package/dist/PrettyCode.client-GWFAIVFN.css.map +0 -1
  81. package/dist/PrettyCode.client-KOHDVPPN.cjs +0 -285
  82. package/dist/PrettyCode.client-KOHDVPPN.cjs.map +0 -1
  83. package/dist/PrettyCode.client-ZGYGKE7G.mjs +0 -283
  84. package/dist/PrettyCode.client-ZGYGKE7G.mjs.map +0 -1
  85. package/dist/TreeRoot-5COOOSWG.mjs +0 -4
  86. package/dist/TreeRoot-5COOOSWG.mjs.map +0 -1
  87. package/dist/TreeRoot-AABP2J6Y.cjs +0 -19
  88. package/dist/TreeRoot-AABP2J6Y.cjs.map +0 -1
  89. package/dist/chunk-2NG4SXEP.mjs +0 -743
  90. package/dist/chunk-2NG4SXEP.mjs.map +0 -1
  91. package/dist/chunk-4LFB7I5K.cjs +0 -1387
  92. package/dist/chunk-4LFB7I5K.cjs.map +0 -1
  93. package/dist/chunk-5D2OCOPQ.cjs +0 -222
  94. package/dist/chunk-5D2OCOPQ.cjs.map +0 -1
  95. package/dist/chunk-5I5QNGUG.cjs +0 -611
  96. package/dist/chunk-5I5QNGUG.cjs.map +0 -1
  97. package/dist/chunk-6ZX2G25W.mjs +0 -1361
  98. package/dist/chunk-6ZX2G25W.mjs.map +0 -1
  99. package/dist/chunk-76NNDZH6.cjs +0 -1061
  100. package/dist/chunk-76NNDZH6.cjs.map +0 -1
  101. package/dist/chunk-7CWGZPO3.mjs +0 -214
  102. package/dist/chunk-7CWGZPO3.mjs.map +0 -1
  103. package/dist/chunk-7EYHNP3E.cjs +0 -965
  104. package/dist/chunk-7EYHNP3E.cjs.map +0 -1
  105. package/dist/chunk-7IYXZUJO.cjs +0 -769
  106. package/dist/chunk-7IYXZUJO.cjs.map +0 -1
  107. package/dist/chunk-ADEN3UA4.cjs +0 -892
  108. package/dist/chunk-ADEN3UA4.cjs.map +0 -1
  109. package/dist/chunk-B6IR5KSC.mjs +0 -59
  110. package/dist/chunk-B6IR5KSC.mjs.map +0 -1
  111. package/dist/chunk-C6GXVH5J.mjs +0 -338
  112. package/dist/chunk-C6GXVH5J.mjs.map +0 -1
  113. package/dist/chunk-DMX7W4XZ.mjs +0 -1113
  114. package/dist/chunk-DMX7W4XZ.mjs.map +0 -1
  115. package/dist/chunk-ECONRHIG.mjs +0 -212
  116. package/dist/chunk-ECONRHIG.mjs.map +0 -1
  117. package/dist/chunk-FEN5S772.cjs +0 -1227
  118. package/dist/chunk-FEN5S772.cjs.map +0 -1
  119. package/dist/chunk-FP2RLYQZ.cjs +0 -187
  120. package/dist/chunk-FP2RLYQZ.cjs.map +0 -1
  121. package/dist/chunk-FVVF7VCD.cjs +0 -1325
  122. package/dist/chunk-FVVF7VCD.cjs.map +0 -1
  123. package/dist/chunk-GYIO7W7M.mjs +0 -1197
  124. package/dist/chunk-GYIO7W7M.mjs.map +0 -1
  125. package/dist/chunk-KNDLV4PI.cjs +0 -1356
  126. package/dist/chunk-KNDLV4PI.cjs.map +0 -1
  127. package/dist/chunk-KNEQRUBA.mjs +0 -181
  128. package/dist/chunk-KNEQRUBA.mjs.map +0 -1
  129. package/dist/chunk-N2XQF2OL.mjs +0 -14
  130. package/dist/chunk-N4MZYNR4.mjs +0 -1342
  131. package/dist/chunk-N4MZYNR4.mjs.map +0 -1
  132. package/dist/chunk-NTVBIIUD.mjs +0 -1439
  133. package/dist/chunk-NTVBIIUD.mjs.map +0 -1
  134. package/dist/chunk-OBRSGM64.mjs +0 -607
  135. package/dist/chunk-OBRSGM64.mjs.map +0 -1
  136. package/dist/chunk-ODO4GMW7.mjs +0 -79
  137. package/dist/chunk-ODO4GMW7.mjs.map +0 -1
  138. package/dist/chunk-OLISEQHS.cjs +0 -18
  139. package/dist/chunk-PVAX67JG.mjs +0 -1041
  140. package/dist/chunk-PVAX67JG.mjs.map +0 -1
  141. package/dist/chunk-QJ6GTUCO.cjs +0 -81
  142. package/dist/chunk-QJ6GTUCO.cjs.map +0 -1
  143. package/dist/chunk-T3MWM23F.cjs +0 -214
  144. package/dist/chunk-T3MWM23F.cjs.map +0 -1
  145. package/dist/chunk-TBSHZO5R.cjs +0 -1134
  146. package/dist/chunk-TBSHZO5R.cjs.map +0 -1
  147. package/dist/chunk-UNCS5V5F.mjs +0 -887
  148. package/dist/chunk-UNCS5V5F.mjs.map +0 -1
  149. package/dist/chunk-VWQ5WOIL.mjs +0 -2059
  150. package/dist/chunk-VWQ5WOIL.mjs.map +0 -1
  151. package/dist/chunk-W75B7Y6C.cjs +0 -1478
  152. package/dist/chunk-W75B7Y6C.cjs.map +0 -1
  153. package/dist/chunk-Y6UTOBF6.mjs +0 -938
  154. package/dist/chunk-Y6UTOBF6.mjs.map +0 -1
  155. package/dist/chunk-YDPDTOSP.cjs +0 -2061
  156. package/dist/chunk-YDPDTOSP.cjs.map +0 -1
  157. package/dist/chunk-YW5IVWHQ.cjs +0 -346
  158. package/dist/chunk-YW5IVWHQ.cjs.map +0 -1
  159. package/dist/chunk-YXZ6GU7H.cjs +0 -63
  160. package/dist/chunk-YXZ6GU7H.cjs.map +0 -1
  161. package/dist/chunk-ZL7FH4NW.mjs +0 -1274
  162. package/dist/chunk-ZL7FH4NW.mjs.map +0 -1
  163. package/dist/components-EHOGXATG.cjs +0 -22
  164. package/dist/components-EHOGXATG.cjs.map +0 -1
  165. package/dist/components-MQ6DR7TX.cjs +0 -26
  166. package/dist/components-MQ6DR7TX.cjs.map +0 -1
  167. package/dist/components-XRX7QGLB.mjs +0 -5
  168. package/dist/components-XRX7QGLB.mjs.map +0 -1
  169. package/dist/components-YATKRWLH.mjs +0 -5
  170. package/dist/components-YATKRWLH.mjs.map +0 -1
  171. package/dist/index.cjs +0 -3674
  172. package/dist/index.cjs.map +0 -1
  173. package/dist/index.css +0 -234
  174. package/dist/index.css.map +0 -1
  175. package/dist/index.d.cts +0 -4929
  176. package/dist/index.d.ts +0 -4929
  177. package/dist/index.mjs +0 -2912
  178. package/dist/index.mjs.map +0 -1
  179. package/dist/launcher-5Y42OBSN.mjs +0 -6
  180. package/dist/launcher-5Y42OBSN.mjs.map +0 -1
  181. package/dist/launcher-PMW2YB24.cjs +0 -59
  182. package/dist/launcher-PMW2YB24.cjs.map +0 -1
  183. package/src/index.ts +0 -157
@@ -1,607 +0,0 @@
1
- import { useImageCache, useMediaCacheStore, generateContentKey } from './chunk-C6GXVH5J.mjs';
2
- import { __name } from './chunk-N2XQF2OL.mjs';
3
- import { useMemo, useState, useEffect, useCallback, useRef } from 'react';
4
- import { ZoomOut, ZoomIn, Maximize2, FlipHorizontal, FlipVertical, RotateCw, Expand, ImageIcon, AlertCircle, ChevronLeft, ChevronRight } from 'lucide-react';
5
- import { useControls, TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
6
- import { Alert, AlertDescription, cn as cn$1, Dialog, DialogContent, DialogTitle } from '@djangocfg/ui-core';
7
- import { useAppT } from '@djangocfg/i18n';
8
- import { useHotkey } from '@djangocfg/ui-core/hooks';
9
- import { Button, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem } from '@djangocfg/ui-core/components';
10
- import { createMediaLogger, cn } from '@djangocfg/ui-core/lib';
11
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
12
-
13
- // src/tools/ImageViewer/utils/constants.ts
14
- var MAX_IMAGE_SIZE = 50 * 1024 * 1024;
15
- var WARNING_IMAGE_SIZE = 10 * 1024 * 1024;
16
- var PROGRESSIVE_LOADING_THRESHOLD = 500 * 1024;
17
- var LQIP_SIZE = 32;
18
- var LQIP_QUALITY = 0.5;
19
- var ZOOM_PRESETS = [
20
- { label: "Fit", value: "fit" },
21
- { label: "25%", value: 0.25 },
22
- { label: "50%", value: 0.5 },
23
- { label: "100%", value: 1 },
24
- { label: "200%", value: 2 },
25
- { label: "400%", value: 4 }
26
- ];
27
- var DEFAULT_TRANSFORM = {
28
- rotation: 0,
29
- flipH: false,
30
- flipV: false
31
- };
32
-
33
- // src/tools/ImageViewer/utils/lqip.ts
34
- async function createLQIP(imageSrc) {
35
- try {
36
- const img = new Image();
37
- img.crossOrigin = "anonymous";
38
- await new Promise((resolve, reject) => {
39
- img.onload = () => resolve();
40
- img.onerror = () => reject(new Error("Failed to load image for LQIP"));
41
- img.src = imageSrc;
42
- });
43
- const aspect = img.naturalWidth / img.naturalHeight;
44
- const width = aspect >= 1 ? LQIP_SIZE : Math.round(LQIP_SIZE * aspect);
45
- const height = aspect >= 1 ? Math.round(LQIP_SIZE / aspect) : LQIP_SIZE;
46
- const canvas = document.createElement("canvas");
47
- canvas.width = width;
48
- canvas.height = height;
49
- const ctx = canvas.getContext("2d");
50
- if (!ctx) return null;
51
- ctx.drawImage(img, 0, 0, width, height);
52
- return canvas.toDataURL("image/jpeg", LQIP_QUALITY);
53
- } catch {
54
- return null;
55
- }
56
- }
57
- __name(createLQIP, "createLQIP");
58
- var imageDebug = createMediaLogger("ImageViewer");
59
- function ImageToolbar({
60
- scale,
61
- transform,
62
- onRotate,
63
- onFlipH,
64
- onFlipV,
65
- onZoomPreset,
66
- onExpand
67
- }) {
68
- const t = useAppT();
69
- const { zoomIn, zoomOut, resetTransform } = useControls();
70
- const labels = useMemo(() => ({
71
- zoomIn: t("tools.image.zoomIn"),
72
- zoomOut: t("tools.image.zoomOut"),
73
- fitToView: t("tools.image.fitToView"),
74
- flipHorizontal: t("tools.image.flipHorizontal"),
75
- flipVertical: t("tools.image.flipVertical"),
76
- rotate: t("tools.image.rotate"),
77
- fullscreen: t("tools.image.fullscreen")
78
- }), [t]);
79
- const zoomLabel = useMemo(() => {
80
- const percent = Math.round(scale * 100);
81
- return `${percent}%`;
82
- }, [scale]);
83
- return /* @__PURE__ */ jsxs("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 z-10 flex items-center gap-0.5 bg-background/90 backdrop-blur-sm border rounded-lg p-1 shadow-lg", children: [
84
- /* @__PURE__ */ jsx(
85
- Button,
86
- {
87
- variant: "ghost",
88
- size: "icon",
89
- className: "h-7 w-7",
90
- onClick: () => zoomOut(),
91
- title: labels.zoomOut,
92
- children: /* @__PURE__ */ jsx(ZoomOut, { className: "h-3.5 w-3.5" })
93
- }
94
- ),
95
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
96
- /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", className: "h-7 px-2 min-w-[52px] font-mono text-xs", children: zoomLabel }) }),
97
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "center", className: "min-w-[80px]", children: ZOOM_PRESETS.map((preset) => /* @__PURE__ */ jsx(
98
- DropdownMenuItem,
99
- {
100
- onClick: () => onZoomPreset(preset.value),
101
- className: "text-xs justify-center",
102
- children: preset.label
103
- },
104
- preset.label
105
- )) })
106
- ] }),
107
- /* @__PURE__ */ jsx(
108
- Button,
109
- {
110
- variant: "ghost",
111
- size: "icon",
112
- className: "h-7 w-7",
113
- onClick: () => zoomIn(),
114
- title: labels.zoomIn,
115
- children: /* @__PURE__ */ jsx(ZoomIn, { className: "h-3.5 w-3.5" })
116
- }
117
- ),
118
- /* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-border mx-1" }),
119
- /* @__PURE__ */ jsx(
120
- Button,
121
- {
122
- variant: "ghost",
123
- size: "icon",
124
- className: "h-7 w-7",
125
- onClick: () => resetTransform(),
126
- title: labels.fitToView,
127
- children: /* @__PURE__ */ jsx(Maximize2, { className: "h-3.5 w-3.5" })
128
- }
129
- ),
130
- /* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-border mx-1" }),
131
- /* @__PURE__ */ jsx(
132
- Button,
133
- {
134
- variant: "ghost",
135
- size: "icon",
136
- className: cn("h-7 w-7", transform.flipH && "bg-accent"),
137
- onClick: onFlipH,
138
- title: labels.flipHorizontal,
139
- children: /* @__PURE__ */ jsx(FlipHorizontal, { className: "h-3.5 w-3.5" })
140
- }
141
- ),
142
- /* @__PURE__ */ jsx(
143
- Button,
144
- {
145
- variant: "ghost",
146
- size: "icon",
147
- className: cn("h-7 w-7", transform.flipV && "bg-accent"),
148
- onClick: onFlipV,
149
- title: labels.flipVertical,
150
- children: /* @__PURE__ */ jsx(FlipVertical, { className: "h-3.5 w-3.5" })
151
- }
152
- ),
153
- /* @__PURE__ */ jsx(
154
- Button,
155
- {
156
- variant: "ghost",
157
- size: "icon",
158
- className: "h-7 w-7",
159
- onClick: onRotate,
160
- title: labels.rotate,
161
- children: /* @__PURE__ */ jsx(RotateCw, { className: "h-3.5 w-3.5" })
162
- }
163
- ),
164
- transform.rotation !== 0 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground font-mono pl-1", children: [
165
- transform.rotation,
166
- "\xB0"
167
- ] }),
168
- onExpand && /* @__PURE__ */ jsxs(Fragment, { children: [
169
- /* @__PURE__ */ jsx("div", { className: "w-px h-4 bg-border mx-1" }),
170
- /* @__PURE__ */ jsx(
171
- Button,
172
- {
173
- variant: "ghost",
174
- size: "icon",
175
- className: "h-7 w-7",
176
- onClick: onExpand,
177
- title: labels.fullscreen,
178
- children: /* @__PURE__ */ jsx(Expand, { className: "h-3.5 w-3.5" })
179
- }
180
- )
181
- ] })
182
- ] });
183
- }
184
- __name(ImageToolbar, "ImageToolbar");
185
- function ImageInfo({ src }) {
186
- const { getDimensions, cacheDimensions } = useImageCache();
187
- const [dimensions, setDimensions] = useState(null);
188
- useEffect(() => {
189
- const cached = getDimensions(src);
190
- if (cached) {
191
- setDimensions({ w: cached.width, h: cached.height });
192
- return;
193
- }
194
- const img = new Image();
195
- img.onload = () => {
196
- const dims = { w: img.naturalWidth, h: img.naturalHeight };
197
- setDimensions(dims);
198
- cacheDimensions(src, { width: dims.w, height: dims.h });
199
- };
200
- img.src = src;
201
- }, [src, getDimensions, cacheDimensions]);
202
- if (!dimensions) return null;
203
- return /* @__PURE__ */ jsxs("div", { className: "absolute top-3 right-3 z-10 px-2 py-1 bg-background/80 backdrop-blur-sm border rounded text-[10px] text-muted-foreground font-mono", children: [
204
- dimensions.w,
205
- " \xD7 ",
206
- dimensions.h
207
- ] });
208
- }
209
- __name(ImageInfo, "ImageInfo");
210
- function useImageTransform(options = {}) {
211
- const { resetKey } = options;
212
- const [transform, setTransform] = useState(DEFAULT_TRANSFORM);
213
- useEffect(() => {
214
- setTransform(DEFAULT_TRANSFORM);
215
- }, [resetKey]);
216
- const rotate = useCallback(() => {
217
- setTransform((prev) => ({
218
- ...prev,
219
- rotation: (prev.rotation + 90) % 360
220
- }));
221
- }, []);
222
- const flipH = useCallback(() => {
223
- setTransform((prev) => ({
224
- ...prev,
225
- flipH: !prev.flipH
226
- }));
227
- }, []);
228
- const flipV = useCallback(() => {
229
- setTransform((prev) => ({
230
- ...prev,
231
- flipV: !prev.flipV
232
- }));
233
- }, []);
234
- const reset = useCallback(() => {
235
- setTransform(DEFAULT_TRANSFORM);
236
- }, []);
237
- const transformStyle = useMemo(() => {
238
- const transforms = [];
239
- if (transform.rotation !== 0) {
240
- transforms.push(`rotate(${transform.rotation}deg)`);
241
- }
242
- if (transform.flipH) {
243
- transforms.push("scaleX(-1)");
244
- }
245
- if (transform.flipV) {
246
- transforms.push("scaleY(-1)");
247
- }
248
- return transforms.join(" ") || "none";
249
- }, [transform]);
250
- return {
251
- transform,
252
- rotate,
253
- flipH,
254
- flipV,
255
- reset,
256
- transformStyle
257
- };
258
- }
259
- __name(useImageTransform, "useImageTransform");
260
- function useImageLoading(options) {
261
- const { content, mimeType, src: directSrc } = options;
262
- const getOrCreateBlobUrl = useMediaCacheStore.getState().getOrCreateBlobUrl;
263
- const releaseBlobUrl = useMediaCacheStore.getState().releaseBlobUrl;
264
- const [src, setSrc] = useState(null);
265
- const [lqip, setLqip] = useState(null);
266
- const [isFullyLoaded, setIsFullyLoaded] = useState(false);
267
- const [error, setError] = useState(null);
268
- const contentKeyRef = useRef(null);
269
- const isMountedRef = useRef(true);
270
- const size = content ? typeof content === "string" ? content.length : content.byteLength : 0;
271
- const hasContent = directSrc ? true : size > 0;
272
- const useProgressiveLoading = directSrc ? false : size > PROGRESSIVE_LOADING_THRESHOLD;
273
- useEffect(() => {
274
- isMountedRef.current = true;
275
- return () => {
276
- isMountedRef.current = false;
277
- if (contentKeyRef.current) {
278
- useMediaCacheStore.getState().releaseBlobUrl(contentKeyRef.current);
279
- contentKeyRef.current = null;
280
- }
281
- };
282
- }, []);
283
- useEffect(() => {
284
- setError(null);
285
- if (directSrc) {
286
- imageDebug.load(directSrc, "url");
287
- setSrc(directSrc);
288
- setIsFullyLoaded(true);
289
- return;
290
- }
291
- if (!hasContent) {
292
- setSrc(null);
293
- return;
294
- }
295
- if (size > MAX_IMAGE_SIZE) {
296
- const sizeMB = (size / 1024 / 1024).toFixed(1);
297
- const errorMsg = `Image too large: ${sizeMB}MB (maximum: 50MB)`;
298
- imageDebug.error(errorMsg, { size, sizeMB, maxSize: MAX_IMAGE_SIZE });
299
- setError(errorMsg);
300
- setSrc(null);
301
- return;
302
- }
303
- if (size > WARNING_IMAGE_SIZE) {
304
- const sizeMB = (size / 1024 / 1024).toFixed(1);
305
- imageDebug.warn(`Large image: ${sizeMB}MB - may impact performance`);
306
- }
307
- if (typeof content === "string") {
308
- if (content.startsWith("data:")) {
309
- imageDebug.load(content.slice(0, 50) + "...", "data-url");
310
- setSrc(content);
311
- return;
312
- }
313
- const encoder = new TextEncoder();
314
- const buffer = encoder.encode(content).buffer;
315
- const contentKey2 = generateContentKey(buffer);
316
- if (contentKeyRef.current && contentKeyRef.current !== contentKey2) {
317
- releaseBlobUrl(contentKeyRef.current);
318
- }
319
- contentKeyRef.current = contentKey2;
320
- const url2 = getOrCreateBlobUrl(contentKey2, buffer, mimeType || "image/png");
321
- imageDebug.load(url2, "blob");
322
- imageDebug.state("loaded", { size, mimeType, contentKey: contentKey2 });
323
- setSrc(url2);
324
- return;
325
- }
326
- const contentKey = generateContentKey(content);
327
- if (contentKeyRef.current && contentKeyRef.current !== contentKey) {
328
- releaseBlobUrl(contentKeyRef.current);
329
- }
330
- contentKeyRef.current = contentKey;
331
- const url = getOrCreateBlobUrl(contentKey, content, mimeType || "image/png");
332
- imageDebug.load(url, "blob");
333
- imageDebug.state("loaded", { size, mimeType, contentKey });
334
- setSrc(url);
335
- }, [content, mimeType, hasContent, size, directSrc]);
336
- useEffect(() => {
337
- if (!src || !useProgressiveLoading) {
338
- setLqip(null);
339
- setIsFullyLoaded(true);
340
- return;
341
- }
342
- setIsFullyLoaded(false);
343
- imageDebug.state("progressive loading", { size });
344
- createLQIP(src).then((placeholder) => {
345
- if (placeholder) {
346
- imageDebug.debug("LQIP created");
347
- setLqip(placeholder);
348
- }
349
- });
350
- const img = new Image();
351
- img.onload = () => {
352
- imageDebug.state("fully loaded");
353
- setIsFullyLoaded(true);
354
- };
355
- img.onerror = () => {
356
- imageDebug.error("Failed to load full image");
357
- };
358
- img.src = src;
359
- }, [src, useProgressiveLoading, size]);
360
- return {
361
- src,
362
- lqip,
363
- isFullyLoaded,
364
- useProgressiveLoading,
365
- error,
366
- contentKey: contentKeyRef.current,
367
- size,
368
- hasContent
369
- };
370
- }
371
- __name(useImageLoading, "useImageLoading");
372
- function ImageViewer({
373
- images,
374
- initialIndex = 0,
375
- inDialog = false
376
- }) {
377
- const t = useAppT();
378
- const [currentIndex, setCurrentIndex] = useState(
379
- () => Math.max(0, Math.min(initialIndex, images.length - 1))
380
- );
381
- useEffect(() => {
382
- setCurrentIndex(Math.max(0, Math.min(initialIndex, images.length - 1)));
383
- }, [initialIndex, images.length]);
384
- const current = images[currentIndex];
385
- const hasMultiple = images.length > 1;
386
- const [scale, setScale] = useState(1);
387
- const [dialogOpen, setDialogOpen] = useState(false);
388
- const [loadError, setLoadError] = useState(false);
389
- const containerRef = useRef(null);
390
- const controlsRef = useRef(null);
391
- const labels = useMemo(() => ({
392
- noImage: t("tools.image.noImage"),
393
- failedToLoad: t("tools.image.failedToLoad"),
394
- loading: t("ui.form.loading")
395
- }), [t]);
396
- const {
397
- src,
398
- lqip,
399
- isFullyLoaded,
400
- useProgressiveLoading,
401
- error,
402
- hasContent
403
- } = useImageLoading({
404
- content: current?.content ?? "",
405
- mimeType: current?.file.mimeType,
406
- src: current?.src
407
- });
408
- useEffect(() => {
409
- setLoadError(false);
410
- }, [src]);
411
- const { transform, rotate, flipH, flipV, transformStyle } = useImageTransform({
412
- resetKey: current?.file.path ?? ""
413
- });
414
- const handleZoomPreset = useCallback((value) => {
415
- if (!controlsRef.current) return;
416
- if (value === "fit") controlsRef.current.resetTransform();
417
- else controlsRef.current.setTransform(0, 0, value);
418
- }, []);
419
- const handleExpand = useCallback(() => setDialogOpen(true), []);
420
- const prev = useCallback(
421
- () => setCurrentIndex((i) => (i - 1 + images.length) % images.length),
422
- [images.length]
423
- );
424
- const next = useCallback(
425
- () => setCurrentIndex((i) => (i + 1) % images.length),
426
- [images.length]
427
- );
428
- useEffect(() => {
429
- const handleKeyDown = /* @__PURE__ */ __name((e) => {
430
- if (!containerRef.current?.contains(document.activeElement) && document.activeElement !== containerRef.current) return;
431
- const controls = controlsRef.current;
432
- if (!controls) return;
433
- switch (e.key) {
434
- case "+":
435
- case "=":
436
- e.preventDefault();
437
- controls.zoomIn();
438
- break;
439
- case "-":
440
- e.preventDefault();
441
- controls.zoomOut();
442
- break;
443
- case "0":
444
- e.preventDefault();
445
- controls.resetTransform();
446
- break;
447
- case "r":
448
- if (!e.metaKey && !e.ctrlKey) {
449
- e.preventDefault();
450
- rotate();
451
- }
452
- break;
453
- }
454
- }, "handleKeyDown");
455
- window.addEventListener("keydown", handleKeyDown);
456
- return () => window.removeEventListener("keydown", handleKeyDown);
457
- }, [rotate]);
458
- useHotkey("ArrowLeft", prev, { enabled: hasMultiple, preventDefault: true });
459
- useHotkey("ArrowRight", next, { enabled: hasMultiple, preventDefault: true });
460
- if (!current) {
461
- return /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center justify-center gap-2 bg-muted/30", children: [
462
- /* @__PURE__ */ jsx(ImageIcon, { className: "w-12 h-12 text-muted-foreground/50" }),
463
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: labels.noImage })
464
- ] });
465
- }
466
- if (error || loadError) {
467
- return /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center justify-center gap-3 bg-muted/30 p-4", children: [
468
- /* @__PURE__ */ jsx(AlertCircle, { className: "w-12 h-12 text-destructive/70" }),
469
- /* @__PURE__ */ jsxs(Alert, { variant: "destructive", className: "max-w-md", children: [
470
- /* @__PURE__ */ jsx(AlertCircle, { className: "h-4 w-4" }),
471
- /* @__PURE__ */ jsx(AlertDescription, { children: error || labels.failedToLoad })
472
- ] })
473
- ] });
474
- }
475
- if (!hasContent) {
476
- return /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col items-center justify-center gap-2 bg-muted/30", children: [
477
- /* @__PURE__ */ jsx(ImageIcon, { className: "w-12 h-12 text-muted-foreground/50" }),
478
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: labels.noImage })
479
- ] });
480
- }
481
- return /* @__PURE__ */ jsxs(
482
- "div",
483
- {
484
- ref: containerRef,
485
- tabIndex: 0,
486
- className: cn$1(
487
- "flex-1 h-full relative overflow-hidden outline-none",
488
- "bg-[length:16px_16px]",
489
- "[background-color:hsl(var(--muted)/0.2)]",
490
- "[background-image:linear-gradient(45deg,hsl(var(--muted)/0.4)_25%,transparent_25%),linear-gradient(-45deg,hsl(var(--muted)/0.4)_25%,transparent_25%),linear-gradient(45deg,transparent_75%,hsl(var(--muted)/0.4)_75%),linear-gradient(-45deg,transparent_75%,hsl(var(--muted)/0.4)_75%)]",
491
- "[background-position:0_0,0_8px,8px_-8px,-8px_0px]"
492
- ),
493
- children: [
494
- src && /* @__PURE__ */ jsx(ImageInfo, { src }),
495
- useProgressiveLoading && !isFullyLoaded && /* @__PURE__ */ jsxs("div", { className: "absolute top-3 left-3 z-10 px-2 py-1 bg-background/80 backdrop-blur-sm border rounded text-[10px] text-muted-foreground font-mono flex items-center gap-1.5", children: [
496
- /* @__PURE__ */ jsx("div", { className: "w-2 h-2 bg-blue-500 rounded-full animate-pulse" }),
497
- labels.loading
498
- ] }),
499
- /* @__PURE__ */ jsxs(
500
- TransformWrapper,
501
- {
502
- initialScale: 1,
503
- minScale: 0.1,
504
- maxScale: 8,
505
- centerOnInit: true,
506
- centerZoomedOut: true,
507
- onTransformed: (ref, state) => {
508
- setScale(state.scale);
509
- controlsRef.current = ref;
510
- },
511
- onInit: (ref) => {
512
- controlsRef.current = ref;
513
- },
514
- wheel: { step: 0.1 },
515
- doubleClick: { mode: "toggle", step: 2 },
516
- panning: { velocityDisabled: false },
517
- children: [
518
- /* @__PURE__ */ jsx(
519
- ImageToolbar,
520
- {
521
- scale,
522
- transform,
523
- onRotate: rotate,
524
- onFlipH: flipH,
525
- onFlipV: flipV,
526
- onZoomPreset: handleZoomPreset,
527
- onExpand: !inDialog ? handleExpand : void 0
528
- }
529
- ),
530
- /* @__PURE__ */ jsx(
531
- TransformComponent,
532
- {
533
- wrapperClass: "!w-full !h-full cursor-grab active:cursor-grabbing",
534
- contentClass: "!w-full !h-full flex items-center justify-center",
535
- children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
536
- useProgressiveLoading && lqip && !isFullyLoaded && /* @__PURE__ */ jsx(
537
- "img",
538
- {
539
- src: lqip,
540
- alt: "",
541
- "aria-hidden": "true",
542
- className: "absolute inset-0 max-w-full max-h-full object-contain select-none",
543
- style: { transform: transformStyle, filter: "blur(20px)", transition: "opacity 0.3s ease-out", opacity: isFullyLoaded ? 0 : 1 },
544
- draggable: false
545
- }
546
- ),
547
- src && /* @__PURE__ */ jsx(
548
- "img",
549
- {
550
- src,
551
- alt: current.file.name,
552
- className: "max-w-full max-h-full object-contain select-none",
553
- style: {
554
- transform: transformStyle,
555
- transition: useProgressiveLoading ? "transform 0.15s ease-out, opacity 0.3s ease-out" : "transform 0.15s ease-out",
556
- opacity: useProgressiveLoading && !isFullyLoaded ? 0 : 1
557
- },
558
- draggable: false,
559
- crossOrigin: "anonymous",
560
- onError: () => setLoadError(true)
561
- }
562
- )
563
- ] })
564
- }
565
- )
566
- ]
567
- }
568
- ),
569
- hasMultiple && /* @__PURE__ */ jsxs(Fragment, { children: [
570
- /* @__PURE__ */ jsx(
571
- "button",
572
- {
573
- type: "button",
574
- onClick: prev,
575
- className: "absolute left-2 top-1/2 -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 transition-colors",
576
- children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-5 w-5" })
577
- }
578
- ),
579
- /* @__PURE__ */ jsx(
580
- "button",
581
- {
582
- type: "button",
583
- onClick: next,
584
- className: "absolute right-2 top-1/2 -translate-y-1/2 z-10 bg-black/50 hover:bg-black/70 text-white rounded-full p-1.5 transition-colors",
585
- children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-5 w-5" })
586
- }
587
- ),
588
- /* @__PURE__ */ jsxs("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 z-10 bg-black/50 text-white text-xs px-2 py-0.5 rounded-full pointer-events-none", children: [
589
- currentIndex + 1,
590
- " / ",
591
- images.length
592
- ] })
593
- ] }),
594
- !inDialog && /* @__PURE__ */ jsx(Dialog, { open: dialogOpen, onOpenChange: setDialogOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "max-w-[95vw] max-h-[95vh] w-[95vw] h-[95vh] p-0 overflow-hidden [&>button]:hidden flex flex-col", children: [
595
- /* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: current.file.name }),
596
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-4 py-2 border-b shrink-0", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-medium truncate", children: current.file.name }) }),
597
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-h-0", children: /* @__PURE__ */ jsx(ImageViewer, { images, initialIndex: currentIndex, inDialog: true }) })
598
- ] }) })
599
- ]
600
- }
601
- );
602
- }
603
- __name(ImageViewer, "ImageViewer");
604
-
605
- export { ImageInfo, ImageToolbar, ImageViewer };
606
- //# sourceMappingURL=chunk-OBRSGM64.mjs.map
607
- //# sourceMappingURL=chunk-OBRSGM64.mjs.map