@fde-desktop/fde-core 0.3.8 → 0.4.2

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 (148) hide show
  1. package/README.md +419 -68
  2. package/dist/CalendarApp-CHLUCAI7.css +744 -0
  3. package/dist/CalendarApp-K7ZOUZ6C.js +2 -0
  4. package/dist/CalendarApp-OTGEERSS.cjs +8 -0
  5. package/dist/CodeServerApp-5KZGO7HL.css +75 -0
  6. package/dist/CodeServerApp-LUZFCQBI.js +3 -0
  7. package/dist/CodeServerApp-P3TMJPLY.cjs +9 -0
  8. package/dist/CreateItemApp-NAZMXOPK.cjs +14 -0
  9. package/dist/CreateItemApp-PQB5GTFG.css +107 -0
  10. package/dist/CreateItemApp-ZHCTSPQE.js +8 -0
  11. package/dist/DeviceInfoApp-R6YNVIGX.cjs +11 -0
  12. package/dist/DeviceInfoApp-YHCYAO6N.js +5 -0
  13. package/dist/DeviceInfoApp-ZSMRSITP.css +7 -0
  14. package/dist/FilesApp-AKCVRTXR.js +8 -0
  15. package/dist/FilesApp-E6L5W3T2.css +1817 -0
  16. package/dist/FilesApp-RW3Y6ILO.cjs +14 -0
  17. package/dist/ImageViewerApp-5UXNSW2O.js +11 -0
  18. package/dist/ImageViewerApp-N2Q7E7WZ.css +215 -0
  19. package/dist/ImageViewerApp-RRRRKSFN.cjs +17 -0
  20. package/dist/ImageViewerMenuBar-I3TFKQPS.cjs +14 -0
  21. package/dist/ImageViewerMenuBar-TV5C6TM2.js +5 -0
  22. package/dist/ImageViewerMenuBar-XLK4LIHW.css +56 -0
  23. package/dist/MenuEditApp-HUZRFEHE.js +9 -0
  24. package/dist/MenuEditApp-MCUHGTKQ.cjs +15 -0
  25. package/dist/MenuEditApp-YA6HSAMJ.css +94 -0
  26. package/dist/MenuEditMenuBar-7VHMZNRM.css +56 -0
  27. package/dist/MenuEditMenuBar-GF6L4PGZ.cjs +15 -0
  28. package/dist/MenuEditMenuBar-IUXFPZE5.js +6 -0
  29. package/dist/NotesApp-37BV33C6.js +10 -0
  30. package/dist/NotesApp-4EVUQEFZ.cjs +16 -0
  31. package/dist/NotesApp-TQ6IHDNX.css +302 -0
  32. package/dist/NotesMenuBar-25LKN3SE.cjs +15 -0
  33. package/dist/NotesMenuBar-MXLOX7OT.css +56 -0
  34. package/dist/NotesMenuBar-SRV3AIAL.js +6 -0
  35. package/dist/PdfApp-5VBDNRMC.cjs +16 -0
  36. package/dist/PdfApp-BUIC5U5H.css +206 -0
  37. package/dist/PdfApp-RH6MZZX5.js +10 -0
  38. package/dist/PdfMenuBar-NLZC6JHS.js +4 -0
  39. package/dist/PdfMenuBar-QUM72EE4.css +56 -0
  40. package/dist/PdfMenuBar-WBRTKMLN.cjs +13 -0
  41. package/dist/SettingsApp-5LDHEHYV.cjs +20 -0
  42. package/dist/SettingsApp-JVOSEFH3.css +283 -0
  43. package/dist/SettingsApp-X6764D7T.js +14 -0
  44. package/dist/SettingsMenuBar-5CBSSMVM.css +56 -0
  45. package/dist/SettingsMenuBar-VLT6TTCM.js +6 -0
  46. package/dist/SettingsMenuBar-Y5QEXDEO.cjs +15 -0
  47. package/dist/StorybookApp-NQ244BER.css +7 -0
  48. package/dist/StorybookApp-NZDV4X3Y.js +1 -0
  49. package/dist/StorybookApp-VF3KIMU3.cjs +7 -0
  50. package/dist/TerminalApp-CDGWRBFJ.cjs +10 -0
  51. package/dist/TerminalApp-EAATMIMX.css +77 -0
  52. package/dist/TerminalApp-GCKJCM55.js +4 -0
  53. package/dist/TerminalMenuBar-3J26O26Q.css +56 -0
  54. package/dist/TerminalMenuBar-7BH7MGNJ.cjs +14 -0
  55. package/dist/TerminalMenuBar-7JAEQUZ4.js +5 -0
  56. package/dist/UploaderApp-2WYRCUQV.js +10 -0
  57. package/dist/UploaderApp-6KV3TGCT.css +1817 -0
  58. package/dist/UploaderApp-EYFC36PM.cjs +16 -0
  59. package/dist/chunk-2FO445RM.cjs +449 -0
  60. package/dist/chunk-2PSTHGTD.cjs +42 -0
  61. package/dist/chunk-2RQX7QBP.cjs +148 -0
  62. package/dist/chunk-3IICBLEA.js +442 -0
  63. package/dist/chunk-43W6UDUZ.cjs +19 -0
  64. package/dist/chunk-4E45FBAH.js +223 -0
  65. package/dist/chunk-4MCFQPKY.js +444 -0
  66. package/dist/chunk-4OH5RPSQ.cjs +38 -0
  67. package/dist/chunk-4XURSNM4.js +43 -0
  68. package/dist/chunk-4ZCRYHL6.js +407 -0
  69. package/dist/chunk-54PYEQLK.js +283 -0
  70. package/dist/chunk-5C6IQE42.cjs +35 -0
  71. package/dist/chunk-5NOHYJNH.js +84 -0
  72. package/dist/chunk-5PYK5ASL.js +162 -0
  73. package/dist/chunk-5YH6AKEO.js +146 -0
  74. package/dist/chunk-657BJOY5.cjs +324 -0
  75. package/dist/chunk-6QOUYSEE.cjs +2303 -0
  76. package/dist/chunk-7SAFECOJ.js +215 -0
  77. package/dist/chunk-7Y7HB7FB.cjs +53 -0
  78. package/dist/chunk-ABIAPZ6S.cjs +45 -0
  79. package/dist/chunk-AQL372JF.cjs +219 -0
  80. package/dist/chunk-AXDUVZVP.cjs +88 -0
  81. package/dist/chunk-AYFNYHMP.js +541 -0
  82. package/dist/chunk-BDO6B7MZ.cjs +451 -0
  83. package/dist/chunk-BKXEA2BK.cjs +286 -0
  84. package/dist/chunk-BLV47DX2.js +47 -0
  85. package/dist/chunk-BQCD5RAF.cjs +48 -0
  86. package/dist/chunk-BQL3YXMV.js +17429 -0
  87. package/dist/chunk-C6BEZNAM.cjs +45 -0
  88. package/dist/chunk-CFWV2JMR.js +234 -0
  89. package/dist/chunk-CV5PUHAE.cjs +86 -0
  90. package/dist/chunk-D5MVFFID.js +42 -0
  91. package/dist/chunk-D7R55WWT.js +1601 -0
  92. package/dist/chunk-DMNF4CNN.cjs +49 -0
  93. package/dist/chunk-DWP2SYF7.js +55 -0
  94. package/dist/chunk-E55VXNLK.cjs +17498 -0
  95. package/dist/chunk-EAELL43F.js +42 -0
  96. package/dist/chunk-EUQLZW6P.js +48 -0
  97. package/dist/chunk-EX5V2ZTU.js +40 -0
  98. package/dist/chunk-FH4ILMKF.js +38 -0
  99. package/dist/chunk-FRHBM2U7.js +33 -0
  100. package/dist/chunk-FX2TPX3L.cjs +45 -0
  101. package/dist/chunk-GCYD6T52.js +32 -0
  102. package/dist/chunk-GRYCUBJZ.js +9 -0
  103. package/dist/chunk-HWHBSAUC.js +40 -0
  104. package/dist/chunk-ICUE6T7J.cjs +50 -0
  105. package/dist/chunk-IDHP3R4I.js +31 -0
  106. package/dist/chunk-IUOQPOEN.js +2293 -0
  107. package/dist/chunk-J7L2S2GT.cjs +34 -0
  108. package/dist/chunk-JEBKLIMU.cjs +123 -0
  109. package/dist/chunk-KQHICFX3.js +121 -0
  110. package/dist/chunk-LMJE6V4N.cjs +42 -0
  111. package/dist/chunk-MVDGM5Y4.js +68 -0
  112. package/dist/chunk-NVEGEK3N.js +31 -0
  113. package/dist/chunk-NWMSWRUD.js +2236 -0
  114. package/dist/chunk-ODXL6BR3.js +77 -0
  115. package/dist/chunk-OJIDKDKF.js +68 -0
  116. package/dist/chunk-PKPQA5NR.js +15 -0
  117. package/dist/chunk-PNDBLFJW.cjs +50 -0
  118. package/dist/chunk-PYTKNRGM.js +280 -0
  119. package/dist/chunk-Q3WA72BF.cjs +70 -0
  120. package/dist/chunk-QB72BLCJ.cjs +237 -0
  121. package/dist/chunk-QHBBLML3.js +86 -0
  122. package/dist/chunk-RDIDAZ3S.cjs +9 -0
  123. package/dist/chunk-RGJPRXYY.js +48 -0
  124. package/dist/chunk-RQ6OZRUW.cjs +41 -0
  125. package/dist/chunk-SBE4SZAN.cjs +226 -0
  126. package/dist/chunk-SYGUWGWK.cjs +2329 -0
  127. package/dist/chunk-TDZ43MUX.cjs +165 -0
  128. package/dist/chunk-TGWMOHAO.js +17 -0
  129. package/dist/chunk-U4RYIS6Z.cjs +548 -0
  130. package/dist/chunk-UIQCTAVM.cjs +59 -0
  131. package/dist/chunk-XVASHRCE.cjs +70 -0
  132. package/dist/chunk-XYSMVQQD.cjs +1608 -0
  133. package/dist/chunk-YAIWI4Z5.js +7 -0
  134. package/dist/chunk-YP2PLNOF.cjs +34 -0
  135. package/dist/chunk-YSOLW4FS.cjs +11 -0
  136. package/dist/chunk-YY6OUR2U.js +44 -0
  137. package/dist/chunk-YZWS7FDT.cjs +409 -0
  138. package/dist/chunk-Z5YGWL65.cjs +39 -0
  139. package/dist/chunk-ZBGWYTCU.cjs +83 -0
  140. package/dist/chunk-ZHB5Q2M6.js +36 -0
  141. package/dist/chunk-ZHNDXNL4.js +45 -0
  142. package/dist/chunk-ZX3EDZ5C.cjs +17 -0
  143. package/dist/index.cjs +4405 -5156
  144. package/dist/index.css +9192 -0
  145. package/dist/index.d.cts +1324 -762
  146. package/dist/index.d.ts +1324 -762
  147. package/dist/index.js +3648 -5038
  148. package/package.json +14 -6
