@fgv/ts-res-ui-components 5.0.0-10

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 (231) hide show
  1. package/.rush/temp/03c8b056281d9db0a97d8a6e25eea798a160d393.tar.log +271 -0
  2. package/.rush/temp/chunked-rush-logs/ts-res-ui-components.build.chunks.jsonl +9 -0
  3. package/.rush/temp/operation/build/all.log +9 -0
  4. package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
  5. package/.rush/temp/operation/build/state.json +3 -0
  6. package/.rush/temp/shrinkwrap-deps.json +1111 -0
  7. package/README.md +18 -0
  8. package/REFACTORING_PLAN.md +171 -0
  9. package/config/jest.config.json +16 -0
  10. package/config/jest.setup.js +64 -0
  11. package/config/rig.json +16 -0
  12. package/lib/components/common/QualifierContextControl.d.ts +14 -0
  13. package/lib/components/common/QualifierContextControl.d.ts.map +1 -0
  14. package/lib/components/common/QualifierContextControl.js +78 -0
  15. package/lib/components/common/QualifierContextControl.js.map +1 -0
  16. package/lib/components/common/ResourceListView.d.ts +11 -0
  17. package/lib/components/common/ResourceListView.d.ts.map +1 -0
  18. package/lib/components/common/ResourceListView.js +20 -0
  19. package/lib/components/common/ResourceListView.js.map +1 -0
  20. package/lib/components/common/ResourceTreeView.d.ts +12 -0
  21. package/lib/components/common/ResourceTreeView.d.ts.map +1 -0
  22. package/lib/components/common/ResourceTreeView.js +162 -0
  23. package/lib/components/common/ResourceTreeView.js.map +1 -0
  24. package/lib/components/forms/HierarchyEditor.d.ts +10 -0
  25. package/lib/components/forms/HierarchyEditor.d.ts.map +1 -0
  26. package/lib/components/forms/HierarchyEditor.js +106 -0
  27. package/lib/components/forms/HierarchyEditor.js.map +1 -0
  28. package/lib/components/forms/QualifierEditForm.d.ts +11 -0
  29. package/lib/components/forms/QualifierEditForm.d.ts.map +1 -0
  30. package/lib/components/forms/QualifierEditForm.js +181 -0
  31. package/lib/components/forms/QualifierEditForm.js.map +1 -0
  32. package/lib/components/forms/QualifierTypeEditForm.d.ts +10 -0
  33. package/lib/components/forms/QualifierTypeEditForm.d.ts.map +1 -0
  34. package/lib/components/forms/QualifierTypeEditForm.js +172 -0
  35. package/lib/components/forms/QualifierTypeEditForm.js.map +1 -0
  36. package/lib/components/forms/ResourceTypeEditForm.d.ts +10 -0
  37. package/lib/components/forms/ResourceTypeEditForm.d.ts.map +1 -0
  38. package/lib/components/forms/ResourceTypeEditForm.js +188 -0
  39. package/lib/components/forms/ResourceTypeEditForm.js.map +1 -0
  40. package/lib/components/forms/index.d.ts +9 -0
  41. package/lib/components/forms/index.d.ts.map +1 -0
  42. package/lib/components/forms/index.js +5 -0
  43. package/lib/components/forms/index.js.map +1 -0
  44. package/lib/components/orchestrator/ResourceOrchestrator.d.ts +14 -0
  45. package/lib/components/orchestrator/ResourceOrchestrator.d.ts.map +1 -0
  46. package/lib/components/orchestrator/ResourceOrchestrator.js +278 -0
  47. package/lib/components/orchestrator/ResourceOrchestrator.js.map +1 -0
  48. package/lib/components/views/CompiledView/index.d.ts +5 -0
  49. package/lib/components/views/CompiledView/index.d.ts.map +1 -0
  50. package/lib/components/views/CompiledView/index.js +595 -0
  51. package/lib/components/views/CompiledView/index.js.map +1 -0
  52. package/lib/components/views/ConfigurationView/index.d.ts +5 -0
  53. package/lib/components/views/ConfigurationView/index.d.ts.map +1 -0
  54. package/lib/components/views/ConfigurationView/index.js +363 -0
  55. package/lib/components/views/ConfigurationView/index.js.map +1 -0
  56. package/lib/components/views/FilterView/index.d.ts +5 -0
  57. package/lib/components/views/FilterView/index.d.ts.map +1 -0
  58. package/lib/components/views/FilterView/index.js +463 -0
  59. package/lib/components/views/FilterView/index.js.map +1 -0
  60. package/lib/components/views/ImportView/index.d.ts +5 -0
  61. package/lib/components/views/ImportView/index.d.ts.map +1 -0
  62. package/lib/components/views/ImportView/index.js +514 -0
  63. package/lib/components/views/ImportView/index.js.map +1 -0
  64. package/lib/components/views/ResolutionView/EditableJsonView.d.ts +21 -0
  65. package/lib/components/views/ResolutionView/EditableJsonView.d.ts.map +1 -0
  66. package/lib/components/views/ResolutionView/EditableJsonView.js +109 -0
  67. package/lib/components/views/ResolutionView/EditableJsonView.js.map +1 -0
  68. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts +19 -0
  69. package/lib/components/views/ResolutionView/ResolutionEditControls.d.ts.map +1 -0
  70. package/lib/components/views/ResolutionView/ResolutionEditControls.js +82 -0
  71. package/lib/components/views/ResolutionView/ResolutionEditControls.js.map +1 -0
  72. package/lib/components/views/ResolutionView/index.d.ts +5 -0
  73. package/lib/components/views/ResolutionView/index.d.ts.map +1 -0
  74. package/lib/components/views/ResolutionView/index.js +255 -0
  75. package/lib/components/views/ResolutionView/index.js.map +1 -0
  76. package/lib/components/views/SourceView/index.d.ts +5 -0
  77. package/lib/components/views/SourceView/index.d.ts.map +1 -0
  78. package/lib/components/views/SourceView/index.js +316 -0
  79. package/lib/components/views/SourceView/index.js.map +1 -0
  80. package/lib/components/views/ZipLoaderView/index.d.ts +5 -0
  81. package/lib/components/views/ZipLoaderView/index.d.ts.map +1 -0
  82. package/lib/components/views/ZipLoaderView/index.js +313 -0
  83. package/lib/components/views/ZipLoaderView/index.js.map +1 -0
  84. package/lib/hooks/useConfigurationState.d.ts +46 -0
  85. package/lib/hooks/useConfigurationState.d.ts.map +1 -0
  86. package/lib/hooks/useConfigurationState.js +239 -0
  87. package/lib/hooks/useConfigurationState.js.map +1 -0
  88. package/lib/hooks/useFilterState.d.ts +7 -0
  89. package/lib/hooks/useFilterState.d.ts.map +1 -0
  90. package/lib/hooks/useFilterState.js +80 -0
  91. package/lib/hooks/useFilterState.js.map +1 -0
  92. package/lib/hooks/useResolutionState.d.ts +8 -0
  93. package/lib/hooks/useResolutionState.d.ts.map +1 -0
  94. package/lib/hooks/useResolutionState.js +253 -0
  95. package/lib/hooks/useResolutionState.js.map +1 -0
  96. package/lib/hooks/useResourceData.d.ts +19 -0
  97. package/lib/hooks/useResourceData.d.ts.map +1 -0
  98. package/lib/hooks/useResourceData.js +368 -0
  99. package/lib/hooks/useResourceData.js.map +1 -0
  100. package/lib/hooks/useViewState.d.ts +10 -0
  101. package/lib/hooks/useViewState.d.ts.map +1 -0
  102. package/lib/hooks/useViewState.js +29 -0
  103. package/lib/hooks/useViewState.js.map +1 -0
  104. package/lib/index.d.ts +27 -0
  105. package/lib/index.d.ts.map +1 -0
  106. package/lib/index.js +34 -0
  107. package/lib/index.js.map +1 -0
  108. package/lib/test/helpers/testDataLoader.d.ts +37 -0
  109. package/lib/test/helpers/testDataLoader.d.ts.map +1 -0
  110. package/lib/test/helpers/testDataLoader.js +171 -0
  111. package/lib/test/helpers/testDataLoader.js.map +1 -0
  112. package/lib/test/unit/utils/configurationUtils.test.d.ts +2 -0
  113. package/lib/test/unit/utils/configurationUtils.test.d.ts.map +1 -0
  114. package/lib/test/unit/utils/configurationUtils.test.js +497 -0
  115. package/lib/test/unit/utils/configurationUtils.test.js.map +1 -0
  116. package/lib/test/unit/utils/fileProcessing.test.d.ts +2 -0
  117. package/lib/test/unit/utils/fileProcessing.test.d.ts.map +1 -0
  118. package/lib/test/unit/utils/fileProcessing.test.js +321 -0
  119. package/lib/test/unit/utils/fileProcessing.test.js.map +1 -0
  120. package/lib/test/unit/utils/filterResources.test.d.ts +2 -0
  121. package/lib/test/unit/utils/filterResources.test.d.ts.map +1 -0
  122. package/lib/test/unit/utils/filterResources.test.js +403 -0
  123. package/lib/test/unit/utils/filterResources.test.js.map +1 -0
  124. package/lib/test/unit/utils/resolutionEditing.test.d.ts +2 -0
  125. package/lib/test/unit/utils/resolutionEditing.test.d.ts.map +1 -0
  126. package/lib/test/unit/utils/resolutionEditing.test.js +439 -0
  127. package/lib/test/unit/utils/resolutionEditing.test.js.map +1 -0
  128. package/lib/test/unit/utils/resolutionUtils.test.d.ts +2 -0
  129. package/lib/test/unit/utils/resolutionUtils.test.d.ts.map +1 -0
  130. package/lib/test/unit/utils/resolutionUtils.test.js +397 -0
  131. package/lib/test/unit/utils/resolutionUtils.test.js.map +1 -0
  132. package/lib/test/unit/utils/tsResIntegration.test.d.ts +2 -0
  133. package/lib/test/unit/utils/tsResIntegration.test.d.ts.map +1 -0
  134. package/lib/test/unit/utils/tsResIntegration.test.js +376 -0
  135. package/lib/test/unit/utils/tsResIntegration.test.js.map +1 -0
  136. package/lib/types/index.d.ts +251 -0
  137. package/lib/types/index.d.ts.map +1 -0
  138. package/lib/types/index.js +2 -0
  139. package/lib/types/index.js.map +1 -0
  140. package/lib/utils/configurationUtils.d.ts +74 -0
  141. package/lib/utils/configurationUtils.d.ts.map +1 -0
  142. package/lib/utils/configurationUtils.js +359 -0
  143. package/lib/utils/configurationUtils.js.map +1 -0
  144. package/lib/utils/fileProcessing.d.ts +18 -0
  145. package/lib/utils/fileProcessing.d.ts.map +1 -0
  146. package/lib/utils/fileProcessing.js +142 -0
  147. package/lib/utils/fileProcessing.js.map +1 -0
  148. package/lib/utils/filterResources.d.ts +38 -0
  149. package/lib/utils/filterResources.d.ts.map +1 -0
  150. package/lib/utils/filterResources.js +153 -0
  151. package/lib/utils/filterResources.js.map +1 -0
  152. package/lib/utils/resolutionEditing.d.ts +58 -0
  153. package/lib/utils/resolutionEditing.d.ts.map +1 -0
  154. package/lib/utils/resolutionEditing.js +246 -0
  155. package/lib/utils/resolutionEditing.js.map +1 -0
  156. package/lib/utils/resolutionUtils.d.ts +28 -0
  157. package/lib/utils/resolutionUtils.d.ts.map +1 -0
  158. package/lib/utils/resolutionUtils.js +216 -0
  159. package/lib/utils/resolutionUtils.js.map +1 -0
  160. package/lib/utils/tsResIntegration.d.ts +71 -0
  161. package/lib/utils/tsResIntegration.d.ts.map +1 -0
  162. package/lib/utils/tsResIntegration.js +294 -0
  163. package/lib/utils/tsResIntegration.js.map +1 -0
  164. package/lib/utils/zipLoader/browserZipLoader.d.ts +48 -0
  165. package/lib/utils/zipLoader/browserZipLoader.d.ts.map +1 -0
  166. package/lib/utils/zipLoader/browserZipLoader.js +247 -0
  167. package/lib/utils/zipLoader/browserZipLoader.js.map +1 -0
  168. package/lib/utils/zipLoader/index.d.ts +8 -0
  169. package/lib/utils/zipLoader/index.d.ts.map +1 -0
  170. package/lib/utils/zipLoader/index.js +13 -0
  171. package/lib/utils/zipLoader/index.js.map +1 -0
  172. package/lib/utils/zipLoader/nodeZipBuilder.d.ts +55 -0
  173. package/lib/utils/zipLoader/nodeZipBuilder.d.ts.map +1 -0
  174. package/lib/utils/zipLoader/nodeZipBuilder.js +98 -0
  175. package/lib/utils/zipLoader/nodeZipBuilder.js.map +1 -0
  176. package/lib/utils/zipLoader/types.d.ts +139 -0
  177. package/lib/utils/zipLoader/types.d.ts.map +1 -0
  178. package/lib/utils/zipLoader/types.js +2 -0
  179. package/lib/utils/zipLoader/types.js.map +1 -0
  180. package/lib/utils/zipLoader/zipUtils.d.ts +53 -0
  181. package/lib/utils/zipLoader/zipUtils.d.ts.map +1 -0
  182. package/lib/utils/zipLoader/zipUtils.js +229 -0
  183. package/lib/utils/zipLoader/zipUtils.js.map +1 -0
  184. package/package.json +69 -0
  185. package/rush-logs/ts-res-ui-components.build.cache.log +3 -0
  186. package/rush-logs/ts-res-ui-components.build.log +9 -0
  187. package/src/components/common/QualifierContextControl.tsx +151 -0
  188. package/src/components/common/ResourceListView.tsx +63 -0
  189. package/src/components/common/ResourceTreeView.tsx +271 -0
  190. package/src/components/forms/HierarchyEditor.tsx +204 -0
  191. package/src/components/forms/QualifierEditForm.tsx +355 -0
  192. package/src/components/forms/QualifierTypeEditForm.tsx +347 -0
  193. package/src/components/forms/ResourceTypeEditForm.tsx +331 -0
  194. package/src/components/forms/index.ts +11 -0
  195. package/src/components/orchestrator/ResourceOrchestrator.tsx +372 -0
  196. package/src/components/views/CompiledView/index.tsx +922 -0
  197. package/src/components/views/ConfigurationView/index.tsx +800 -0
  198. package/src/components/views/FilterView/index.tsx +825 -0
  199. package/src/components/views/ImportView/index.tsx +717 -0
  200. package/src/components/views/ResolutionView/EditableJsonView.tsx +214 -0
  201. package/src/components/views/ResolutionView/ResolutionEditControls.tsx +170 -0
  202. package/src/components/views/ResolutionView/index.tsx +591 -0
  203. package/src/components/views/SourceView/index.tsx +536 -0
  204. package/src/components/views/ZipLoaderView/index.tsx +485 -0
  205. package/src/hooks/useConfigurationState.ts +374 -0
  206. package/src/hooks/useFilterState.ts +97 -0
  207. package/src/hooks/useResolutionState.ts +355 -0
  208. package/src/hooks/useResourceData.ts +467 -0
  209. package/src/hooks/useViewState.ts +44 -0
  210. package/src/index.ts +45 -0
  211. package/src/test/helpers/testDataLoader.ts +195 -0
  212. package/src/test/unit/utils/configurationUtils.test.ts +630 -0
  213. package/src/test/unit/utils/fileProcessing.test.ts +391 -0
  214. package/src/test/unit/utils/filterResources.test.ts +574 -0
  215. package/src/test/unit/utils/resolutionEditing.test.ts +556 -0
  216. package/src/test/unit/utils/resolutionUtils.test.ts +521 -0
  217. package/src/test/unit/utils/tsResIntegration.test.ts +433 -0
  218. package/src/types/index.ts +322 -0
  219. package/src/utils/configurationUtils.ts +424 -0
  220. package/src/utils/fileProcessing.ts +160 -0
  221. package/src/utils/filterResources.ts +206 -0
  222. package/src/utils/resolutionEditing.ts +319 -0
  223. package/src/utils/resolutionUtils.ts +289 -0
  224. package/src/utils/tsResIntegration.ts +440 -0
  225. package/src/utils/zipLoader/browserZipLoader.ts +319 -0
  226. package/src/utils/zipLoader/index.ts +26 -0
  227. package/src/utils/zipLoader/nodeZipBuilder.ts +153 -0
  228. package/src/utils/zipLoader/types.ts +175 -0
  229. package/src/utils/zipLoader/zipUtils.ts +266 -0
  230. package/temp/build/typescript/ts_gZid87Hu.json +1 -0
  231. package/tsconfig.json +15 -0
@@ -0,0 +1,485 @@
1
+ import React, { useState, useCallback, useRef } from 'react';
2
+ import {
3
+ DocumentArrowDownIcon,
4
+ FolderOpenIcon,
5
+ CheckCircleIcon,
6
+ ExclamationTriangleIcon,
7
+ InformationCircleIcon,
8
+ ArrowPathIcon
9
+ } from '@heroicons/react/24/outline';
10
+ import { ZipLoaderViewProps } from '../../../types';
11
+ import {
12
+ createBrowserZipLoader,
13
+ ZipLoadOptions,
14
+ ZipLoadResult,
15
+ ZipLoadingStage,
16
+ formatFileSize,
17
+ isZipFile
18
+ } from '../../../utils/zipLoader';
19
+
20
+ interface LoadingState {
21
+ isLoading: boolean;
22
+ stage: ZipLoadingStage | null;
23
+ progress: number;
24
+ message: string;
25
+ }
26
+
27
+ interface LoadedState {
28
+ result: ZipLoadResult | null;
29
+ error: string | null;
30
+ fileName: string | null;
31
+ fileSize: number | null;
32
+ }
33
+
34
+ export const ZipLoaderView: React.FC<ZipLoaderViewProps> = ({
35
+ zipFileUrl,
36
+ zipPath,
37
+ onImport,
38
+ onConfigurationLoad,
39
+ onLoadComplete,
40
+ onMessage,
41
+ className = ''
42
+ }) => {
43
+ const [loadingState, setLoadingState] = useState<LoadingState>({
44
+ isLoading: false,
45
+ stage: null,
46
+ progress: 0,
47
+ message: ''
48
+ });
49
+
50
+ const [loadedState, setLoadedState] = useState<LoadedState>({
51
+ result: null,
52
+ error: null,
53
+ fileName: null,
54
+ fileSize: null
55
+ });
56
+
57
+ const fileInputRef = useRef<HTMLInputElement>(null);
58
+
59
+ const handleDragOver = useCallback((e: React.DragEvent) => {
60
+ e.preventDefault();
61
+ e.stopPropagation();
62
+ }, []);
63
+
64
+ // Load ZIP file with progress tracking
65
+ const loadZipFileInternal = useCallback(
66
+ async (file: File) => {
67
+ setLoadingState({
68
+ isLoading: true,
69
+ stage: 'reading-file',
70
+ progress: 0,
71
+ message: 'Starting to load ZIP file...'
72
+ });
73
+
74
+ setLoadedState({
75
+ result: null,
76
+ error: null,
77
+ fileName: file.name,
78
+ fileSize: file.size
79
+ });
80
+
81
+ const options: ZipLoadOptions = {
82
+ autoApplyConfig: false,
83
+ autoProcessResources: false
84
+ };
85
+
86
+ const progressCallback = (stage: ZipLoadingStage, progress: number, message?: string) => {
87
+ setLoadingState({
88
+ isLoading: stage !== 'complete',
89
+ stage,
90
+ progress,
91
+ message: message || getStageMessage(stage)
92
+ });
93
+ };
94
+
95
+ const loader = createBrowserZipLoader();
96
+ const result = await loader.loadFromFile(file, options, progressCallback);
97
+
98
+ if (result.isSuccess()) {
99
+ const zipResult = result.value;
100
+
101
+ setLoadedState((prev) => ({
102
+ ...prev,
103
+ result: zipResult,
104
+ error: null
105
+ }));
106
+
107
+ setLoadingState({
108
+ isLoading: false,
109
+ stage: 'complete',
110
+ progress: 100,
111
+ message: 'ZIP file loaded successfully'
112
+ });
113
+
114
+ onMessage?.('success', `Loaded ZIP file: ${file.name}`);
115
+
116
+ // Load configuration if found
117
+ if (zipResult.config) {
118
+ onConfigurationLoad?.(zipResult.config);
119
+ onMessage?.('info', `Configuration loaded from ZIP`);
120
+ }
121
+
122
+ // Import the raw data for processing by the orchestrator
123
+ if (zipResult.directory) {
124
+ onImport?.(zipResult.directory);
125
+ } else if (zipResult.files.length > 0) {
126
+ onImport?.(zipResult.files);
127
+ }
128
+
129
+ onLoadComplete?.();
130
+ } else {
131
+ const errorMessage = result.message;
132
+ setLoadedState((prev) => ({
133
+ ...prev,
134
+ error: errorMessage,
135
+ result: null
136
+ }));
137
+
138
+ setLoadingState({
139
+ isLoading: false,
140
+ stage: null,
141
+ progress: 0,
142
+ message: ''
143
+ });
144
+
145
+ onMessage?.('error', `Failed to load ZIP: ${errorMessage}`);
146
+ }
147
+ },
148
+ [onMessage, onLoadComplete]
149
+ );
150
+
151
+ // Load ZIP from URL
152
+ const loadFromUrl = useCallback(
153
+ async (url: string) => {
154
+ setLoadingState({
155
+ isLoading: true,
156
+ stage: 'reading-file',
157
+ progress: 0,
158
+ message: 'Fetching ZIP from URL...'
159
+ });
160
+
161
+ setLoadedState({
162
+ result: null,
163
+ error: null,
164
+ fileName: url.split('/').pop() || 'remote.zip',
165
+ fileSize: null
166
+ });
167
+
168
+ const options: ZipLoadOptions = {
169
+ autoApplyConfig: false,
170
+ autoProcessResources: false
171
+ };
172
+
173
+ const progressCallback = (stage: ZipLoadingStage, progress: number, message?: string) => {
174
+ setLoadingState({
175
+ isLoading: stage !== 'complete',
176
+ stage,
177
+ progress,
178
+ message: message || getStageMessage(stage)
179
+ });
180
+ };
181
+
182
+ const loader = createBrowserZipLoader();
183
+ const result = await loader.loadFromUrl(url, options, progressCallback);
184
+
185
+ if (result.isSuccess()) {
186
+ const zipResult = result.value;
187
+
188
+ setLoadedState((prev) => ({
189
+ ...prev,
190
+ result: zipResult,
191
+ error: null
192
+ }));
193
+
194
+ setLoadingState({
195
+ isLoading: false,
196
+ stage: 'complete',
197
+ progress: 100,
198
+ message: 'ZIP file loaded successfully'
199
+ });
200
+
201
+ onMessage?.('success', `Loaded ZIP from URL: ${url}`);
202
+
203
+ // Load configuration if found
204
+ if (zipResult.config) {
205
+ onConfigurationLoad?.(zipResult.config);
206
+ onMessage?.('info', `Configuration loaded from ZIP`);
207
+ }
208
+
209
+ // Import the raw data for processing by the orchestrator
210
+ if (zipResult.directory) {
211
+ onImport?.(zipResult.directory);
212
+ } else if (zipResult.files.length > 0) {
213
+ onImport?.(zipResult.files);
214
+ }
215
+
216
+ onLoadComplete?.();
217
+ } else {
218
+ const errorMessage = result.message;
219
+ setLoadedState((prev) => ({
220
+ ...prev,
221
+ error: errorMessage,
222
+ result: null
223
+ }));
224
+
225
+ setLoadingState({
226
+ isLoading: false,
227
+ stage: null,
228
+ progress: 0,
229
+ message: ''
230
+ });
231
+
232
+ onMessage?.('error', `Failed to load ZIP from URL: ${errorMessage}`);
233
+ }
234
+ },
235
+ [onMessage, onLoadComplete]
236
+ );
237
+
238
+ // Handle file selection - now defined after loadZipFileInternal
239
+ const handleFileSelect = useCallback(
240
+ async (files: FileList | null) => {
241
+ if (!files || files.length === 0) return;
242
+
243
+ const file = files[0];
244
+ if (!isZipFile(file.name)) {
245
+ setLoadedState((prev) => ({
246
+ ...prev,
247
+ error: `Selected file "${file.name}" is not a ZIP file`,
248
+ result: null
249
+ }));
250
+ onMessage?.('error', `File "${file.name}" is not a ZIP file`);
251
+ return;
252
+ }
253
+
254
+ await loadZipFileInternal(file);
255
+ },
256
+ [loadZipFileInternal, onMessage]
257
+ );
258
+
259
+ // Handle drag and drop - now defined after handleFileSelect
260
+ const handleDrop = useCallback(
261
+ async (e: React.DragEvent) => {
262
+ e.preventDefault();
263
+ e.stopPropagation();
264
+
265
+ const files = e.dataTransfer.files;
266
+ await handleFileSelect(files);
267
+ },
268
+ [handleFileSelect]
269
+ );
270
+
271
+ // Auto-load from URL parameter
272
+ React.useEffect(() => {
273
+ if (zipFileUrl && !loadingState.isLoading && !loadedState.result && !loadedState.error) {
274
+ loadFromUrl(zipFileUrl);
275
+ }
276
+ }, [zipFileUrl, loadFromUrl, loadingState.isLoading, loadedState.result, loadedState.error]);
277
+
278
+ const getStageMessage = (stage: ZipLoadingStage): string => {
279
+ switch (stage) {
280
+ case 'reading-file':
281
+ return 'Reading ZIP file...';
282
+ case 'parsing-zip':
283
+ return 'Parsing ZIP archive...';
284
+ case 'loading-manifest':
285
+ return 'Loading manifest...';
286
+ case 'loading-config':
287
+ return 'Loading configuration...';
288
+ case 'extracting-files':
289
+ return 'Extracting files...';
290
+ case 'processing-resources':
291
+ return 'Processing resources...';
292
+ case 'complete':
293
+ return 'Complete!';
294
+ default:
295
+ return 'Processing...';
296
+ }
297
+ };
298
+
299
+ const getStageIcon = (stage: ZipLoadingStage | null) => {
300
+ switch (stage) {
301
+ case 'complete':
302
+ return CheckCircleIcon;
303
+ case null:
304
+ return DocumentArrowDownIcon;
305
+ default:
306
+ return ArrowPathIcon;
307
+ }
308
+ };
309
+
310
+ return (
311
+ <div className={`p-6 ${className}`}>
312
+ <div className="flex items-center space-x-3 mb-6">
313
+ <DocumentArrowDownIcon className="h-8 w-8 text-blue-600" />
314
+ <h2 className="text-2xl font-bold text-gray-900">ZIP Loader</h2>
315
+ </div>
316
+
317
+ <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
318
+ {/* URL Loading Section */}
319
+ {zipFileUrl && !loadedState.result && !loadedState.error && (
320
+ <div className="mb-6 p-4 bg-blue-50 rounded-lg">
321
+ <h3 className="text-lg font-medium text-blue-900 mb-2">Loading from URL</h3>
322
+ <p className="text-sm text-blue-700 break-all">{zipFileUrl}</p>
323
+ </div>
324
+ )}
325
+
326
+ {/* File Upload Area */}
327
+ {!loadingState.isLoading && !loadedState.result && (
328
+ <div
329
+ className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:border-gray-400 transition-colors cursor-pointer"
330
+ onDrop={handleDrop}
331
+ onDragOver={handleDragOver}
332
+ onClick={() => fileInputRef.current?.click()}
333
+ >
334
+ <FolderOpenIcon className="h-12 w-12 text-gray-400 mx-auto mb-4" />
335
+ <h3 className="text-lg font-medium text-gray-900 mb-2">Select or Drop ZIP File</h3>
336
+ <p className="text-gray-600 mb-4">Choose a ZIP file containing resources to load and process</p>
337
+ <button
338
+ type="button"
339
+ className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
340
+ >
341
+ <DocumentArrowDownIcon className="w-4 h-4 mr-2" />
342
+ Browse Files
343
+ </button>
344
+ <input
345
+ ref={fileInputRef}
346
+ type="file"
347
+ accept=".zip"
348
+ onChange={(e) => handleFileSelect(e.target.files)}
349
+ className="hidden"
350
+ />
351
+ </div>
352
+ )}
353
+
354
+ {/* Loading Progress */}
355
+ {loadingState.isLoading && (
356
+ <div className="mb-6">
357
+ <div className="flex items-center justify-between mb-2">
358
+ <div className="flex items-center space-x-2">
359
+ {React.createElement(getStageIcon(loadingState.stage), {
360
+ className: `w-5 h-5 ${
361
+ loadingState.stage === 'complete' ? 'text-green-500' : 'text-blue-500 animate-spin'
362
+ }`
363
+ })}
364
+ <span className="text-sm font-medium text-gray-900">{loadingState.message}</span>
365
+ </div>
366
+ <span className="text-sm text-gray-500">{loadingState.progress}%</span>
367
+ </div>
368
+ <div className="w-full bg-gray-200 rounded-full h-2">
369
+ <div
370
+ className="bg-blue-600 h-2 rounded-full transition-all duration-300"
371
+ style={{ width: `${loadingState.progress}%` }}
372
+ />
373
+ </div>
374
+ </div>
375
+ )}
376
+
377
+ {/* Loaded Content Summary */}
378
+ {loadedState.result && (
379
+ <div className="space-y-4">
380
+ <div className="flex items-center space-x-2 text-green-600">
381
+ <CheckCircleIcon className="w-5 h-5" />
382
+ <span className="font-medium">ZIP file loaded successfully</span>
383
+ </div>
384
+
385
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
386
+ <div className="bg-gray-50 rounded-lg p-4">
387
+ <h4 className="font-medium text-gray-900 mb-2">File Information</h4>
388
+ <div className="space-y-1 text-sm">
389
+ <div>
390
+ <strong>Name:</strong> {loadedState.fileName}
391
+ </div>
392
+ {loadedState.fileSize && (
393
+ <div>
394
+ <strong>Size:</strong> {formatFileSize(loadedState.fileSize)}
395
+ </div>
396
+ )}
397
+ <div>
398
+ <strong>Files:</strong> {loadedState.result.files.length}
399
+ </div>
400
+ </div>
401
+ </div>
402
+
403
+ <div className="bg-gray-50 rounded-lg p-4">
404
+ <h4 className="font-medium text-gray-900 mb-2">Content</h4>
405
+ <div className="space-y-1 text-sm">
406
+ {loadedState.result.manifest && (
407
+ <div className="flex items-center space-x-1">
408
+ <CheckCircleIcon className="w-4 h-4 text-green-500" />
409
+ <span>Manifest found</span>
410
+ </div>
411
+ )}
412
+ {loadedState.result.config && (
413
+ <div className="flex items-center space-x-1">
414
+ <CheckCircleIcon className="w-4 h-4 text-green-500" />
415
+ <span>Configuration found</span>
416
+ </div>
417
+ )}
418
+ {loadedState.result.processedResources && (
419
+ <div className="flex items-center space-x-1">
420
+ <CheckCircleIcon className="w-4 h-4 text-green-500" />
421
+ <span>Resources processed</span>
422
+ </div>
423
+ )}
424
+ </div>
425
+ </div>
426
+ </div>
427
+
428
+ {loadedState.result.manifest && (
429
+ <div className="bg-blue-50 rounded-lg p-4">
430
+ <h4 className="font-medium text-blue-900 mb-2">Archive Information</h4>
431
+ <div className="text-sm text-blue-800">
432
+ <div>
433
+ <strong>Created:</strong>{' '}
434
+ {new Date(loadedState.result.manifest.timestamp).toLocaleString()}
435
+ </div>
436
+ {loadedState.result.manifest.input && (
437
+ <div>
438
+ <strong>Source:</strong> {loadedState.result.manifest.input.type} -{' '}
439
+ {loadedState.result.manifest.input.originalPath}
440
+ </div>
441
+ )}
442
+ </div>
443
+ </div>
444
+ )}
445
+ </div>
446
+ )}
447
+
448
+ {/* Error Display */}
449
+ {loadedState.error && (
450
+ <div className="bg-red-50 border border-red-200 rounded-lg p-4">
451
+ <div className="flex items-center space-x-2 text-red-600 mb-2">
452
+ <ExclamationTriangleIcon className="w-5 h-5" />
453
+ <span className="font-medium">Error Loading ZIP</span>
454
+ </div>
455
+ <p className="text-sm text-red-700">{loadedState.error}</p>
456
+ <button
457
+ onClick={() => setLoadedState((prev) => ({ ...prev, error: null }))}
458
+ className="mt-3 inline-flex items-center px-3 py-1.5 border border-transparent text-xs font-medium rounded text-red-700 bg-red-100 hover:bg-red-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
459
+ >
460
+ Try Again
461
+ </button>
462
+ </div>
463
+ )}
464
+
465
+ {/* Help Information */}
466
+ {!loadingState.isLoading && !loadedState.result && !loadedState.error && (
467
+ <div className="mt-6 bg-blue-50 rounded-lg p-4">
468
+ <div className="flex items-start space-x-2">
469
+ <InformationCircleIcon className="w-5 h-5 text-blue-500 mt-0.5 flex-shrink-0" />
470
+ <div className="text-sm text-blue-800">
471
+ <p className="font-medium mb-1">ZIP Loader</p>
472
+ <p>
473
+ Load ZIP archives containing resource files and configurations. The loader supports
474
+ automatic configuration application and resource processing.
475
+ </p>
476
+ </div>
477
+ </div>
478
+ </div>
479
+ )}
480
+ </div>
481
+ </div>
482
+ );
483
+ };
484
+
485
+ export default ZipLoaderView;