@@ -0,0 +1,541 @@
1
+ import { useMenuEditStore } from './chunk-D5MVFFID.js';
2
+ import { VscIcon_default } from './chunk-GRYCUBJZ.js';
3
+ import { AppIcon_default } from './chunk-GCYD6T52.js';
4
+ import { useCloseInterceptor } from './chunk-ODXL6BR3.js';
5
+ import { useDesktopStore, APPS } from './chunk-NWMSWRUD.js';
6
+ import { isDocker } from './chunk-FRHBM2U7.js';
7
+ import { Box, Text, Group, ActionIcon, Card, TextInput, UnstyledButton } from './chunk-BQL3YXMV.js';
8
+ import { createContext, useState, useCallback, useMemo, useContext, useRef, useEffect } from 'react';
9
+ import { useTranslation } from 'react-i18next';
10
+ import { DndContext, pointerWithin, useDraggable, useDroppable, DragOverlay, defaultDropAnimation } from '@dnd-kit/core';
11
+ import { jsx, jsxs } from 'react/jsx-runtime';
12
+
13
+ var FOLDER_ID_TO_TRANSLATION_KEY = {
14
+ "launcher-folder-social-links": "socialLinks",
15
+ "launcher-folder-office": "office",
16
+ "launcher-folder-programming": "programming",
17
+ "launcher-folder-games": "games",
18
+ "launcher-folder-custom-apps": "customApps"
19
+ };
20
+ function useFolderName(folder) {
21
+ const { t } = useTranslation("apps");
22
+ if (!folder.isPredefined) {
23
+ return folder.name;
24
+ }
25
+ const translationKey = FOLDER_ID_TO_TRANSLATION_KEY[folder.id];
26
+ if (!translationKey) {
27
+ return folder.name;
28
+ }
29
+ return t(`folders.${translationKey}`, folder.name);
30
+ }
31
+ var DragDropStateContext = createContext(null);
32
+ var DragDropProvider = ({ children, onDrop, onCancel }) => {
33
+ const [activeItem, setActiveItem] = useState(null);
34
+ const [overTarget, setOverTarget] = useState(null);
35
+ const handleDragStart = useCallback((event) => {
36
+ const item = event.active.data.current;
37
+ if (item) {
38
+ setActiveItem(item);
39
+ }
40
+ }, []);
41
+ const handleDragOver = useCallback((event) => {
42
+ if (event.over) {
43
+ const target = event.over.data.current;
44
+ if (target) {
45
+ setOverTarget(target);
46
+ }
47
+ } else {
48
+ setOverTarget(null);
49
+ }
50
+ }, []);
51
+ const handleDragEnd = useCallback(
52
+ (event) => {
53
+ const { active, over } = event;
54
+ if (over && active.data.current) {
55
+ const item = active.data.current;
56
+ const target = over.data.current;
57
+ if (target.accepts.includes(item.type)) {
58
+ onDrop(item, target);
59
+ }
60
+ } else if (!over && onCancel) {
61
+ onCancel();
62
+ }
63
+ setActiveItem(null);
64
+ setOverTarget(null);
65
+ },
66
+ [onDrop, onCancel]
67
+ );
68
+ const value = useMemo(() => ({ activeItem, overTarget }), [activeItem, overTarget]);
69
+ return /* @__PURE__ */ jsx(DragDropStateContext.Provider, { value, children: /* @__PURE__ */ jsx(
70
+ DndContext,
71
+ {
72
+ onDragStart: handleDragStart,
73
+ onDragOver: handleDragOver,
74
+ onDragEnd: handleDragEnd,
75
+ collisionDetection: pointerWithin,
76
+ children
77
+ }
78
+ ) });
79
+ };
80
+ function useDraggableItem(options) {
81
+ const { item, disabled = false } = options;
82
+ const { attributes, listeners, setNodeRef, transform, isDragging } = useDraggable({
83
+ id: `${item.sourceId}-${item.id}`,
84
+ data: item,
85
+ disabled
86
+ });
87
+ const style = {
88
+ opacity: isDragging ? 0 : 1,
89
+ cursor: disabled ? "default" : "grab"
90
+ };
91
+ return {
92
+ attributes,
93
+ listeners,
94
+ setNodeRef,
95
+ isDragging,
96
+ transform: transform ? { x: transform.x, y: transform.y } : null,
97
+ style
98
+ };
99
+ }
100
+ function useDroppableArea(options) {
101
+ const { target, disabled = false } = options;
102
+ const context = useContext(DragDropStateContext);
103
+ const { setNodeRef, isOver } = useDroppable({
104
+ id: target.id,
105
+ data: target,
106
+ disabled
107
+ });
108
+ return {
109
+ setNodeRef,
110
+ isOver,
111
+ active: context?.activeItem ?? null
112
+ };
113
+ }
114
+ function useDragDropState() {
115
+ const context = useContext(DragDropStateContext);
116
+ if (!context) {
117
+ throw new Error("useDragDropState must be used within a DragDropProvider");
118
+ }
119
+ return context;
120
+ }
121
+
122
+ // src/hooks/useDragDrop/modifiers/snapCenterToCursor.ts
123
+ var snapCenterToCursor = ({ activatorEvent, transform, draggingNodeRect }) => {
124
+ if (!activatorEvent || !draggingNodeRect) {
125
+ return transform;
126
+ }
127
+ const event = activatorEvent;
128
+ let cursorX = 0;
129
+ let cursorY = 0;
130
+ if ("touches" in event && event.touches.length > 0) {
131
+ cursorX = event.touches[0].clientX;
132
+ cursorY = event.touches[0].clientY;
133
+ } else if ("clientX" in event) {
134
+ cursorX = event.clientX;
135
+ cursorY = event.clientY;
136
+ }
137
+ const offsetX = cursorX - draggingNodeRect.left - draggingNodeRect.width / 2;
138
+ const offsetY = cursorY - draggingNodeRect.top - draggingNodeRect.height / 2;
139
+ return {
140
+ ...transform,
141
+ x: transform.x + offsetX,
142
+ y: transform.y + offsetY
143
+ };
144
+ };
145
+
146
+ // src/hooks/useDragDrop/DragOverlay.module.css
147
+ var DragOverlay_default = {};
148
+ var DragOverlayComponent = () => {
149
+ const { activeItem } = useDragDropState();
150
+ if (!activeItem) return null;
151
+ return /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: defaultDropAnimation, modifiers: [snapCenterToCursor], children: /* @__PURE__ */ jsxs(Box, { className: DragOverlay_default.dragOverlay, children: [
152
+ /* @__PURE__ */ jsx(
153
+ AppIcon_default,
154
+ {
155
+ fcIcon: activeItem.metadata?.fcIcon,
156
+ iconUrl: activeItem.metadata?.iconUrl,
157
+ fallback: activeItem.metadata?.icon,
158
+ size: 20
159
+ }
160
+ ),
161
+ /* @__PURE__ */ jsx(Text, { size: "xs", children: activeItem.name })
162
+ ] }) });
163
+ };
164
+
165
+ // src/hooks/useDragDrop/constants.ts
166
+ var ACCEPTED_TYPES = {
167
+ "launcher-folder": ["app"],
168
+ "available-apps": ["app"]
169
+ };
170
+
171
+ // src/components/Apps/MenuEditApp/MenuEditApp.module.css
172
+ var MenuEditApp_default = {};
173
+ var FolderItem = ({
174
+ folder,
175
+ isEditing,
176
+ editingName,
177
+ onEdit,
178
+ onDelete,
179
+ onRename,
180
+ onSetEditingName,
181
+ onRemoveApp
182
+ }) => {
183
+ const { t } = useTranslation("menuedit");
184
+ const folderName = useFolderName(folder);
185
+ const { activeItem } = useDragDropState();
186
+ const { setNodeRef, isOver } = useDroppableArea({
187
+ target: {
188
+ type: "launcher-folder",
189
+ id: folder.id,
190
+ name: folderName,
191
+ accepts: ACCEPTED_TYPES["launcher-folder"]
192
+ }
193
+ });
194
+ return /* @__PURE__ */ jsxs(
195
+ Card,
196
+ {
197
+ ref: setNodeRef,
198
+ className: MenuEditApp_default.folderCard,
199
+ shadow: "xs",
200
+ padding: "xs",
201
+ "data-is-over": isOver && activeItem?.type === "app" ? "true" : void 0,
202
+ children: [
203
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: 4, children: [
204
+ isEditing ? /* @__PURE__ */ jsx(
205
+ TextInput,
206
+ {
207
+ value: editingName,
208
+ onChange: (e) => onSetEditingName(e.target.value),
209
+ onBlur: () => onRename(folder.id),
210
+ onKeyDown: (e) => {
211
+ if (e.key === "Enter") {
212
+ e.preventDefault();
213
+ onRename(folder.id);
214
+ }
215
+ },
216
+ autoFocus: true,
217
+ size: "xs",
218
+ style: { flex: 1 }
219
+ }
220
+ ) : /* @__PURE__ */ jsxs(Group, { gap: "xs", children: [
221
+ /* @__PURE__ */ jsx(AppIcon_default, { fcIcon: folder.fcIcon, size: 16 }),
222
+ /* @__PURE__ */ jsx(Text, { size: "sm", fw: 500, children: folderName }),
223
+ folder.isPredefined && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", children: [
224
+ "(",
225
+ t("predefined"),
226
+ ")"
227
+ ] })
228
+ ] }),
229
+ !folder.isPredefined && !isEditing && /* @__PURE__ */ jsxs(Group, { gap: 4, children: [
230
+ /* @__PURE__ */ jsx(
231
+ ActionIcon,
232
+ {
233
+ variant: "subtle",
234
+ size: "xs",
235
+ onClick: () => onEdit(folder.id),
236
+ "aria-label": t("rename"),
237
+ children: /* @__PURE__ */ jsx(VscIcon_default, { name: "VscEdit", size: 14 })
238
+ }
239
+ ),
240
+ onDelete && /* @__PURE__ */ jsx(
241
+ ActionIcon,
242
+ {
243
+ variant: "subtle",
244
+ size: "xs",
245
+ color: "red",
246
+ onClick: () => onDelete(folder.id),
247
+ "aria-label": t("delete"),
248
+ children: /* @__PURE__ */ jsx(VscIcon_default, { name: "VscTrash", size: 14 })
249
+ }
250
+ )
251
+ ] })
252
+ ] }),
253
+ /* @__PURE__ */ jsx(Box, { className: MenuEditApp_default.appList, children: folder.appIds.map((appId) => {
254
+ const app = APPS.find((a) => a.id === appId);
255
+ if (!app) return null;
256
+ return /* @__PURE__ */ jsx(
257
+ DraggableApp,
258
+ {
259
+ app,
260
+ sourceId: folder.id,
261
+ onContextMenu: (e) => {
262
+ e.preventDefault();
263
+ e.stopPropagation();
264
+ onRemoveApp(appId, folder.id);
265
+ }
266
+ },
267
+ appId
268
+ );
269
+ }) })
270
+ ]
271
+ }
272
+ );
273
+ };
274
+ var DraggableApp = ({ app, sourceId, onContextMenu }) => {
275
+ const { attributes, listeners, setNodeRef, isDragging, style } = useDraggableItem({
276
+ item: {
277
+ type: "app",
278
+ id: app.id,
279
+ name: app.name,
280
+ sourceId,
281
+ sourceType: "launcher-folder",
282
+ metadata: {
283
+ fcIcon: app.fcIcon,
284
+ iconUrl: app.iconUrl,
285
+ icon: app.icon
286
+ }
287
+ }
288
+ });
289
+ return /* @__PURE__ */ jsxs(
290
+ UnstyledButton,
291
+ {
292
+ ref: setNodeRef,
293
+ className: MenuEditApp_default.appItem,
294
+ style: { ...style, opacity: isDragging ? 0.5 : 1 },
295
+ onContextMenu,
296
+ ...attributes,
297
+ ...listeners,
298
+ children: [
299
+ /* @__PURE__ */ jsx(AppIcon_default, { fcIcon: app.fcIcon, iconUrl: app.iconUrl, fallback: app.icon, size: 16 }),
300
+ /* @__PURE__ */ jsx(Text, { size: "xs", children: app.name })
301
+ ]
302
+ }
303
+ );
304
+ };
305
+ var AvailableApps = ({ customFolderId }) => {
306
+ const { t } = useTranslation("menuedit");
307
+ const { activeItem } = useDragDropState();
308
+ const { setNodeRef, isOver } = useDroppableArea({
309
+ target: {
310
+ type: "available-apps",
311
+ id: "available-apps",
312
+ name: "Available Apps",
313
+ accepts: ACCEPTED_TYPES["available-apps"]
314
+ }
315
+ });
316
+ const appsInFolders = new Set(useDesktopStore.getState().launcherFolders.flatMap((f) => f.appIds));
317
+ const availableApps = APPS.filter((app) => app.id !== "settings" && app.id !== "menuedit");
318
+ const addAppToFolder = useDesktopStore((state) => state.addAppToFolder);
319
+ return /* @__PURE__ */ jsxs(
320
+ Box,
321
+ {
322
+ ref: setNodeRef,
323
+ className: MenuEditApp_default.availableApps,
324
+ "data-is-over": isOver && activeItem?.type === "app" ? "true" : void 0,
325
+ children: [
326
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mb: 8, children: t("availableApps") }),
327
+ customFolderId === null && /* @__PURE__ */ jsx(Text, { size: "xs", c: "orange", mb: 4, children: t("canCreateNewFolder") }),
328
+ /* @__PURE__ */ jsx(Box, { className: MenuEditApp_default.appGrid, children: availableApps.filter((app) => !appsInFolders.has(app.id)).map((app) => /* @__PURE__ */ jsx(
329
+ DraggableAppFromAvailable,
330
+ {
331
+ app,
332
+ targetFolderId: customFolderId,
333
+ onAdd: addAppToFolder
334
+ },
335
+ app.id
336
+ )) })
337
+ ]
338
+ }
339
+ );
340
+ };
341
+ var DraggableAppFromAvailable = ({ app, targetFolderId, onAdd }) => {
342
+ const { attributes, listeners, setNodeRef, isDragging, style } = useDraggableItem({
343
+ item: {
344
+ type: "app",
345
+ id: app.id,
346
+ name: app.name,
347
+ sourceId: "available-apps",
348
+ sourceType: "available-apps",
349
+ metadata: {
350
+ fcIcon: app.fcIcon,
351
+ iconUrl: app.iconUrl,
352
+ icon: app.icon
353
+ }
354
+ }
355
+ });
356
+ const handleClick = () => {
357
+ if (targetFolderId) {
358
+ onAdd(app.id, targetFolderId);
359
+ }
360
+ };
361
+ return /* @__PURE__ */ jsxs(
362
+ UnstyledButton,
363
+ {
364
+ ref: setNodeRef,
365
+ className: MenuEditApp_default.appItem,
366
+ style: { ...style, opacity: isDragging ? 0 : 1 },
367
+ onClick: handleClick,
368
+ disabled: !targetFolderId,
369
+ ...attributes,
370
+ ...listeners,
371
+ children: [
372
+ /* @__PURE__ */ jsx(AppIcon_default, { fcIcon: app.fcIcon, iconUrl: app.iconUrl, fallback: app.icon, size: 16 }),
373
+ /* @__PURE__ */ jsx(Text, { size: "xs", children: app.name })
374
+ ]
375
+ }
376
+ );
377
+ };
378
+ var MenuEditAppContent = ({ window: win, notifyReady }) => {
379
+ const { t } = useTranslation("menuedit");
380
+ const windowId = win?.id;
381
+ const launcherFolders = useDesktopStore((state) => state.launcherFolders);
382
+ const setLauncherFolders = useDesktopStore((state) => state.setLauncherFolders);
383
+ const createLauncherFolder = useDesktopStore((state) => state.createLauncherFolder);
384
+ const updateLauncherFolder = useDesktopStore((state) => state.updateLauncherFolder);
385
+ const deleteLauncherFolder = useDesktopStore((state) => state.deleteLauncherFolder);
386
+ const removeAppFromFolder = useDesktopStore((state) => state.removeAppFromFolder);
387
+ const [editingFolderId, setEditingFolderId] = useState(null);
388
+ const [editingName, setEditingName] = useState("");
389
+ const initializedRef = useRef(false);
390
+ useEffect(() => {
391
+ if (!windowId) return;
392
+ if (initializedRef.current) return;
393
+ initializedRef.current = true;
394
+ useMenuEditStore.getState().reset(windowId);
395
+ }, [windowId]);
396
+ useEffect(() => {
397
+ if (!windowId) return;
398
+ const store = useMenuEditStore.getState();
399
+ if (store.getSnapshot(windowId) === null) {
400
+ store.takeSnapshot(windowId, launcherFolders);
401
+ } else {
402
+ const snapshot = store.getSnapshot(windowId);
403
+ if (snapshot) {
404
+ const isDirty = JSON.stringify(launcherFolders) !== JSON.stringify(snapshot);
405
+ store.setIsDirty(windowId, isDirty);
406
+ }
407
+ }
408
+ }, [windowId, launcherFolders]);
409
+ const handleSave = useCallback(() => {
410
+ if (!windowId) return;
411
+ useMenuEditStore.getState().takeSnapshot(windowId, launcherFolders);
412
+ }, [windowId, launcherFolders]);
413
+ const revertToSnapshot = useCallback(() => {
414
+ if (!windowId) return;
415
+ const snapshot = useMenuEditStore.getState().getSnapshot(windowId);
416
+ if (snapshot) {
417
+ setLauncherFolders(snapshot);
418
+ }
419
+ }, [windowId, setLauncherFolders]);
420
+ const isDirtyGetter = useCallback(() => {
421
+ if (!windowId) return false;
422
+ const snapshot = useMenuEditStore.getState().getSnapshot(windowId);
423
+ return JSON.stringify(launcherFolders) !== JSON.stringify(snapshot ?? []);
424
+ }, [windowId, launcherFolders]);
425
+ useCloseInterceptor({
426
+ isDirtyGetter,
427
+ windowId: win?.id,
428
+ onDiscard: revertToSnapshot,
429
+ onSave: handleSave
430
+ });
431
+ const handleCreateFolder = useCallback(() => {
432
+ const folder = createLauncherFolder({
433
+ name: t("newFolder"),
434
+ fcIcon: "VscFolder",
435
+ order: launcherFolders.length
436
+ });
437
+ setEditingFolderId(folder.id);
438
+ setEditingName(folder.name);
439
+ }, [createLauncherFolder, launcherFolders.length, t]);
440
+ useEffect(() => {
441
+ notifyReady?.({
442
+ createFolder: handleCreateFolder,
443
+ save: handleSave,
444
+ discard: revertToSnapshot
445
+ });
446
+ }, [handleCreateFolder, handleSave, revertToSnapshot, notifyReady]);
447
+ const handleEditFolder = useCallback(
448
+ (folderId) => {
449
+ const folder = launcherFolders.find((f) => f.id === folderId);
450
+ if (folder) {
451
+ setEditingFolderId(folder.id);
452
+ setEditingName(folder.name);
453
+ }
454
+ },
455
+ [launcherFolders]
456
+ );
457
+ const handleRename = useCallback(
458
+ (folderId) => {
459
+ if (editingName.trim()) {
460
+ updateLauncherFolder(folderId, { name: editingName.trim() });
461
+ }
462
+ setEditingFolderId(null);
463
+ },
464
+ [editingName, updateLauncherFolder]
465
+ );
466
+ const dockerMode = isDocker();
467
+ const predefinedFolders = launcherFolders.filter((f) => f.isPredefined);
468
+ const customFolders = launcherFolders.filter((f) => !f.isPredefined);
469
+ const firstCustomFolderId = customFolders.length > 0 ? customFolders[0].id : null;
470
+ return /* @__PURE__ */ jsx(Box, { className: MenuEditApp_default.container, "data-windowid": win?.id, "data-menuedit-container": true, children: /* @__PURE__ */ jsxs(Box, { className: MenuEditApp_default.content, children: [
471
+ predefinedFolders.length > 0 && /* @__PURE__ */ jsxs(Box, { mb: "md", children: [
472
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mb: 8, children: t("predefinedFolders") }),
473
+ /* @__PURE__ */ jsx(Box, { className: MenuEditApp_default.folderList, children: predefinedFolders.map((folder) => /* @__PURE__ */ jsx(
474
+ FolderItem,
475
+ {
476
+ folder,
477
+ isEditing: false,
478
+ editingName: "",
479
+ onEdit: () => {
480
+ },
481
+ onRemoveApp: removeAppFromFolder,
482
+ onRename: () => {
483
+ },
484
+ onSetEditingName: () => {
485
+ }
486
+ },
487
+ folder.id
488
+ )) })
489
+ ] }),
490
+ dockerMode && customFolders.length > 0 && /* @__PURE__ */ jsxs(Box, { mb: "md", children: [
491
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mb: 8, children: t("customFolders") }),
492
+ /* @__PURE__ */ jsx(Box, { className: MenuEditApp_default.folderList, children: customFolders.map((folder) => /* @__PURE__ */ jsx(
493
+ FolderItem,
494
+ {
495
+ folder,
496
+ isEditing: editingFolderId === folder.id,
497
+ editingName,
498
+ onEdit: handleEditFolder,
499
+ onDelete: deleteLauncherFolder,
500
+ onRename: handleRename,
501
+ onSetEditingName: setEditingName,
502
+ onRemoveApp: removeAppFromFolder
503
+ },
504
+ folder.id
505
+ )) })
506
+ ] }),
507
+ /* @__PURE__ */ jsxs(Group, { justify: "space-between", mb: "md", children: [
508
+ /* @__PURE__ */ jsx(AvailableApps, { customFolderId: firstCustomFolderId }),
509
+ /* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", mt: "md", children: t("instructions") }),
510
+ dockerMode && /* @__PURE__ */ jsx(ActionIcon, { variant: "light", onClick: handleCreateFolder, "aria-label": t("addFolder"), children: /* @__PURE__ */ jsx(VscIcon_default, { name: "VscAdd", size: 16 }) })
511
+ ] })
512
+ ] }) });
513
+ };
514
+ var MenuEditApp = (props) => {
515
+ const addAppToFolder = useDesktopStore((state) => state.addAppToFolder);
516
+ const removeAppFromFolder = useDesktopStore((state) => state.removeAppFromFolder);
517
+ const handleDrop = useCallback(
518
+ (item, target) => {
519
+ if (item.type !== "app") return;
520
+ if (target.type === "available-apps") {
521
+ if (item.sourceType === "launcher-folder") {
522
+ removeAppFromFolder(item.id, item.sourceId);
523
+ }
524
+ return;
525
+ }
526
+ if (target.type !== "launcher-folder") return;
527
+ if (item.sourceId !== target.id && item.sourceType === "launcher-folder") {
528
+ removeAppFromFolder(item.id, item.sourceId);
529
+ }
530
+ addAppToFolder(item.id, target.id);
531
+ },
532
+ [addAppToFolder, removeAppFromFolder]
533
+ );
534
+ return /* @__PURE__ */ jsxs(DragDropProvider, { onDrop: handleDrop, children: [
535
+ /* @__PURE__ */ jsx(MenuEditAppContent, { ...props }),
536
+ /* @__PURE__ */ jsx(DragOverlayComponent, {})
537
+ ] });
538
+ };
539
+ var MenuEditApp_default2 = MenuEditApp;
540
+
541
+ export { DragDropProvider, MenuEditApp_default2 as MenuEditApp_default, useDragDropState, useDraggableItem, useDroppableArea, useFolderName };